use super::*; use common_data::{asset::Asset, protocol_config::ProtocolConfig, vault_config::VaultConfig}; use near_sdk::AccountId; use near_workspaces::{ network::Sandbox, types::{Gas, NearToken}, Account, Contract, Worker, }; use serde_json::json; /// Integrated test context with vault and tokens (no kernel) pub struct IntegrationTestContext { pub sandbox: Worker, pub owner: Account, pub users: Vec, // Deployed contracts (AccountId references) pub vault_id: AccountId, // Mock token contracts pub mock_mt: Option, pub mock_ft_primary: Option, pub mock_ft_secondary: Option, // Asset definitions pub base_asset: Asset, pub accepted_assets: Vec, } impl IntegrationTestContext { /// Get vault contract reference from sandbox pub fn vault(&self) -> Contract { Contract::from_secret_key( self.vault_id.clone(), self.owner.secret_key().clone(), &self.sandbox, ) } } // ==================== Direct Deployment Helpers ==================== /// Deploy vault contract directly (owner = regular account, not kernel) async fn deploy_vault_direct( sandbox: &Worker, owner: &Account, base_asset: Asset, accepted_assets: Vec, ) -> TestResult { println!("Deploying vault contract directly..."); // Load vault WASM let vault_wasm = load_dew_vault_wasm()?; // Create a fresh dev account for the vault let vault_account = sandbox.dev_create_account().await?; // Deploy contract to the new account let vault = vault_account.deploy(&vault_wasm).await?.into_result()?; println!(" Vault deployed at: {}", vault.id()); // Create protocol config for testing let protocol_config = ProtocolConfig { protocol_management_fee_cut_bps: 100, // 1% for testing protocol_performance_fee_cut_bps: 1000, // 10% for testing protocol_fee_recipient: owner.id().clone(), }; // Initialize with new_with_config - owner is now the vault owner (not kernel) owner .call(vault.id(), "new_with_config") .args_json(json!({ "owner_account_id": owner.id(), // Owner is the test owner account "base_asset": base_asset, "accepted_deposit_assets": accepted_assets, "available_redeem_assets": accepted_assets, "share_token_symbol": "DEW", "share_token_name": "Dew Vault Shares", "share_token_decimals": DEFAULT_TOKEN_DECIMALS, "vault_config": VaultConfig::default(), "fee_recipient": owner.id(), "protocol_account": owner.id(), "protocol_config": protocol_config })) .gas(Gas::from_tgas(HIGH_GAS)) .transact() .await? .into_result()?; println!("Vault initialized at: {}", vault.id()); Ok(vault.id().clone()) } /// Deploy vault contract with custom VaultConfig async fn deploy_vault_direct_with_config( sandbox: &Worker, owner: &Account, base_asset: Asset, accepted_assets: Vec, vault_config: VaultConfig, ) -> TestResult { println!("Deploying vault contract directly with custom config..."); // Load vault WASM let vault_wasm = load_dew_vault_wasm()?; // Create a fresh dev account for the vault let vault_account = sandbox.dev_create_account().await?; // Deploy contract to the new account let vault = vault_account.deploy(&vault_wasm).await?.into_result()?; println!(" Vault deployed at: {}", vault.id()); // Create protocol config for testing let protocol_config = ProtocolConfig { protocol_management_fee_cut_bps: 100, // 1% for testing protocol_performance_fee_cut_bps: 1000, // 10% for testing protocol_fee_recipient: owner.id().clone(), }; // Initialize with new_with_config and provided vault_config owner .call(vault.id(), "new_with_config") .args_json(json!({ "owner_account_id": owner.id(), // Owner is the test owner account "base_asset": base_asset, "accepted_deposit_assets": accepted_assets, "available_redeem_assets": accepted_assets, "share_token_symbol": "DEW", "share_token_name": "Dew Vault Shares", "share_token_decimals": DEFAULT_TOKEN_DECIMALS, "vault_config": vault_config, "fee_recipient": owner.id(), "protocol_account": owner.id(), "protocol_config": protocol_config })) .gas(Gas::from_tgas(HIGH_GAS)) .transact() .await? .into_result()?; println!("Vault (custom config) initialized at: {}", vault.id()); Ok(vault.id().clone()) } /// Deploy vault with custom protocol config async fn deploy_vault_with_custom_protocol_config( sandbox: &Worker, owner: &Account, base_asset: Asset, accepted_assets: Vec, vault_config: VaultConfig, protocol_config: ProtocolConfig, ) -> TestResult { println!("Deploying vault contract with custom vault + protocol config..."); // Load vault WASM let vault_wasm = load_dew_vault_wasm()?; // Create a fresh dev account for the vault let vault_account = sandbox.dev_create_account().await?; // Deploy contract to the new account let vault = vault_account.deploy(&vault_wasm).await?.into_result()?; println!(" Vault deployed at: {}", vault.id()); // Initialize with new_with_config and provided vault_config + protocol_config owner .call(vault.id(), "new_with_config") .args_json(json!({ "owner_account_id": owner.id(), "base_asset": base_asset, "accepted_deposit_assets": accepted_assets, "available_redeem_assets": accepted_assets, "share_token_symbol": "DEW", "share_token_name": "Dew Vault Shares", "share_token_decimals": DEFAULT_TOKEN_DECIMALS, "vault_config": vault_config, "fee_recipient": owner.id(), "protocol_account": owner.id(), "protocol_config": protocol_config })) .gas(Gas::from_tgas(HIGH_GAS)) .transact() .await? .into_result()?; println!( "Vault (custom vault + protocol config) initialized at: {}", vault.id() ); Ok(vault.id().clone()) } /// Setup full integration test environment (vault-only, no kernel) pub async fn setup_full_integration_test( num_users: usize, use_mt: bool, num_ft_tokens: usize, ) -> TestResult { println!("🚀 Setting up full integration test environment (vault-only)"); println!(" Users: {}", num_users); println!(" Use MT: {}", use_mt); println!(" FT tokens: {}", num_ft_tokens); let sandbox = near_workspaces::sandbox().await?; let owner = sandbox.dev_create_account().await?; // Deploy mock token contracts let (mock_mt, mock_ft_primary, mock_ft_secondary, base_asset, accepted_assets) = deploy_mock_tokens(&sandbox, &owner, use_mt, num_ft_tokens).await?; // Deploy vault (owner is test owner account, no kernel) let vault_id = deploy_vault_direct( &sandbox, &owner, base_asset.clone(), accepted_assets.clone(), ) .await?; // Create test user accounts let users = create_test_users(&sandbox, num_users).await?; println!(" Users created: {}", users.len()); // Setup initial balances setup_integration_balances( &mock_mt, &mock_ft_primary, &mock_ft_secondary, &vault_id, &owner, &users, ) .await?; println!("Full integration test environment ready (vault-only)"); Ok(IntegrationTestContext { sandbox, owner, users, vault_id, mock_mt, mock_ft_primary, mock_ft_secondary, base_asset, accepted_assets, }) } /// Setup full integration test environment with a custom VaultConfig (vault-only) pub async fn setup_full_integration_test_with_vault_config( num_users: usize, use_mt: bool, num_ft_tokens: usize, vault_config: VaultConfig, ) -> TestResult { println!("🚀 Setting up full integration test env (custom vault config, vault-only)"); println!(" Users: {}", num_users); println!(" Use MT: {}", use_mt); println!(" FT tokens: {}", num_ft_tokens); let sandbox = near_workspaces::sandbox().await?; let owner = sandbox.dev_create_account().await?; // Deploy mock token contracts let (mock_mt, mock_ft_primary, mock_ft_secondary, base_asset, accepted_assets) = deploy_mock_tokens(&sandbox, &owner, use_mt, num_ft_tokens).await?; // Deploy vault with provided config (owner is test owner account) let vault_id = deploy_vault_direct_with_config( &sandbox, &owner, base_asset.clone(), accepted_assets.clone(), vault_config, ) .await?; // Create test user accounts let users = create_test_users(&sandbox, num_users).await?; println!(" Users created: {}", users.len()); // Setup initial balances setup_integration_balances( &mock_mt, &mock_ft_primary, &mock_ft_secondary, &vault_id, &owner, &users, ) .await?; println!("Full integration test environment ready (custom vault config, vault-only)"); Ok(IntegrationTestContext { sandbox, owner, users, vault_id, mock_mt, mock_ft_primary, mock_ft_secondary, base_asset, accepted_assets, }) } /// Setup full integration test with custom vault config AND protocol config pub async fn setup_full_integration_test_with_protocol_config( num_users: usize, use_mt: bool, num_ft_tokens: usize, vault_config: VaultConfig, mut protocol_config: ProtocolConfig, ) -> TestResult { println!("🚀 Setting up full integration test env (custom vault + protocol config)"); println!(" Users: {}", num_users); println!(" Use MT: {}", use_mt); println!(" FT tokens: {}", num_ft_tokens); let sandbox = near_workspaces::sandbox().await?; let owner = sandbox.dev_create_account().await?; protocol_config.protocol_fee_recipient = owner.id().clone(); // Deploy mock token contracts let (mock_mt, mock_ft_primary, mock_ft_secondary, base_asset, accepted_assets) = deploy_mock_tokens(&sandbox, &owner, use_mt, num_ft_tokens).await?; // Deploy vault with provided configs let vault_id = deploy_vault_with_custom_protocol_config( &sandbox, &owner, base_asset.clone(), accepted_assets.clone(), vault_config, protocol_config, ) .await?; // Create test user accounts let users = create_test_users(&sandbox, num_users).await?; println!(" Users created: {}", users.len()); // Setup initial balances setup_integration_balances( &mock_mt, &mock_ft_primary, &mock_ft_secondary, &vault_id, &owner, &users, ) .await?; println!("Full integration test environment ready (custom vault + protocol config)"); Ok(IntegrationTestContext { sandbox, owner, users, vault_id, mock_mt, mock_ft_primary, mock_ft_secondary, base_asset, accepted_assets, }) } /// Setup minimal vault integration test (1 MT, 2 users) pub async fn setup_minimal_vault() -> TestResult { println!("Setting up minimal vault integration test"); setup_full_integration_test(2, true, 0).await } /// Setup multi-asset integration test (1 MT + 2 FT, 5 users) pub async fn setup_multi_asset_integration() -> TestResult { println!("Setting up multi-asset integration test"); setup_full_integration_test(5, true, 2).await } /// Deploy mock token contracts based on configuration async fn deploy_mock_tokens( sandbox: &Worker, owner: &Account, use_mt: bool, num_ft_tokens: usize, ) -> TestResult<( Option, Option, Option, Asset, Vec, )> { let mut mock_mt = None; let mut mock_ft_primary = None; let mut mock_ft_secondary = None; let mut accepted_assets = Vec::new(); // Deploy MT contract if requested if use_mt { println!(" Deploying mock MT contract..."); let mt_wasm = load_mock_mt_wasm()?; let mt_deployer = sandbox.dev_create_account().await?; let mt = mt_deployer.deploy(&mt_wasm).await?.into_result()?; mt.call("new") .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await? .into_result()?; let mt_asset = Asset::MultiToken { contract_id: mt.id().clone(), token_id: "test_token".to_string(), }; accepted_assets.push(mt_asset.clone()); mock_mt = Some(mt); println!(" MT deployed: {:?}", mt_asset); } // Deploy FT contracts if requested if num_ft_tokens >= 1 { println!(" Deploying primary mock FT contract..."); let ft_wasm = load_mock_ft_wasm()?; let ft_deployer = sandbox.dev_create_account().await?; let ft = ft_deployer.deploy(&ft_wasm).await?.into_result()?; ft.call("new_default_meta") .args_json(json!({ "owner_id": owner.id(), "total_supply": INITIAL_BALANCE.to_string() })) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await? .into_result()?; let ft_asset = Asset::FungibleToken { contract_id: ft.id().clone(), }; accepted_assets.push(ft_asset.clone()); mock_ft_primary = Some(ft); println!(" Primary FT deployed: {:?}", ft_asset); } if num_ft_tokens >= 2 { println!(" Deploying secondary mock FT contract..."); let ft_wasm = load_mock_ft_wasm()?; let ft2_deployer = sandbox.dev_create_account().await?; let ft2 = ft2_deployer.deploy(&ft_wasm).await?.into_result()?; ft2_deployer .call(ft2.id(), "new_default_meta") .args_json(json!({ "owner_id": owner.id(), "total_supply": INITIAL_BALANCE.to_string() })) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await? .into_result()?; let ft2_asset = Asset::FungibleToken { contract_id: ft2.id().clone(), }; accepted_assets.push(ft2_asset.clone()); mock_ft_secondary = Some(ft2); println!(" Secondary FT deployed: {:?}", ft2_asset); } // Determine base asset (first in list) let base_asset = accepted_assets.first().ok_or("No assets deployed")?.clone(); Ok(( mock_mt, mock_ft_primary, mock_ft_secondary, base_asset, accepted_assets, )) } /// Setup initial token balances for integration tests async fn setup_integration_balances( mock_mt: &Option, mock_ft_primary: &Option, mock_ft_secondary: &Option, vault_id: &AccountId, owner: &Account, users: &[Account], ) -> TestResult<()> { println!(" Setting up token balances..."); // Setup storage deposits for vault shares for user in users.iter() { let _ = user .call(vault_id, "storage_deposit") .args_json(json!({ "account_id": user.id(), "registration_only": false })) .deposit(NearToken::from_near(1)) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await; } // Setup MT balances if let Some(mt) = mock_mt { for user in users.iter() { mt.call("mint") .args_json(json!({ "account_id": user.id(), "token_id": "test_token", "amount": INITIAL_BALANCE.to_string() })) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await? .into_result()?; } println!(" MT balances minted"); } // Setup primary FT balances if let Some(ft) = mock_ft_primary { // Register vault for storage let _ = owner .call(ft.id(), "storage_deposit") .args_json(json!({ "account_id": vault_id, "registration_only": false })) .deposit(NearToken::from_near(1)) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await; // Transfer to users for user in users.iter() { // Register user let _ = user .call(ft.id(), "storage_deposit") .args_json(json!({ "account_id": user.id(), "registration_only": false })) .deposit(NearToken::from_near(1)) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await; // Transfer tokens owner .call(ft.id(), "ft_transfer") .args_json(json!({ "receiver_id": user.id(), "amount": (INITIAL_BALANCE / users.len() as u128).to_string(), "memo": "Initial balance" })) .deposit(NearToken::from_yoctonear(1)) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await? .into_result()?; } println!(" Primary FT balances transferred"); } // Setup secondary FT balances if let Some(ft2) = mock_ft_secondary { // Register vault for storage let _ = owner .call(ft2.id(), "storage_deposit") .args_json(json!({ "account_id": vault_id, "registration_only": false })) .deposit(NearToken::from_near(1)) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await; // Transfer to users for user in users.iter() { // Register user let _ = user .call(ft2.id(), "storage_deposit") .args_json(json!({ "account_id": user.id(), "registration_only": false })) .deposit(NearToken::from_near(1)) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await; // Transfer tokens owner .call(ft2.id(), "ft_transfer") .args_json(json!({ "receiver_id": user.id(), "amount": (INITIAL_BALANCE / users.len() as u128).to_string(), "memo": "Initial balance" })) .deposit(NearToken::from_yoctonear(1)) .gas(Gas::from_tgas(DEFAULT_GAS)) .transact() .await? .into_result()?; } println!(" Secondary FT balances transferred"); } Ok(()) } /// Helper to load mock MT WASM fn load_mock_mt_wasm() -> TestResult> { std::fs::read("./target/near/mock_mt/mock_mt.wasm").map_err(|_| { "Mock MT WASM not found. Run: cd mock_contracts/mock_mt && cargo near build non-reproducible-wasm".into() }) } /// Helper to load mock FT WASM pub fn load_mock_ft_wasm() -> TestResult> { std::fs::read("./target/near/mock_ft/mock_ft.wasm").map_err(|_| { "Mock FT WASM not found. Run: cd mock_contracts/mock_ft && cargo near build non-reproducible-wasm".into() }) } /// Create test user accounts with storage deposits pub async fn create_test_users( sandbox: &Worker, count: usize, ) -> TestResult> { let mut users = Vec::new(); for i in 0..count { let user = sandbox .dev_create_account() .await .map_err(|e| format!("Failed to create user {}: {}", i, e))?; users.push(user); } Ok(users) }