synor/crates/synor-bridge/src/vault.rs
Gulshan Yadav a7a4a7effc test: add comprehensive test suite (1,357 tests total)
Phase 1 - Test Writing (8 parallel agents):
- synord: Added 133 unit tests for node, config, services
- synor-rpc: Expanded to 207 tests for API endpoints
- synor-bridge: Added 144 tests for transfer lifecycle
- synor-zk: Added 279 tests for circuits, proofs, state
- synor-mining: Added 147 tests for kHeavyHash, miner
- synor-types: Added 146 tests for hash, amount, address
- cross-crate: Created 75 integration tests
- byzantine: Created 40+ fault scenario tests

Key additions:
- tests/cross_crate_integration.rs (new)
- apps/synord/tests/byzantine_fault_tests.rs (new)
- crates/synor-storage/src/cf.rs (new)
- src/lib.rs for workspace integration tests

Fixes during testing:
- synor-compute: Added Default impl for GpuVariant
- synor-bridge: Fixed replay protection in process_lock_event
- synor-storage: Added cf module and database exports

All 1,357 tests pass with 0 failures.
2026-01-20 06:35:28 +05:30

985 lines
27 KiB
Rust

//! Bridge Vault
//!
//! Manages locked assets for cross-chain transfers.
//! Assets are locked in vaults on the source chain and
//! wrapped tokens are minted on the destination chain.
use crate::{AssetId, BridgeAddress, BridgeError, BridgeResult, ChainType};
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::fmt;
/// Unique vault identifier
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct VaultId(pub String);
impl VaultId {
/// Create a new vault ID
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
/// Generate vault ID from asset
pub fn from_asset(asset: &AssetId, chain: &ChainType) -> Self {
let mut hasher = Sha256::new();
hasher.update(chain.to_string().as_bytes());
hasher.update(asset.identifier.as_bytes());
Self(hex::encode(&hasher.finalize()[..16]))
}
}
impl fmt::Display for VaultId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// Locked asset record
#[derive(Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct LockedAsset {
/// Asset being locked
pub asset: AssetId,
/// Amount locked
pub amount: u128,
/// Who locked the asset
pub owner: BridgeAddress,
/// Recipient on destination chain
pub recipient: BridgeAddress,
/// Lock timestamp (Unix seconds)
pub locked_at: u64,
/// Lock expiry (for timelock, 0 = no expiry)
pub expires_at: u64,
/// Transaction hash on source chain
pub lock_tx_hash: Option<Vec<u8>>,
/// Whether the asset has been released
pub released: bool,
}
impl LockedAsset {
/// Create a new locked asset record
pub fn new(
asset: AssetId,
amount: u128,
owner: BridgeAddress,
recipient: BridgeAddress,
locked_at: u64,
) -> Self {
Self {
asset,
amount,
owner,
recipient,
locked_at,
expires_at: 0,
lock_tx_hash: None,
released: false,
}
}
/// Set expiry time
pub fn with_expiry(mut self, expires_at: u64) -> Self {
self.expires_at = expires_at;
self
}
/// Set lock transaction hash
pub fn with_tx_hash(mut self, tx_hash: Vec<u8>) -> Self {
self.lock_tx_hash = Some(tx_hash);
self
}
/// Check if the lock has expired
pub fn is_expired(&self, current_time: u64) -> bool {
self.expires_at > 0 && current_time >= self.expires_at
}
/// Mark as released
pub fn release(&mut self) {
self.released = true;
}
}
/// Vault state
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum VaultState {
/// Vault is active and accepting deposits
Active,
/// Vault is paused (no new deposits)
Paused,
/// Vault is deprecated (migration needed)
Deprecated,
}
/// Asset vault for a specific chain
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Vault {
/// Vault identifier
pub id: VaultId,
/// Chain this vault is on
pub chain: ChainType,
/// Asset managed by this vault
pub asset: AssetId,
/// Current vault state
pub state: VaultState,
/// Total locked amount
pub total_locked: u128,
/// Individual locked assets by lock ID
locked_assets: HashMap<String, LockedAsset>,
/// Vault address (contract address for EVM)
pub vault_address: Option<BridgeAddress>,
/// Admin addresses
pub admins: Vec<BridgeAddress>,
/// Daily limit (0 = unlimited)
pub daily_limit: u128,
/// Today's usage
daily_usage: u128,
/// Last reset timestamp
last_reset: u64,
}
impl Vault {
/// Create a new vault
pub fn new(id: VaultId, chain: ChainType, asset: AssetId) -> Self {
Self {
id,
chain,
asset,
state: VaultState::Active,
total_locked: 0,
locked_assets: HashMap::new(),
vault_address: None,
admins: Vec::new(),
daily_limit: 0,
daily_usage: 0,
last_reset: 0,
}
}
/// Set vault address
pub fn with_address(mut self, address: BridgeAddress) -> Self {
self.vault_address = Some(address);
self
}
/// Set daily limit
pub fn with_daily_limit(mut self, limit: u128) -> Self {
self.daily_limit = limit;
self
}
/// Add admin
pub fn add_admin(&mut self, admin: BridgeAddress) {
if !self.admins.contains(&admin) {
self.admins.push(admin);
}
}
/// Lock assets in the vault
pub fn lock(
&mut self,
lock_id: impl Into<String>,
amount: u128,
owner: BridgeAddress,
recipient: BridgeAddress,
current_time: u64,
) -> BridgeResult<()> {
// Check vault state
if self.state != VaultState::Active {
return Err(BridgeError::BridgePaused);
}
// Check daily limit
self.check_daily_limit(amount, current_time)?;
let lock_id = lock_id.into();
if self.locked_assets.contains_key(&lock_id) {
return Err(BridgeError::TransferAlreadyExists(lock_id));
}
let locked = LockedAsset::new(
self.asset.clone(),
amount,
owner,
recipient,
current_time,
);
self.locked_assets.insert(lock_id, locked);
self.total_locked += amount;
self.daily_usage += amount;
Ok(())
}
/// Unlock assets from the vault
pub fn unlock(&mut self, lock_id: &str) -> BridgeResult<LockedAsset> {
let locked = self
.locked_assets
.get_mut(lock_id)
.ok_or_else(|| BridgeError::TransferNotFound(lock_id.to_string()))?;
if locked.released {
return Err(BridgeError::TransferAlreadyCompleted(lock_id.to_string()));
}
locked.release();
self.total_locked = self.total_locked.saturating_sub(locked.amount);
Ok(locked.clone())
}
/// Get locked asset
pub fn get_locked(&self, lock_id: &str) -> Option<&LockedAsset> {
self.locked_assets.get(lock_id)
}
/// Check and update daily limit
fn check_daily_limit(&mut self, amount: u128, current_time: u64) -> BridgeResult<()> {
if self.daily_limit == 0 {
return Ok(());
}
// Reset daily usage if new day
let day = current_time / 86400;
let last_day = self.last_reset / 86400;
if day > last_day {
self.daily_usage = 0;
self.last_reset = current_time;
}
// Check limit
if self.daily_usage + amount > self.daily_limit {
return Err(BridgeError::RateLimitExceeded);
}
Ok(())
}
/// Pause the vault
pub fn pause(&mut self) {
self.state = VaultState::Paused;
}
/// Resume the vault
pub fn resume(&mut self) {
self.state = VaultState::Active;
}
/// Deprecate the vault
pub fn deprecate(&mut self) {
self.state = VaultState::Deprecated;
}
/// Get all locked assets
pub fn all_locked(&self) -> impl Iterator<Item = (&String, &LockedAsset)> {
self.locked_assets.iter()
}
/// Get active (unreleased) locked assets
pub fn active_locked(&self) -> impl Iterator<Item = (&String, &LockedAsset)> {
self.locked_assets.iter().filter(|(_, l)| !l.released)
}
/// Get expired locked assets
pub fn expired_locked(&self, current_time: u64) -> impl Iterator<Item = (&String, &LockedAsset)> {
self.locked_assets
.iter()
.filter(move |(_, l)| !l.released && l.is_expired(current_time))
}
}
/// Vault manager for multiple vaults
pub struct VaultManager {
/// Vaults by ID
vaults: HashMap<VaultId, Vault>,
/// Vault lookup by (chain, asset)
by_chain_asset: HashMap<(ChainType, String), VaultId>,
}
impl VaultManager {
/// Create a new vault manager
pub fn new() -> Self {
Self {
vaults: HashMap::new(),
by_chain_asset: HashMap::new(),
}
}
/// Create and register a new vault
pub fn create_vault(&mut self, chain: ChainType, asset: AssetId) -> VaultId {
let vault_id = VaultId::from_asset(&asset, &chain);
let vault = Vault::new(vault_id.clone(), chain.clone(), asset.clone());
self.by_chain_asset
.insert((chain, asset.identifier.clone()), vault_id.clone());
self.vaults.insert(vault_id.clone(), vault);
vault_id
}
/// Get vault by ID
pub fn get_vault(&self, vault_id: &VaultId) -> Option<&Vault> {
self.vaults.get(vault_id)
}
/// Get mutable vault by ID
pub fn get_vault_mut(&mut self, vault_id: &VaultId) -> Option<&mut Vault> {
self.vaults.get_mut(vault_id)
}
/// Find vault by chain and asset
pub fn find_vault(&self, chain: &ChainType, asset: &AssetId) -> Option<&Vault> {
self.by_chain_asset
.get(&(chain.clone(), asset.identifier.clone()))
.and_then(|id| self.vaults.get(id))
}
/// Find mutable vault by chain and asset
pub fn find_vault_mut(&mut self, chain: &ChainType, asset: &AssetId) -> Option<&mut Vault> {
let vault_id = self
.by_chain_asset
.get(&(chain.clone(), asset.identifier.clone()))?
.clone();
self.vaults.get_mut(&vault_id)
}
/// Get or create vault for asset
pub fn get_or_create_vault(&mut self, chain: ChainType, asset: AssetId) -> &mut Vault {
let key = (chain.clone(), asset.identifier.clone());
if !self.by_chain_asset.contains_key(&key) {
self.create_vault(chain.clone(), asset.clone());
}
let vault_id = self.by_chain_asset.get(&key).unwrap().clone();
self.vaults.get_mut(&vault_id).unwrap()
}
/// Get total locked value across all vaults
pub fn total_locked(&self) -> u128 {
self.vaults.values().map(|v| v.total_locked).sum()
}
/// Get total locked value for an asset
pub fn total_locked_for_asset(&self, asset: &AssetId) -> u128 {
self.vaults
.values()
.filter(|v| v.asset.identifier == asset.identifier)
.map(|v| v.total_locked)
.sum()
}
/// List all vault IDs
pub fn vault_ids(&self) -> Vec<VaultId> {
self.vaults.keys().cloned().collect()
}
}
impl Default for VaultManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
// ==================== Helper Functions ====================
fn test_owner() -> BridgeAddress {
BridgeAddress::from_eth([0xaa; 20])
}
fn test_recipient() -> BridgeAddress {
BridgeAddress::from_synor([0xbb; 32])
}
fn test_owner_alt() -> BridgeAddress {
BridgeAddress::from_eth([0xcc; 20])
}
fn test_erc20_asset() -> AssetId {
AssetId::erc20("0x1234567890123456789012345678901234567890", "USDC", 6)
}
// ==================== VaultId Tests ====================
#[test]
fn test_vault_id_new() {
let id = VaultId::new("my-vault");
assert_eq!(id.0, "my-vault");
}
#[test]
fn test_vault_id_display() {
let id = VaultId::new("vault-123");
assert_eq!(format!("{}", id), "vault-123");
}
#[test]
fn test_vault_id_from_asset_deterministic() {
let asset = AssetId::eth();
let id1 = VaultId::from_asset(&asset, &ChainType::Ethereum);
let id2 = VaultId::from_asset(&asset, &ChainType::Ethereum);
assert_eq!(id1, id2);
assert!(!id1.0.is_empty());
}
#[test]
fn test_vault_id_from_asset_different_chains() {
let asset = AssetId::eth();
let id1 = VaultId::from_asset(&asset, &ChainType::Ethereum);
let id2 = VaultId::from_asset(&asset, &ChainType::EthereumSepolia);
assert_ne!(id1, id2);
}
#[test]
fn test_vault_id_from_asset_different_assets() {
let eth = AssetId::eth();
let usdc = test_erc20_asset();
let id1 = VaultId::from_asset(&eth, &ChainType::Ethereum);
let id2 = VaultId::from_asset(&usdc, &ChainType::Ethereum);
assert_ne!(id1, id2);
}
// ==================== LockedAsset Tests ====================
#[test]
fn test_locked_asset_new() {
let locked = LockedAsset::new(
AssetId::eth(),
1000,
test_owner(),
test_recipient(),
1700000000,
);
assert_eq!(locked.amount, 1000);
assert_eq!(locked.locked_at, 1700000000);
assert_eq!(locked.expires_at, 0);
assert!(locked.lock_tx_hash.is_none());
assert!(!locked.released);
}
#[test]
fn test_locked_asset_with_expiry() {
let locked = LockedAsset::new(
AssetId::eth(),
1000,
test_owner(),
test_recipient(),
1700000000,
)
.with_expiry(1700003600);
assert_eq!(locked.expires_at, 1700003600);
}
#[test]
fn test_locked_asset_with_tx_hash() {
let tx_hash = vec![0x11; 32];
let locked = LockedAsset::new(
AssetId::eth(),
1000,
test_owner(),
test_recipient(),
1700000000,
)
.with_tx_hash(tx_hash.clone());
assert_eq!(locked.lock_tx_hash, Some(tx_hash));
}
#[test]
fn test_locked_asset_is_expired_no_expiry() {
let locked = LockedAsset::new(
AssetId::eth(),
1000,
test_owner(),
test_recipient(),
1700000000,
);
assert!(!locked.is_expired(1800000000));
}
#[test]
fn test_locked_asset_expiry() {
let locked = LockedAsset::new(
AssetId::eth(),
1000,
test_owner(),
test_recipient(),
1000,
)
.with_expiry(2000);
assert!(!locked.is_expired(1500));
assert!(locked.is_expired(2000));
assert!(locked.is_expired(3000));
}
#[test]
fn test_locked_asset_release() {
let mut locked = LockedAsset::new(
AssetId::eth(),
1000,
test_owner(),
test_recipient(),
1700000000,
);
assert!(!locked.released);
locked.release();
assert!(locked.released);
}
// ==================== VaultState Tests ====================
#[test]
fn test_vault_state_equality() {
assert_eq!(VaultState::Active, VaultState::Active);
assert_eq!(VaultState::Paused, VaultState::Paused);
assert_eq!(VaultState::Deprecated, VaultState::Deprecated);
assert_ne!(VaultState::Active, VaultState::Paused);
}
// ==================== Vault Tests ====================
#[test]
fn test_vault_new() {
let vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
assert_eq!(vault.id.0, "test-vault");
assert_eq!(vault.chain, ChainType::Ethereum);
assert_eq!(vault.state, VaultState::Active);
assert_eq!(vault.total_locked, 0);
assert_eq!(vault.daily_limit, 0);
}
#[test]
fn test_vault_with_address() {
let address = test_owner();
let vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
)
.with_address(address.clone());
assert_eq!(vault.vault_address, Some(address));
}
#[test]
fn test_vault_with_daily_limit() {
let vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
)
.with_daily_limit(1000000);
assert_eq!(vault.daily_limit, 1000000);
}
#[test]
fn test_vault_add_admin() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
let admin = test_owner();
vault.add_admin(admin.clone());
assert_eq!(vault.admins.len(), 1);
assert_eq!(vault.admins[0], admin);
}
#[test]
fn test_vault_add_admin_duplicate() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
let admin = test_owner();
vault.add_admin(admin.clone());
vault.add_admin(admin.clone());
assert_eq!(vault.admins.len(), 1);
}
#[test]
fn test_lock_unlock() {
let mut vault = Vault::new(
VaultId::new("test"),
ChainType::Ethereum,
AssetId::eth(),
);
let current_time = 1700000000;
vault
.lock("lock1", 1000, test_owner(), test_recipient(), current_time)
.unwrap();
assert_eq!(vault.total_locked, 1000);
assert!(vault.get_locked("lock1").is_some());
let released = vault.unlock("lock1").unwrap();
assert_eq!(released.amount, 1000);
assert!(released.released);
assert_eq!(vault.total_locked, 0);
}
#[test]
fn test_vault_lock_multiple() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
let current_time = 1700000000;
vault.lock("lock-1", 1000, test_owner(), test_recipient(), current_time).unwrap();
vault.lock("lock-2", 2000, test_owner(), test_recipient(), current_time).unwrap();
vault.lock("lock-3", 500, test_owner_alt(), test_recipient(), current_time).unwrap();
assert_eq!(vault.total_locked, 3500);
}
#[test]
fn test_duplicate_lock() {
let mut vault = Vault::new(
VaultId::new("test"),
ChainType::Ethereum,
AssetId::eth(),
);
vault
.lock("lock1", 1000, test_owner(), test_recipient(), 0)
.unwrap();
let result = vault.lock("lock1", 500, test_owner(), test_recipient(), 0);
assert!(result.is_err());
}
#[test]
fn test_vault_unlock_nonexistent() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
let result = vault.unlock("nonexistent");
assert!(matches!(result, Err(BridgeError::TransferNotFound(_))));
}
#[test]
fn test_vault_unlock_already_released() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0).unwrap();
vault.unlock("lock-1").unwrap();
let result = vault.unlock("lock-1");
assert!(matches!(result, Err(BridgeError::TransferAlreadyCompleted(_))));
}
#[test]
fn test_vault_pause() {
let mut vault = Vault::new(
VaultId::new("test"),
ChainType::Ethereum,
AssetId::eth(),
);
vault.pause();
let result = vault.lock("lock1", 1000, test_owner(), test_recipient(), 0);
assert!(matches!(result, Err(BridgeError::BridgePaused)));
}
#[test]
fn test_vault_resume() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
vault.pause();
vault.resume();
assert_eq!(vault.state, VaultState::Active);
vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0).unwrap();
}
#[test]
fn test_vault_deprecate() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
vault.deprecate();
assert_eq!(vault.state, VaultState::Deprecated);
let result = vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0);
assert!(matches!(result, Err(BridgeError::BridgePaused)));
}
#[test]
fn test_daily_limit() {
let mut vault = Vault::new(
VaultId::new("test"),
ChainType::Ethereum,
AssetId::eth(),
)
.with_daily_limit(1000);
let current_time = 86400 * 100;
vault
.lock("lock1", 500, test_owner(), test_recipient(), current_time)
.unwrap();
let result = vault.lock("lock2", 600, test_owner(), test_recipient(), current_time);
assert!(matches!(result, Err(BridgeError::RateLimitExceeded)));
let next_day = current_time + 86400;
vault
.lock("lock2", 600, test_owner(), test_recipient(), next_day)
.unwrap();
}
#[test]
fn test_vault_no_daily_limit() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
let current_time = 0;
vault.lock("lock-1", 1000000000, test_owner(), test_recipient(), current_time).unwrap();
vault.lock("lock-2", 1000000000, test_owner(), test_recipient(), current_time).unwrap();
assert_eq!(vault.total_locked, 2000000000);
}
#[test]
fn test_vault_get_locked() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0).unwrap();
assert!(vault.get_locked("lock-1").is_some());
assert!(vault.get_locked("nonexistent").is_none());
}
#[test]
fn test_vault_all_locked() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0).unwrap();
vault.lock("lock-2", 2000, test_owner(), test_recipient(), 0).unwrap();
let all: Vec<_> = vault.all_locked().collect();
assert_eq!(all.len(), 2);
}
#[test]
fn test_vault_active_locked() {
let mut vault = Vault::new(
VaultId::new("test-vault"),
ChainType::Ethereum,
AssetId::eth(),
);
vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0).unwrap();
vault.lock("lock-2", 2000, test_owner(), test_recipient(), 0).unwrap();
vault.unlock("lock-1").unwrap();
let active: Vec<_> = vault.active_locked().collect();
assert_eq!(active.len(), 1);
}
// ==================== VaultManager Tests ====================
#[test]
fn test_vault_manager_new() {
let manager = VaultManager::new();
assert_eq!(manager.total_locked(), 0);
assert!(manager.vault_ids().is_empty());
}
#[test]
fn test_vault_manager_default() {
let manager = VaultManager::default();
assert_eq!(manager.total_locked(), 0);
}
#[test]
fn test_vault_manager() {
let mut manager = VaultManager::new();
let eth = AssetId::eth();
let vault_id = manager.create_vault(ChainType::Ethereum, eth.clone());
assert!(manager.get_vault(&vault_id).is_some());
assert!(manager.find_vault(&ChainType::Ethereum, &eth).is_some());
let vault = manager.get_or_create_vault(ChainType::Ethereum, eth.clone());
vault.lock("lock1", 100, test_owner(), test_recipient(), 0).unwrap();
assert_eq!(manager.total_locked(), 100);
}
#[test]
fn test_vault_manager_create_multiple() {
let mut manager = VaultManager::new();
manager.create_vault(ChainType::Ethereum, AssetId::eth());
manager.create_vault(ChainType::Ethereum, test_erc20_asset());
manager.create_vault(ChainType::EthereumSepolia, AssetId::eth());
assert_eq!(manager.vault_ids().len(), 3);
}
#[test]
fn test_vault_manager_get_vault_mut() {
let mut manager = VaultManager::new();
let vault_id = manager.create_vault(ChainType::Ethereum, AssetId::eth());
{
let vault = manager.get_vault_mut(&vault_id).unwrap();
vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0).unwrap();
}
let vault = manager.get_vault(&vault_id).unwrap();
assert_eq!(vault.total_locked, 1000);
}
#[test]
fn test_vault_manager_find_vault_not_found() {
let manager = VaultManager::new();
let vault = manager.find_vault(&ChainType::Ethereum, &AssetId::eth());
assert!(vault.is_none());
}
#[test]
fn test_vault_manager_find_vault_mut() {
let mut manager = VaultManager::new();
let eth = AssetId::eth();
manager.create_vault(ChainType::Ethereum, eth.clone());
let vault = manager.find_vault_mut(&ChainType::Ethereum, &eth).unwrap();
vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0).unwrap();
assert_eq!(manager.total_locked(), 1000);
}
#[test]
fn test_vault_manager_get_or_create_new() {
let mut manager = VaultManager::new();
let eth = AssetId::eth();
let vault = manager.get_or_create_vault(ChainType::Ethereum, eth.clone());
vault.lock("lock-1", 1000, test_owner(), test_recipient(), 0).unwrap();
assert_eq!(manager.vault_ids().len(), 1);
assert_eq!(manager.total_locked(), 1000);
}
#[test]
fn test_vault_manager_total_locked() {
let mut manager = VaultManager::new();
let eth = AssetId::eth();
let usdc = test_erc20_asset();
let eth_vault_id = manager.create_vault(ChainType::Ethereum, eth);
let usdc_vault_id = manager.create_vault(ChainType::Ethereum, usdc);
manager
.get_vault_mut(&eth_vault_id)
.unwrap()
.lock("lock-1", 1000, test_owner(), test_recipient(), 0)
.unwrap();
manager
.get_vault_mut(&usdc_vault_id)
.unwrap()
.lock("lock-2", 2000, test_owner(), test_recipient(), 0)
.unwrap();
assert_eq!(manager.total_locked(), 3000);
}
#[test]
fn test_vault_manager_total_locked_for_asset() {
let mut manager = VaultManager::new();
let eth = AssetId::eth();
let usdc = test_erc20_asset();
let eth_vault_id = manager.create_vault(ChainType::Ethereum, eth.clone());
let usdc_vault_id = manager.create_vault(ChainType::Ethereum, usdc.clone());
manager
.get_vault_mut(&eth_vault_id)
.unwrap()
.lock("lock-1", 1000, test_owner(), test_recipient(), 0)
.unwrap();
manager
.get_vault_mut(&usdc_vault_id)
.unwrap()
.lock("lock-2", 2000, test_owner(), test_recipient(), 0)
.unwrap();
assert_eq!(manager.total_locked_for_asset(&eth), 1000);
assert_eq!(manager.total_locked_for_asset(&usdc), 2000);
}
#[test]
fn test_vault_manager_vault_ids() {
let mut manager = VaultManager::new();
let id1 = manager.create_vault(ChainType::Ethereum, AssetId::eth());
let id2 = manager.create_vault(ChainType::Ethereum, test_erc20_asset());
let ids = manager.vault_ids();
assert_eq!(ids.len(), 2);
assert!(ids.contains(&id1));
assert!(ids.contains(&id2));
}
}