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
74 changes: 49 additions & 25 deletions contracts/base-token/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub enum DataKey {
Admin,
Allowance(Address, Address), // (from, spender)
Balance(Address),
Metadata, // TokenMetadata struct
Metadata, // TokenMetadata struct
FeeConfig, // FeeConfiguration struct
}

Expand All @@ -33,14 +33,19 @@ pub struct FeeConfiguration {
}

fn read_balance(env: &Env, address: &Address) -> i128 {
env.storage().persistent().get(&DataKey::Balance(address.clone())).unwrap_or(0)
env.storage()
.persistent()
.get(&DataKey::Balance(address.clone()))
.unwrap_or(0)
}

fn write_balance(env: &Env, address: &Address, amount: i128) {
if amount == 0 {
return; // optimization
}
env.storage().persistent().set(&DataKey::Balance(address.clone()), &amount);
env.storage()
.persistent()
.set(&DataKey::Balance(address.clone()), &amount);
}

fn read_allowance(env: &Env, from: &Address, spender: &Address) -> i128 {
Expand Down Expand Up @@ -74,12 +79,16 @@ impl BaseToken {
if env.storage().instance().has(&DataKey::Admin) {
panic!("Already initialized");
}

env.storage().instance().set(&DataKey::Admin, &admin);

let metadata = TokenMetadata { decimal, name, symbol };
let metadata = TokenMetadata {
decimal,
name,
symbol,
};
env.storage().instance().set(&DataKey::Metadata, &metadata);

if fee_basis_points > 10000 {
panic!("Fee basis points cannot exceed 10000");
}
Expand All @@ -88,33 +97,44 @@ impl BaseToken {
recipient: fee_recipient,
fee_basis_points,
};
env.storage().instance().set(&DataKey::FeeConfig, &fee_config);
env.storage()
.instance()
.set(&DataKey::FeeConfig, &fee_config);
}

/// Admin can update fee configuration
pub fn set_fee_config(env: Env, recipient: Address, fee_basis_points: u32) {
let admin: Address = env.storage().instance().get(&DataKey::Admin).expect("Not initialized");
let admin: Address = env
.storage()
.instance()
.get(&DataKey::Admin)
.expect("Not initialized");
admin.require_auth();

if fee_basis_points > 10000 {
panic!("Fee basis points cannot exceed 10000");
}

let fee_config = FeeConfiguration { recipient, fee_basis_points };
env.storage().instance().set(&DataKey::FeeConfig, &fee_config);

let fee_config = FeeConfiguration {
recipient,
fee_basis_points,
};
env.storage()
.instance()
.set(&DataKey::FeeConfig, &fee_config);
}

pub fn mint(env: Env, to: Address, amount: i128) {
if amount < 0 {
panic!("Negative amount");
}

let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap();
admin.require_auth();

let bal = read_balance(&env, &to);
write_balance(&env, &to, bal.checked_add(amount).unwrap());

Events::new(&env).mint(admin, to, amount);
}

Expand Down Expand Up @@ -171,12 +191,12 @@ impl token::Interface for BaseToken {
if amount < 0 {
panic!("Negative amount");
}

let bal = read_balance(&env, &from);
if bal < amount {
panic!("Insufficient balance");
}

write_balance(&env, &from, bal - amount);
Events::new(&env).burn(from, amount);
}
Expand All @@ -186,17 +206,17 @@ impl token::Interface for BaseToken {
if amount < 0 {
panic!("Negative amount");
}

let allowance = read_allowance(&env, &from, &spender);
if allowance < amount {
panic!("Insufficient allowance");
}

let bal = read_balance(&env, &from);
if bal < amount {
panic!("Insufficient balance");
}

write_allowance(&env, &from, &spender, allowance - amount);
write_balance(&env, &from, bal - amount);
Events::new(&env).burn(from, amount);
Expand Down Expand Up @@ -232,15 +252,19 @@ impl BaseToken {
if bal < amount {
panic!("Insufficient balance");
}

let fee_config: FeeConfiguration = env.storage().instance().get(&DataKey::FeeConfig).unwrap();


let fee_config: FeeConfiguration =
env.storage().instance().get(&DataKey::FeeConfig).unwrap();

// Calculate fee
// fee = (amount * fee_basis_points) / 10000
let fee = (amount.checked_mul(fee_config.fee_basis_points as i128).unwrap()) / 10000;

let fee = (amount
.checked_mul(fee_config.fee_basis_points as i128)
.unwrap())
/ 10000;

// Handle dust limits carefully:
// If transfer implies a fee, but due to integer math it evaluates to 0,
// If transfer implies a fee, but due to integer math it evaluates to 0,
// AND the user is not exempt, then small literal transfers bypass the fee.
// For dust limits, we intercept this by forcing a minimum fee of 1 if fee evaluates to 0 but fee_basis_points > 0.
// Exception: if the amount is 1, a fee of 1 is 100%, which destroys the transfer.
Expand Down
2 changes: 1 addition & 1 deletion contracts/base-token/src/test.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![cfg(test)]

use super::*;
use soroban_sdk::{testutils::Address as _, Address, Env, String};
use soroban_sdk::token::Client as TokenClient;
use soroban_sdk::{testutils::Address as _, Address, Env, String};

#[test]
fn test_transfer_with_fees() {
Expand Down
18 changes: 12 additions & 6 deletions contracts/escrow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use shared::{
Amount, Dispute, DisputeResolution, DisputeStatus, EscrowInfo, Hash, JurorInfo, Milestone,
MilestoneStatus, PauseState, PendingUpgrade, VoteCommitment,
},
MIN_APPROVAL_THRESHOLD, MAX_APPROVAL_THRESHOLD,
MAX_APPROVAL_THRESHOLD, MIN_APPROVAL_THRESHOLD,
};
use soroban_sdk::{contract, contractimpl, token::TokenClient, Address, BytesN, Env, IntoVal, Vec};

Expand Down Expand Up @@ -62,7 +62,9 @@ impl EscrowContract {
return Err(Error::AlreadyInit);
}

if approval_threshold < MIN_APPROVAL_THRESHOLD || approval_threshold > MAX_APPROVAL_THRESHOLD {
if approval_threshold < MIN_APPROVAL_THRESHOLD
|| approval_threshold > MAX_APPROVAL_THRESHOLD
{
return Err(Error::InvInput);
}

Expand Down Expand Up @@ -1190,8 +1192,10 @@ impl EscrowContract {
execute_not_before: now + UPGRADE_TIME_LOCK_SECS,
};
set_pending_upgrade(&env, &pending);
env.events()
.publish((UPGRADE_SCHEDULED,), (admin, new_wasm_hash, pending.execute_not_before));
env.events().publish(
(UPGRADE_SCHEDULED,),
(admin, new_wasm_hash, pending.execute_not_before),
);
Ok(())
}

Expand All @@ -1210,9 +1214,11 @@ impl EscrowContract {
if now < pending.execute_not_before {
return Err(Error::UpgTooEarly);
}
env.deployer().update_current_contract_wasm(pending.wasm_hash.clone());
env.deployer()
.update_current_contract_wasm(pending.wasm_hash.clone());
clear_pending_upgrade(&env);
env.events().publish((UPGRADE_EXECUTED,), (admin, pending.wasm_hash));
env.events()
.publish((UPGRADE_EXECUTED,), (admin, pending.wasm_hash));
Ok(())
}

Expand Down
8 changes: 4 additions & 4 deletions contracts/escrow/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use shared::errors::Error;
use shared::types::{Amount, Dispute, EscrowInfo, JurorInfo, Milestone, PauseState, PendingUpgrade, VoteCommitment};
use shared::types::{
Amount, Dispute, EscrowInfo, JurorInfo, Milestone, PauseState, PendingUpgrade, VoteCommitment,
};
use soroban_sdk::{Address, Env, Vec};

/// Storage keys for escrow data structures
Expand Down Expand Up @@ -158,9 +160,7 @@ pub fn get_total_milestone_amount(env: &Env, project_id: u64) -> Result<Amount,

for milestone_id in 0..counter {
if let Ok(milestone) = get_milestone(env, project_id, milestone_id) {
total = total
.checked_add(milestone.amount)
.ok_or(Error::InvInput)?;
total = total.checked_add(milestone.amount).ok_or(Error::InvInput)?;
}
}

Expand Down
43 changes: 34 additions & 9 deletions contracts/escrow/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@ mod tests {

fn setup_with_admin(
env: &Env,
) -> (Address, Address, Address, Vec<Address>, EscrowContractClient) {
) -> (
Address,
Address,
Address,
Vec<Address>,
EscrowContractClient,
) {
let admin = Address::generate(env);
let creator = Address::generate(env);
let token = Address::generate(env);
Expand Down Expand Up @@ -412,7 +418,10 @@ mod tests {
env.mock_all_auths();

let result = client.try_initialize(&1, &creator, &token, &validators, &10000);
assert!(result.is_ok(), "10000 basis points (100%) should be accepted");
assert!(
result.is_ok(),
"10000 basis points (100%) should be accepted"
);
}

#[test]
Expand Down Expand Up @@ -641,7 +650,10 @@ mod tests {

let description_hash = BytesN::from_array(&env, &[1u8; 32]);
let result = client.try_create_milestone(&1, &description_hash, &500);
assert!(result.is_err(), "create_milestone should be blocked when paused");
assert!(
result.is_err(),
"create_milestone should be blocked when paused"
);
}

#[test]
Expand All @@ -658,7 +670,10 @@ mod tests {

let proof_hash = BytesN::from_array(&env, &[9u8; 32]);
let result = client.try_submit_milestone(&1, &0, &proof_hash);
assert!(result.is_err(), "submit_milestone should be blocked when paused");
assert!(
result.is_err(),
"submit_milestone should be blocked when paused"
);
}

#[test]
Expand All @@ -677,7 +692,10 @@ mod tests {

let voter = validators.get(0).unwrap();
let result = client.try_vote_milestone(&1, &0, &voter, &true);
assert!(result.is_err(), "vote_milestone should be blocked when paused");
assert!(
result.is_err(),
"vote_milestone should be blocked when paused"
);
}

#[test]
Expand All @@ -691,7 +709,10 @@ mod tests {

env.ledger().set_timestamp(1000 + 3600);
let result = client.try_resume(&admin);
assert!(result.is_err(), "resume should fail before time delay expires");
assert!(
result.is_err(),
"resume should fail before time delay expires"
);
}

#[test]
Expand Down Expand Up @@ -796,9 +817,13 @@ mod tests {
let wasm_hash = BytesN::from_array(&env, &[42u8; 32]);
client.schedule_upgrade(&admin, &wasm_hash);

env.ledger().set_timestamp(1000 + shared::UPGRADE_TIME_LOCK_SECS + 1);
env.ledger()
.set_timestamp(1000 + shared::UPGRADE_TIME_LOCK_SECS + 1);
let result = client.try_execute_upgrade(&admin);
assert!(result.is_err(), "execute_upgrade should fail when not paused");
assert!(
result.is_err(),
"execute_upgrade should fail when not paused"
);
}

#[test]
Expand Down Expand Up @@ -828,4 +853,4 @@ mod tests {
let result = client.try_schedule_upgrade(&random, &wasm_hash);
assert!(result.is_err());
}
}
}
Loading
Loading