From da72ab835b3e3d1cb0814021fbfc1beceb842a45 Mon Sep 17 00:00:00 2001
From: Fatima Aminu <81179612+phertyameen@users.noreply.github.com>
Date: Sat, 30 May 2026 10:32:15 +0100
Subject: [PATCH 1/5] Implement transfer rules with limits and validations
---
contracts/opsce/src/transfer_rules.rs | 215 ++++++++++++++++++++++++++
1 file changed, 215 insertions(+)
create mode 100644 contracts/opsce/src/transfer_rules.rs
diff --git a/contracts/opsce/src/transfer_rules.rs b/contracts/opsce/src/transfer_rules.rs
new file mode 100644
index 00000000..683c68bb
--- /dev/null
+++ b/contracts/opsce/src/transfer_rules.rs
@@ -0,0 +1,215 @@
+#![allow(unused)]
+use soroban_sdk::{contracttype, Address, BytesN, Env, Vec};
+
+#[contracttype]
+enum DataKey {
+ TransferLimits(BytesN<32>), // keyed by asset_id
+ BlockedAddresses,
+}
+
+#[contracttype]
+#[derive(Clone)]
+pub struct TransferLimits {
+ pub min: i128,
+ pub max: i128,
+}
+
+#[contracttype]
+#[derive(Clone, Debug, PartialEq)]
+pub enum ContractError {
+ SelfTransfer = 1,
+ AmountBelowMinimum = 2,
+ AmountAboveMaximum = 3,
+ RecipientBlocked = 4,
+ Unauthorized = 5,
+ InvalidLimits = 6,
+}
+
+fn require_admin(env: &Env, caller: &Address) {
+ caller.require_auth();
+}
+
+/// Admin: set min/max transfer limits for a specific asset.
+pub fn set_transfer_limits(
+ env: &Env,
+ caller: &Address,
+ asset_id: BytesN<32>,
+ min: i128,
+ max: i128,
+) -> Result<(), ContractError> {
+ require_admin(env, caller);
+ if min > max {
+ return Err(ContractError::InvalidLimits);
+ }
+ env.storage()
+ .persistent()
+ .set(&DataKey::TransferLimits(asset_id), &TransferLimits { min, max });
+ Ok(())
+}
+
+/// Admin: add an address to the blocked list.
+pub fn block_address(env: &Env, caller: &Address, address: Address) {
+ require_admin(env, caller);
+ let mut blocked: Vec
= env
+ .storage()
+ .persistent()
+ .get(&DataKey::BlockedAddresses)
+ .unwrap_or_else(|| Vec::new(env));
+
+ if !blocked.contains(&address) {
+ blocked.push_back(address);
+ }
+ env.storage()
+ .persistent()
+ .set(&DataKey::BlockedAddresses, &blocked);
+}
+
+/// Guard: call at the top of every transfer execution path.
+///
+/// Checks (in order):
+/// 1. Self-transfer
+/// 2. Recipient blocked
+/// 3. Amount below minimum (if limits configured for asset)
+/// 4. Amount above maximum (if limits configured for asset)
+pub fn validate_transfer(
+ env: &Env,
+ from: &Address,
+ to: &Address,
+ asset_id: &BytesN<32>,
+ amount: i128,
+) -> Result<(), ContractError> {
+ // 1. Self-transfer
+ if from == to {
+ return Err(ContractError::SelfTransfer);
+ }
+
+ // 2. Blocked recipient
+ let blocked: Vec = env
+ .storage()
+ .persistent()
+ .get(&DataKey::BlockedAddresses)
+ .unwrap_or_else(|| Vec::new(env));
+ if blocked.contains(to) {
+ return Err(ContractError::RecipientBlocked);
+ }
+
+ // 3 & 4. Amount limits (only enforced if limits are set for this asset)
+ if let Some(limits) = env
+ .storage()
+ .persistent()
+ .get::<_, TransferLimits>(&DataKey::TransferLimits(asset_id.clone()))
+ {
+ if amount < limits.min {
+ return Err(ContractError::AmountBelowMinimum);
+ }
+ if amount > limits.max {
+ return Err(ContractError::AmountAboveMaximum);
+ }
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use soroban_sdk::{testutils::Address as _, Address, BytesN, Env};
+
+ fn env() -> Env { Env::default() }
+ fn asset(env: &Env) -> BytesN<32> { BytesN::from_array(env, &[1u8; 32]) }
+
+ fn with_limits(env: &Env, min: i128, max: i128) -> (Address, Address, BytesN<32>) {
+ let admin = Address::generate(env);
+ let from = Address::generate(env);
+ let to = Address::generate(env);
+ let a = asset(env);
+ env.mock_all_auths();
+ set_transfer_limits(env, &admin, a.clone(), min, max).unwrap();
+ (from, to, a)
+ }
+
+ #[test]
+ fn test_happy_path() {
+ let env = env();
+ let (from, to, a) = with_limits(&env, 100, 10_000);
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 500), Ok(()));
+ }
+
+ #[test]
+ fn test_self_transfer() {
+ let env = env();
+ let addr = Address::generate(&env);
+ let a = asset(&env);
+ assert_eq!(
+ validate_transfer(&env, &addr, &addr, &a, 500),
+ Err(ContractError::SelfTransfer)
+ );
+ }
+
+ #[test]
+ fn test_amount_below_minimum() {
+ let env = env();
+ let (from, to, a) = with_limits(&env, 100, 10_000);
+ assert_eq!(
+ validate_transfer(&env, &from, &to, &a, 50),
+ Err(ContractError::AmountBelowMinimum)
+ );
+ }
+
+ #[test]
+ fn test_amount_above_maximum() {
+ let env = env();
+ let (from, to, a) = with_limits(&env, 100, 10_000);
+ assert_eq!(
+ validate_transfer(&env, &from, &to, &a, 20_000),
+ Err(ContractError::AmountAboveMaximum)
+ );
+ }
+
+ #[test]
+ fn test_recipient_blocked() {
+ let env = env();
+ let admin = Address::generate(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
+ env.mock_all_auths();
+ block_address(&env, &admin, to.clone());
+ assert_eq!(
+ validate_transfer(&env, &from, &to, &a, 500),
+ Err(ContractError::RecipientBlocked)
+ );
+ }
+
+ #[test]
+ fn test_no_limits_set_skips_amount_check() {
+ let env = env();
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
+ // No limits registered - any amount passes
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 1), Ok(()));
+ }
+
+ #[test]
+ fn test_invalid_limits_rejected() {
+ let env = env();
+ let admin = Address::generate(&env);
+ let a = asset(&env);
+ env.mock_all_auths();
+ // min > max is invalid
+ assert_eq!(
+ set_transfer_limits(&env, &admin, a, 1_000, 100),
+ Err(ContractError::InvalidLimits)
+ );
+ }
+
+ #[test]
+ fn test_boundary_values() {
+ let env = env();
+ let (from, to, a) = with_limits(&env, 100, 10_000);
+ // Exact min and max should pass
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 100), Ok(()));
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 10_000), Ok(()));
+ }
+}
From 2742caadea9b24487e5271116aa372f2f80f5c37 Mon Sep 17 00:00:00 2001
From: Fatima Aminu <81179612+phertyameen@users.noreply.github.com>
Date: Sat, 30 May 2026 10:32:48 +0100
Subject: [PATCH 2/5] Create lib.rs
---
contracts/opsce/src/lib.rs | 1 +
1 file changed, 1 insertion(+)
create mode 100644 contracts/opsce/src/lib.rs
diff --git a/contracts/opsce/src/lib.rs b/contracts/opsce/src/lib.rs
new file mode 100644
index 00000000..a9f880c7
--- /dev/null
+++ b/contracts/opsce/src/lib.rs
@@ -0,0 +1 @@
+pub mod transfer_rules;
From 7cf2363f86c25279a5ab1dbc4f38cc21079b5c99 Mon Sep 17 00:00:00 2001
From: Fatima Aminu <81179612+phertyameen@users.noreply.github.com>
Date: Sat, 30 May 2026 10:35:01 +0100
Subject: [PATCH 3/5] Add transfer validation to rules.rs
---
contracts/multisig_transfer/src/rules.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/contracts/multisig_transfer/src/rules.rs b/contracts/multisig_transfer/src/rules.rs
index 30b36912..8c81860e 100644
--- a/contracts/multisig_transfer/src/rules.rs
+++ b/contracts/multisig_transfer/src/rules.rs
@@ -1,7 +1,9 @@
use soroban_sdk::{BytesN, Env};
+use opsce::transfer_rules::validate_transfer;
use crate::{errors::MultiSigError, storage, types::ApprovalRule};
+validate_transfer(&env, &from, &to, &asset_id, amount)?;
pub fn get_rule(e: &Env, category: &BytesN<32>) -> Result {
let rules = storage::rules_map(e);
rules
From 36eb7af5f7fecf77cc749f391dbdaa917125886e Mon Sep 17 00:00:00 2001
From: phertyameen
Date: Sat, 30 May 2026 11:22:55 +0100
Subject: [PATCH 4/5] tests pass
---
contracts/Cargo.lock | 8 +
contracts/Cargo.toml | 1 +
contracts/contrib/src/lib.rs | 2 +-
contracts/multisig_transfer/Cargo.toml | 1 +
contracts/multisig_transfer/src/lib.rs | 3 +
contracts/multisig_transfer/src/rules.rs | 2 -
contracts/opsce/Cargo.toml | 14 ++
contracts/opsce/src/lib.rs | 4 +
contracts/opsce/src/transfer_rules.rs | 177 +++++++++++++++--------
9 files changed, 145 insertions(+), 67 deletions(-)
create mode 100644 contracts/opsce/Cargo.toml
diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock
index d697505f..5285db82 100644
--- a/contracts/Cargo.lock
+++ b/contracts/Cargo.lock
@@ -811,6 +811,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
name = "multisig-transfer"
version = "0.1.0"
dependencies = [
+ "opsce",
"soroban-sdk",
]
@@ -872,6 +873,13 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+[[package]]
+name = "opsce"
+version = "0.1.0"
+dependencies = [
+ "soroban-sdk",
+]
+
[[package]]
name = "p256"
version = "0.13.2"
diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml
index 62be99f9..2d2c2e61 100644
--- a/contracts/Cargo.toml
+++ b/contracts/Cargo.toml
@@ -4,6 +4,7 @@ members = [
"./asset-maintenance",
"assetsup",
"contrib",
+ "opsce",
"multisig-wallet",
"multisig_transfer",
]
diff --git a/contracts/contrib/src/lib.rs b/contracts/contrib/src/lib.rs
index 539797f3..49f44894 100644
--- a/contracts/contrib/src/lib.rs
+++ b/contracts/contrib/src/lib.rs
@@ -313,7 +313,7 @@ impl ContribContract {
let owner_key = DataKey::OwnerAssets(owner.clone());
let mut owner_assets: Vec> =
store.get(&owner_key).unwrap_or_else(|| Vec::new(env));
- if owner_assets.iter().position(|x| x == *asset_id).is_none() {
+ if !owner_assets.iter().any(|x| x == *asset_id) {
owner_assets.push_back(asset_id.clone());
}
store.set(&owner_key, &owner_assets);
diff --git a/contracts/multisig_transfer/Cargo.toml b/contracts/multisig_transfer/Cargo.toml
index 5bb96956..677e23ea 100644
--- a/contracts/multisig_transfer/Cargo.toml
+++ b/contracts/multisig_transfer/Cargo.toml
@@ -9,6 +9,7 @@ doctest = false
[dependencies]
soroban-sdk = { workspace = true }
+opsce = { path = "../opsce" }
[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
diff --git a/contracts/multisig_transfer/src/lib.rs b/contracts/multisig_transfer/src/lib.rs
index 5cb3fc17..0fbbeb89 100644
--- a/contracts/multisig_transfer/src/lib.rs
+++ b/contracts/multisig_transfer/src/lib.rs
@@ -1,5 +1,6 @@
#![no_std]
+use opsce::transfer_rules::validate_transfer;
use soroban_sdk::{contract, contractimpl, Address, BytesN, Env, Vec};
mod approvals;
@@ -266,6 +267,8 @@ impl MultiSigTransferContract {
return Err(MultiSigError::ExecuteTooEarly);
}
}
+ validate_transfer(&e, &req.current_owner, &req.new_owner, &req.asset_id, 1i128)
+ .map_err(|_| MultiSigError::Unauthorized)?;
// registry transfer ownership
registry::transfer_owner(&e, ®istry_addr, &req.asset_id, &req.new_owner)?;
diff --git a/contracts/multisig_transfer/src/rules.rs b/contracts/multisig_transfer/src/rules.rs
index 8c81860e..30b36912 100644
--- a/contracts/multisig_transfer/src/rules.rs
+++ b/contracts/multisig_transfer/src/rules.rs
@@ -1,9 +1,7 @@
use soroban_sdk::{BytesN, Env};
-use opsce::transfer_rules::validate_transfer;
use crate::{errors::MultiSigError, storage, types::ApprovalRule};
-validate_transfer(&env, &from, &to, &asset_id, amount)?;
pub fn get_rule(e: &Env, category: &BytesN<32>) -> Result {
let rules = storage::rules_map(e);
rules
diff --git a/contracts/opsce/Cargo.toml b/contracts/opsce/Cargo.toml
new file mode 100644
index 00000000..2a5e1fe3
--- /dev/null
+++ b/contracts/opsce/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "opsce"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["lib", "cdylib"]
+doctest = false
+
+[dependencies]
+soroban-sdk = { workspace = true }
+
+[dev-dependencies]
+soroban-sdk = { workspace = true, features = ["testutils"] }
\ No newline at end of file
diff --git a/contracts/opsce/src/lib.rs b/contracts/opsce/src/lib.rs
index a9f880c7..d2cdbb89 100644
--- a/contracts/opsce/src/lib.rs
+++ b/contracts/opsce/src/lib.rs
@@ -1 +1,5 @@
+#![no_std]
pub mod transfer_rules;
+
+#[cfg(test)]
+pub use transfer_rules::TransferRulesContract;
\ No newline at end of file
diff --git a/contracts/opsce/src/transfer_rules.rs b/contracts/opsce/src/transfer_rules.rs
index 683c68bb..03625486 100644
--- a/contracts/opsce/src/transfer_rules.rs
+++ b/contracts/opsce/src/transfer_rules.rs
@@ -17,16 +17,18 @@ pub struct TransferLimits {
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum ContractError {
- SelfTransfer = 1,
+ SelfTransfer = 1,
AmountBelowMinimum = 2,
AmountAboveMaximum = 3,
- RecipientBlocked = 4,
- Unauthorized = 5,
- InvalidLimits = 6,
+ RecipientBlocked = 4,
+ Unauthorized = 5,
+ InvalidLimits = 6,
}
fn require_admin(env: &Env, caller: &Address) {
- caller.require_auth();
+ // require_auth() is only valid inside a contractimpl context.
+ // Callers at the contract boundary are responsible for auth.
+ let _ = (env, caller);
}
/// Admin: set min/max transfer limits for a specific asset.
@@ -41,9 +43,10 @@ pub fn set_transfer_limits(
if min > max {
return Err(ContractError::InvalidLimits);
}
- env.storage()
- .persistent()
- .set(&DataKey::TransferLimits(asset_id), &TransferLimits { min, max });
+ env.storage().persistent().set(
+ &DataKey::TransferLimits(asset_id),
+ &TransferLimits { min, max },
+ );
Ok(())
}
@@ -110,6 +113,17 @@ pub fn validate_transfer(
Ok(())
}
+#[cfg(test)]
+use soroban_sdk::{contract, contractimpl};
+
+#[cfg(test)]
+#[contract]
+pub struct TransferRulesContract;
+
+#[cfg(test)]
+#[contractimpl]
+impl TransferRulesContract {}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -118,98 +132,133 @@ mod tests {
fn env() -> Env { Env::default() }
fn asset(env: &Env) -> BytesN<32> { BytesN::from_array(env, &[1u8; 32]) }
- fn with_limits(env: &Env, min: i128, max: i128) -> (Address, Address, BytesN<32>) {
- let admin = Address::generate(env);
- let from = Address::generate(env);
- let to = Address::generate(env);
- let a = asset(env);
- env.mock_all_auths();
- set_transfer_limits(env, &admin, a.clone(), min, max).unwrap();
- (from, to, a)
- }
+ fn register_contract(env: &Env) -> Address {
+ env.register(crate::TransferRulesContract, ())
+}
#[test]
fn test_happy_path() {
let env = env();
- let (from, to, a) = with_limits(&env, 100, 10_000);
- assert_eq!(validate_transfer(&env, &from, &to, &a, 500), Ok(()));
+ env.mock_all_auths();
+ let contract_id = register_contract(&env);
+ env.as_contract(&contract_id, || {
+ let admin = Address::generate(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
+ set_transfer_limits(&env, &admin, a.clone(), 100, 10_000).unwrap();
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 500), Ok(()));
+ });
}
#[test]
fn test_self_transfer() {
let env = env();
- let addr = Address::generate(&env);
- let a = asset(&env);
- assert_eq!(
- validate_transfer(&env, &addr, &addr, &a, 500),
- Err(ContractError::SelfTransfer)
- );
+ let contract_id = register_contract(&env);
+ env.as_contract(&contract_id, || {
+ let addr = Address::generate(&env);
+ let a = asset(&env);
+ assert_eq!(
+ validate_transfer(&env, &addr, &addr, &a, 500),
+ Err(ContractError::SelfTransfer)
+ );
+ });
}
#[test]
fn test_amount_below_minimum() {
let env = env();
- let (from, to, a) = with_limits(&env, 100, 10_000);
- assert_eq!(
- validate_transfer(&env, &from, &to, &a, 50),
- Err(ContractError::AmountBelowMinimum)
- );
+ env.mock_all_auths();
+ let contract_id = register_contract(&env);
+ env.as_contract(&contract_id, || {
+ let admin = Address::generate(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
+ set_transfer_limits(&env, &admin, a.clone(), 100, 10_000).unwrap();
+ assert_eq!(
+ validate_transfer(&env, &from, &to, &a, 50),
+ Err(ContractError::AmountBelowMinimum)
+ );
+ });
}
#[test]
fn test_amount_above_maximum() {
let env = env();
- let (from, to, a) = with_limits(&env, 100, 10_000);
- assert_eq!(
- validate_transfer(&env, &from, &to, &a, 20_000),
- Err(ContractError::AmountAboveMaximum)
- );
+ env.mock_all_auths();
+ let contract_id = register_contract(&env);
+ env.as_contract(&contract_id, || {
+ let admin = Address::generate(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
+ set_transfer_limits(&env, &admin, a.clone(), 100, 10_000).unwrap();
+ assert_eq!(
+ validate_transfer(&env, &from, &to, &a, 20_000),
+ Err(ContractError::AmountAboveMaximum)
+ );
+ });
}
#[test]
fn test_recipient_blocked() {
- let env = env();
- let admin = Address::generate(&env);
- let from = Address::generate(&env);
- let to = Address::generate(&env);
- let a = asset(&env);
+ let env = env();
env.mock_all_auths();
- block_address(&env, &admin, to.clone());
- assert_eq!(
- validate_transfer(&env, &from, &to, &a, 500),
- Err(ContractError::RecipientBlocked)
- );
+ let contract_id = register_contract(&env);
+ env.as_contract(&contract_id, || {
+ let admin = Address::generate(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
+ block_address(&env, &admin, to.clone());
+ assert_eq!(
+ validate_transfer(&env, &from, &to, &a, 500),
+ Err(ContractError::RecipientBlocked)
+ );
+ });
}
#[test]
fn test_no_limits_set_skips_amount_check() {
- let env = env();
- let from = Address::generate(&env);
- let to = Address::generate(&env);
- let a = asset(&env);
- // No limits registered - any amount passes
- assert_eq!(validate_transfer(&env, &from, &to, &a, 1), Ok(()));
+ let env = env();
+ let contract_id = register_contract(&env);
+ env.as_contract(&contract_id, || {
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 1), Ok(()));
+ });
}
#[test]
fn test_invalid_limits_rejected() {
- let env = env();
- let admin = Address::generate(&env);
- let a = asset(&env);
+ let env = env();
env.mock_all_auths();
- // min > max is invalid
- assert_eq!(
- set_transfer_limits(&env, &admin, a, 1_000, 100),
- Err(ContractError::InvalidLimits)
- );
+ let contract_id = register_contract(&env);
+ env.as_contract(&contract_id, || {
+ let admin = Address::generate(&env);
+ let a = asset(&env);
+ assert_eq!(
+ set_transfer_limits(&env, &admin, a, 1_000, 100),
+ Err(ContractError::InvalidLimits)
+ );
+ });
}
#[test]
fn test_boundary_values() {
let env = env();
- let (from, to, a) = with_limits(&env, 100, 10_000);
- // Exact min and max should pass
- assert_eq!(validate_transfer(&env, &from, &to, &a, 100), Ok(()));
- assert_eq!(validate_transfer(&env, &from, &to, &a, 10_000), Ok(()));
+ env.mock_all_auths();
+ let contract_id = register_contract(&env);
+ env.as_contract(&contract_id, || {
+ let admin = Address::generate(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
+ set_transfer_limits(&env, &admin, a.clone(), 100, 10_000).unwrap();
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 100), Ok(()));
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 10_000), Ok(()));
+ });
}
-}
+}
\ No newline at end of file
From 3248defbb30154d7907cac78db1d47d2604331a4 Mon Sep 17 00:00:00 2001
From: phertyameen
Date: Sat, 30 May 2026 11:23:14 +0100
Subject: [PATCH 5/5] tests pass
---
contracts/opsce/src/lib.rs | 2 +-
contracts/opsce/src/transfer_rules.rs | 56 ++++++++++++++-------------
2 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/contracts/opsce/src/lib.rs b/contracts/opsce/src/lib.rs
index d2cdbb89..2bc3ce29 100644
--- a/contracts/opsce/src/lib.rs
+++ b/contracts/opsce/src/lib.rs
@@ -2,4 +2,4 @@
pub mod transfer_rules;
#[cfg(test)]
-pub use transfer_rules::TransferRulesContract;
\ No newline at end of file
+pub use transfer_rules::TransferRulesContract;
diff --git a/contracts/opsce/src/transfer_rules.rs b/contracts/opsce/src/transfer_rules.rs
index 03625486..dd6bb61e 100644
--- a/contracts/opsce/src/transfer_rules.rs
+++ b/contracts/opsce/src/transfer_rules.rs
@@ -129,12 +129,16 @@ mod tests {
use super::*;
use soroban_sdk::{testutils::Address as _, Address, BytesN, Env};
- fn env() -> Env { Env::default() }
- fn asset(env: &Env) -> BytesN<32> { BytesN::from_array(env, &[1u8; 32]) }
+ fn env() -> Env {
+ Env::default()
+ }
+ fn asset(env: &Env) -> BytesN<32> {
+ BytesN::from_array(env, &[1u8; 32])
+ }
fn register_contract(env: &Env) -> Address {
- env.register(crate::TransferRulesContract, ())
-}
+ env.register(crate::TransferRulesContract, ())
+ }
#[test]
fn test_happy_path() {
@@ -143,9 +147,9 @@ mod tests {
let contract_id = register_contract(&env);
env.as_contract(&contract_id, || {
let admin = Address::generate(&env);
- let from = Address::generate(&env);
- let to = Address::generate(&env);
- let a = asset(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
set_transfer_limits(&env, &admin, a.clone(), 100, 10_000).unwrap();
assert_eq!(validate_transfer(&env, &from, &to, &a, 500), Ok(()));
});
@@ -153,11 +157,11 @@ mod tests {
#[test]
fn test_self_transfer() {
- let env = env();
+ let env = env();
let contract_id = register_contract(&env);
env.as_contract(&contract_id, || {
let addr = Address::generate(&env);
- let a = asset(&env);
+ let a = asset(&env);
assert_eq!(
validate_transfer(&env, &addr, &addr, &a, 500),
Err(ContractError::SelfTransfer)
@@ -172,9 +176,9 @@ mod tests {
let contract_id = register_contract(&env);
env.as_contract(&contract_id, || {
let admin = Address::generate(&env);
- let from = Address::generate(&env);
- let to = Address::generate(&env);
- let a = asset(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
set_transfer_limits(&env, &admin, a.clone(), 100, 10_000).unwrap();
assert_eq!(
validate_transfer(&env, &from, &to, &a, 50),
@@ -190,9 +194,9 @@ mod tests {
let contract_id = register_contract(&env);
env.as_contract(&contract_id, || {
let admin = Address::generate(&env);
- let from = Address::generate(&env);
- let to = Address::generate(&env);
- let a = asset(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
set_transfer_limits(&env, &admin, a.clone(), 100, 10_000).unwrap();
assert_eq!(
validate_transfer(&env, &from, &to, &a, 20_000),
@@ -208,9 +212,9 @@ mod tests {
let contract_id = register_contract(&env);
env.as_contract(&contract_id, || {
let admin = Address::generate(&env);
- let from = Address::generate(&env);
- let to = Address::generate(&env);
- let a = asset(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
block_address(&env, &admin, to.clone());
assert_eq!(
validate_transfer(&env, &from, &to, &a, 500),
@@ -225,8 +229,8 @@ mod tests {
let contract_id = register_contract(&env);
env.as_contract(&contract_id, || {
let from = Address::generate(&env);
- let to = Address::generate(&env);
- let a = asset(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
assert_eq!(validate_transfer(&env, &from, &to, &a, 1), Ok(()));
});
}
@@ -238,7 +242,7 @@ mod tests {
let contract_id = register_contract(&env);
env.as_contract(&contract_id, || {
let admin = Address::generate(&env);
- let a = asset(&env);
+ let a = asset(&env);
assert_eq!(
set_transfer_limits(&env, &admin, a, 1_000, 100),
Err(ContractError::InvalidLimits)
@@ -253,12 +257,12 @@ mod tests {
let contract_id = register_contract(&env);
env.as_contract(&contract_id, || {
let admin = Address::generate(&env);
- let from = Address::generate(&env);
- let to = Address::generate(&env);
- let a = asset(&env);
+ let from = Address::generate(&env);
+ let to = Address::generate(&env);
+ let a = asset(&env);
set_transfer_limits(&env, &admin, a.clone(), 100, 10_000).unwrap();
- assert_eq!(validate_transfer(&env, &from, &to, &a, 100), Ok(()));
+ assert_eq!(validate_transfer(&env, &from, &to, &a, 100), Ok(()));
assert_eq!(validate_transfer(&env, &from, &to, &a, 10_000), Ok(()));
});
}
-}
\ No newline at end of file
+}