From 8afde141e71ffaf3ae285ce07b724ef714b1c156 Mon Sep 17 00:00:00 2001 From: akiraonstarknet Date: Mon, 13 Oct 2025 13:58:41 +0800 Subject: [PATCH] changes --- .gitignore | 3 +- .tool-versions | 2 + my_scripts/cli.2.ts | 175 +++++ .../src/redeem_request/redeem_request.cairo | 11 + packages/vault/src/test/units/vault.cairo | 263 ++++++-- packages/vault/src/vault/errors.cairo | 4 + packages/vault/src/vault/interface.cairo | 8 +- packages/vault/src/vault/vault.cairo | 166 ++++- .../base_decoder_and_sanitizer.cairo | 12 +- ...ring_snf_style_decoder_and_sanitizer.cairo | 34 + .../defi_spring_snf_style/interface.cairo | 12 + .../decoders_and_sanitizers/interface.cairo | 9 +- .../multiply_decoder_and_sanitizer.cairo | 4 +- .../simple_decoder_and_sanitizer.cairo | 43 ++ .../interface.cairo | 15 + ...knet_vault_kit_decoder_and_sanitizer.cairo | 42 ++ .../interface.cairo | 12 + .../uncap_decoder_and_sanitizer.cairo | 37 ++ .../interface.cairo | 2 + .../vesu_decoder_and_sanitizer.cairo | 18 +- .../vesu_v2_decoder_and_sanitizer.cairo | 10 +- ...su_v2_specific_decoder_and_sanitizer.cairo | 46 ++ .../src/integration_interfaces/vesu.cairo | 396 ----------- .../src/integration_interfaces/vesu_v1.cairo | 23 + .../src/integration_interfaces/vesu_v2.cairo | 28 + packages/vault_allocator/src/lib.cairo | 20 +- .../vault_allocator/src/manager/errors.cairo | 12 - .../src/manager/interface.cairo | 10 - .../vault_allocator/src/manager/manager.cairo | 116 +--- .../vault_allocator/src/mocks/flashloan.cairo | 139 ---- .../vault_allocator/src/mocks/vault.cairo | 179 +++++ .../vault_allocator/src/test/creator.cairo | 41 +- .../src/test/integrations/avnu.cairo | 25 +- .../integrations/vault_bring_liquidity.cairo | 136 ++++ .../src/test/integrations/vesu_v1.cairo | 56 +- .../vault_allocator/src/test/register.cairo | 85 --- .../leveraged_loop_staked_ether.cairo | 272 -------- .../test/scenarios/stable_carry_loop.cairo | 73 ++- .../src/test/units/manager.cairo | 428 ++---------- packages/vault_allocator/src/test/utils.cairo | 576 +--------------- .../src/vault_allocator/vault_allocator.cairo | 2 + scripts/cli.ts | 175 +++++ scripts/package.json | 24 + scripts/pnpm-lock.yaml | 619 ++++++++++++++++++ scripts/tsconfig.json | 24 + 45 files changed, 2227 insertions(+), 2160 deletions(-) create mode 100644 .tool-versions create mode 100644 my_scripts/cli.2.ts create mode 100644 packages/vault_allocator/src/decoders_and_sanitizers/defi_spring_snf_style/defi_spring_snf_style_decoder_and_sanitizer.cairo create mode 100644 packages/vault_allocator/src/decoders_and_sanitizers/defi_spring_snf_style/interface.cairo create mode 100644 packages/vault_allocator/src/decoders_and_sanitizers/starknet_vault_kit_decoder_and_sanitizer/interface.cairo create mode 100644 packages/vault_allocator/src/decoders_and_sanitizers/starknet_vault_kit_decoder_and_sanitizer/starknet_vault_kit_decoder_and_sanitizer.cairo create mode 100644 packages/vault_allocator/src/decoders_and_sanitizers/uncap_decoder_and_sanitizer/interface.cairo create mode 100644 packages/vault_allocator/src/decoders_and_sanitizers/uncap_decoder_and_sanitizer/uncap_decoder_and_sanitizer.cairo create mode 100644 packages/vault_allocator/src/decoders_and_sanitizers/vesu_v2_specific_decoder_and_sanitizer.cairo delete mode 100644 packages/vault_allocator/src/integration_interfaces/vesu.cairo create mode 100644 packages/vault_allocator/src/integration_interfaces/vesu_v1.cairo create mode 100644 packages/vault_allocator/src/integration_interfaces/vesu_v2.cairo delete mode 100644 packages/vault_allocator/src/mocks/flashloan.cairo create mode 100644 packages/vault_allocator/src/mocks/vault.cairo create mode 100644 packages/vault_allocator/src/test/integrations/vault_bring_liquidity.cairo delete mode 100644 packages/vault_allocator/src/test/register.cairo delete mode 100644 packages/vault_allocator/src/test/scenarios/leveraged_loop_staked_ether.cairo create mode 100644 scripts/cli.ts create mode 100644 scripts/package.json create mode 100644 scripts/pnpm-lock.yaml create mode 100644 scripts/tsconfig.json diff --git a/.gitignore b/.gitignore index 14690868..37acc0b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target .snfoundry_cache -.env \ No newline at end of file +.env +node_modules \ No newline at end of file diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..60f4c2fa --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +scarb 2.12.2 +starknet-foundry 0.48.0 \ No newline at end of file diff --git a/my_scripts/cli.2.ts b/my_scripts/cli.2.ts new file mode 100644 index 00000000..e3204468 --- /dev/null +++ b/my_scripts/cli.2.ts @@ -0,0 +1,175 @@ +#!/usr/bin/env node + +import { Command } from 'commander'; +import { execSync } from 'child_process'; +import { readFileSync, existsSync } from 'fs'; +import { join, resolve } from 'path'; +import { ContractClass, extractContractHashes, json } from 'starknet'; + +const program = new Command(); + +interface CompileOptions { + package: string; + contract: string; + profile?: string; +} + +/** + * Compile a Cairo contract using Scarb and return its class hash + */ +async function compileContract(options: CompileOptions): Promise { + const { package: packageName, contract: contractName, profile = 'release' } = options; + + console.log(`šŸ”Ø Compiling contract ${contractName} from package ${packageName}...`); + + // Determine the package path - look for packages directory in current or parent directories + let projectRoot = resolve(process.cwd(), '..'); + while (!existsSync(join(projectRoot, 'packages')) && projectRoot !== '/') { + projectRoot = resolve(projectRoot, '..'); + } + + if (!existsSync(join(projectRoot, 'packages'))) { + throw new Error('Could not find packages directory. Please run from project root or a subdirectory.'); + } + + // Check if Scarb.toml exists in the package + const scarbTomlPath = join(projectRoot, 'Scarb.toml'); + if (!existsSync(scarbTomlPath)) { + throw new Error(`Scarb.toml not found in package ${packageName}`); + } + + try { + // Compile the contract using Scarb + console.log(`šŸ“¦ Building package ${packageName} with profile ${profile}...`); + const buildCommand = `scarb --profile ${profile} build --package ${packageName}`; + execSync(buildCommand, { + stdio: 'pipe', + cwd: projectRoot + }); + + console.log('āœ… Contract compiled successfully'); + + // Find the compiled contract file + const targetDir = join(projectRoot, 'target', profile); + + const compiledSierra = json.parse( + readFileSync(`${targetDir}/${packageName}_${contractName}.contract_class.json`).toString("ascii") + ) + const compiledCasm = json.parse( + readFileSync(`${targetDir}/${packageName}_${contractName}.compiled_contract_class.json`).toString("ascii") + ) + + // Read and parse the contract class + const payload = { + contract: compiledSierra, + casm: compiledCasm + }; + + const result = extractContractHashes(payload); + + console.log(`šŸŽÆ Class hash: ${result.classHash}`); + return result.classHash; + + } catch (error) { + if (error instanceof Error) { + throw new Error(`Compilation failed: ${error.message}`); + } + throw error; + } +} + +/** + * List available packages and contracts + */ +function listPackages(): void { + console.log('šŸ“¦ Available packages:'); + + // Find project root + let projectRoot = process.cwd(); + while (!existsSync(join(projectRoot, 'packages')) && projectRoot !== '/') { + projectRoot = resolve(projectRoot, '..'); + } + + if (!existsSync(join(projectRoot, 'packages'))) { + console.log(' No packages directory found'); + return; + } + + const packagesDir = join(projectRoot, 'packages'); + + if (!existsSync(packagesDir)) { + console.log(' No packages directory found'); + return; + } + + try { + const packages = execSync('ls packages', { encoding: 'utf8', cwd: projectRoot }).trim().split('\n'); + + packages.forEach(pkg => { + const packagePath = join(packagesDir, pkg); + const scarbTomlPath = join(packagePath, 'Scarb.toml'); + + if (existsSync(scarbTomlPath)) { + console.log(` šŸ“ ${pkg}`); + + // Try to find contract files + const srcPath = join(packagePath, 'src'); + if (existsSync(srcPath)) { + try { + const contractFiles = execSync(`find ${srcPath} -name "*.cairo" -type f`, { encoding: 'utf8' }) + .trim() + .split('\n') + .filter(file => file.includes('contract') || file.includes('interface')); + + if (contractFiles.length > 0) { + contractFiles.forEach(file => { + const fileName = file.split('/').pop()?.replace('.cairo', '') || ''; + console.log(` šŸ“„ ${fileName}`); + }); + } + } catch { + // Ignore errors when searching for contract files + } + } + } + }); + } catch (error) { + console.log(' Error listing packages'); + } +} + +// CLI setup +program + .name('starknet-compiler') + .description('Compile Cairo contracts and get their class hashes') + .version('1.0.0'); + +program + .command('compile') + .description('Compile a contract and return its class hash') + .requiredOption('-p, --package ', 'Package name (e.g., vault, vault_allocator)') + .requiredOption('-c, --contract ', 'Contract name') + .option('--profile ', 'Build profile (dev, release)', 'release') + .action(async (options) => { + try { + const classHash = await compileContract(options); + console.log(`\nšŸŽ‰ Success! Class hash: ${classHash}`); + } catch (error) { + console.error(`āŒ Error: ${error instanceof Error ? error.message : 'Unknown error'}`); + process.exit(1); + } + }); + +program + .command('list') + .description('List available packages and contracts') + .action(() => { + listPackages(); + }); + +program.parse(); + +// Show help if no command provided +if (!process.argv.slice(2).length) { + program.outputHelp(); +} diff --git a/packages/vault/src/redeem_request/redeem_request.cairo b/packages/vault/src/redeem_request/redeem_request.cairo index a30d2bab..acabc1e0 100644 --- a/packages/vault/src/redeem_request/redeem_request.cairo +++ b/packages/vault/src/redeem_request/redeem_request.cairo @@ -9,6 +9,7 @@ mod RedeemRequest { }; use openzeppelin::interfaces::upgrades::IUpgradeable; use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc721::extensions::ERC721EnumerableComponent; use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use openzeppelin::upgrades::upgradeable::UpgradeableComponent; use starknet::storage::{ @@ -22,10 +23,16 @@ mod RedeemRequest { component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!( + path: ERC721EnumerableComponent, storage: erc721_enumerable, event: ERC721EnumerableEvent, + ); component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); #[abi(embed_v0)] impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl; + #[abi(embed_v0)] + impl ERC721EnumerableImpl = + ERC721EnumerableComponent::ERC721EnumerableImpl; impl ERC721InternalImpl = ERC721Component::InternalImpl; impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; @@ -37,6 +44,8 @@ mod RedeemRequest { #[substorage(v0)] erc721: ERC721Component::Storage, #[substorage(v0)] + erc721_enumerable: ERC721EnumerableComponent::Storage, + #[substorage(v0)] upgradeable: UpgradeableComponent::Storage, id_len: u256, id_to_info: Map, @@ -49,6 +58,8 @@ mod RedeemRequest { #[flat] ERC721Event: ERC721Component::Event, #[flat] + ERC721EnumerableEvent: ERC721EnumerableComponent::Event, + #[flat] SRC5Event: SRC5Component::Event, #[flat] UpgradeableEvent: UpgradeableComponent::Event, diff --git a/packages/vault/src/test/units/vault.cairo b/packages/vault/src/test/units/vault.cairo index b0052e9e..4193e9b7 100644 --- a/packages/vault/src/test/units/vault.cairo +++ b/packages/vault/src/test/units/vault.cairo @@ -2,7 +2,7 @@ // Copyright (c) 2025 Starknet Vault Kit // Licensed under the MIT License. See LICENSE file for details. -use core::num::traits::Zero; +use core::num::traits::{Bounded, Zero}; use openzeppelin::interfaces::accesscontrol::{ IAccessControlDispatcher, IAccessControlDispatcherTrait, }; @@ -465,6 +465,7 @@ fn test_request_redeem_success(x: u256) { let shares = erc4626_dispatcher.deposit(deposit_amount, DUMMY_ADDRESS()); let shares_to_redeem = between(1, shares, x); + println!("shares_to_redeem: {}", shares_to_redeem); let erc20_dispatcher_vault = ERC20ABIDispatcher { contract_address: vault.contract_address }; cheat_caller_address_once(vault.contract_address, DUMMY_ADDRESS()); @@ -499,20 +500,24 @@ fn test_request_redeem_success(x: u256) { 'Shares not burned', ); - let expected_shares_fee_recipient = shares_to_redeem * REDEEM_FEES() / Vault::WAD; + let mut expected_shares_fee_recipient = shares_to_redeem * REDEEM_FEES() / Vault::WAD; + if ((shares_to_redeem * REDEEM_FEES() % Vault::WAD).is_non_zero()) { + expected_shares_fee_recipient += 1; + } let remaining_shares = shares_to_redeem - expected_shares_fee_recipient; let expected_assets = math::u256_mul_div( remaining_shares, deposit_amount * 10 + 1, deposit_amount + 1, Rounding::Floor, ); - let expected_redeem_nominal = 2 * Vault::WAD + expected_assets; - - assert(vault.redeem_nominal(epoch) == expected_redeem_nominal, 'Redeem nominal not updated'); - let id_info = redeem_request.id_to_info(id); assert(id_info.epoch == epoch, 'Epoch not set correctly'); + assert(id_info.nominal == expected_assets, 'Nominal not set correctly'); + let expected_redeem_nominal = 2 * Vault::WAD + expected_assets; + + assert(vault.redeem_nominal(epoch) == expected_redeem_nominal, 'Redeem nominal not updated'); + let erc721_dispatcher = ERC721ABIDispatcher { contract_address: redeem_request.contract_address, }; @@ -584,7 +589,10 @@ fn test_request_redeem_over_multiple_calls_same_epoch() { cheat_caller_address_once(vault.contract_address, OTHER_DUMMY_ADDRESS()); let id_a = vault.request_redeem(shares_a, DUMMY_ADDRESS(), DUMMY_ADDRESS()); - let expected_shares_fee_recipient_a = shares_a * REDEEM_FEES() / Vault::WAD; + let mut expected_shares_fee_recipient_a = shares_a * REDEEM_FEES() / Vault::WAD; + if ((expected_shares_fee_recipient_a % Vault::WAD).is_non_zero()) { + expected_shares_fee_recipient_a += 1; + } let remaining_shares_a = shares_a - expected_shares_fee_recipient_a; let expected_assets_a = math::u256_mul_div( remaining_shares_a, deposit_amount * 10 + 1, deposit_amount + 1, Rounding::Floor, @@ -1077,7 +1085,8 @@ fn test_claim_redeem_proportional_distribution() { let redeem_request_2_info = redeem_request.id_to_info(id2); let expected_assets_1 = (redeem_request_1_info.nominal * available_assets) / redeem_nominal; - let expected_assets_2 = (redeem_request_2_info.nominal * available_assets) / redeem_nominal; + let expected_assets_2 = (redeem_request_2_info.nominal * (available_assets - expected_assets_1)) + / (redeem_nominal - redeem_request_1_info.nominal); let balance_1_before = erc20_dispatcher.balance_of(DUMMY_ADDRESS()); let balance_2_before = erc20_dispatcher.balance_of(OTHER_DUMMY_ADDRESS()); @@ -1559,7 +1568,7 @@ fn setup_report_simple_deposit_epoch_0() -> ( let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_buffer + expected_aum - (expected_management_fees_assets) + 1, + expected_buffer + expected_aum - (expected_management_fees_assets + 0) + 1, Rounding::Floor, ); @@ -1663,16 +1672,17 @@ fn setup_report_simple_deposit_with_profit_epoch_1() -> ( * MANAGEMENT_FEES() * REPORT_DELAY().into()) / (Vault::WAD * Vault::YEAR.into()); + let net_profit_after_mgmt = profit_amount - expected_management_fees_assets; + let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_total_assets - (expected_management_fees_assets) + 1, + expected_total_assets + - (expected_management_fees_assets + expected_performance_fee_assets) + + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; - - let net_profit_after_mgmt = profit_amount - expected_management_fees_assets; - let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; let performance_fee_shares = math::u256_mul_div( expected_performance_fee_assets, expected_total_supply + 1, @@ -1776,7 +1786,7 @@ fn setup_report_simple_deposit_with_loss_epoch_1() -> ( let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - (expected_total_assets - expected_management_fees_assets) + 1, + (expected_total_assets - (expected_management_fees_assets + 0)) + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; @@ -1893,19 +1903,21 @@ fn setup_report_simple_redeem_unhandled_with_profit_epoch_1() -> ( let expected_total_assets = liqudity_after - expected_redeem_assets_after_cut_epoch_1; + let management_fee_assets_for_shareholders = expected_management_fees_assets + - (expected_nominal - expected_redeem_assets_after_cut_epoch_1); + + let net_profit_after_mgmt = profit_amount - management_fee_assets_for_shareholders; + let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; + let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_total_assets - (expected_management_fees_assets) + 1, + expected_total_assets + - (expected_management_fees_assets + expected_performance_fee_assets) + + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; - - let management_fee_assets_for_shareholders = expected_management_fees_assets - - (expected_nominal - expected_redeem_assets_after_cut_epoch_1); - - let net_profit_after_mgmt = profit_amount - management_fee_assets_for_shareholders; - let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; let performance_fee_shares = math::u256_mul_div( expected_performance_fee_assets, expected_total_supply + 1, @@ -2044,19 +2056,21 @@ fn setup_report_simple_redeem_matched_with_profit_epoch_1() -> ( let expected_total_assets = liqudity_after - expected_redeem_assets_after_cut_epoch_1; + let management_fee_assets_for_shareholders = expected_management_fees_assets + - (expected_nominal - expected_redeem_assets_after_cut_epoch_1); + + let net_profit_after_mgmt = profit_amount - management_fee_assets_for_shareholders; + let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; + let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_total_assets - (expected_management_fees_assets) + 1, + expected_total_assets + - (expected_management_fees_assets + expected_performance_fee_assets) + + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; - - let management_fee_assets_for_shareholders = expected_management_fees_assets - - (expected_nominal - expected_redeem_assets_after_cut_epoch_1); - - let net_profit_after_mgmt = profit_amount - management_fee_assets_for_shareholders; - let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; let performance_fee_shares = math::u256_mul_div( expected_performance_fee_assets, expected_total_supply + 1, @@ -2190,19 +2204,21 @@ fn setup_report_simple_redeem_handled_with_bring_liquidity_with_profit_epoch_1() let expected_total_assets = liqudity_after - expected_redeem_assets_after_cut_epoch_1; + let management_fee_assets_for_shareholders = expected_management_fees_assets + - (expected_nominal - expected_redeem_assets_after_cut_epoch_1); + + let net_profit_after_mgmt = profit_amount - management_fee_assets_for_shareholders; + let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; + let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_total_assets - (expected_management_fees_assets) + 1, + expected_total_assets + - (expected_management_fees_assets + expected_performance_fee_assets) + + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; - - let management_fee_assets_for_shareholders = expected_management_fees_assets - - (expected_nominal - expected_redeem_assets_after_cut_epoch_1); - - let net_profit_after_mgmt = profit_amount - management_fee_assets_for_shareholders; - let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; let performance_fee_shares = math::u256_mul_div( expected_performance_fee_assets, expected_total_supply + 1, @@ -2336,19 +2352,21 @@ fn setup_report_simple_redeem_not_handled_with_bring_liquidity_with_profit_epoch let expected_total_assets = liqudity_after - expected_redeem_assets_after_cut_epoch_1; + let management_fee_assets_for_shareholders = expected_management_fees_assets + - (expected_nominal - expected_redeem_assets_after_cut_epoch_1); + + let net_profit_after_mgmt = profit_amount - management_fee_assets_for_shareholders; + let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; + let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_total_assets - (expected_management_fees_assets) + 1, + expected_total_assets + - (expected_management_fees_assets + expected_performance_fee_assets) + + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; - - let management_fee_assets_for_shareholders = expected_management_fees_assets - - (expected_nominal - expected_redeem_assets_after_cut_epoch_1); - - let net_profit_after_mgmt = profit_amount - management_fee_assets_for_shareholders; - let expected_performance_fee_assets = PERFORMANCE_FEES() * net_profit_after_mgmt / Vault::WAD; let performance_fee_shares = math::u256_mul_div( expected_performance_fee_assets, expected_total_supply + 1, @@ -2486,11 +2504,14 @@ fn setup_report_simple_redeem_unhandled_with_loss_epoch_1() -> ( - cut; let expected_total_assets = liqudity_after - expected_redeem_assets_after_cut_epoch_1; + let expected_performance_fee_assets = 0; let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_total_assets - (expected_management_fees_assets) + 1, + expected_total_assets + - (expected_management_fees_assets + expected_performance_fee_assets) + + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; @@ -2635,11 +2656,14 @@ fn setup_report_simple_redeem_unhandled_not_enough_buffer_with_loss_epoch_1() -> - cut; let expected_total_assets = liqudity_after - expected_redeem_assets_after_cut_epoch_1; + let expected_performance_fee_assets = 0; let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_total_assets - (expected_management_fees_assets) + 1, + expected_total_assets + - (expected_management_fees_assets + expected_performance_fee_assets) + + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; @@ -2731,7 +2755,10 @@ fn setup_report_simple_redeem_unhandled_not_enough_buffer_with_loss_epoch_2_hand let redeem_request_id = vault .request_redeem(shares_to_redeem, OTHER_DUMMY_ADDRESS(), OTHER_DUMMY_ADDRESS()); - let expected_redeem_fee_shares = shares_to_redeem * REDEEM_FEES() / Vault::WAD; + let mut expected_redeem_fee_shares = shares_to_redeem * REDEEM_FEES() / Vault::WAD; + if ((expected_redeem_fee_shares % Vault::WAD).is_non_zero()) { + expected_redeem_fee_shares += 1; + } let remaining_shares_after_reem_fees = shares_to_redeem - expected_redeem_fee_shares; let expected_nominal = math::u256_mul_div( remaining_shares_after_reem_fees, @@ -2820,11 +2847,14 @@ fn setup_report_simple_redeem_unhandled_not_enough_buffer_with_loss_epoch_2_hand let expected_total_assets = liqudity_after - (expected_redeem_assets_after_cut_epoch_1 + expected_redeem_assets_after_cut_epoch_2); + let expected_performance_fee_assets = 0; let management_fee_shares = math::u256_mul_div( expected_management_fees_assets, expected_total_supply + 1, - expected_total_assets - (expected_management_fees_assets) + 1, + expected_total_assets + - (expected_management_fees_assets + expected_performance_fee_assets) + + 1, Rounding::Floor, ); expected_total_supply = expected_total_supply + management_fee_shares; @@ -2878,3 +2908,144 @@ fn setup_report_simple_redeem_unhandled_not_enough_buffer_with_loss_epoch_2_hand fn test_report_simple_redeem_unhandled_not_enough_buffer_with_loss_epoch_2_handled_epoch_1() { setup_report_simple_redeem_unhandled_not_enough_buffer_with_loss_epoch_2_handled_epoch_1(); } + +#[test] +fn test_deposit_limit() { + let (underlying, vault, _) = set_up(); + let erc4626_dispatcher = IERC4626Dispatcher { contract_address: vault.contract_address }; + let deposit_cap = Vault::WAD * 100; + cheat_caller_address_once(vault.contract_address, OWNER()); + vault.set_deposit_limit(deposit_cap); + assert(vault.get_deposit_limit() == deposit_cap, 'Deposit limit not set'); + assert( + erc4626_dispatcher.max_deposit(DUMMY_ADDRESS()) == deposit_cap, 'Initial max deposit wrong', + ); + let first_deposit = Vault::WAD * 30; + cheat_caller_address_once(underlying, OWNER()); + ERC20ABIDispatcher { contract_address: underlying }.transfer(DUMMY_ADDRESS(), first_deposit); + cheat_caller_address_once(underlying, DUMMY_ADDRESS()); + ERC20ABIDispatcher { contract_address: underlying } + .approve(vault.contract_address, first_deposit); + cheat_caller_address_once(vault.contract_address, DUMMY_ADDRESS()); + erc4626_dispatcher.deposit(first_deposit, DUMMY_ADDRESS()); + let expected_remaining = deposit_cap - first_deposit; + assert( + erc4626_dispatcher.max_deposit(DUMMY_ADDRESS()) == expected_remaining, + 'Max deposit not reduced', + ); + cheat_caller_address_once(vault.contract_address, OWNER()); + vault.set_deposit_limit(Bounded::MAX); + assert(vault.get_deposit_limit() == Bounded::MAX, 'Unlimited not set'); + assert( + erc4626_dispatcher.max_deposit(DUMMY_ADDRESS()) == Bounded::MAX, + 'Max deposit not unlimited', + ); +} + +#[test] +fn test_mint_limit() { + let (underlying, vault, _) = set_up(); + let erc4626_dispatcher = IERC4626Dispatcher { contract_address: vault.contract_address }; + let deposit_cap = Vault::WAD * 100; + cheat_caller_address_once(vault.contract_address, OWNER()); + vault.set_deposit_limit(deposit_cap); + let mint_limit_config = Vault::WAD * 50; + cheat_caller_address_once(vault.contract_address, OWNER()); + vault.set_mint_limit(mint_limit_config); + assert(vault.get_mint_limit() == mint_limit_config, 'Mint limit not set'); + let initial_max_mint = erc4626_dispatcher.max_mint(DUMMY_ADDRESS()); + assert(initial_max_mint == deposit_cap, 'Initial max mint wrong'); + let first_deposit = Vault::WAD * 30; + cheat_caller_address_once(underlying, OWNER()); + ERC20ABIDispatcher { contract_address: underlying }.transfer(DUMMY_ADDRESS(), first_deposit); + cheat_caller_address_once(underlying, DUMMY_ADDRESS()); + ERC20ABIDispatcher { contract_address: underlying } + .approve(vault.contract_address, first_deposit); + cheat_caller_address_once(vault.contract_address, DUMMY_ADDRESS()); + erc4626_dispatcher.deposit(first_deposit, DUMMY_ADDRESS()); + let remaining_deposit_cap = deposit_cap - first_deposit; // 70 WAD remaining + let expected_max_mint = erc4626_dispatcher.convert_to_shares(remaining_deposit_cap); + let actual_max_mint = erc4626_dispatcher.max_mint(DUMMY_ADDRESS()); + assert(actual_max_mint == expected_max_mint, 'Max mint not adjusted'); + cheat_caller_address_once(vault.contract_address, OWNER()); + vault.set_deposit_limit(Bounded::MAX); + cheat_caller_address_once(vault.contract_address, OWNER()); + vault.set_mint_limit(Bounded::MAX); + + assert(erc4626_dispatcher.max_mint(DUMMY_ADDRESS()) == Bounded::MAX, 'Max mint not unlimited'); +} + + +#[test] +#[should_panic(expected: ('Caller is missing role',))] +fn test_set_deposit_limit_unauthorized() { + let (_, vault, _) = set_up(); + + cheat_caller_address_once(vault.contract_address, DUMMY_ADDRESS()); + vault.set_deposit_limit(Vault::WAD); +} + +#[test] +#[should_panic(expected: ('Caller is missing role',))] +fn test_set_mint_limit_unauthorized() { + let (_, vault, _) = set_up(); + + cheat_caller_address_once(vault.contract_address, DUMMY_ADDRESS()); + vault.set_mint_limit(Vault::WAD); +} + + +#[test] +fn test_deposit_with_limit() { + let (underlying, vault, _) = set_up(); + let erc4626_dispatcher = IERC4626Dispatcher { contract_address: vault.contract_address }; + + let deposit_limit = Vault::WAD; + cheat_caller_address_once(vault.contract_address, OWNER()); + vault.set_deposit_limit(deposit_limit); + + cheat_caller_address_once(underlying, OWNER()); + ERC20ABIDispatcher { contract_address: underlying }.transfer(DUMMY_ADDRESS(), Vault::WAD * 2); + cheat_caller_address_once(underlying, DUMMY_ADDRESS()); + ERC20ABIDispatcher { contract_address: underlying } + .approve(vault.contract_address, Vault::WAD * 2); + + cheat_caller_address_once(vault.contract_address, DUMMY_ADDRESS()); + erc4626_dispatcher.deposit(deposit_limit, DUMMY_ADDRESS()); + + assert( + ERC20ABIDispatcher { contract_address: vault.contract_address } + .balance_of(DUMMY_ADDRESS()) > 0, + 'Deposit failed', + ); +} + +#[test] +#[should_panic(expected: 'ERC4626: exceeds max deposit')] +fn test_deposit_exceeds_limit() { + let (underlying, vault, _) = set_up(); + let erc4626_dispatcher = IERC4626Dispatcher { contract_address: vault.contract_address }; + + let deposit_limit = Vault::WAD; + cheat_caller_address_once(vault.contract_address, OWNER()); + vault.set_deposit_limit(deposit_limit); + + cheat_caller_address_once(underlying, OWNER()); + ERC20ABIDispatcher { contract_address: underlying }.transfer(DUMMY_ADDRESS(), Vault::WAD * 2); + cheat_caller_address_once(underlying, DUMMY_ADDRESS()); + ERC20ABIDispatcher { contract_address: underlying } + .approve(vault.contract_address, Vault::WAD * 2); + + cheat_caller_address_once(vault.contract_address, DUMMY_ADDRESS()); + erc4626_dispatcher.deposit(deposit_limit + 1, DUMMY_ADDRESS()); +} + +#[test] +#[should_panic(expected: "Caller is not the vault allocator")] +fn test_bring_liquidity_unauthorized() { + let (_underlying, vault, _) = set_up(); + + // Try to call bring_liquidity from unauthorized address + cheat_caller_address_once(vault.contract_address, DUMMY_ADDRESS()); + vault.bring_liquidity(1000); +} diff --git a/packages/vault/src/vault/errors.cairo b/packages/vault/src/vault/errors.cairo index 01ee5f24..4d9d9bf9 100644 --- a/packages/vault/src/vault/errors.cairo +++ b/packages/vault/src/vault/errors.cairo @@ -66,4 +66,8 @@ pub mod Errors { pub fn invalid_new_aum(new_aum: u256) { panic!("Invalid new AUM: {}", new_aum); } + + pub fn caller_is_not_vault_allocator() { + panic!("Caller is not the vault allocator"); + } } diff --git a/packages/vault/src/vault/interface.cairo b/packages/vault/src/vault/interface.cairo index 5e7d9295..0afcf2cc 100644 --- a/packages/vault/src/vault/interface.cairo +++ b/packages/vault/src/vault/interface.cairo @@ -4,7 +4,6 @@ // Standard library imports use starknet::ContractAddress; -use vault::redeem_request::interface::RedeemRequestInfo; #[starknet::interface] pub trait IVault { @@ -46,5 +45,12 @@ pub trait IVault { fn last_report_timestamp(self: @TContractState) -> u64; fn max_delta(self: @TContractState) -> u256; fn due_assets_from_id(self: @TContractState, id: u256) -> u256; + fn due_assets_from_owner(self: @TContractState, owner: ContractAddress) -> u256; + + // Limit configuration functions + fn set_deposit_limit(ref self: TContractState, limit: u256); + fn set_mint_limit(ref self: TContractState, limit: u256); + fn get_deposit_limit(self: @TContractState) -> u256; + fn get_mint_limit(self: @TContractState) -> u256; } diff --git a/packages/vault/src/vault/vault.cairo b/packages/vault/src/vault/vault.cairo index af0030e1..3963c0d5 100644 --- a/packages/vault/src/vault/vault.cairo +++ b/packages/vault/src/vault/vault.cairo @@ -19,18 +19,21 @@ #[starknet::contract] pub mod Vault { - use core::num::traits::Zero; + use core::num::traits::{Zero, Bounded}; use openzeppelin::access::accesscontrol::AccessControlComponent; use openzeppelin::interfaces::erc20::{ ERC20ABIDispatcher, ERC20ABIDispatcherTrait, IERC20Metadata, }; - use openzeppelin::interfaces::erc721::{ERC721ABIDispatcher, ERC721ABIDispatcherTrait}; + use openzeppelin::interfaces::erc721::{ + ERC721ABIDispatcher, ERC721ABIDispatcherTrait, IERC721EnumerableDispatcher, + IERC721EnumerableDispatcherTrait, + }; use openzeppelin::interfaces::upgrades::IUpgradeable; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::security::pausable::PausableComponent; use openzeppelin::token::erc20::extensions::erc4626::ERC4626Component::Fee; use openzeppelin::token::erc20::extensions::erc4626::{ - DefaultConfig, ERC4626Component, ERC4626DefaultNoFees, ERC4626DefaultNoLimits, + DefaultConfig, ERC4626Component, ERC4626DefaultNoFees, }; use openzeppelin::token::erc20::{ DefaultConfig as ERC20DefaultConfig, ERC20Component, ERC20HooksEmptyImpl, @@ -84,6 +87,72 @@ pub mod Vault { impl ERC4626Impl = ERC4626Component::ERC4626Impl; impl ERC4626InternalImpl = ERC4626Component::InternalImpl; + // --- Custom ERC4626 Limits Implementation --- + // Custom implementation of deposit/withdraw limits + // Uses max u256 as sentinel value for "unlimited" + impl ERC4626LimitConfigImpl of ERC4626Component::LimitConfigTrait { + /// The max deposit allowed based on cap. + /// Returns remaining capacity: cap - total_assets + /// Returns None for no limit (when limit equals max u256) + fn deposit_limit( + self: @ERC4626Component::ComponentState, receiver: ContractAddress, + ) -> Option { + let contract_state = self.get_contract(); + let limit = contract_state.deposit_limit.read(); + if limit == Bounded::MAX { + Option::None + } else { + let total_assets = self.get_total_assets(); + if total_assets >= limit { + Option::Some(0) + } else { + Option::Some(limit - total_assets) + } + } + } + + /// The max mint allowed based on cap. + /// Returns shares equivalent of remaining deposit capacity + /// Returns None for no limit (when limit equals max u256) + fn mint_limit( + self: @ERC4626Component::ComponentState, receiver: ContractAddress, + ) -> Option { + let contract_state = self.get_contract(); + let limit = contract_state.mint_limit.read(); + if limit == Bounded::MAX { + Option::None + } else { + let deposit_limit_opt = self.deposit_limit(receiver); + match deposit_limit_opt { + Option::None => Option::None, + Option::Some(deposit_remaining) => { + if deposit_remaining == 0 { + Option::Some(0) + } else { + let shares = self._convert_to_shares(deposit_remaining, Rounding::Floor); + Option::Some(shares) + } + } + } + } + } + + /// The max withdraw allowed. + /// Not implemented - withdrawals are disabled in this vault + fn withdraw_limit( + self: @ERC4626Component::ComponentState, owner: ContractAddress, + ) -> Option { + Option::None + } + + /// The max redeem allowed. + fn redeem_limit( + self: @ERC4626Component::ComponentState, owner: ContractAddress, + ) -> Option { + Option::None + } + } + // --- ERC20 Implementation --- // Share token functionality with standard and camelCase interfaces #[abi(embed_v0)] @@ -142,7 +211,11 @@ pub mod Vault { management_fees: u256, // Annual management fee rate (in WAD) performance_fees: u256, // Performance fee on profits (in WAD) // --- Redemption System --- - redeem_request: IRedeemRequestDispatcher // NFT contract for tracking redemption requests + redeem_request: IRedeemRequestDispatcher, // NFT contract for tracking redemption requests + // --- ERC4626 Limits --- + // Note: max u256 means unlimited, any other value (including 0) sets a specific limit + deposit_limit: u256, // Maximum deposit amount + mint_limit: u256, // Maximum mint amount } // --- Events --- @@ -159,7 +232,8 @@ pub mod Vault { // Vault-specific events RedeemRequested: RedeemRequested, // Emitted when a redemption is requested RedeemClaimed: RedeemClaimed, // Emitted when a redemption is claimed - Report: Report // Emitted when oracle reports new AUM + Report: Report, // Emitted when oracle reports new AUM + BringLiquidity: BringLiquidity // Emitted when liquidity is brought back from allocators } /// Event emitted when a user requests a redemption @@ -194,6 +268,16 @@ pub mod Vault { pub performance_fee_shares: u256 // Performance fee shares minted } + /// Event emitted when liquidity is brought back from allocators + #[derive(Drop, starknet::Event)] + pub struct BringLiquidity { + pub caller: ContractAddress, // Address that initiated the liquidity transfer + pub amount: u256, // Amount of assets brought back to vault + pub new_buffer: u256, // New buffer amount after the transfer + pub new_aum: u256, // New AUM amount after the transfer + pub epoch: u256 // Epoch when the liquidity was brought back + } + /// Initialize the vault with configuration parameters /// Sets up all components, roles, and fee structure #[constructor] @@ -240,6 +324,11 @@ pub mod Vault { // Initialize timestamp for fee calculations self.last_report_timestamp.write(get_block_timestamp()); + // Initialize limits to max u256 (unlimited) + // max u256 is used as sentinel value for "no limit" + let max_limit: u256 = Bounded::MAX; + self.deposit_limit.write(max_limit); + self.mint_limit.write(max_limit); self .emit( Report { @@ -736,7 +825,7 @@ pub mod Vault { let management_fee_shares = math::u256_mul_div( management_fee_assets, total_supply + 1, - (total_assets - management_fee_assets) + 1, + (total_assets - (management_fee_assets + performance_fee_assets)) + 1, Rounding::Floor, ); if (management_fee_shares.is_non_zero()) { @@ -775,10 +864,24 @@ pub mod Vault { fn bring_liquidity( ref self: ContractState, amount: u256, ) { // Amount of assets to bring back + let caller = get_caller_address(); + // Only the registered vault allocator can bring liquidity + if (caller != self.vault_allocator.read()) { + Errors::caller_is_not_vault_allocator(); + } ERC20ABIDispatcher { contract_address: self.erc4626.asset() } - .transfer_from(get_caller_address(), starknet::get_contract_address(), amount); - self.buffer.write(self.buffer.read() + amount); // Increase buffer - self.aum.write(self.aum.read() - amount); // Decrease deployed AUM + .transfer_from(caller, starknet::get_contract_address(), amount); + let new_buffer = self.buffer.read() + amount; // Calculate new buffer + let new_aum = self.aum.read() - amount; // Calculate new AUM + self.buffer.write(new_buffer); // Increase buffer + self.aum.write(new_aum); // Decrease deployed AUM + + self + .emit( + BringLiquidity { + caller, amount, new_buffer, new_aum, epoch: self.epoch.read(), + }, + ); } // --- State Getter Functions --- @@ -860,6 +963,51 @@ pub mod Vault { self.max_delta.read() } + // --- Limit Configuration Functions --- + + /// Set the deposit limit (max u256 for unlimited, any other value including 0 for specific limit) + /// Only callable by owner + fn set_deposit_limit(ref self: ContractState, limit: u256) { + self.access_control.assert_only_role(OWNER_ROLE); + self.deposit_limit.write(limit); + } + + /// Set the mint limit (max u256 for unlimited, any other value including 0 for specific limit) + /// Only callable by owner + fn set_mint_limit(ref self: ContractState, limit: u256) { + self.access_control.assert_only_role(OWNER_ROLE); + self.mint_limit.write(limit); + } + + /// Get the current deposit limit (max u256 means unlimited) + fn get_deposit_limit(self: @ContractState) -> u256 { + self.deposit_limit.read() + } + + /// Get the current mint limit (max u256 means unlimited) + fn get_mint_limit(self: @ContractState) -> u256 { + self.mint_limit.read() + } + + fn due_assets_from_owner(self: @ContractState, owner: ContractAddress) -> u256 { + let balance = ERC721ABIDispatcher { + contract_address: self.redeem_request.read().contract_address, + } + .balance_of(owner); + let mut total_due_assets = 0; + for i in 0..balance { + total_due_assets += self + .due_assets_from_id( + IERC721EnumerableDispatcher { + contract_address: self.redeem_request.read().contract_address, + } + .token_of_owner_by_index(owner, i), + ); + } + total_due_assets + } + + fn due_assets_from_id(self: @ContractState, id: u256) -> u256 { let redeem_request_info = self.redeem_request.read().id_to_info(id); let redeem_request_nominal = redeem_request_info diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/base_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/base_decoder_and_sanitizer.cairo index 63304d20..478f7ddd 100644 --- a/packages/vault_allocator/src/decoders_and_sanitizers/base_decoder_and_sanitizer.cairo +++ b/packages/vault_allocator/src/decoders_and_sanitizers/base_decoder_and_sanitizer.cairo @@ -28,18 +28,8 @@ pub mod BaseDecoderAndSanitizerComponent { serialized_struct.span() } - fn flash_loan( - self: @ComponentState, - receiver: ContractAddress, - asset: ContractAddress, - amount: u256, - is_legacy: bool, - data: Span, - ) -> Span { + fn bring_liquidity(self: @ComponentState, amount: u256) -> Span { let mut serialized_struct: Array = ArrayTrait::new(); - receiver.serialize(ref serialized_struct); - asset.serialize(ref serialized_struct); - is_legacy.serialize(ref serialized_struct); serialized_struct.span() } } diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/defi_spring_snf_style/defi_spring_snf_style_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/defi_spring_snf_style/defi_spring_snf_style_decoder_and_sanitizer.cairo new file mode 100644 index 00000000..7ac5067b --- /dev/null +++ b/packages/vault_allocator/src/decoders_and_sanitizers/defi_spring_snf_style/defi_spring_snf_style_decoder_and_sanitizer.cairo @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 Starknet Vault Kit +// Licensed under the MIT License. See LICENSE file for details. + +// Helps claim rewards from Defi spring rewards and any possible rewards +// with similar claim contract structure +// This is called as SNFStyle because there can be other reward contracts (e.g. Ekubo) +// This is the default contract structure by SNF for rewards + +#[starknet::component] +pub mod DefiSpringSNFStyleDecoderAndSanitizerComponent { + use vault_allocator::decoders_and_sanitizers::defi_spring_snf_style::interface::IDefiSpringSNFStyleDecoderAndSanitizer; + + #[storage] + pub struct Storage {} + + #[event] + #[derive(Drop, Debug, PartialEq, starknet::Event)] + pub enum Event {} + + #[embeddable_as(DefiSpringSNFStyleDecoderAndSanitizerImpl)] + impl DefiSpringSNFStyleDecoderAndSanitizer< + TContractState, +HasComponent, + > of IDefiSpringSNFStyleDecoderAndSanitizer> { + fn claim( + self: @ComponentState, + amount: u128, + proof: Span, + ) -> Span { + let mut serialized_struct: Array = ArrayTrait::new(); + serialized_struct.span() + } + } +} diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/defi_spring_snf_style/interface.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/defi_spring_snf_style/interface.cairo new file mode 100644 index 00000000..92609cfe --- /dev/null +++ b/packages/vault_allocator/src/decoders_and_sanitizers/defi_spring_snf_style/interface.cairo @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 Starknet Vault Kit +// Licensed under the MIT License. See LICENSE file for details. + +#[starknet::interface] +pub trait IDefiSpringSNFStyleDecoderAndSanitizer { + fn claim( + self: @T, + amount: u128, + proof: Span, + ) -> Span; +} \ No newline at end of file diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/interface.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/interface.cairo index 7314c955..e5f266e1 100644 --- a/packages/vault_allocator/src/decoders_and_sanitizers/interface.cairo +++ b/packages/vault_allocator/src/decoders_and_sanitizers/interface.cairo @@ -7,12 +7,5 @@ use starknet::ContractAddress; #[starknet::interface] pub trait IBaseDecoderAndSanitizer { fn approve(self: @T, spender: ContractAddress, amount: u256) -> Span; - fn flash_loan( - self: @T, - receiver: ContractAddress, - asset: ContractAddress, - amount: u256, - is_legacy: bool, - data: Span, - ) -> Span; + fn bring_liquidity(self: @T, amount: u256) -> Span; } diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/multiply_decoder_and_sanitizer/multiply_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/multiply_decoder_and_sanitizer/multiply_decoder_and_sanitizer.cairo index 6d4a8ff6..f096cd45 100644 --- a/packages/vault_allocator/src/decoders_and_sanitizers/multiply_decoder_and_sanitizer/multiply_decoder_and_sanitizer.cairo +++ b/packages/vault_allocator/src/decoders_and_sanitizers/multiply_decoder_and_sanitizer/multiply_decoder_and_sanitizer.cairo @@ -16,8 +16,8 @@ pub mod MultiplyDecoderAndSanitizerComponent { #[derive(Drop, Debug, PartialEq, starknet::Event)] pub enum Event {} - #[embeddable_as(VesuDecoderAndSanitizerImpl)] - impl VesuDecoderAndSanitizer< + #[embeddable_as(MultiplyDecoderAndSanitizerImpl)] + impl MultiplyDecoderAndSanitizer< TContractState, +HasComponent, > of IMultiplyDecoderAndSanitizer> { fn modify_lever( diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/simple_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/simple_decoder_and_sanitizer.cairo index 857e206a..1a892a39 100644 --- a/packages/vault_allocator/src/decoders_and_sanitizers/simple_decoder_and_sanitizer.cairo +++ b/packages/vault_allocator/src/decoders_and_sanitizers/simple_decoder_and_sanitizer.cairo @@ -7,7 +7,10 @@ pub mod SimpleDecoderAndSanitizer { use vault_allocator::decoders_and_sanitizers::avnu_exchange_decoder_and_sanitizer::avnu_exchange_decoder_and_sanitizer::AvnuExchangeDecoderAndSanitizerComponent; use vault_allocator::decoders_and_sanitizers::base_decoder_and_sanitizer::BaseDecoderAndSanitizerComponent; use vault_allocator::decoders_and_sanitizers::erc4626_decoder_and_sanitizer::erc4626_decoder_and_sanitizer::Erc4626DecoderAndSanitizerComponent; + use vault_allocator::decoders_and_sanitizers::multiply_decoder_and_sanitizer::multiply_decoder_and_sanitizer::MultiplyDecoderAndSanitizerComponent; + use vault_allocator::decoders_and_sanitizers::starknet_vault_kit_decoder_and_sanitizer::starknet_vault_kit_decoder_and_sanitizer::StarknetVaultKitDecoderAndSanitizerComponent; use vault_allocator::decoders_and_sanitizers::vesu_decoder_and_sanitizer::vesu_decoder_and_sanitizer::VesuDecoderAndSanitizerComponent; + use vault_allocator::decoders_and_sanitizers::defi_spring_snf_style::defi_spring_snf_style_decoder_and_sanitizer::DefiSpringSNFStyleDecoderAndSanitizerComponent; component!( path: BaseDecoderAndSanitizerComponent, @@ -20,6 +23,12 @@ pub mod SimpleDecoderAndSanitizer { event: Erc4626DecoderAndSanitizerEvent, ); + component!( + path: StarknetVaultKitDecoderAndSanitizerComponent, + storage: starknet_vault_kit_decoder_and_sanitizer, + event: StarknetVaultKitDecoderAndSanitizerEvent, + ); + component!( path: VesuDecoderAndSanitizerComponent, storage: vesu_decoder_and_sanitizer, @@ -32,6 +41,18 @@ pub mod SimpleDecoderAndSanitizer { event: AvnuExchangeDecoderAndSanitizerEvent, ); + component!( + path: DefiSpringSNFStyleDecoderAndSanitizerComponent, + storage: defi_spring_snf_style_decoder_and_sanitizer, + event: DefiSpringSNFStyleDecoderAndSanitizerEvent, + ); + + component!( + path: MultiplyDecoderAndSanitizerComponent, + storage: multiply_decoder_and_sanitizer, + event: MultiplyDecoderAndSanitizerEvent, + ); + #[abi(embed_v0)] impl BaseDecoderAndSanitizerImpl = BaseDecoderAndSanitizerComponent::BaseDecoderAndSanitizerImpl; @@ -50,6 +71,16 @@ pub mod SimpleDecoderAndSanitizer { ContractState, >; + #[abi(embed_v0)] + impl DefiSpringSNFStyleDecoderAndSanitizerImpl = + DefiSpringSNFStyleDecoderAndSanitizerComponent::DefiSpringSNFStyleDecoderAndSanitizerImpl< + ContractState, + >; + + #[abi(embed_v0)] + impl MultiplyDecoderAndSanitizerImpl = + MultiplyDecoderAndSanitizerComponent::MultiplyDecoderAndSanitizerImpl; + #[storage] pub struct Storage { #[substorage(v0)] @@ -60,6 +91,12 @@ pub mod SimpleDecoderAndSanitizer { pub vesu_decoder_and_sanitizer: VesuDecoderAndSanitizerComponent::Storage, #[substorage(v0)] pub avnu_exchange_decoder_and_sanitizer: AvnuExchangeDecoderAndSanitizerComponent::Storage, + #[substorage(v0)] + pub defi_spring_snf_style_decoder_and_sanitizer: DefiSpringSNFStyleDecoderAndSanitizerComponent::Storage, + #[substorage(v0)] + pub starknet_vault_kit_decoder_and_sanitizer: StarknetVaultKitDecoderAndSanitizerComponent::Storage, + #[substorage(v0)] + pub multiply_decoder_and_sanitizer: MultiplyDecoderAndSanitizerComponent::Storage, } #[event] @@ -73,5 +110,11 @@ pub mod SimpleDecoderAndSanitizer { VesuDecoderAndSanitizerEvent: VesuDecoderAndSanitizerComponent::Event, #[flat] AvnuExchangeDecoderAndSanitizerEvent: AvnuExchangeDecoderAndSanitizerComponent::Event, + #[flat] + DefiSpringSNFStyleDecoderAndSanitizerEvent: DefiSpringSNFStyleDecoderAndSanitizerComponent::Event, + #[flat] + StarknetVaultKitDecoderAndSanitizerEvent: StarknetVaultKitDecoderAndSanitizerComponent::Event, + #[flat] + MultiplyDecoderAndSanitizerEvent: MultiplyDecoderAndSanitizerComponent::Event, } } diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/starknet_vault_kit_decoder_and_sanitizer/interface.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/starknet_vault_kit_decoder_and_sanitizer/interface.cairo new file mode 100644 index 00000000..837d592b --- /dev/null +++ b/packages/vault_allocator/src/decoders_and_sanitizers/starknet_vault_kit_decoder_and_sanitizer/interface.cairo @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 Starknet Vault Kit +// Licensed under the MIT License. See LICENSE file for details. + +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IStarknetVaultKitDecoderAndSanitizer { + fn request_redeem( + self: @T, shares: u256, receiver: ContractAddress, owner: ContractAddress, + ) -> Span; + + fn claim_redeem(self: @T, id: u256) -> Span; +} + diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/starknet_vault_kit_decoder_and_sanitizer/starknet_vault_kit_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/starknet_vault_kit_decoder_and_sanitizer/starknet_vault_kit_decoder_and_sanitizer.cairo new file mode 100644 index 00000000..0abf72ce --- /dev/null +++ b/packages/vault_allocator/src/decoders_and_sanitizers/starknet_vault_kit_decoder_and_sanitizer/starknet_vault_kit_decoder_and_sanitizer.cairo @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 Starknet Vault Kit +// Licensed under the MIT License. See LICENSE file for details. + +#[starknet::component] +pub mod StarknetVaultKitDecoderAndSanitizerComponent { + use starknet::ContractAddress; + use vault_allocator::decoders_and_sanitizers::erc4626_decoder_and_sanitizer::erc4626_decoder_and_sanitizer::Erc4626DecoderAndSanitizerComponent; + use vault_allocator::decoders_and_sanitizers::erc4626_decoder_and_sanitizer::erc4626_decoder_and_sanitizer::Erc4626DecoderAndSanitizerComponent::Erc4626DecoderAndSanitizerImpl; + use vault_allocator::decoders_and_sanitizers::starknet_vault_kit_decoder_and_sanitizer::interface::IStarknetVaultKitDecoderAndSanitizer; + #[storage] + pub struct Storage {} + + #[event] + #[derive(Drop, Debug, PartialEq, starknet::Event)] + pub enum Event {} + + #[embeddable_as(VesuDecoderAndSanitizerImpl)] + impl StarknetVaultKitDecoderAndSanitizer< + TContractState, + +HasComponent, + +Erc4626DecoderAndSanitizerComponent::HasComponent, + > of IStarknetVaultKitDecoderAndSanitizer> { + fn request_redeem( + self: @ComponentState, + shares: u256, + receiver: ContractAddress, + owner: ContractAddress, + ) -> Span { + let mut serialized_struct: Array = ArrayTrait::new(); + receiver.serialize(ref serialized_struct); + owner.serialize(ref serialized_struct); + serialized_struct.span() + } + + fn claim_redeem(self: @ComponentState, id: u256) -> Span { + let mut serialized_struct: Array = ArrayTrait::new(); + id.serialize(ref serialized_struct); + serialized_struct.span() + } + } +} diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/uncap_decoder_and_sanitizer/interface.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/uncap_decoder_and_sanitizer/interface.cairo new file mode 100644 index 00000000..b86d7bac --- /dev/null +++ b/packages/vault_allocator/src/decoders_and_sanitizers/uncap_decoder_and_sanitizer/interface.cairo @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 Starknet Vault Kit +// Licensed under the MIT License. See LICENSE file for details. + +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IUnCapDecoderAndSanitizer { + fn provide_to_sp(self: @T, top_up: u256, do_claim: bool) -> Span; + fn withdraw_from_sp(self: @T, amount: u256, do_claim: bool) -> Span; +} + diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/uncap_decoder_and_sanitizer/uncap_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/uncap_decoder_and_sanitizer/uncap_decoder_and_sanitizer.cairo new file mode 100644 index 00000000..581fa73e --- /dev/null +++ b/packages/vault_allocator/src/decoders_and_sanitizers/uncap_decoder_and_sanitizer/uncap_decoder_and_sanitizer.cairo @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 Starknet Vault Kit +// Licensed under the MIT License. See LICENSE file for details. + +#[starknet::component] +pub mod UnCapDecoderAndSanitizerComponent { + use starknet::ContractAddress; + use vault_allocator::decoders_and_sanitizers::uncap_decoder_and_sanitizer::interface::IUnCapDecoderAndSanitizer; + + #[storage] + pub struct Storage { + pub vault_allocator: ContractAddress, + } + + #[event] + #[derive(Drop, Debug, PartialEq, starknet::Event)] + pub enum Event {} + + #[embeddable_as(UnCapDecoderAndSanitizerImpl)] + impl UnCapDecoderAndSanitizer< + TContractState, +HasComponent, + > of IUnCapDecoderAndSanitizer> { + fn provide_to_sp( + self: @ComponentState, top_up: u256, do_claim: bool, + ) -> Span { + let mut serialized_struct: Array = ArrayTrait::new(); + serialized_struct.span() + } + + fn withdraw_from_sp( + self: @ComponentState, amount: u256, do_claim: bool, + ) -> Span { + let mut serialized_struct: Array = ArrayTrait::new(); + serialized_struct.span() + } + } +} diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/vesu_decoder_and_sanitizer/interface.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/vesu_decoder_and_sanitizer/interface.cairo index e8a25500..40f573f5 100644 --- a/packages/vault_allocator/src/decoders_and_sanitizers/vesu_decoder_and_sanitizer/interface.cairo +++ b/packages/vault_allocator/src/decoders_and_sanitizers/vesu_decoder_and_sanitizer/interface.cairo @@ -3,9 +3,11 @@ // Licensed under the MIT License. See LICENSE file for details. use vault_allocator::decoders_and_sanitizers::decoder_custom_types::ModifyPositionParams; +use starknet::ContractAddress; #[starknet::interface] pub trait IVesuDecoderAndSanitizer { fn modify_position(self: @T, params: ModifyPositionParams) -> Span; + fn modify_delegation(self: @T, delegatee: ContractAddress, delegation: bool) -> Span; } diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/vesu_decoder_and_sanitizer/vesu_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/vesu_decoder_and_sanitizer/vesu_decoder_and_sanitizer.cairo index 91747a65..d3a16b07 100644 --- a/packages/vault_allocator/src/decoders_and_sanitizers/vesu_decoder_and_sanitizer/vesu_decoder_and_sanitizer.cairo +++ b/packages/vault_allocator/src/decoders_and_sanitizers/vesu_decoder_and_sanitizer/vesu_decoder_and_sanitizer.cairo @@ -5,10 +5,9 @@ #[starknet::component] pub mod VesuDecoderAndSanitizerComponent { use vault_allocator::decoders_and_sanitizers::decoder_custom_types::ModifyPositionParams; - use vault_allocator::decoders_and_sanitizers::erc4626_decoder_and_sanitizer::erc4626_decoder_and_sanitizer::Erc4626DecoderAndSanitizerComponent; - use vault_allocator::decoders_and_sanitizers::erc4626_decoder_and_sanitizer::erc4626_decoder_and_sanitizer::Erc4626DecoderAndSanitizerComponent::Erc4626DecoderAndSanitizerImpl; use vault_allocator::decoders_and_sanitizers::vesu_decoder_and_sanitizer::interface::IVesuDecoderAndSanitizer; - + use starknet::ContractAddress; + #[storage] pub struct Storage {} @@ -18,9 +17,7 @@ pub mod VesuDecoderAndSanitizerComponent { #[embeddable_as(VesuDecoderAndSanitizerImpl)] impl VesuDecoderAndSanitizer< - TContractState, - +HasComponent, - +Erc4626DecoderAndSanitizerComponent::HasComponent, + TContractState, +HasComponent, > of IVesuDecoderAndSanitizer> { fn modify_position( self: @ComponentState, params: ModifyPositionParams, @@ -32,5 +29,14 @@ pub mod VesuDecoderAndSanitizerComponent { params.user.serialize(ref serialized_struct); serialized_struct.span() } + + fn modify_delegation( + self: @ComponentState, + delegatee: ContractAddress, delegation: bool + ) -> Span { + let mut serialized_struct: Array = ArrayTrait::new(); + delegatee.serialize(ref serialized_struct); + serialized_struct.span() + } } } diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/vesu_v2_decoder_and_sanitizer/vesu_v2_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/vesu_v2_decoder_and_sanitizer/vesu_v2_decoder_and_sanitizer.cairo index 996c50a1..f4d10f31 100644 --- a/packages/vault_allocator/src/decoders_and_sanitizers/vesu_v2_decoder_and_sanitizer/vesu_v2_decoder_and_sanitizer.cairo +++ b/packages/vault_allocator/src/decoders_and_sanitizers/vesu_v2_decoder_and_sanitizer/vesu_v2_decoder_and_sanitizer.cairo @@ -5,8 +5,6 @@ #[starknet::component] pub mod VesuV2DecoderAndSanitizerComponent { use vault_allocator::decoders_and_sanitizers::decoder_custom_types::ModifyPositionParamsV2; - use vault_allocator::decoders_and_sanitizers::erc4626_decoder_and_sanitizer::erc4626_decoder_and_sanitizer::Erc4626DecoderAndSanitizerComponent; - use vault_allocator::decoders_and_sanitizers::erc4626_decoder_and_sanitizer::erc4626_decoder_and_sanitizer::Erc4626DecoderAndSanitizerComponent::Erc4626DecoderAndSanitizerImpl; use vault_allocator::decoders_and_sanitizers::vesu_v2_decoder_and_sanitizer::interface::IVesuV2DecoderAndSanitizer; #[storage] @@ -16,11 +14,9 @@ pub mod VesuV2DecoderAndSanitizerComponent { #[derive(Drop, Debug, PartialEq, starknet::Event)] pub enum Event {} - #[embeddable_as(VesuDecoderAndSanitizerImpl)] - impl VesuDecoderAndSanitizer< - TContractState, - +HasComponent, - +Erc4626DecoderAndSanitizerComponent::HasComponent, + #[embeddable_as(VesuV2DecoderAndSanitizerImpl)] + impl VesuV2DecoderAndSanitizer< + TContractState, +HasComponent, > of IVesuV2DecoderAndSanitizer> { fn modify_position( self: @ComponentState, params: ModifyPositionParamsV2, diff --git a/packages/vault_allocator/src/decoders_and_sanitizers/vesu_v2_specific_decoder_and_sanitizer.cairo b/packages/vault_allocator/src/decoders_and_sanitizers/vesu_v2_specific_decoder_and_sanitizer.cairo new file mode 100644 index 00000000..3e36004d --- /dev/null +++ b/packages/vault_allocator/src/decoders_and_sanitizers/vesu_v2_specific_decoder_and_sanitizer.cairo @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 Starknet Vault Kit +// Licensed under the MIT License. See LICENSE file for details. + +#[starknet::contract] +pub mod VesuV2SpecificDecoderAndSanitizer { + use vault_allocator::decoders_and_sanitizers::base_decoder_and_sanitizer::BaseDecoderAndSanitizerComponent; + use vault_allocator::decoders_and_sanitizers::vesu_v2_decoder_and_sanitizer::vesu_v2_decoder_and_sanitizer::VesuV2DecoderAndSanitizerComponent; + + component!( + path: BaseDecoderAndSanitizerComponent, + storage: base_decoder_and_sanitizer, + event: BaseDecoderAndSanitizerEvent, + ); + + component!( + path: VesuV2DecoderAndSanitizerComponent, + storage: vesu_v2_decoder_and_sanitizer, + event: VesuV2DecoderAndSanitizerEvent, + ); + + #[abi(embed_v0)] + impl BaseDecoderAndSanitizerImpl = + BaseDecoderAndSanitizerComponent::BaseDecoderAndSanitizerImpl; + + #[abi(embed_v0)] + impl VesuV2DecoderAndSanitizerImpl = + VesuV2DecoderAndSanitizerComponent::VesuV2DecoderAndSanitizerImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub base_decoder_and_sanitizer: BaseDecoderAndSanitizerComponent::Storage, + #[substorage(v0)] + pub vesu_v2_decoder_and_sanitizer: VesuV2DecoderAndSanitizerComponent::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + BaseDecoderAndSanitizerEvent: BaseDecoderAndSanitizerComponent::Event, + #[flat] + VesuV2DecoderAndSanitizerEvent: VesuV2DecoderAndSanitizerComponent::Event, + } +} diff --git a/packages/vault_allocator/src/integration_interfaces/vesu.cairo b/packages/vault_allocator/src/integration_interfaces/vesu.cairo deleted file mode 100644 index 73aaccd4..00000000 --- a/packages/vault_allocator/src/integration_interfaces/vesu.cairo +++ /dev/null @@ -1,396 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2025 Starknet Vault Kit -// Licensed under the MIT License. See LICENSE file for details. - -use starknet::ContractAddress; - -#[starknet::interface] -pub trait ISingletonV2< - TContractState, -> { // fn creator_nonce(self: @TContractState, creator: ContractAddress) -> felt252; - fn extension(self: @TContractState, pool_id: felt252) -> ContractAddress; - // fn asset_config_unsafe( - // self: @TContractState, pool_id: felt252, asset: ContractAddress, - // ) -> (AssetConfig, u256); - // fn asset_config( - // ref self: TContractState, pool_id: felt252, asset: ContractAddress, - // ) -> (AssetConfig, u256); - // fn ltv_config( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // ) -> LTVConfig; - // fn position_unsafe( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // ) -> (Position, u256, u256); - // fn position( - // ref self: TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // ) -> (Position, u256, u256); - // fn check_collateralization_unsafe( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // ) -> (bool, u256, u256); - // fn check_collateralization( - // ref self: TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // ) -> (bool, u256, u256); - // fn rate_accumulator_unsafe( - // self: @TContractState, pool_id: felt252, asset: ContractAddress, - // ) -> u256; - // fn rate_accumulator(ref self: TContractState, pool_id: felt252, asset: ContractAddress) -> - // u256; - // fn utilization_unsafe(self: @TContractState, pool_id: felt252, asset: ContractAddress) -> - // u256; - // fn utilization(ref self: TContractState, pool_id: felt252, asset: ContractAddress) -> u256; - // fn delegation( - // self: @TContractState, - // pool_id: felt252, - // delegator: ContractAddress, - // delegatee: ContractAddress, - // ) -> bool; - // fn calculate_pool_id( - // self: @TContractState, caller_address: ContractAddress, nonce: felt252, - // ) -> felt252; - // fn calculate_debt( - // self: @TContractState, nominal_debt: i257, rate_accumulator: u256, asset_scale: u256, - // ) -> u256; - // fn calculate_nominal_debt( - // self: @TContractState, debt: i257, rate_accumulator: u256, asset_scale: u256, - // ) -> u256; - // fn calculate_collateral_shares_unsafe( - // self: @TContractState, pool_id: felt252, asset: ContractAddress, collateral: i257, - // ) -> u256; - // fn calculate_collateral_shares( - // ref self: TContractState, pool_id: felt252, asset: ContractAddress, collateral: i257, - // ) -> u256; - // fn calculate_collateral_unsafe( - // self: @TContractState, pool_id: felt252, asset: ContractAddress, collateral_shares: i257, - // ) -> u256; - // fn calculate_collateral( - // ref self: TContractState, pool_id: felt252, asset: ContractAddress, collateral_shares: - // i257, - // ) -> u256; - // fn deconstruct_collateral_amount_unsafe( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // collateral: Amount, - // ) -> (i257, i257); - // fn deconstruct_collateral_amount( - // ref self: TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // collateral: Amount, - // ) -> (i257, i257); - // fn deconstruct_debt_amount_unsafe( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // debt: Amount, - // ) -> (i257, i257); - // fn deconstruct_debt_amount( - // ref self: TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // debt: Amount, - // ) -> (i257, i257); - // fn context_unsafe( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // ) -> Context; - // fn context( - // ref self: TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // user: ContractAddress, - // ) -> Context; - // fn create_pool( - // ref self: TContractState, - // asset_params: Span, - // ltv_params: Span, - // extension: ContractAddress, - // ) -> felt252; - // fn modify_position( - // ref self: TContractState, params: ModifyPositionParams, - // ) -> UpdatePositionResponse; - // fn liquidate_position( - // ref self: TContractState, params: LiquidatePositionParams, - // ) -> UpdatePositionResponse; - fn flash_loan( - ref self: TContractState, - receiver: ContractAddress, - asset: ContractAddress, - amount: u256, - is_legacy: bool, - data: Span, - ); - // fn modify_delegation( -// ref self: TContractState, pool_id: felt252, delegatee: ContractAddress, delegation: bool, -// ); -// fn donate_to_reserve( -// ref self: TContractState, pool_id: felt252, asset: ContractAddress, amount: u256, -// ); -// fn retrieve_from_reserve( -// ref self: TContractState, -// pool_id: felt252, -// asset: ContractAddress, -// receiver: ContractAddress, -// amount: u256, -// ); -// fn set_asset_config(ref self: TContractState, pool_id: felt252, params: AssetParams); -// fn set_ltv_config( -// ref self: TContractState, -// pool_id: felt252, -// collateral_asset: ContractAddress, -// debt_asset: ContractAddress, -// ltv_config: LTVConfig, -// ); -// fn set_asset_parameter( -// ref self: TContractState, -// pool_id: felt252, -// asset: ContractAddress, -// parameter: felt252, -// value: u256, -// ); -// fn set_extension(ref self: TContractState, pool_id: felt252, extension: ContractAddress); -// fn set_extension_whitelist( -// ref self: TContractState, extension: ContractAddress, approved: bool, -// ); -// fn claim_fee_shares(ref self: TContractState, pool_id: felt252, asset: ContractAddress); - - // fn migrate_pool( -// ref self: TContractState, -// pool_id: felt252, -// extension: ContractAddress, -// creator: ContractAddress, -// asset_configs: Span<(ContractAddress, AssetConfig)>, -// ltv_configs: Span<(ContractAddress, ContractAddress, LTVConfig)>, -// ); -// fn migrate_position( -// ref self: TContractState, -// pool_id: felt252, -// collateral_asset: ContractAddress, -// debt_asset: ContractAddress, -// from: ContractAddress, -// to: ContractAddress, -// ); -// fn set_migrator(ref self: TContractState, migrator: ContractAddress); - - // fn upgrade_name(self: @TContractState) -> felt252; -// fn upgrade(ref self: TContractState, new_implementation: ClassHash); -} - -#[starknet::interface] -pub trait IDefaultExtensionPOV2< - TContractState, -> { // fn pool_name(self: @TContractState, pool_id: felt252) -> felt252; - // fn pool_owner(self: @TContractState, pool_id: felt252) -> ContractAddress; - // fn shutdown_mode_agent(self: @TContractState, pool_id: felt252) -> ContractAddress; - // fn pragma_oracle(self: @TContractState) -> ContractAddress; - // fn pragma_summary(self: @TContractState) -> ContractAddress; - // fn oracle_config( - // self: @TContractState, pool_id: felt252, asset: ContractAddress, - // ) -> OracleConfig; - // fn fee_config(self: @TContractState, pool_id: felt252) -> FeeConfig; - // fn debt_caps( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // ) -> u256; - // fn interest_rate_config( - // self: @TContractState, pool_id: felt252, asset: ContractAddress, - // ) -> InterestRateConfig; - // fn liquidation_config( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // ) -> LiquidationConfig; - // fn shutdown_config(self: @TContractState, pool_id: felt252) -> ShutdownConfig; - // fn shutdown_ltv_config( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // ) -> LTVConfig; - // fn shutdown_status( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // ) -> ShutdownStatus; - // fn pairs( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // ) -> Pair; - // fn violation_timestamp_for_pair( - // self: @TContractState, - // pool_id: felt252, - // collateral_asset: ContractAddress, - // debt_asset: ContractAddress, - // ) -> u64; - // fn violation_timestamp_count( - // self: @TContractState, pool_id: felt252, violation_timestamp: u64, - // ) -> u128; - // fn oldest_violation_timestamp(self: @TContractState, pool_id: felt252) -> u64; - // fn next_violation_timestamp( - // self: @TContractState, pool_id: felt252, violation_timestamp: u64, - // ) -> u64; - fn v_token_for_collateral_asset( - self: @TContractState, pool_id: felt252, collateral_asset: ContractAddress, - ) -> ContractAddress; - // fn collateral_asset_for_v_token( -// self: @TContractState, pool_id: felt252, v_token: ContractAddress, -// ) -> ContractAddress; -// fn create_pool( -// ref self: TContractState, -// name: felt252, -// asset_params: Span, -// v_token_params: Span, -// ltv_params: Span, -// interest_rate_configs: Span, -// pragma_oracle_params: Span, -// liquidation_params: Span, -// debt_caps: Span, -// shutdown_params: ShutdownParams, -// fee_params: FeeParams, -// owner: ContractAddress, -// ) -> felt252; -// fn add_asset( -// ref self: TContractState, -// pool_id: felt252, -// asset_params: AssetParams, -// v_token_params: VTokenParams, -// interest_rate_config: InterestRateConfig, -// pragma_oracle_params: PragmaOracleParams, -// ); -// fn set_asset_parameter( -// ref self: TContractState, -// pool_id: felt252, -// asset: ContractAddress, -// parameter: felt252, -// value: u256, -// ); -// fn set_debt_cap( -// ref self: TContractState, -// pool_id: felt252, -// collateral_asset: ContractAddress, -// debt_asset: ContractAddress, -// debt_cap: u256, -// ); -// fn set_interest_rate_parameter( -// ref self: TContractState, -// pool_id: felt252, -// asset: ContractAddress, -// parameter: felt252, -// value: u256, -// ); -// fn set_oracle_parameter( -// ref self: TContractState, -// pool_id: felt252, -// asset: ContractAddress, -// parameter: felt252, -// value: felt252, -// ); -// fn set_liquidation_config( -// ref self: TContractState, -// pool_id: felt252, -// collateral_asset: ContractAddress, -// debt_asset: ContractAddress, -// liquidation_config: LiquidationConfig, -// ); -// fn set_ltv_config( -// ref self: TContractState, -// pool_id: felt252, -// collateral_asset: ContractAddress, -// debt_asset: ContractAddress, -// ltv_config: LTVConfig, -// ); -// fn set_shutdown_config( -// ref self: TContractState, pool_id: felt252, shutdown_config: ShutdownConfig, -// ); -// fn set_shutdown_ltv_config( -// ref self: TContractState, -// pool_id: felt252, -// collateral_asset: ContractAddress, -// debt_asset: ContractAddress, -// shutdown_ltv_config: LTVConfig, -// ); -// fn set_shutdown_mode(ref self: TContractState, pool_id: felt252, shutdown_mode: -// ShutdownMode); -// fn set_pool_owner(ref self: TContractState, pool_id: felt252, owner: ContractAddress); -// fn set_shutdown_mode_agent( -// ref self: TContractState, pool_id: felt252, shutdown_mode_agent: ContractAddress, -// ); -// fn update_shutdown_status( -// ref self: TContractState, -// pool_id: felt252, -// collateral_asset: ContractAddress, -// debt_asset: ContractAddress, -// ) -> ShutdownMode; -// fn set_fee_config(ref self: TContractState, pool_id: felt252, fee_config: FeeConfig); -// fn claim_fees(ref self: TContractState, pool_id: felt252, collateral_asset: ContractAddress); - - // fn migrate_pool( -// ref self: TContractState, -// pool_id: felt252, -// name: felt252, -// v_token_configs: Span<(felt252, felt252, ContractAddress, ContractAddress)>, -// interest_rate_configs: Span<(ContractAddress, InterestRateConfig)>, -// pragma_oracle_configs: Span<(ContractAddress, OracleConfig)>, -// liquidation_configs: Span<(ContractAddress, ContractAddress, LiquidationConfig)>, -// pairs: Span<(ContractAddress, ContractAddress, Pair)>, -// debt_caps: Span<(ContractAddress, ContractAddress, u256)>, -// shutdown_ltv_configs: Span<(ContractAddress, ContractAddress, LTVConfig)>, -// shutdown_config: ShutdownConfig, -// fee_config: FeeConfig, -// owner: ContractAddress, -// ); -// fn set_migrator(ref self: TContractState, migrator: ContractAddress); - - // fn set_extension_utils_class_hash(ref self: TContractState, extension: felt252); -// // Upgrade -// fn upgrade_name(self: @TContractState) -> felt252; -// fn upgrade(ref self: TContractState, new_implementation: ClassHash); -} - -#[starknet::interface] -pub trait IFlashloanReceiver { - fn on_flash_loan( - ref self: TContractState, - sender: ContractAddress, - asset: ContractAddress, - amount: u256, - data: Span, - ); -} diff --git a/packages/vault_allocator/src/integration_interfaces/vesu_v1.cairo b/packages/vault_allocator/src/integration_interfaces/vesu_v1.cairo new file mode 100644 index 00000000..ffe96a60 --- /dev/null +++ b/packages/vault_allocator/src/integration_interfaces/vesu_v1.cairo @@ -0,0 +1,23 @@ +use starknet::ContractAddress; + +#[derive(PartialEq, Copy, Drop, Serde)] +pub struct Position { + collateral_shares: u256, + nominal_debt: u256, +} + +#[starknet::interface] +pub trait IV1Token { + fn pool_id(self: @TContractState) -> felt252; +} + +#[starknet::interface] +pub trait ISingletonV2 { + fn position( + ref self: TContractState, + pool_id: felt252, + collateral_asset: ContractAddress, + debt_asset: ContractAddress, + user: ContractAddress, + ) -> (Position, u256, u256); +} diff --git a/packages/vault_allocator/src/integration_interfaces/vesu_v2.cairo b/packages/vault_allocator/src/integration_interfaces/vesu_v2.cairo new file mode 100644 index 00000000..2ce1275c --- /dev/null +++ b/packages/vault_allocator/src/integration_interfaces/vesu_v2.cairo @@ -0,0 +1,28 @@ +use starknet::ContractAddress; +use vault_allocator::integration_interfaces::vesu_v1::Position; + +#[starknet::interface] +pub trait IV2Token { + fn pool_contract(self: @TContractState) -> ContractAddress; +} + +#[starknet::interface] +pub trait IPool { + fn position( + self: @TContractState, + collateral_asset: ContractAddress, + debt_asset: ContractAddress, + user: ContractAddress, + ) -> (Position, u256, u256); +} + + +#[starknet::interface] +pub trait IOracle { + fn price(self: @TContractState, asset: ContractAddress) -> AssetPrice; +} +#[derive(PartialEq, Copy, Drop, Serde, Default)] +pub struct AssetPrice { + pub value: u256, + pub is_valid: bool, +} diff --git a/packages/vault_allocator/src/lib.cairo b/packages/vault_allocator/src/lib.cairo index 00dfd9ae..119f2b15 100644 --- a/packages/vault_allocator/src/lib.cairo +++ b/packages/vault_allocator/src/lib.cairo @@ -17,7 +17,8 @@ pub mod manager { pub mod integration_interfaces { pub mod avnu; pub mod pragma; - pub mod vesu; + pub mod vesu_v1; + pub mod vesu_v2; } pub mod periphery { @@ -41,6 +42,7 @@ pub mod decoders_and_sanitizers { pub mod decoder_custom_types; pub mod interface; pub mod simple_decoder_and_sanitizer; + pub mod vesu_v2_specific_decoder_and_sanitizer; pub mod avnu_exchange_decoder_and_sanitizer { pub mod avnu_exchange_decoder_and_sanitizer; pub mod interface; @@ -53,12 +55,18 @@ pub mod decoders_and_sanitizers { pub mod interface; pub mod vesu_decoder_and_sanitizer; } - + pub mod defi_spring_snf_style { + pub mod defi_spring_snf_style_decoder_and_sanitizer; + pub mod interface; + } + pub mod starknet_vault_kit_decoder_and_sanitizer { + pub mod interface; + pub mod starknet_vault_kit_decoder_and_sanitizer; + } pub mod vesu_v2_decoder_and_sanitizer { pub mod interface; pub mod vesu_v2_decoder_and_sanitizer; } - pub mod multiply_decoder_and_sanitizer { pub mod interface; pub mod multiply_decoder_and_sanitizer; @@ -69,13 +77,12 @@ pub mod mocks { pub mod counter; pub mod erc20; pub mod erc4626; - pub mod flashloan; + pub mod vault; } #[cfg(test)] pub mod test { // pub mod creator; - pub mod register; pub mod utils; pub mod units { pub mod manager; @@ -83,11 +90,10 @@ pub mod test { } pub mod integrations { pub mod avnu; + pub mod vault_bring_liquidity; pub mod vesu_v1; } - pub mod scenarios { - pub mod leveraged_loop_staked_ether; pub mod stable_carry_loop; } } diff --git a/packages/vault_allocator/src/manager/errors.cairo b/packages/vault_allocator/src/manager/errors.cairo index c232c949..8eb9aa4c 100644 --- a/packages/vault_allocator/src/manager/errors.cairo +++ b/packages/vault_allocator/src/manager/errors.cairo @@ -18,16 +18,4 @@ pub mod Errors { pub fn not_vault_allocator() { panic!("Not vault allocator"); } - - pub fn flash_loan_not_executed() { - panic!("Flash loan not executed"); - } - - pub fn not_vesu_singleton() { - panic!("Not vesu singleton"); - } - - pub fn bad_flash_loan_intent_hash() { - panic!("Bad flash loan intent hash"); - } } diff --git a/packages/vault_allocator/src/manager/interface.cairo b/packages/vault_allocator/src/manager/interface.cairo index 21e59cda..e5546961 100644 --- a/packages/vault_allocator/src/manager/interface.cairo +++ b/packages/vault_allocator/src/manager/interface.cairo @@ -6,7 +6,6 @@ use starknet::ContractAddress; #[starknet::interface] pub trait IManager { - fn vesu_singleton(self: @T) -> ContractAddress; fn vault_allocator(self: @T) -> ContractAddress; fn set_manage_root(ref self: T, target: ContractAddress, root: felt252); fn manage_root(self: @T, target: ContractAddress) -> felt252; @@ -20,14 +19,5 @@ pub trait IManager { selectors: Span, calldatas: Span>, ); - - fn flash_loan( - ref self: T, - recipient: ContractAddress, - asset: ContractAddress, - amount: u256, - is_legacy: bool, - data: Span, - ); } diff --git a/packages/vault_allocator/src/manager/manager.cairo b/packages/vault_allocator/src/manager/manager.cairo index 1deeb830..b95db6ee 100644 --- a/packages/vault_allocator/src/manager/manager.cairo +++ b/packages/vault_allocator/src/manager/manager.cairo @@ -8,10 +8,8 @@ pub mod Manager { pub const OWNER_ROLE: felt252 = selector!("OWNER_ROLE"); pub const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE"); use core::hash::HashStateTrait; - use core::num::traits::Zero; use core::pedersen::PedersenTrait; use openzeppelin::access::accesscontrol::AccessControlComponent; - use openzeppelin::interfaces::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; use openzeppelin::interfaces::upgrades::IUpgradeable; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::merkle_tree::merkle_proof; @@ -22,14 +20,9 @@ pub mod Manager { Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, StoragePointerWriteAccess, }; - use starknet::{ContractAddress, get_caller_address, get_contract_address}; - use vault_allocator::integration_interfaces::vesu::{ - IFlashloanReceiver, ISingletonV2Dispatcher, ISingletonV2DispatcherTrait, - }; + use starknet::{ContractAddress, get_caller_address}; use vault_allocator::manager::errors::Errors; - use vault_allocator::manager::interface::{ - IManager, IManagerDispatcher, IManagerDispatcherTrait, - }; + use vault_allocator::manager::interface::IManager; use vault_allocator::vault_allocator::interface::{ IVaultAllocatorDispatcher, IVaultAllocatorDispatcherTrait, }; @@ -50,28 +43,26 @@ pub mod Manager { #[substorage(v0)] pausable: PausableComponent::Storage, vault_allocator: IVaultAllocatorDispatcher, - vesu_singleton: ISingletonV2Dispatcher, manage_root: Map, - flash_loan_intent_hash: felt252, - performing_flash_loan: bool, } #[event] #[derive(Drop, starknet::Event)] pub enum Event { + // #[flat] SRC5Event: SRC5Component::Event, + // #[flat] AccessControlEvent: AccessControlComponent::Event, + // #[flat] UpgradeableEvent: UpgradeableComponent::Event, + // #[flat] PausableEvent: PausableComponent::Event, } #[constructor] fn constructor( - ref self: ContractState, - owner: ContractAddress, - vault_allocator: ContractAddress, - vesu_singleton: ContractAddress, + ref self: ContractState, owner: ContractAddress, vault_allocator: ContractAddress, ) { self.access_control.initializer(); self.access_control.set_role_admin(OWNER_ROLE, OWNER_ROLE); @@ -79,7 +70,6 @@ pub mod Manager { self.access_control._grant_role(OWNER_ROLE, owner); self.access_control._grant_role(PAUSER_ROLE, owner); self.vault_allocator.write(IVaultAllocatorDispatcher { contract_address: vault_allocator }); - self.vesu_singleton.write(ISingletonV2Dispatcher { contract_address: vesu_singleton }); } @@ -101,67 +91,8 @@ pub mod Manager { } } - #[abi(embed_v0)] - impl ManagerFlashloanReceiverImpl of IFlashloanReceiver { - fn on_flash_loan( - ref self: ContractState, - sender: ContractAddress, - asset: ContractAddress, - amount: u256, - data: Span, - ) { - let vesu_singleton = self.vesu_singleton.read().contract_address; - if (get_caller_address() != vesu_singleton) { - Errors::not_vesu_singleton(); - } - let intent_hash = self._get_flash_loan_intent_hash_from_span(data); - if (intent_hash != self.flash_loan_intent_hash.read()) { - Errors::bad_flash_loan_intent_hash(); - } - self.flash_loan_intent_hash.write(0); - - let vault_allocator = self.vault_allocator.read(); - let asset_dispatcher = ERC20ABIDispatcher { contract_address: asset }; - - asset_dispatcher.transfer(vault_allocator.contract_address, amount); - - let mut data = data.clone(); - let (proofs, decoder_and_sanitizers, targets, selectors, calldatas) = Serde::< - ( - Span>, - Span, - Span, - Span, - Span>, - ), - >::deserialize(ref data) - .unwrap(); - - let this = get_contract_address(); - IManagerDispatcher { contract_address: this } - .manage_vault_with_merkle_verification( - proofs, decoder_and_sanitizers, targets, selectors, calldatas, - ); - - let mut calldata = ArrayTrait::new(); - this.serialize(ref calldata); - amount.serialize(ref calldata); - - vault_allocator - .manage( - Call { to: asset, selector: selector!("transfer"), calldata: calldata.span() }, - ); - asset_dispatcher.approve(vesu_singleton, amount); - } - } - - #[abi(embed_v0)] impl ManagerImpl of IManager { - fn vesu_singleton(self: @ContractState) -> ContractAddress { - self.vesu_singleton.read().contract_address - } - fn vault_allocator(self: @ContractState) -> ContractAddress { self.vault_allocator.read().contract_address } @@ -220,26 +151,6 @@ pub mod Manager { .manage(Call { to: target, selector: selector, calldata: calldata }); } } - - fn flash_loan( - ref self: ContractState, - recipient: ContractAddress, - asset: ContractAddress, - amount: u256, - is_legacy: bool, - data: Span, - ) { - if (get_caller_address() != self.vault_allocator.read().contract_address) { - Errors::not_vault_allocator(); - } - self.flash_loan_intent_hash.write(self._get_flash_loan_intent_hash_from_span(data)); - self.performing_flash_loan.write(true); - self.vesu_singleton.read().flash_loan(recipient, asset, amount, is_legacy, data); - self.performing_flash_loan.write(false); - if (self.flash_loan_intent_hash.read().is_non_zero()) { - Errors::flash_loan_not_executed(); - } - } } @@ -305,18 +216,5 @@ pub mod Manager { let leaf_hash = state.finalize(); merkle_proof::verify_pedersen(proof, root, leaf_hash) } - - fn _get_flash_loan_intent_hash_from_span( - self: @ContractState, data: Span, - ) -> felt252 { - let mut serialized_struct: Array = ArrayTrait::new(); - data.serialize(ref serialized_struct); - let first_element = serialized_struct.pop_front().unwrap(); - let mut state = PedersenTrait::new(first_element); - while let Some(value) = serialized_struct.pop_front() { - state = state.update(value); - } - state.finalize() - } } } diff --git a/packages/vault_allocator/src/mocks/flashloan.cairo b/packages/vault_allocator/src/mocks/flashloan.cairo deleted file mode 100644 index cc7ce590..00000000 --- a/packages/vault_allocator/src/mocks/flashloan.cairo +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2025 Starknet Vault Kit -// Licensed under the MIT License. See LICENSE file for details. -use openzeppelin::interfaces::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; -use starknet::{ContractAddress, get_contract_address}; - -fn transfer_asset( - asset: ContractAddress, - sender: ContractAddress, - to: ContractAddress, - amount: u256, - is_legacy: bool, -) { - let erc20 = ERC20ABIDispatcher { contract_address: asset }; - if sender == get_contract_address() { - assert!(erc20.transfer(to, amount), "transfer-failed"); - } else if is_legacy { - assert!(erc20.transferFrom(sender, to, amount), "transferFrom-failed"); - } else { - assert!(erc20.transfer_from(sender, to, amount), "transfer-from-failed"); - } -} - -#[starknet::contract] -pub mod FlashLoanSingletonMock { - use core::num::traits::Zero; - use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - use starknet::{ContractAddress, get_caller_address}; - use vault_allocator::integration_interfaces::vesu::{ - IFlashloanReceiverDispatcher, IFlashloanReceiverDispatcherTrait, - }; - use super::{get_contract_address, transfer_asset}; - - - #[storage] - struct Storage { - do_nothing: bool, - i_did_something: bool, - do_wrong_callback: bool, - } - - #[constructor] - fn constructor(ref self: ContractState) {} - - #[abi(embed_v0)] - impl FlashLoanSingletonMockImpl of super::IFlashLoanSingletonMock { - fn do_wrong_callback(self: @ContractState) -> bool { - self.do_wrong_callback.read() - } - - fn set_do_wrong_callback(ref self: ContractState, do_wrong_callback: bool) { - self.do_wrong_callback.write(do_wrong_callback); - } - - fn flash_loan( - ref self: ContractState, - receiver: ContractAddress, - asset: ContractAddress, - amount: u256, - is_legacy: bool, - data: Span, - ) { - if (!self.do_nothing.read()) { - if (!self.do_wrong_callback.read()) { - transfer_asset(asset, get_contract_address(), receiver, amount, is_legacy); - IFlashloanReceiverDispatcher { contract_address: receiver } - .on_flash_loan(get_caller_address(), asset, amount, data); - transfer_asset(asset, receiver, get_contract_address(), amount, is_legacy); - } else { - let mut flash_loan_data_proofs: Array> = ArrayTrait::new(); - flash_loan_data_proofs.append(array![Zero::zero()].span()); - let mut flash_loan_data_decoder_and_sanitizer: Array = - ArrayTrait::new(); - flash_loan_data_decoder_and_sanitizer.append(Zero::zero()); - let mut flash_loan_data_target: Array = ArrayTrait::new(); - flash_loan_data_target.append(Zero::zero()); - let mut flash_loan_data_selector: Array = ArrayTrait::new(); - flash_loan_data_selector.append(Zero::zero()); - let mut flash_loan_data_calldata: Array> = ArrayTrait::new(); - flash_loan_data_calldata.append(array![Zero::zero()].span()); - let mut serialized_flash_loan_data = ArrayTrait::new(); - ( - flash_loan_data_proofs.span(), - flash_loan_data_decoder_and_sanitizer.span(), - flash_loan_data_target.span(), - flash_loan_data_selector.span(), - flash_loan_data_calldata.span(), - ) - .serialize(ref serialized_flash_loan_data); - - transfer_asset(asset, get_contract_address(), receiver, amount, is_legacy); - IFlashloanReceiverDispatcher { contract_address: receiver } - .on_flash_loan( - get_caller_address(), asset, amount, serialized_flash_loan_data.span(), - ); - transfer_asset(asset, receiver, get_contract_address(), amount, is_legacy); - } - } - } - - fn approve(ref self: ContractState, token: ContractAddress, amount: u256) { - transfer_asset(token, get_caller_address(), get_contract_address(), amount, true); - transfer_asset(token, get_contract_address(), get_caller_address(), amount, true); - self.i_did_something.write(true) - } - - fn i_did_something(self: @ContractState) -> bool { - self.i_did_something.read() - } - - fn do_nothing(self: @ContractState) -> bool { - self.do_nothing.read() - } - - fn set_do_nothing(ref self: ContractState, do_nothing: bool) { - self.do_nothing.write(do_nothing); - } - } -} - -#[starknet::interface] -pub trait IFlashLoanSingletonMock { - fn flash_loan( - ref self: TContractState, - receiver: ContractAddress, - asset: ContractAddress, - amount: u256, - is_legacy: bool, - data: Span, - ); - - fn approve(ref self: TContractState, token: ContractAddress, amount: u256); - - fn i_did_something(self: @TContractState) -> bool; - fn do_nothing(self: @TContractState) -> bool; - fn do_wrong_callback(self: @TContractState) -> bool; - fn set_do_nothing(ref self: TContractState, do_nothing: bool); - fn set_do_wrong_callback(ref self: TContractState, do_wrong_callback: bool); -} diff --git a/packages/vault_allocator/src/mocks/vault.cairo b/packages/vault_allocator/src/mocks/vault.cairo new file mode 100644 index 00000000..05a5aeb8 --- /dev/null +++ b/packages/vault_allocator/src/mocks/vault.cairo @@ -0,0 +1,179 @@ +#[starknet::contract] +pub mod MockVault { + use openzeppelin::interfaces::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; + use openzeppelin::token::erc20::extensions::erc4626::{ + DefaultConfig, ERC4626Component, ERC4626DefaultNoFees, ERC4626DefaultNoLimits, + }; + use openzeppelin::token::erc20::{ + DefaultConfig as ERC20DefaultConfig, ERC20Component, ERC20HooksEmptyImpl, + }; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + use starknet::{ContractAddress, get_caller_address}; + + // --- OpenZeppelin Component Integrations --- + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: ERC4626Component, storage: erc4626, event: ERC4626Event); + + // --- ERC4626 Implementation --- + #[abi(embed_v0)] + impl ERC4626Impl = ERC4626Component::ERC4626Impl; + impl ERC4626InternalImpl = ERC4626Component::InternalImpl; + + // --- ERC20 Implementation --- + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + // --- ERC20 Metadata Implementation --- + #[abi(embed_v0)] + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; + + #[storage] + pub struct Storage { + // --- Component Storage --- + #[substorage(v0)] + erc20: ERC20Component::Storage, + #[substorage(v0)] + erc4626: ERC4626Component::Storage, + // --- Vault State --- + buffer: u256, // Assets available in vault for instant redemption + aum: u256 // Assets under management (deployed to allocators) + } + + #[event] + #[derive(Drop, starknet::Event)] + pub enum Event { + ERC20Event: ERC20Component::Event, + ERC4626Event: ERC4626Component::Event, + } + + #[constructor] + fn constructor(ref self: ContractState, underlying_asset: ContractAddress) { + self.erc20.initializer("MockVault", "MVAULT"); + self.erc4626.initializer(underlying_asset); + } + + + /// Implementation of asset management for ERC4626 compatibility + impl MockVaultAssetsManagementImpl of ERC4626Component::AssetsManagementTrait { + fn get_total_assets(self: @ERC4626Component::ComponentState) -> u256 { + let contract_state = self.get_contract(); + contract_state.buffer.read() + contract_state.aum.read() + } + + fn transfer_assets_in( + ref self: ERC4626Component::ComponentState, + from: ContractAddress, + assets: u256, + ) { + let this = starknet::get_contract_address(); + let asset_dispatcher = ERC20ABIDispatcher { + contract_address: self.ERC4626_asset.read(), + }; + assert( + asset_dispatcher.transfer_from(from, this, assets), + ERC4626Component::Errors::TOKEN_TRANSFER_FAILED, + ); + } + + fn transfer_assets_out( + ref self: ERC4626Component::ComponentState, + to: ContractAddress, + assets: u256, + ) { + let asset_dispatcher = ERC20ABIDispatcher { + contract_address: self.ERC4626_asset.read(), + }; + assert( + asset_dispatcher.transfer(to, assets), + ERC4626Component::Errors::TOKEN_TRANSFER_FAILED, + ); + } + } + + pub impl MockVaultHooksImpl of ERC4626Component::ERC4626HooksTrait { + fn before_withdraw( + ref self: ERC4626Component::ComponentState, + caller: ContractAddress, + receiver: ContractAddress, + owner: ContractAddress, + assets: u256, + shares: u256, + fee: Option, + ) {} + + fn after_withdraw( + ref self: ERC4626Component::ComponentState, + caller: ContractAddress, + receiver: ContractAddress, + owner: ContractAddress, + assets: u256, + shares: u256, + fee: Option, + ) { + let mut contract_state = self.get_contract_mut(); + contract_state.buffer.write(contract_state.buffer.read() - assets); + } + + fn before_deposit( + ref self: ERC4626Component::ComponentState, + caller: ContractAddress, + receiver: ContractAddress, + assets: u256, + shares: u256, + fee: Option, + ) {} + + fn after_deposit( + ref self: ERC4626Component::ComponentState, + caller: ContractAddress, + receiver: ContractAddress, + assets: u256, + shares: u256, + fee: Option, + ) { + let mut contract_state = self.get_contract_mut(); + contract_state.buffer.write(contract_state.buffer.read() + assets); + } + } + + #[abi(embed_v0)] + impl MockVaultImpl of MockVaultTrait { + /// Bring assets back from allocators to vault buffer + fn bring_liquidity(ref self: ContractState, amount: u256) { + ERC20ABIDispatcher { contract_address: self.erc4626.asset() } + .transfer_from(get_caller_address(), starknet::get_contract_address(), amount); + self.buffer.write(self.buffer.read() + amount); + self.aum.write(self.aum.read() - amount); + } + + /// Get current buffer amount + fn buffer(self: @ContractState) -> u256 { + self.buffer.read() + } + + /// Get current assets under management + fn aum(self: @ContractState) -> u256 { + self.aum.read() + } + + /// Set buffer for testing purposes + fn set_buffer(ref self: ContractState, amount: u256) { + self.buffer.write(amount); + } + + /// Set AUM for testing purposes + fn set_aum(ref self: ContractState, amount: u256) { + self.aum.write(amount); + } + } + + #[starknet::interface] + pub trait MockVaultTrait { + fn bring_liquidity(ref self: TContractState, amount: u256); + fn buffer(self: @TContractState) -> u256; + fn aum(self: @TContractState) -> u256; + fn set_buffer(ref self: TContractState, amount: u256); + fn set_aum(ref self: TContractState, amount: u256); + } +} diff --git a/packages/vault_allocator/src/test/creator.cairo b/packages/vault_allocator/src/test/creator.cairo index b40b4796..9f01c5c0 100644 --- a/packages/vault_allocator/src/test/creator.cairo +++ b/packages/vault_allocator/src/test/creator.cairo @@ -3,13 +3,15 @@ // Licensed under the MIT License. See LICENSE file for details. use alexandria_math::i257::I257Impl; use starknet::ContractAddress; -use vault_allocator::test::register::{ETH, GENESIS_POOL_ID, wstETH}; -use vault_allocator::test::utils::{ - ManageLeaf, _add_avnu_leafs, _add_vesu_flash_loan_leafs, _add_vesu_leafs, - _pad_leafs_to_power_of_two, generate_merkle_tree, get_leaf_hash, +use vault_allocator::merkle_tree::base::{ + ManageLeaf, _pad_leafs_to_power_of_two, generate_merkle_tree, get_leaf_hash, }; +use vault_allocator::merkle_tree::integrations::avnu::{AvnuConfig, _add_avnu_leafs}; +use vault_allocator::merkle_tree::integrations::vesu_v1::{VesuV1Config, _add_vesu_v1_leafs}; +use vault_allocator::merkle_tree::registery::{ETH, GENESIS_POOL_ID, wstETH}; use super::utils::DUMMY_ADDRESS; + #[derive(PartialEq, Drop, Serde, Debug, Clone)] pub struct ManageLeafAdditionalData { pub decoder_and_sanitizer: ContractAddress, @@ -34,39 +36,30 @@ fn test_creator() { let router = DUMMY_ADDRESS(); // INTEGRATIONS - let flash_loan_asset = wstETH(); - let is_legacy = false; - _add_vesu_flash_loan_leafs( - ref leafs, - ref leaf_index, - vault_allocator, - decoder_and_sanitizer, - manager, - flash_loan_asset, - is_legacy, - ); let pool_id = GENESIS_POOL_ID; - let mut assets_to_supply = ArrayTrait::new(); - assets_to_supply.append(wstETH()); - let mut assets_to_borrow_per_assets_to_supply = ArrayTrait::new(); - assets_to_borrow_per_assets_to_supply.append(array![ETH()].span()); - _add_vesu_leafs( + _add_vesu_v1_leafs( ref leafs, ref leaf_index, vault_allocator, decoder_and_sanitizer, - pool_id, - assets_to_supply.span(), - assets_to_borrow_per_assets_to_supply.span(), + array![ + VesuV1Config { pool_id, collateral_asset: wstETH(), debt_assets: array![ETH()].span() }, + ] + .span(), ); let mut pairs_to_swap = ArrayTrait::new(); pairs_to_swap.append((ETH(), wstETH())); _add_avnu_leafs( - ref leafs, ref leaf_index, vault_allocator, decoder_and_sanitizer, router, pairs_to_swap, + ref leafs, + ref leaf_index, + vault_allocator, + decoder_and_sanitizer, + router, + array![AvnuConfig { sell_token: ETH(), buy_token: wstETH() }].span(), ); let leaf_used = leafs.len(); diff --git a/packages/vault_allocator/src/test/integrations/avnu.cairo b/packages/vault_allocator/src/test/integrations/avnu.cairo index 4e8b69a0..57360edc 100644 --- a/packages/vault_allocator/src/test/integrations/avnu.cairo +++ b/packages/vault_allocator/src/test/integrations/avnu.cairo @@ -9,27 +9,30 @@ use snforge_std::{map_entry_address, store}; use starknet::ContractAddress; use vault_allocator::decoders_and_sanitizers::decoder_custom_types::Route; use vault_allocator::manager::interface::IManagerDispatcherTrait; -use vault_allocator::middlewares::avnu_middleware::interface::{ - IAvnuMiddlewareDispatcher, IAvnuMiddlewareDispatcherTrait, +use vault_allocator::merkle_tree::base::{ + ManageLeaf, _get_proofs_using_tree, _pad_leafs_to_power_of_two, generate_merkle_tree, }; -use vault_allocator::test::register::{ETH, VESU_SINGLETON, wstETH}; +use vault_allocator::merkle_tree::integrations::avnu::{AvnuConfig, _add_avnu_leafs}; +use vault_allocator::merkle_tree::registery::{ETH, wstETH}; use vault_allocator::test::utils::{ - ManageLeaf, OWNER, STRATEGIST, WAD, _add_avnu_leafs, _get_proofs_using_tree, - _pad_leafs_to_power_of_two, cheat_caller_address_once, deploy_avnu_middleware, deploy_manager, + OWNER, STRATEGIST, WAD, cheat_caller_address_once, deploy_avnu_middleware, deploy_manager, deploy_price_router, deploy_simple_decoder_and_sanitizer, deploy_vault_allocator, - generate_merkle_tree, initialize_price_router, + initialize_price_router, }; use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcherTrait; + #[fork("AVNU")] #[test] fn test_manage_vault_with_merkle_verification_avnu() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); let price_router = deploy_price_router(); initialize_price_router(price_router); - let avnu_middleware = deploy_avnu_middleware(price_router); + let avnu_middleware = deploy_avnu_middleware( + vault_allocator.contract_address, price_router, 100, 100000, 1000000, + ); // 1% slippage let mut leafs: Array = ArrayTrait::new(); let mut leaf_index: u256 = 0; @@ -40,7 +43,7 @@ fn test_manage_vault_with_merkle_verification_avnu() { vault_allocator.contract_address, simple_decoder_and_sanitizer, avnu_middleware, - array![(wstETH(), ETH())], + array![AvnuConfig { sell_token: wstETH(), buy_token: ETH() }].span(), ); _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); @@ -143,10 +146,6 @@ fn test_manage_vault_with_merkle_verification_avnu() { manage_leafs.append(leafs.at(0).clone()); manage_leafs.append(leafs.at(1).clone()); - cheat_caller_address_once(avnu_middleware, OWNER()); - IAvnuMiddlewareDispatcher { contract_address: avnu_middleware } - .set_slippage_tolerance_bps(100); // 1% slippage - let manage_proofs = _get_proofs_using_tree(manage_leafs, tree.clone()); cheat_caller_address_once(manager.contract_address, STRATEGIST()); diff --git a/packages/vault_allocator/src/test/integrations/vault_bring_liquidity.cairo b/packages/vault_allocator/src/test/integrations/vault_bring_liquidity.cairo new file mode 100644 index 00000000..acd56489 --- /dev/null +++ b/packages/vault_allocator/src/test/integrations/vault_bring_liquidity.cairo @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 Starknet Vault Kit +// Licensed under the MIT License. See LICENSE file for details. + +use openzeppelin::interfaces::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; +use openzeppelin::interfaces::erc4626::IERC4626Dispatcher; +use snforge_std::{map_entry_address, store}; +use vault_allocator::manager::interface::IManagerDispatcherTrait; +use vault_allocator::merkle_tree::base::{ + ManageLeaf, _add_vault_allocator_leafs, _get_proofs_using_tree, _pad_leafs_to_power_of_two, + generate_merkle_tree, +}; +use vault_allocator::mocks::vault::MockVault::MockVaultTraitDispatcherTrait; +use vault_allocator::test::utils::{ + OWNER, STRATEGIST, WAD, cheat_caller_address_once, deploy_erc20_mock, deploy_manager, + deploy_mock_vault, deploy_simple_decoder_and_sanitizer, deploy_vault_allocator, +}; +use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcherTrait; + +#[test] +fn test_manage_vault_with_merkle_verification_bring_liquidity() { + let vault_allocator = deploy_vault_allocator(); + let manager = deploy_manager(vault_allocator); + let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); + + let underlying_token = deploy_erc20_mock(); + let mock_vault = deploy_mock_vault(underlying_token); + + let mut leafs: Array = ArrayTrait::new(); + let mut leaf_index: u256 = 0; + + _add_vault_allocator_leafs( + ref leafs, + ref leaf_index, + vault_allocator.contract_address, + simple_decoder_and_sanitizer, + mock_vault.contract_address, + ); + + _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); + + let tree = generate_merkle_tree(leafs.span()); + let root = *tree.at(tree.len() - 1).at(0); + + cheat_caller_address_once(vault_allocator.contract_address, OWNER()); + vault_allocator.set_manager(manager.contract_address); + + cheat_caller_address_once(manager.contract_address, OWNER()); + manager.set_manage_root(STRATEGIST(), root); + + // Set initial balance and liquidity for mock vault + let initial_liquidity: u256 = 10 * WAD; + let initial_buffer: u256 = 5 * WAD; + + // Add underlying token balance to vault allocator + let mut cheat_calldata = ArrayTrait::new(); + initial_liquidity.serialize(ref cheat_calldata); + store( + underlying_token, + map_entry_address( + selector!("ERC20_balances"), array![vault_allocator.contract_address.into()].span(), + ), + cheat_calldata.span(), + ); + + // Set initial buffer and liquidity in mock vault + cheat_caller_address_once(mock_vault.contract_address, OWNER()); + mock_vault.set_buffer(initial_buffer); + + cheat_caller_address_once(mock_vault.contract_address, OWNER()); + mock_vault.set_aum(initial_liquidity); + + let underlying_disp = ERC20ABIDispatcher { contract_address: underlying_token }; + assert( + underlying_disp.balance_of(vault_allocator.contract_address) == initial_liquidity, + 'underlying balance incorrect', + ); + + // Prepare to call bring_liquidity + let mut array_of_decoders_and_sanitizers = ArrayTrait::new(); + array_of_decoders_and_sanitizers.append(simple_decoder_and_sanitizer); + array_of_decoders_and_sanitizers.append(simple_decoder_and_sanitizer); + + let mut array_of_targets = ArrayTrait::new(); + array_of_targets.append(underlying_token); + array_of_targets.append(mock_vault.contract_address); + + let mut array_of_selectors = ArrayTrait::new(); + array_of_selectors.append(selector!("approve")); + array_of_selectors.append(selector!("bring_liquidity")); + + let mut array_of_calldatas = ArrayTrait::new(); + + let bring_liquidity_amount: u256 = 2 * WAD; + + // Approval calldata + let mut array_of_calldata_approve: Array = ArrayTrait::new(); + mock_vault.contract_address.serialize(ref array_of_calldata_approve); + bring_liquidity_amount.serialize(ref array_of_calldata_approve); + array_of_calldatas.append(array_of_calldata_approve.span()); + + // Bring liquidity calldata (empty for this function) + let mut array_of_calldata_bring_liquidity: Array = ArrayTrait::new(); + bring_liquidity_amount.serialize(ref array_of_calldata_bring_liquidity); + array_of_calldatas.append(array_of_calldata_bring_liquidity.span()); + + let mut manage_leafs: Array = ArrayTrait::new(); + manage_leafs.append(leafs.at(0).clone()); + manage_leafs.append(leafs.at(1).clone()); + + let manage_proofs = _get_proofs_using_tree(manage_leafs, tree.clone()); + + cheat_caller_address_once(manager.contract_address, STRATEGIST()); + manager + .manage_vault_with_merkle_verification( + manage_proofs.span(), + array_of_decoders_and_sanitizers.span(), + array_of_targets.span(), + array_of_selectors.span(), + array_of_calldatas.span(), + ); + + // Verify that bring_liquidity was called successfully + // The mock vault should have received some underlying tokens and updated its state + let new_liquidity = underlying_disp.balance_of(vault_allocator.contract_address); + assert( + new_liquidity == initial_liquidity - bring_liquidity_amount, 'tokens should be transferred', + ); + + // Check that the vault has increased liquidity + let new_buffer = mock_vault.buffer(); + let new_aum = mock_vault.aum(); + + assert(new_buffer == initial_buffer + bring_liquidity_amount, 'buffer should increase'); + assert(new_aum == initial_liquidity - bring_liquidity_amount, 'aum should decrease'); +} diff --git a/packages/vault_allocator/src/test/integrations/vesu_v1.cairo b/packages/vault_allocator/src/test/integrations/vesu_v1.cairo index b58c05cf..4e72a03c 100644 --- a/packages/vault_allocator/src/test/integrations/vesu_v1.cairo +++ b/packages/vault_allocator/src/test/integrations/vesu_v1.cairo @@ -9,16 +9,18 @@ use snforge_std::{map_entry_address, store}; use vault_allocator::decoders_and_sanitizers::decoder_custom_types::{ Amount, AmountDenomination, AmountType, }; -use vault_allocator::integration_interfaces::vesu::{ - IDefaultExtensionPOV2Dispatcher, IDefaultExtensionPOV2DispatcherTrait, ISingletonV2Dispatcher, - ISingletonV2DispatcherTrait, -}; use vault_allocator::manager::interface::IManagerDispatcherTrait; -use vault_allocator::test::register::{ETH, GENESIS_POOL_ID, VESU_SINGLETON, wstETH}; +use vault_allocator::merkle_tree::base::{ + ManageLeaf, _get_proofs_using_tree, _pad_leafs_to_power_of_two, generate_merkle_tree, +}; +use vault_allocator::merkle_tree::integrations::erc4626::_add_erc4626_leafs; +use vault_allocator::merkle_tree::integrations::vesu_v1::{VesuV1Config, _add_vesu_v1_leafs}; +use vault_allocator::merkle_tree::registery::{ + ETH, GENESIS_POOL_ID, VESU_GENESIS_POOL_V_TOKEN_WSTETH, VESU_SINGLETON, wstETH, +}; use vault_allocator::test::utils::{ - ManageLeaf, OWNER, STRATEGIST, WAD, _add_vesu_leafs, _get_proofs_using_tree, - _pad_leafs_to_power_of_two, cheat_caller_address_once, deploy_manager, - deploy_simple_decoder_and_sanitizer, deploy_vault_allocator, generate_merkle_tree, + OWNER, STRATEGIST, WAD, cheat_caller_address_once, deploy_manager, + deploy_simple_decoder_and_sanitizer, deploy_vault_allocator, }; use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcherTrait; @@ -26,20 +28,18 @@ use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcherTrait; #[test] fn test_manage_vault_with_merkle_verification_earn_mode() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); let mut leafs: Array = ArrayTrait::new(); let mut leaf_index: u256 = 0; - _add_vesu_leafs( + _add_erc4626_leafs( ref leafs, ref leaf_index, vault_allocator.contract_address, simple_decoder_and_sanitizer, - GENESIS_POOL_ID, - array![wstETH()].span(), - array![array![ETH()].span()].span(), + VESU_GENESIS_POOL_V_TOKEN_WSTETH(), ); _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); @@ -78,11 +78,7 @@ fn test_manage_vault_with_merkle_verification_earn_mode() { let mut array_of_targets = ArrayTrait::new(); array_of_targets.append(wstETH()); - let v_token = IDefaultExtensionPOV2Dispatcher { - contract_address: ISingletonV2Dispatcher { contract_address: VESU_SINGLETON() } - .extension(GENESIS_POOL_ID), - } - .v_token_for_collateral_asset(GENESIS_POOL_ID, wstETH()); + let v_token = VESU_GENESIS_POOL_V_TOKEN_WSTETH(); array_of_targets.append(v_token); let mut array_of_selectors = ArrayTrait::new(); @@ -277,20 +273,25 @@ fn test_manage_vault_with_merkle_verification_earn_mode() { #[test] fn test_manage_vault_with_merkle_verification_debt_mode() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); let mut leafs: Array = ArrayTrait::new(); let mut leaf_index: u256 = 0; - _add_vesu_leafs( + _add_vesu_v1_leafs( ref leafs, ref leaf_index, vault_allocator.contract_address, simple_decoder_and_sanitizer, - GENESIS_POOL_ID, - array![wstETH()].span(), - array![array![ETH()].span()].span(), + array![ + VesuV1Config { + pool_id: GENESIS_POOL_ID, + collateral_asset: wstETH(), + debt_assets: array![ETH()].span(), + }, + ] + .span(), ); _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); @@ -322,10 +323,7 @@ fn test_manage_vault_with_merkle_verification_debt_mode() { // first scenario is depositing wsteth to vesu genesis pool, transfer the position and borrow // ETH let deposit_amount: u256 = WAD; - let extension = ISingletonV2Dispatcher { contract_address: VESU_SINGLETON() } - .extension(GENESIS_POOL_ID); - let v_token = IDefaultExtensionPOV2Dispatcher { contract_address: extension } - .v_token_for_collateral_asset(GENESIS_POOL_ID, wstETH()); + let debt_amount: u256 = WAD / 40; // 2.5% of the deposit let mut array_of_decoders_and_sanitizers = ArrayTrait::new(); @@ -385,8 +383,8 @@ fn test_manage_vault_with_merkle_verification_debt_mode() { array_of_calldatas.append(array_of_calldata_modify_position.span()); let mut manage_leafs: Array = ArrayTrait::new(); - manage_leafs.append(leafs.at(5).clone()); - manage_leafs.append(leafs.at(6).clone()); + manage_leafs.append(leafs.at(0).clone()); + manage_leafs.append(leafs.at(1).clone()); let manage_proofs = _get_proofs_using_tree(manage_leafs, tree.clone()); cheat_caller_address_once(manager.contract_address, STRATEGIST()); diff --git a/packages/vault_allocator/src/test/register.cairo b/packages/vault_allocator/src/test/register.cairo deleted file mode 100644 index 5a8cf769..00000000 --- a/packages/vault_allocator/src/test/register.cairo +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2025 Starknet Vault Kit -// Licensed under the MIT License. See LICENSE file for details. - -// tokens -use starknet::ContractAddress; - -pub fn STRK() -> ContractAddress { - 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d.try_into().unwrap() -} - -pub fn WBTC() -> ContractAddress { - 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac.try_into().unwrap() -} - -pub fn USDC() -> ContractAddress { - 0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8.try_into().unwrap() -} - -pub fn USDT() -> ContractAddress { - 0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8.try_into().unwrap() -} - -pub fn ETH() -> ContractAddress { - 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7.try_into().unwrap() -} - -pub fn DAI() -> ContractAddress { - 0x05574eb6b8789a91466f902c380d978e472db68170ff82a5b650b95a58ddf4ad.try_into().unwrap() -} - -pub fn wstETH() -> ContractAddress { - 0x0057912720381af14b0e5c87aa4718ed5e527eab60b3801ebf702ab09139e38b.try_into().unwrap() -} - - -// VESU - -pub fn VESU_SINGLETON() -> ContractAddress { - 0x000d8d6dfec4d33bfb6895de9f3852143a17c6f92fd2a21da3d6924d34870160.try_into().unwrap() -} - -pub const GENESIS_POOL_ID: felt252 = - 2198503327643286920898110335698706244522220458610657370981979460625005526824; - -// PRAGMA - -pub fn PRAGMA() -> ContractAddress { - 0x2a85bd616f912537c50a49a4076db02c00b29b2cdc8a197ce92ed1837fa875b.try_into().unwrap() -} - - -pub fn STRK_PRAGMA_ID() -> felt252 { - 6004514686061859652.try_into().unwrap() -} - -pub fn WBTC_PRAGMA_ID() -> felt252 { - 6287680677296296772.try_into().unwrap() -} - -pub fn USDC_PRAGMA_ID() -> felt252 { - 6148332971638477636.try_into().unwrap() -} - -pub fn USDT_PRAGMA_ID() -> felt252 { - 6148333044652921668.try_into().unwrap() -} - -pub fn ETH_PRAGMA_ID() -> felt252 { - 19514442401534788.try_into().unwrap() -} - -pub fn DAI_PRAGMA_ID() -> felt252 { - 19212080998863684.try_into().unwrap() -} - -pub fn wstETH_PRAGMA_ID() -> felt252 { - 412383036120118613857092.try_into().unwrap() -} - - -// AVNU -pub fn AVNU_ROUTER() -> ContractAddress { - 0x04270219d365d6b017231b52e92b3fb5d7c8378b05e9abc97724537a80e93b0f.try_into().unwrap() -} diff --git a/packages/vault_allocator/src/test/scenarios/leveraged_loop_staked_ether.cairo b/packages/vault_allocator/src/test/scenarios/leveraged_loop_staked_ether.cairo deleted file mode 100644 index 5fef2aa9..00000000 --- a/packages/vault_allocator/src/test/scenarios/leveraged_loop_staked_ether.cairo +++ /dev/null @@ -1,272 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2025 Starknet Vault Kit -// Licensed under the MIT License. See LICENSE file for details. -use alexandria_math::i257::{I257Impl, I257Trait}; -use core::num::traits::Zero; -use openzeppelin::interfaces::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; -use snforge_std::{map_entry_address, store}; -use starknet::ContractAddress; -use vault_allocator::decoders_and_sanitizers::decoder_custom_types::{ - Amount, AmountDenomination, AmountType, Route, -}; -use vault_allocator::manager::interface::IManagerDispatcherTrait; -use vault_allocator::test::register::{ETH, GENESIS_POOL_ID, VESU_SINGLETON, wstETH}; -use vault_allocator::test::utils::{ - ManageLeaf, OWNER, STRATEGIST, WAD, _add_avnu_leafs, _add_vesu_flash_loan_leafs, - _add_vesu_leafs, _get_proofs_using_tree, _pad_leafs_to_power_of_two, cheat_caller_address_once, - deploy_avnu_middleware, deploy_manager, deploy_price_router, - deploy_simple_decoder_and_sanitizer, deploy_vault_allocator, generate_merkle_tree, - initialize_price_router, -}; -use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcherTrait; - -#[fork("SLLSE")] -#[test] -fn test_leveraged_loop_staked_ether() { - let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); - let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); - let price_router = deploy_price_router(); - initialize_price_router(price_router); - let avnu_middleware = deploy_avnu_middleware(price_router); - - let mut leafs: Array = ArrayTrait::new(); - let mut leaf_index: u256 = 0; - - let collateral_asset = wstETH(); - - _add_vesu_flash_loan_leafs( - ref leafs, - ref leaf_index, - vault_allocator.contract_address, - simple_decoder_and_sanitizer, - manager.contract_address, - collateral_asset, - false, - ); - - _add_vesu_leafs( - ref leafs, - ref leaf_index, - vault_allocator.contract_address, - simple_decoder_and_sanitizer, - GENESIS_POOL_ID, - array![wstETH()].span(), - array![array![ETH()].span()].span(), - ); - - _add_avnu_leafs( - ref leafs, - ref leaf_index, - vault_allocator.contract_address, - simple_decoder_and_sanitizer, - avnu_middleware, - array![(ETH(), wstETH())], - ); - - _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); - - let tree = generate_merkle_tree(leafs.span()); - let root = *tree.at(tree.len() - 1).at(0); - cheat_caller_address_once(vault_allocator.contract_address, OWNER()); - vault_allocator.set_manager(manager.contract_address); - - cheat_caller_address_once(manager.contract_address, OWNER()); - manager.set_manage_root(STRATEGIST(), root); - - cheat_caller_address_once(manager.contract_address, OWNER()); - manager.set_manage_root(manager.contract_address, root); - - // config - let collateral_asset = wstETH(); - let initial_collateral_balance: u256 = WAD; - let collateral_to_flash_loan: u256 = WAD; - let debt_asset = ETH(); - let allowed_slippage: u256 = 10; // 0.1% - let required_debt_amount_to_refund_flash_loan = 1208100829164930048 - + 1208100829164930048 * allowed_slippage / 10000; - - let mut cheat_calldata = ArrayTrait::new(); - initial_collateral_balance.serialize(ref cheat_calldata); - store( - collateral_asset, - map_entry_address( - selector!("ERC20_balances"), array![vault_allocator.contract_address.into()].span(), - ), - cheat_calldata.span(), - ); - - let underlying_disp = ERC20ABIDispatcher { contract_address: collateral_asset }; - assert( - underlying_disp.balance_of(vault_allocator.contract_address) == initial_collateral_balance, - 'wsteth balance is not correct', - ); - - let mut array_of_decoders_and_sanitizers = ArrayTrait::new(); - array_of_decoders_and_sanitizers.append(simple_decoder_and_sanitizer); - - let mut array_of_targets = ArrayTrait::new(); - array_of_targets.append(manager.contract_address); - - let mut array_of_selectors = ArrayTrait::new(); - array_of_selectors.append(selector!("flash_loan")); - - let mut array_of_calldatas = ArrayTrait::new(); - let mut array_of_calldatas_flash_loan = ArrayTrait::new(); - manager.contract_address.serialize(ref array_of_calldatas_flash_loan); - collateral_asset.serialize(ref array_of_calldatas_flash_loan); - collateral_to_flash_loan.serialize(ref array_of_calldatas_flash_loan); - false.serialize(ref array_of_calldatas_flash_loan); - - let mut flash_loan_data_decoder_and_sanitizer: Array = ArrayTrait::new(); - flash_loan_data_decoder_and_sanitizer.append(simple_decoder_and_sanitizer); - flash_loan_data_decoder_and_sanitizer.append(simple_decoder_and_sanitizer); - flash_loan_data_decoder_and_sanitizer.append(simple_decoder_and_sanitizer); - flash_loan_data_decoder_and_sanitizer.append(simple_decoder_and_sanitizer); - - let mut flash_loan_data_target: Array = ArrayTrait::new(); - flash_loan_data_target.append(collateral_asset); - flash_loan_data_target.append(VESU_SINGLETON()); - flash_loan_data_target.append(debt_asset); - flash_loan_data_target.append(avnu_middleware); - - let mut flash_loan_data_selector: Array = ArrayTrait::new(); - flash_loan_data_selector.append(selector!("approve")); - flash_loan_data_selector.append(selector!("modify_position")); - flash_loan_data_selector.append(selector!("approve")); - flash_loan_data_selector.append(selector!("multi_route_swap")); - - let mut flash_loan_data_calldata: Array> = ArrayTrait::new(); - - // approve wsteth initial collateral + flash loan amount - let mut flash_loan_data_calldata_approve = ArrayTrait::new(); - VESU_SINGLETON().serialize(ref flash_loan_data_calldata_approve); - (initial_collateral_balance + collateral_to_flash_loan) - .serialize(ref flash_loan_data_calldata_approve); - flash_loan_data_calldata.append(flash_loan_data_calldata_approve.span()); - - // modify position supplying wsteth initial collateral + flash loan amount and borrowing eth - // equivalent to refund flashloan - let mut flash_loan_data_calldata_modify_position = ArrayTrait::new(); - GENESIS_POOL_ID.serialize(ref flash_loan_data_calldata_modify_position); - wstETH().serialize(ref flash_loan_data_calldata_modify_position); - ETH().serialize(ref flash_loan_data_calldata_modify_position); - vault_allocator.contract_address.serialize(ref flash_loan_data_calldata_modify_position); - - let value_for_collateral_modify_position = I257Trait::new( - initial_collateral_balance + collateral_to_flash_loan, false, - ); - let collateral_modify_position: Amount = Amount { - amount_type: AmountType::Delta, - denomination: AmountDenomination::Assets, - value: value_for_collateral_modify_position, - }; - collateral_modify_position.serialize(ref flash_loan_data_calldata_modify_position); - - let value_for_debt_modify_position = I257Trait::new( - required_debt_amount_to_refund_flash_loan, false, - ); - let debt_modify_position: Amount = Amount { - amount_type: AmountType::Delta, - denomination: AmountDenomination::Assets, - value: value_for_debt_modify_position, - }; - debt_modify_position.serialize(ref flash_loan_data_calldata_modify_position); - - let data: Span = array![].span(); - data.serialize(ref flash_loan_data_calldata_modify_position); - - flash_loan_data_calldata.append(flash_loan_data_calldata_modify_position.span()); - - // approve debt asset to avnu middleware - let mut flash_loan_data_calldata_approve_debt_asset: Array = ArrayTrait::new(); - avnu_middleware.serialize(ref flash_loan_data_calldata_approve_debt_asset); - required_debt_amount_to_refund_flash_loan - .serialize(ref flash_loan_data_calldata_approve_debt_asset); - flash_loan_data_calldata.append(flash_loan_data_calldata_approve_debt_asset.span()); - - // multi route swap: sell debt asset to avnu middleware for collateral to refund flashloan - let mut array_of_calldata_multi_route_swap: Array = ArrayTrait::new(); - debt_asset.serialize(ref array_of_calldata_multi_route_swap); - required_debt_amount_to_refund_flash_loan.serialize(ref array_of_calldata_multi_route_swap); - collateral_asset.serialize(ref array_of_calldata_multi_route_swap); - // buy token amount is 0 because we are selling - let buy_token_amount: u256 = Zero::zero(); - buy_token_amount.serialize(ref array_of_calldata_multi_route_swap); - // buy_token_min_amount is set to 0 because we are protected by price router whatever - let buy_token_min_amount: u256 = Zero::zero(); - buy_token_min_amount.serialize(ref array_of_calldata_multi_route_swap); - vault_allocator.contract_address.serialize(ref array_of_calldata_multi_route_swap); - let integrator_fee_amount_bps: u128 = Zero::zero(); - integrator_fee_amount_bps.serialize(ref array_of_calldata_multi_route_swap); - let integrator_fee_recipient: ContractAddress = Zero::zero(); - integrator_fee_recipient.serialize(ref array_of_calldata_multi_route_swap); - - let mut routes: Array = ArrayTrait::new(); - // hardcode route to ekubo wsteth/eth - let mut additional_swap_params: Array = ArrayTrait::new(); - collateral_asset.serialize(ref additional_swap_params); - debt_asset.serialize(ref additional_swap_params); - - let fee: u128 = 0x68db8bac710cb4000000000000000; - fee.serialize(ref additional_swap_params); - let tick_spacing: u128 = 0xc8; - tick_spacing.serialize(ref additional_swap_params); - let extension: ContractAddress = Zero::zero(); - extension.serialize(ref additional_swap_params); - let sqrt_ratio_distance: felt252 = 0x290d5f61e20000000000000000000; - sqrt_ratio_distance.serialize(ref additional_swap_params); - - routes - .append( - Route { - sell_token: debt_asset, - buy_token: collateral_asset, - exchange_address: 0x5dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b - .try_into() - .unwrap(), // ekubo - percent: 1000000000000, - additional_swap_params, - }, - ); - - routes.serialize(ref array_of_calldata_multi_route_swap); - - flash_loan_data_calldata.append(array_of_calldata_multi_route_swap.span()); - - let mut flash_loan_manager_leafs: Array = ArrayTrait::new(); - flash_loan_manager_leafs.append(leafs.at(6).clone()); - flash_loan_manager_leafs.append(leafs.at(7).clone()); - flash_loan_manager_leafs.append(leafs.at(8).clone()); - flash_loan_manager_leafs.append(leafs.at(9).clone()); - - let mut flash_loan_proofs = _get_proofs_using_tree(flash_loan_manager_leafs, tree.clone()); - - let mut serialized_flash_loan_data = ArrayTrait::new(); - ( - flash_loan_proofs.span(), - flash_loan_data_decoder_and_sanitizer.span(), - flash_loan_data_target.span(), - flash_loan_data_selector.span(), - flash_loan_data_calldata.span(), - ) - .serialize(ref serialized_flash_loan_data); - - serialized_flash_loan_data.span().serialize(ref array_of_calldatas_flash_loan); - array_of_calldatas.append(array_of_calldatas_flash_loan.span()); - - let mut manage_leafs: Array = ArrayTrait::new(); - manage_leafs.append(leafs.at(0).clone()); - - let proofs = _get_proofs_using_tree(manage_leafs, tree.clone()); - - cheat_caller_address_once(manager.contract_address, STRATEGIST()); - manager - .manage_vault_with_merkle_verification( - proofs.span(), - array_of_decoders_and_sanitizers.span(), - array_of_targets.span(), - array_of_selectors.span(), - array_of_calldatas.span(), - ); -} diff --git a/packages/vault_allocator/src/test/scenarios/stable_carry_loop.cairo b/packages/vault_allocator/src/test/scenarios/stable_carry_loop.cairo index 89924332..47e6f9f9 100644 --- a/packages/vault_allocator/src/test/scenarios/stable_carry_loop.cairo +++ b/packages/vault_allocator/src/test/scenarios/stable_carry_loop.cairo @@ -9,20 +9,20 @@ use starknet::ContractAddress; use vault_allocator::decoders_and_sanitizers::decoder_custom_types::{ Amount, AmountDenomination, AmountType, Route, }; -use vault_allocator::integration_interfaces::vesu::{ - IDefaultExtensionPOV2Dispatcher, IDefaultExtensionPOV2DispatcherTrait, ISingletonV2Dispatcher, - ISingletonV2DispatcherTrait, -}; use vault_allocator::manager::interface::IManagerDispatcherTrait; -use vault_allocator::middlewares::avnu_middleware::interface::{ - IAvnuMiddlewareDispatcher, IAvnuMiddlewareDispatcherTrait, +use vault_allocator::merkle_tree::base::{ + ManageLeaf, _get_proofs_using_tree, _pad_leafs_to_power_of_two, generate_merkle_tree, +}; +use vault_allocator::merkle_tree::integrations::avnu::{AvnuConfig, _add_avnu_leafs}; +use vault_allocator::merkle_tree::integrations::erc4626::_add_erc4626_leafs; +use vault_allocator::merkle_tree::integrations::vesu_v1::{VesuV1Config, _add_vesu_v1_leafs}; +use vault_allocator::merkle_tree::registery::{ + ETH, GENESIS_POOL_ID, USDC, USDT, VESU_GENESIS_POOL_V_TOKEN_USDT, VESU_SINGLETON, }; -use vault_allocator::test::register::{ETH, GENESIS_POOL_ID, USDC, USDT, VESU_SINGLETON}; use vault_allocator::test::utils::{ - ManageLeaf, OWNER, STRATEGIST, WAD, _add_avnu_leafs, _add_vesu_leafs, _get_proofs_using_tree, - _pad_leafs_to_power_of_two, cheat_caller_address_once, deploy_avnu_middleware, deploy_manager, + OWNER, STRATEGIST, WAD, cheat_caller_address_once, deploy_avnu_middleware, deploy_manager, deploy_price_router, deploy_simple_decoder_and_sanitizer, deploy_vault_allocator, - generate_merkle_tree, initialize_price_router, + initialize_price_router, }; use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcherTrait; @@ -30,22 +30,40 @@ use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcherTrait; #[test] fn test_stable_carry_loop() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); let price_router = deploy_price_router(); initialize_price_router(price_router); - let avnu_middleware = deploy_avnu_middleware(price_router); + let avnu_middleware = deploy_avnu_middleware( + vault_allocator.contract_address, price_router, 100, 100000, 1000000, + ); let mut leafs: Array = ArrayTrait::new(); let mut leaf_index: u256 = 0; - _add_vesu_leafs( + + // add vesu v1 leafs + _add_vesu_v1_leafs( + ref leafs, + ref leaf_index, + vault_allocator.contract_address, + simple_decoder_and_sanitizer, + array![ + VesuV1Config { + pool_id: GENESIS_POOL_ID, + collateral_asset: ETH(), + debt_assets: array![USDC()].span(), + }, + ] + .span(), + ); + + // add erc4626 leafs for usdt v-token + _add_erc4626_leafs( ref leafs, ref leaf_index, vault_allocator.contract_address, simple_decoder_and_sanitizer, - GENESIS_POOL_ID, - array![ETH(), USDT()].span(), - array![array![USDC()].span(), array![].span()].span(), + VESU_GENESIS_POOL_V_TOKEN_USDT(), ); _add_avnu_leafs( @@ -54,7 +72,7 @@ fn test_stable_carry_loop() { vault_allocator.contract_address, simple_decoder_and_sanitizer, avnu_middleware, - array![(USDC(), USDT())], + array![AvnuConfig { sell_token: USDC(), buy_token: USDT() }].span(), ); _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); @@ -109,12 +127,7 @@ fn test_stable_carry_loop() { array_of_targets.append(USDC()); array_of_targets.append(avnu_middleware); array_of_targets.append(USDT()); - let v_token = IDefaultExtensionPOV2Dispatcher { - contract_address: ISingletonV2Dispatcher { contract_address: VESU_SINGLETON() } - .extension(GENESIS_POOL_ID), - } - .v_token_for_collateral_asset(GENESIS_POOL_ID, USDT()); - array_of_targets.append(v_token); + array_of_targets.append(VESU_GENESIS_POOL_V_TOKEN_USDT()); let mut array_of_selectors = ArrayTrait::new(); array_of_selectors.append(selector!("approve")); @@ -214,7 +227,7 @@ fn test_stable_carry_loop() { // approve usdt to the v-token let mut array_of_calldata_approve_vtoken: Array = ArrayTrait::new(); - v_token.serialize(ref array_of_calldata_approve_vtoken); + VESU_GENESIS_POOL_V_TOKEN_USDT().serialize(ref array_of_calldata_approve_vtoken); earn_amount_from_swap.serialize(ref array_of_calldata_approve_vtoken); array_of_calldatas.append(array_of_calldata_approve_vtoken.span()); @@ -225,16 +238,12 @@ fn test_stable_carry_loop() { array_of_calldatas.append(array_of_calldata_deposit.span()); let mut manage_leafs: Array = ArrayTrait::new(); - manage_leafs.append(leafs.at(5).clone()); - manage_leafs.append(leafs.at(6).clone()); - manage_leafs.append(leafs.at(12).clone()); - manage_leafs.append(leafs.at(13).clone()); + manage_leafs.append(leafs.at(0).clone()); + manage_leafs.append(leafs.at(1).clone()); manage_leafs.append(leafs.at(7).clone()); manage_leafs.append(leafs.at(8).clone()); - - cheat_caller_address_once(avnu_middleware, OWNER()); - IAvnuMiddlewareDispatcher { contract_address: avnu_middleware } - .set_slippage_tolerance_bps(100); // 1% slippage + manage_leafs.append(leafs.at(2).clone()); + manage_leafs.append(leafs.at(3).clone()); let manage_proofs = _get_proofs_using_tree(manage_leafs, tree.clone()); diff --git a/packages/vault_allocator/src/test/units/manager.cairo b/packages/vault_allocator/src/test/units/manager.cairo index 3df3f8ee..aa0590b8 100644 --- a/packages/vault_allocator/src/test/units/manager.cairo +++ b/packages/vault_allocator/src/test/units/manager.cairo @@ -8,30 +8,24 @@ use openzeppelin::interfaces::accesscontrol::{ use openzeppelin::interfaces::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; use openzeppelin::interfaces::security::pausable::{IPausableDispatcher, IPausableDispatcherTrait}; use openzeppelin::interfaces::upgrades::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait}; -use starknet::ContractAddress; -use vault_allocator::integration_interfaces::vesu::{ - IFlashloanReceiverDispatcher, IFlashloanReceiverDispatcherTrait, -}; use vault_allocator::manager::interface::{IManagerDispatcher, IManagerDispatcherTrait}; use vault_allocator::manager::manager::Manager::{OWNER_ROLE, PAUSER_ROLE}; -use vault_allocator::mocks::counter::{ICounterDispatcher, ICounterDispatcherTrait}; -use vault_allocator::mocks::flashloan::{ - IFlashLoanSingletonMockDispatcher, IFlashLoanSingletonMockDispatcherTrait, +use vault_allocator::merkle_tree::base::{ + ManageLeaf, _get_proofs_using_tree, _pad_leafs_to_power_of_two, generate_merkle_tree, }; -use vault_allocator::test::register::VESU_SINGLETON; +use vault_allocator::merkle_tree::integrations::erc4626::_add_erc4626_leafs; +use vault_allocator::mocks::counter::{ICounterDispatcher, ICounterDispatcherTrait}; use vault_allocator::test::utils::{ - DUMMY_ADDRESS, ManageLeaf, OWNER, STRATEGIST, WAD, _add_erc4626_leafs, - _add_vesu_flash_loan_leafs, _get_proofs_using_tree, _pad_leafs_to_power_of_two, - cheat_caller_address_once, deploy_counter, deploy_erc20_mock, deploy_erc4626_mock, - deploy_flashloan_mock, deploy_manager, deploy_simple_decoder_and_sanitizer, - deploy_vault_allocator, generate_merkle_tree, + DUMMY_ADDRESS, OWNER, STRATEGIST, WAD, cheat_caller_address_once, deploy_counter, + deploy_erc20_mock, deploy_erc4626_mock, deploy_manager, deploy_simple_decoder_and_sanitizer, + deploy_vault_allocator, }; use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcherTrait; #[test] fn test_constructor() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let access_control_dispatcher = IAccessControlDispatcher { contract_address: manager.contract_address, }; @@ -44,7 +38,6 @@ fn test_constructor() { let has_role = access_control_dispatcher.has_role(PAUSER_ROLE, OWNER()); assert(has_role, 'Pauser is not set correctly'); - assert(manager.vesu_singleton() == VESU_SINGLETON(), ' singleton is not set correctly'); assert( manager.vault_allocator() == vault_allocator.contract_address, 'allocator is not set correctly', @@ -55,7 +48,7 @@ fn test_constructor() { #[should_panic(expected: ('Caller is missing role',))] fn test_upgrade_not_owner() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let (_, counter_class_hash) = deploy_counter(); IUpgradeableDispatcher { contract_address: manager.contract_address } .upgrade(counter_class_hash); @@ -64,7 +57,7 @@ fn test_upgrade_not_owner() { #[test] fn test_upgrade() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let (_, counter_class_hash) = deploy_counter(); cheat_caller_address_once(manager.contract_address, OWNER()); IUpgradeableDispatcher { contract_address: manager.contract_address } @@ -75,7 +68,7 @@ fn test_upgrade() { #[test] fn test_set_manage_root() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let target = 0x123.try_into().unwrap(); let root = 0x456; @@ -91,7 +84,7 @@ fn test_set_manage_root() { #[should_panic(expected: ('Caller is missing role',))] fn test_set_manage_root_not_owner() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let target = 0x123.try_into().unwrap(); let root = 0x456; @@ -102,7 +95,7 @@ fn test_set_manage_root_not_owner() { #[test] fn test_pause() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let manager_dispatcher = IManagerDispatcher { contract_address: manager.contract_address }; let pausable_dispatcher = IPausableDispatcher { contract_address: manager.contract_address }; @@ -118,14 +111,14 @@ fn test_pause() { #[should_panic(expected: ('Caller is missing role',))] fn test_pause_not_pauser() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); manager.pause(); } #[test] fn test_unpause() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let manager_dispatcher = IManagerDispatcher { contract_address: manager.contract_address }; let pausable_dispatcher = IPausableDispatcher { contract_address: manager.contract_address }; @@ -143,7 +136,7 @@ fn test_unpause() { #[should_panic(expected: ('Caller is missing role',))] fn test_unpause_not_owner() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); cheat_caller_address_once(manager.contract_address, OWNER()); manager.pause(); manager.unpause(); @@ -153,7 +146,7 @@ fn test_unpause_not_owner() { #[should_panic(expected: "Inconsistent lengths")] fn test_manage_vault_with_merkle_verification_inconsistent_lengths_1() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); manager .manage_vault_with_merkle_verification( array![array![].span()].span(), @@ -168,7 +161,7 @@ fn test_manage_vault_with_merkle_verification_inconsistent_lengths_1() { #[should_panic(expected: "Inconsistent lengths")] fn test_manage_vault_with_merkle_verification_inconsistent_lengths_2() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); manager .manage_vault_with_merkle_verification( array![array![].span()].span(), @@ -183,7 +176,7 @@ fn test_manage_vault_with_merkle_verification_inconsistent_lengths_2() { #[should_panic(expected: "Inconsistent lengths")] fn test_manage_vault_with_merkle_verification_inconsistent_lengths_3() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); manager .manage_vault_with_merkle_verification( @@ -199,7 +192,7 @@ fn test_manage_vault_with_merkle_verification_inconsistent_lengths_3() { #[should_panic(expected: "Inconsistent lengths")] fn test_manage_vault_with_merkle_verification_inconsistent_lengths_4() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); manager .manage_vault_with_merkle_verification( @@ -211,11 +204,27 @@ fn test_manage_vault_with_merkle_verification_inconsistent_lengths_4() { ); } +#[test] +#[should_panic(expected: "Invalid manage proof length")] +fn test_manage_vault_with_merkle_verification_empty_proof() { + let vault_allocator = deploy_vault_allocator(); + let manager = deploy_manager(vault_allocator); + + manager + .manage_vault_with_merkle_verification( + array![array![].span()].span(), + array![DUMMY_ADDRESS()].span(), + array![DUMMY_ADDRESS()].span(), + array![3].span(), + array![array![].span()].span(), + ); +} + #[test] #[should_panic(expected: "Invalid manage proof")] fn test_manage_vault_with_merkle_verification_invalid_proof() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); let underlying = deploy_erc20_mock(); let erc4626 = deploy_erc4626_mock(underlying); @@ -272,7 +281,7 @@ fn test_manage_vault_with_merkle_verification_invalid_proof() { #[test] fn test_manage_vault_with_merkle_verification_valid_proof() { let vault_allocator = deploy_vault_allocator(); - let manager = deploy_manager(vault_allocator, VESU_SINGLETON()); + let manager = deploy_manager(vault_allocator); let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); let underlying = deploy_erc20_mock(); let erc4626 = deploy_erc4626_mock(underlying); @@ -351,364 +360,3 @@ fn test_manage_vault_with_merkle_verification_valid_proof() { assert(underlying_balance == WAD * 9, 'Balance wrong'); } -#[test] -#[should_panic(expected: "Not vault allocator")] -fn test_flash_loan_not_vault_allocator() { - let vault_allocator = deploy_vault_allocator(); - let flashloan_mock = deploy_flashloan_mock(); - let manager = deploy_manager(vault_allocator, flashloan_mock); - manager - .flash_loan( - manager.contract_address, manager.contract_address, WAD, false, array!['0xdead'].span(), - ); -} - -#[test] -#[should_panic(expected: "Not vesu singleton")] -fn test_on_flash_loan_not_vesu_singleton() { - let vault_allocator = deploy_vault_allocator(); - let flashloan_mock = deploy_flashloan_mock(); - let manager = deploy_manager(vault_allocator, flashloan_mock); - let flash_loan_recipient_dispatcher = IFlashloanReceiverDispatcher { - contract_address: manager.contract_address, - }; - flash_loan_recipient_dispatcher - .on_flash_loan(DUMMY_ADDRESS(), DUMMY_ADDRESS(), WAD, array!['0xdead'].span()); -} - -#[test] -#[should_panic(expected: "Flash loan not executed")] -fn test_flash_loan_not_executed() { - let vault_allocator = deploy_vault_allocator(); - let flashloan_mock = deploy_flashloan_mock(); - let underlying = deploy_erc20_mock(); - let underlying_disp = ERC20ABIDispatcher { contract_address: underlying }; - cheat_caller_address_once(underlying, OWNER()); - underlying_disp.transfer(flashloan_mock, WAD * 10); - let manager = deploy_manager(vault_allocator, flashloan_mock); - let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); - - let mut leafs: Array = ArrayTrait::new(); - let mut leaf_index: u256 = 0; - - _add_vesu_flash_loan_leafs( - ref leafs, - ref leaf_index, - vault_allocator.contract_address, - simple_decoder_and_sanitizer, - manager.contract_address, - underlying, - false, - ); - - _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); - - let tree = generate_merkle_tree(leafs.span()); - - let root = *tree.at(tree.len() - 1).at(0); - cheat_caller_address_once(vault_allocator.contract_address, OWNER()); - vault_allocator.set_manager(manager.contract_address); - - cheat_caller_address_once(manager.contract_address, OWNER()); - manager.set_manage_root(STRATEGIST(), root); - - // Since the manager calls to itself to fulfill the flashloan, we need to set its root. - cheat_caller_address_once(manager.contract_address, OWNER()); - manager.set_manage_root(manager.contract_address, root); - - let mut array_of_decoders_and_sanitizers = ArrayTrait::new(); - array_of_decoders_and_sanitizers.append(simple_decoder_and_sanitizer); - - let mut array_of_targets = ArrayTrait::new(); - array_of_targets.append(manager.contract_address); - - let mut array_of_selectors = ArrayTrait::new(); - array_of_selectors.append(selector!("flash_loan")); - - let mut array_of_calldatas = ArrayTrait::new(); - let mut array_of_calldatas_flash_loan = ArrayTrait::new(); - manager.contract_address.serialize(ref array_of_calldatas_flash_loan); - underlying.serialize(ref array_of_calldatas_flash_loan); - WAD.serialize(ref array_of_calldatas_flash_loan); - false.serialize(ref array_of_calldatas_flash_loan); - - let mut flash_loan_data_proofs: Array> = ArrayTrait::new(); - let mut flash_loan_data_decoder_and_sanitizer: Array = ArrayTrait::new(); - let mut flash_loan_data_target: Array = ArrayTrait::new(); - let mut flash_loan_data_selector: Array = ArrayTrait::new(); - let mut flash_loan_data_calldata: Array> = ArrayTrait::new(); - let mut serialized_flash_loan_data = ArrayTrait::new(); - ( - flash_loan_data_proofs.span(), - flash_loan_data_decoder_and_sanitizer.span(), - flash_loan_data_target.span(), - flash_loan_data_selector.span(), - flash_loan_data_calldata.span(), - ) - .serialize(ref serialized_flash_loan_data); - - serialized_flash_loan_data.span().serialize(ref array_of_calldatas_flash_loan); - array_of_calldatas.append(array_of_calldatas_flash_loan.span()); - - let mut manage_leafs: Array = ArrayTrait::new(); - manage_leafs.append(leafs.at(0).clone()); - - let proofs = _get_proofs_using_tree(manage_leafs, tree.clone()); - - IFlashLoanSingletonMockDispatcher { contract_address: flashloan_mock }.set_do_nothing(true); - - cheat_caller_address_once(manager.contract_address, STRATEGIST()); - manager - .manage_vault_with_merkle_verification( - proofs.span(), - array_of_decoders_and_sanitizers.span(), - array_of_targets.span(), - array_of_selectors.span(), - array_of_calldatas.span(), - ); -} - -#[test] -#[should_panic(expected: "Bad flash loan intent hash")] -fn test_flash_loan_bad_flash_loan_intent_hash() { - let vault_allocator = deploy_vault_allocator(); - let flashloan_mock = deploy_flashloan_mock(); - let underlying = deploy_erc20_mock(); - let underlying_disp = ERC20ABIDispatcher { contract_address: underlying }; - cheat_caller_address_once(underlying, OWNER()); - underlying_disp.transfer(flashloan_mock, WAD * 10); - let manager = deploy_manager(vault_allocator, flashloan_mock); - let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); - - let mut leafs: Array = ArrayTrait::new(); - let mut leaf_index: u256 = 0; - - _add_vesu_flash_loan_leafs( - ref leafs, - ref leaf_index, - vault_allocator.contract_address, - simple_decoder_and_sanitizer, - manager.contract_address, - underlying, - false, - ); - - _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); - - let tree = generate_merkle_tree(leafs.span()); - - let root = *tree.at(tree.len() - 1).at(0); - cheat_caller_address_once(vault_allocator.contract_address, OWNER()); - vault_allocator.set_manager(manager.contract_address); - - cheat_caller_address_once(manager.contract_address, OWNER()); - manager.set_manage_root(STRATEGIST(), root); - - // Since the manager calls to itself to fulfill the flashloan, we need to set its root. - cheat_caller_address_once(manager.contract_address, OWNER()); - manager.set_manage_root(manager.contract_address, root); - - let mut array_of_decoders_and_sanitizers = ArrayTrait::new(); - array_of_decoders_and_sanitizers.append(simple_decoder_and_sanitizer); - - let mut array_of_targets = ArrayTrait::new(); - array_of_targets.append(manager.contract_address); - - let mut array_of_selectors = ArrayTrait::new(); - array_of_selectors.append(selector!("flash_loan")); - - let mut array_of_calldatas = ArrayTrait::new(); - let mut array_of_calldatas_flash_loan = ArrayTrait::new(); - manager.contract_address.serialize(ref array_of_calldatas_flash_loan); - underlying.serialize(ref array_of_calldatas_flash_loan); - WAD.serialize(ref array_of_calldatas_flash_loan); - false.serialize(ref array_of_calldatas_flash_loan); - - let mut flash_loan_data_proofs: Array> = ArrayTrait::new(); - let mut flash_loan_data_decoder_and_sanitizer: Array = ArrayTrait::new(); - let mut flash_loan_data_target: Array = ArrayTrait::new(); - let mut flash_loan_data_selector: Array = ArrayTrait::new(); - let mut flash_loan_data_calldata: Array> = ArrayTrait::new(); - let mut serialized_flash_loan_data = ArrayTrait::new(); - ( - flash_loan_data_proofs.span(), - flash_loan_data_decoder_and_sanitizer.span(), - flash_loan_data_target.span(), - flash_loan_data_selector.span(), - flash_loan_data_calldata.span(), - ) - .serialize(ref serialized_flash_loan_data); - - serialized_flash_loan_data.span().serialize(ref array_of_calldatas_flash_loan); - array_of_calldatas.append(array_of_calldatas_flash_loan.span()); - - let mut manage_leafs: Array = ArrayTrait::new(); - manage_leafs.append(leafs.at(0).clone()); - - let proofs = _get_proofs_using_tree(manage_leafs, tree.clone()); - - IFlashLoanSingletonMockDispatcher { contract_address: flashloan_mock } - .set_do_wrong_callback(true); - - cheat_caller_address_once(manager.contract_address, STRATEGIST()); - manager - .manage_vault_with_merkle_verification( - proofs.span(), - array_of_decoders_and_sanitizers.span(), - array_of_targets.span(), - array_of_selectors.span(), - array_of_calldatas.span(), - ); -} - -#[test] -fn test_flash_loan_bad_flash_loan() { - let vault_allocator = deploy_vault_allocator(); - let flashloan_mock = deploy_flashloan_mock(); - let underlying = deploy_erc20_mock(); - let underlying_disp = ERC20ABIDispatcher { contract_address: underlying }; - cheat_caller_address_once(underlying, OWNER()); - underlying_disp.transfer(flashloan_mock, WAD * 10); - let manager = deploy_manager(vault_allocator, flashloan_mock); - let simple_decoder_and_sanitizer = deploy_simple_decoder_and_sanitizer(); - - let mut leafs: Array = ArrayTrait::new(); - let mut leaf_index: u256 = 0; - - _add_vesu_flash_loan_leafs( - ref leafs, - ref leaf_index, - vault_allocator.contract_address, - simple_decoder_and_sanitizer, - manager.contract_address, - underlying, - false, - ); - - let amount_flash_loan = WAD; - - // approve the mock flashloan token to spend underlying - - let mut argument_addresses_approve = ArrayTrait::new(); - flashloan_mock.serialize(ref argument_addresses_approve); - leafs - .append( - ManageLeaf { - decoder_and_sanitizer: simple_decoder_and_sanitizer, - target: underlying, - selector: selector!("approve"), - argument_addresses: argument_addresses_approve.span(), - description: "", - }, - ); - - leaf_index += 1; - - // do something function called approve from the mock flashloan - let mut argument_addresses_fake_approve_func = ArrayTrait::new(); - underlying.serialize(ref argument_addresses_fake_approve_func); - leafs - .append( - ManageLeaf { - decoder_and_sanitizer: simple_decoder_and_sanitizer, - target: flashloan_mock, - selector: selector!("approve"), - argument_addresses: argument_addresses_fake_approve_func.span(), - description: "Approve", - }, - ); - leaf_index += 1; - - _pad_leafs_to_power_of_two(ref leafs, ref leaf_index); - - let tree = generate_merkle_tree(leafs.span()); - - let root = *tree.at(tree.len() - 1).at(0); - cheat_caller_address_once(vault_allocator.contract_address, OWNER()); - vault_allocator.set_manager(manager.contract_address); - - cheat_caller_address_once(manager.contract_address, OWNER()); - manager.set_manage_root(STRATEGIST(), root); - - // Since the manager calls to itself to fulfill the flashloan, we need to set its root. - cheat_caller_address_once(manager.contract_address, OWNER()); - manager.set_manage_root(manager.contract_address, root); - - let mut array_of_decoders_and_sanitizers = ArrayTrait::new(); - array_of_decoders_and_sanitizers.append(simple_decoder_and_sanitizer); - - let mut array_of_targets = ArrayTrait::new(); - array_of_targets.append(manager.contract_address); - - let mut array_of_selectors = ArrayTrait::new(); - array_of_selectors.append(selector!("flash_loan")); - - let mut array_of_calldatas = ArrayTrait::new(); - let mut array_of_calldatas_flash_loan = ArrayTrait::new(); - manager.contract_address.serialize(ref array_of_calldatas_flash_loan); - underlying.serialize(ref array_of_calldatas_flash_loan); - WAD.serialize(ref array_of_calldatas_flash_loan); - false.serialize(ref array_of_calldatas_flash_loan); - - /// construct the flash loan data to trigger do_something from the flashloan mock - let mut flash_loan_data_decoder_and_sanitizer: Array = ArrayTrait::new(); - flash_loan_data_decoder_and_sanitizer.append(simple_decoder_and_sanitizer); - flash_loan_data_decoder_and_sanitizer.append(simple_decoder_and_sanitizer); - - let mut flash_loan_data_target: Array = ArrayTrait::new(); - flash_loan_data_target.append(underlying); - flash_loan_data_target.append(flashloan_mock); - - let mut flash_loan_data_selector: Array = ArrayTrait::new(); - flash_loan_data_selector.append(selector!("approve")); - flash_loan_data_selector.append(selector!("approve")); - - let mut flash_loan_data_calldata: Array> = ArrayTrait::new(); - let mut flash_loan_data_calldata_approve = ArrayTrait::new(); - flashloan_mock.serialize(ref flash_loan_data_calldata_approve); - amount_flash_loan.serialize(ref flash_loan_data_calldata_approve); - - let mut flash_loan_data_calldata_approve_fake = ArrayTrait::new(); - underlying.serialize(ref flash_loan_data_calldata_approve_fake); - amount_flash_loan.serialize(ref flash_loan_data_calldata_approve_fake); - flash_loan_data_calldata.append(flash_loan_data_calldata_approve.span()); - flash_loan_data_calldata.append(flash_loan_data_calldata_approve_fake.span()); - - let mut flash_loan_manager_leafs: Array = ArrayTrait::new(); - flash_loan_manager_leafs.append(leafs.at(1).clone()); - flash_loan_manager_leafs.append(leafs.at(2).clone()); - - let mut flash_loan_proofs = _get_proofs_using_tree(flash_loan_manager_leafs, tree.clone()); - - let mut serialized_flash_loan_data = ArrayTrait::new(); - ( - flash_loan_proofs.span(), - flash_loan_data_decoder_and_sanitizer.span(), - flash_loan_data_target.span(), - flash_loan_data_selector.span(), - flash_loan_data_calldata.span(), - ) - .serialize(ref serialized_flash_loan_data); - - serialized_flash_loan_data.span().serialize(ref array_of_calldatas_flash_loan); - array_of_calldatas.append(array_of_calldatas_flash_loan.span()); - - let mut manage_leafs: Array = ArrayTrait::new(); - manage_leafs.append(leafs.at(0).clone()); - - let proofs = _get_proofs_using_tree(manage_leafs, tree.clone()); - - cheat_caller_address_once(manager.contract_address, STRATEGIST()); - manager - .manage_vault_with_merkle_verification( - proofs.span(), - array_of_decoders_and_sanitizers.span(), - array_of_targets.span(), - array_of_selectors.span(), - array_of_calldatas.span(), - ); - assert( - IFlashLoanSingletonMockDispatcher { contract_address: flashloan_mock }.i_did_something(), - 'i_did_something is not true', - ); -} diff --git a/packages/vault_allocator/src/test/utils.cairo b/packages/vault_allocator/src/test/utils.cairo index d000d071..050deec2 100644 --- a/packages/vault_allocator/src/test/utils.cairo +++ b/packages/vault_allocator/src/test/utils.cairo @@ -2,32 +2,22 @@ // Copyright (c) 2025 Starknet Vault Kit // Licensed under the MIT License. See LICENSE file for details. -use core::hash::HashStateTrait; -use core::num::traits::Zero; -use core::pedersen::PedersenTrait; -use openzeppelin::interfaces::erc4626::{IERC4626Dispatcher, IERC4626DispatcherTrait}; use openzeppelin::merkle_tree::hashes::PedersenCHasher; use snforge_std::{CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare}; -use starknet::syscalls::call_contract_syscall; use starknet::{ClassHash, ContractAddress}; -use vault_allocator::integration_interfaces::vesu::{ - IDefaultExtensionPOV2Dispatcher, IDefaultExtensionPOV2DispatcherTrait, ISingletonV2Dispatcher, - ISingletonV2DispatcherTrait, -}; use vault_allocator::manager::interface::IManagerDispatcher; +use vault_allocator::merkle_tree::registery::{ + DAI, DAI_PRAGMA_ID, ETH, ETH_PRAGMA_ID, PRAGMA, STRK, STRK_PRAGMA_ID, USDC, USDC_PRAGMA_ID, + USDT, USDT_PRAGMA_ID, WBTC, WBTC_PRAGMA_ID, wstETH, wstETH_PRAGMA_ID, +}; use vault_allocator::mocks::counter::ICounterDispatcher; +use vault_allocator::mocks::vault::MockVault::MockVaultTraitDispatcher; use vault_allocator::periphery::price_router::interface::{ IPriceRouterDispatcher, IPriceRouterDispatcherTrait, }; -use vault_allocator::test::register::{ - AVNU_ROUTER, DAI, DAI_PRAGMA_ID, ETH, ETH_PRAGMA_ID, PRAGMA, STRK, STRK_PRAGMA_ID, USDC, - USDC_PRAGMA_ID, USDT, USDT_PRAGMA_ID, VESU_SINGLETON, WBTC, WBTC_PRAGMA_ID, wstETH, - wstETH_PRAGMA_ID, -}; use vault_allocator::vault_allocator::interface::IVaultAllocatorDispatcher; pub const WAD: u256 = 1_000_000_000_000_000_000; pub const INITIAL_SLIPPAGE_BPS: u256 = 100; // 1% -use core::to_byte_array::FormatAsByteArray; pub fn OWNER() -> ContractAddress { @@ -55,14 +45,11 @@ pub fn deploy_vault_allocator() -> IVaultAllocatorDispatcher { IVaultAllocatorDispatcher { contract_address: vault_allocator_address } } -pub fn deploy_manager( - vault_allocator: IVaultAllocatorDispatcher, vesu_singleton: ContractAddress, -) -> IManagerDispatcher { +pub fn deploy_manager(vault_allocator: IVaultAllocatorDispatcher) -> IManagerDispatcher { let manager = declare("Manager").unwrap().contract_class(); let mut calldata = ArrayTrait::new(); OWNER().serialize(ref calldata); vault_allocator.contract_address.serialize(ref calldata); - vesu_singleton.serialize(ref calldata); let (manager_address, _) = manager.deploy(@calldata).unwrap(); IManagerDispatcher { contract_address: manager_address } } @@ -76,12 +63,6 @@ pub fn deploy_counter() -> (ICounterDispatcher, ClassHash) { (ICounterDispatcher { contract_address: counter_address }, *counter.class_hash) } -pub fn deploy_flashloan_mock() -> ContractAddress { - let flashloan = declare("FlashLoanSingletonMock").unwrap().contract_class(); - let mut calldata = ArrayTrait::new(); - let (flashloan_address, _) = flashloan.deploy(@calldata).unwrap(); - flashloan_address -} pub fn deploy_erc4626_mock(underlying: ContractAddress) -> ContractAddress { let erc4626 = declare("Erc4626Mock").unwrap().contract_class(); @@ -101,6 +82,14 @@ pub fn deploy_erc20_mock() -> ContractAddress { erc20_address } +pub fn deploy_mock_vault(underlying: ContractAddress) -> MockVaultTraitDispatcher { + let mock_vault = declare("MockVault").unwrap().contract_class(); + let mut calldata = ArrayTrait::new(); + underlying.serialize(ref calldata); + let (mock_vault_address, _) = mock_vault.deploy(@calldata).unwrap(); + MockVaultTraitDispatcher { contract_address: mock_vault_address } +} + pub fn deploy_simple_decoder_and_sanitizer() -> ContractAddress { let simple_decoder_and_sanitizer = declare("SimpleDecoderAndSanitizer") .unwrap() @@ -145,13 +134,21 @@ pub fn initialize_price_router(price_router: ContractAddress) { price_router.set_asset_to_id(DAI(), DAI_PRAGMA_ID()); } -pub fn deploy_avnu_middleware(price_router: ContractAddress) -> ContractAddress { +pub fn deploy_avnu_middleware( + vault_allocator: ContractAddress, + price_router: ContractAddress, + slippage: u16, + period: u64, + allowed_calls_per_period: u64, +) -> ContractAddress { let avnu_middleware = declare("AvnuMiddleware").unwrap().contract_class(); let mut calldata = ArrayTrait::new(); OWNER().serialize(ref calldata); - AVNU_ROUTER().serialize(ref calldata); + vault_allocator.serialize(ref calldata); price_router.serialize(ref calldata); - INITIAL_SLIPPAGE_BPS.serialize(ref calldata); + slippage.serialize(ref calldata); + period.serialize(ref calldata); + allowed_calls_per_period.serialize(ref calldata); let (avnu_middleware_address, _) = avnu_middleware.deploy(@calldata).unwrap(); avnu_middleware_address } @@ -163,526 +160,3 @@ pub fn cheat_caller_address_once( cheat_caller_address(:contract_address, :caller_address, span: CheatSpan::TargetCalls(1)); } - -#[derive(PartialEq, Drop, Serde, Debug, Clone)] -pub struct ManageLeaf { - pub decoder_and_sanitizer: ContractAddress, - pub target: ContractAddress, - pub selector: felt252, - pub argument_addresses: Span, - pub description: ByteArray, -} - -pub fn get_leaf_hash(leaf: ManageLeaf) -> felt252 { - let mut serialized_struct: Array = ArrayTrait::new(); - leaf.decoder_and_sanitizer.serialize(ref serialized_struct); - leaf.target.serialize(ref serialized_struct); - leaf.selector.serialize(ref serialized_struct); - leaf.argument_addresses.serialize(ref serialized_struct); - let first_element = serialized_struct.pop_front().unwrap(); - let mut state = PedersenTrait::new(first_element); - while let Some(value) = serialized_struct.pop_front() { - state = state.update(value); - } - state.finalize() -} - -pub fn generate_merkle_tree(manage_leafs: Span) -> Array> { - let mut first_layer = ArrayTrait::new(); - let leafs_length = manage_leafs.len(); - for i in 0..leafs_length { - first_layer.append(get_leaf_hash(manage_leafs.at(i).clone())); - } - let mut leafs = ArrayTrait::new(); - leafs.append(first_layer); - _build_tree(leafs) -} - -pub fn _build_tree(merkle_tree_in: Array>) -> Array> { - let merkle_tree_in_length = merkle_tree_in.len(); - - let mut current_layer_index = merkle_tree_in_length - 1; - let current_layer_length = merkle_tree_in[current_layer_index].len(); - let mut next_layer_length = 0; - if (current_layer_length % 2 != 0) { - next_layer_length = (current_layer_length + 1) / 2; - } else { - next_layer_length = current_layer_length / 2; - } - let mut current_layer = ArrayTrait::new(); - let mut count = 0; - let mut i = 0; - while i < current_layer_length { - current_layer - .append( - PedersenCHasher::commutative_hash( - *merkle_tree_in[current_layer_index].at(i), - *merkle_tree_in[current_layer_index].at(i + 1), - ), - ); - count += 1; - i += 2; - } - - let mut merkle_tree_out = merkle_tree_in.clone(); - merkle_tree_out.append(current_layer); - - if (next_layer_length > 1) { - _build_tree(merkle_tree_out) - } else { - merkle_tree_out - } -} - -fn _next_power_of_two(x: u256) -> u256 { - let mut power = 1_u256; - while power < x { - power = power * 2_u256; - } - power -} -// pub fn _pad_leafs_to_power_of_two(ref leafs: Array, ref leaf_index: u256) { -// let next_power = _next_power_of_two(leaf_index); -// let padding_needed = next_power - leaf_index; - -// let default_leaf = ManageLeaf { -// decoder_and_sanitizer: Zero::zero(), -// target: Zero::zero(), -// selector: Zero::zero(), -// argument_addresses: ArrayTrait::new().span(), -// }; - -// let mut i = 0; -// while i < padding_needed { -// leafs.append(default_leaf); -// leaf_index += 1; -// i += 1; -// } -// } - -pub fn _pad_leafs_to_power_of_two(ref leafs: Array, ref leaf_index: u256) { - let target_len = if leaf_index < 4_u256 { - 4_u256 - } else { - _next_power_of_two(leaf_index) - }; - let padding_needed = target_len - leaf_index; - - let mut i: u256 = 0_u256; - while i < padding_needed { - leafs - .append( - ManageLeaf { - decoder_and_sanitizer: Zero::zero(), - target: Zero::zero(), - selector: Zero::zero(), - argument_addresses: ArrayTrait::new().span(), - description: "", - }, - ); - leaf_index += 1_u256; - i += 1_u256; - } -} - - -pub fn _get_proofs_using_tree( - leafs: Array, tree: Array>, -) -> Array> { - let mut proofs = ArrayTrait::new(); - for i in 0..leafs.len() { - let leaf = leafs.at(i); - let mut serialized_struct: Array = ArrayTrait::new(); - leaf.decoder_and_sanitizer.serialize(ref serialized_struct); - leaf.target.serialize(ref serialized_struct); - leaf.selector.serialize(ref serialized_struct); - leaf.argument_addresses.serialize(ref serialized_struct); - let first_element = serialized_struct.pop_front().unwrap(); - let mut state = PedersenTrait::new(first_element); - - while let Some(value) = serialized_struct.pop_front() { - state = state.update(value); - } - let leaf_hash = state.finalize(); - let proof = _generate_proof(leaf_hash, tree.clone()); - proofs.append(proof); - } - proofs -} - -pub fn _generate_proof(mut leaf: felt252, tree: Array>) -> Span { - let tree_length = tree.len(); - let mut proof = ArrayTrait::new(); - for i in 0..tree_length - 1 { - let tree_current_layer = tree.at(i); - let tree_current_layer_length = tree_current_layer.len(); - for j in 0..tree_current_layer_length { - if leaf == *tree_current_layer.at(j) { - let element_to_append = if j % 2 == 0 { - *tree_current_layer.at(j + 1) - } else { - *tree_current_layer.at(j - 1) - }; - leaf = PedersenCHasher::commutative_hash(leaf, element_to_append); - proof.append(element_to_append); - break; - } else { - assert(j != tree_current_layer_length - 1, 'leaf not found in tree'); - } - } - } - proof.span() -} - - -// ========================================= ERC4626 ========================================= - -pub fn _add_erc4626_leafs( - ref leafs: Array, - ref leaf_index: u256, - vault: ContractAddress, - decoder_and_sanitizer: ContractAddress, - erc4626: ContractAddress, -) { - let erc4626_erc4646_disp = IERC4626Dispatcher { contract_address: erc4626 }; - let asset = erc4626_erc4646_disp.asset(); - - // Approvals - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: asset, - selector: selector!("approve"), - argument_addresses: array![erc4626.into()].span(), - description: "Approve" - + " " - + get_symbol(erc4626) - + " " - + "to spend" - + " " - + get_symbol(asset), - }, - ); - leaf_index += 1; - - // Deposits - - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: erc4626, - selector: selector!("deposit"), - argument_addresses: array![vault.into()].span(), - description: "Deposit" - + " " - + get_symbol(asset) - + " " - + "for" - + " " - + get_symbol(erc4626), - }, - ); - leaf_index += 1; - - // Withdrawals - - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: erc4626, - selector: selector!("withdraw"), - argument_addresses: array![vault.into(), vault.into()].span(), - description: "Withdraw" - + " " - + get_symbol(asset) - + " " - + "from" - + " " - + get_symbol(erc4626), - }, - ); - leaf_index += 1; - - // Minting - - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: erc4626, - selector: selector!("mint"), - argument_addresses: array![vault.into()].span(), - description: "Mint" - + " " - + get_symbol(erc4626) - + " " - + "from" - + " " - + get_symbol(asset), - }, - ); - leaf_index += 1; - - // Redeeming - - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: erc4626, - selector: selector!("redeem"), - argument_addresses: array![vault.into(), vault.into()].span(), - description: "Redeem" - + " " - + get_symbol(erc4626) - + " " - + "for" - + " " - + get_symbol(asset), - }, - ); - leaf_index += 1; -} - - -// ========================================= VESU ========================================= - -pub fn _add_vesu_leafs( - ref leafs: Array, - ref leaf_index: u256, - vault: ContractAddress, - decoder_and_sanitizer: ContractAddress, - pool_id: felt252, - collateral_assets: Span, - debt_assets_per_collateral_asset: Span>, -) { - assert(collateral_assets.len() == debt_assets_per_collateral_asset.len(), 'inconsistent len'); - let singleton = ISingletonV2Dispatcher { contract_address: VESU_SINGLETON() }; - let pool_extension = singleton.extension(pool_id); - assert(pool_extension != Zero::zero(), 'pool extension not found'); - let pool_extension = IDefaultExtensionPOV2Dispatcher { contract_address: pool_extension }; - - for i in 0..collateral_assets.len() { - let collateral_asset = *collateral_assets.at(i); - let debt_assets = *debt_assets_per_collateral_asset.at(i); - - let v_token = pool_extension.v_token_for_collateral_asset(pool_id, collateral_asset); - assert(v_token != Zero::zero(), 'v token not found'); - - // earn mode - _add_erc4626_leafs(ref leafs, ref leaf_index, vault, decoder_and_sanitizer, v_token); - - // debt mode - let debt_assets_len = debt_assets.len(); - if debt_assets_len > 0 { - for j in 0..debt_assets_len { - let mut pool_id_str: ByteArray = FormatAsByteArray::format_as_byte_array( - @pool_id, 16, - ); - - // APPROVAL of collateral asset to the singleton - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: collateral_asset, - selector: selector!("approve"), - argument_addresses: array![VESU_SINGLETON().into()].span(), - description: "Approve" - + " " - + "singleton" - + "_" - + pool_id_str.clone() - + " " - + "to spend" - + " " - + get_symbol(collateral_asset), - }, - ); - leaf_index += 1; - - let debt_asset = *debt_assets.at(j); - - // MODIFY POSITION - let mut argument_addresses_modify_position = ArrayTrait::new(); - - // pool_id - pool_id.serialize(ref argument_addresses_modify_position); - - // collateral_asset - collateral_asset.serialize(ref argument_addresses_modify_position); - - // debt_asset - debt_asset.serialize(ref argument_addresses_modify_position); - - // user - vault.serialize(ref argument_addresses_modify_position); - - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: singleton.contract_address, - selector: selector!("modify_position"), - argument_addresses: argument_addresses_modify_position.span(), - description: "Modify position" - + " " - + "extension_pid" - + "_" - + pool_id_str - + " " - + "with collateral" - + " " - + get_symbol(collateral_asset) - + " " - + "and debt" - + " " - + get_symbol(debt_asset), - }, - ); - leaf_index += 1; - } - } - } -} - - -pub fn _add_vesu_flash_loan_leafs( - ref leafs: Array, - ref leaf_index: u256, - vault: ContractAddress, - decoder_and_sanitizer: ContractAddress, - manager: ContractAddress, - asset: ContractAddress, - is_legacy: bool, -) { - let mut argument_addresses = ArrayTrait::new(); - manager.serialize(ref argument_addresses); - asset.serialize(ref argument_addresses); - is_legacy.serialize(ref argument_addresses); - - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: manager, - selector: selector!("flash_loan"), - argument_addresses: argument_addresses.span(), - description: "Flash loan" + " " + get_symbol(asset), - }, - ); - leaf_index += 1; -} - -// ========================================= AVNU ========================================= -pub fn _add_avnu_leafs( - ref leafs: Array, - ref leaf_index: u256, - vault: ContractAddress, - decoder_and_sanitizer: ContractAddress, - router: ContractAddress, - sell_and_buy_token_address: Array<(ContractAddress, ContractAddress)>, -) { - let mut seen_sells: Array = ArrayTrait::new(); - - for i in 0..sell_and_buy_token_address.len() { - let (sell_token_address, buy_token_address) = *sell_and_buy_token_address.at(i); - - if !_contains_address(seen_sells.span(), sell_token_address) { - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: sell_token_address, - selector: selector!("approve"), - argument_addresses: array![router.into()].span(), - description: "Approve" - + " " - + "avnu_router" - + " " - + "to spend" - + " " - + get_symbol(sell_token_address), - }, - ); - leaf_index += 1; - seen_sells.append(sell_token_address); - } - - // swap leaf Ć  chaque paire - let mut argument_addresses = ArrayTrait::new(); - sell_token_address.serialize(ref argument_addresses); - buy_token_address.serialize(ref argument_addresses); - vault.serialize(ref argument_addresses); - - leafs - .append( - ManageLeaf { - decoder_and_sanitizer, - target: router, - selector: selector!("multi_route_swap"), - argument_addresses: argument_addresses.span(), - description: "Multi route swap" - + " " - + get_symbol(sell_token_address) - + " " - + "for" - + " " - + get_symbol(buy_token_address), - }, - ); - leaf_index += 1; - } -} - -fn _contains_address(span: Span, addr: ContractAddress) -> bool { - let mut i = 0; - while i < span.len() { - if *span.at(i) == addr { - return true; - } - i += 1; - } - false -} - - -fn get_symbol(contract_address: ContractAddress) -> ByteArray { - let ret_data = call_contract_syscall(contract_address, selector!("symbol"), array![].span()); - match ret_data { - Ok(res) => { - let res_len: u32 = res.len(); - if (res_len == 1) { - let symbol_felt = *res.at(0); - let mut symbol_byte_array: ByteArray = ""; - symbol_byte_array.append_word(symbol_felt, bytes_in_felt(symbol_felt)); - symbol_byte_array - } else { - let mut res_span = res; - Serde::::deserialize(ref res_span).unwrap() - } - }, - Err(revert_reason) => { panic!("revert_reason: {:?}", revert_reason); }, - } -} - - -fn bytes_in_felt(word: felt252) -> usize { - if word == 0 { - return 0; - } - let x: u256 = word.try_into().unwrap(); - - let mut p: u256 = 1_u256; - let mut bytes: usize = 0; - - while p <= x && bytes < 31 { - p = p * 256_u256; - bytes += 1; - } - - bytes -} diff --git a/packages/vault_allocator/src/vault_allocator/vault_allocator.cairo b/packages/vault_allocator/src/vault_allocator/vault_allocator.cairo index 54be9608..0d9896aa 100644 --- a/packages/vault_allocator/src/vault_allocator/vault_allocator.cairo +++ b/packages/vault_allocator/src/vault_allocator/vault_allocator.cairo @@ -29,7 +29,9 @@ pub mod VaultAllocator { #[event] #[derive(Drop, starknet::Event)] pub enum Event { + // #[flat] OwnableEvent: OwnableComponent::Event, + // #[flat] UpgradeableEvent: UpgradeableComponent::Event, CallPerformed: CallPerformed, } diff --git a/scripts/cli.ts b/scripts/cli.ts new file mode 100644 index 00000000..e3204468 --- /dev/null +++ b/scripts/cli.ts @@ -0,0 +1,175 @@ +#!/usr/bin/env node + +import { Command } from 'commander'; +import { execSync } from 'child_process'; +import { readFileSync, existsSync } from 'fs'; +import { join, resolve } from 'path'; +import { ContractClass, extractContractHashes, json } from 'starknet'; + +const program = new Command(); + +interface CompileOptions { + package: string; + contract: string; + profile?: string; +} + +/** + * Compile a Cairo contract using Scarb and return its class hash + */ +async function compileContract(options: CompileOptions): Promise { + const { package: packageName, contract: contractName, profile = 'release' } = options; + + console.log(`šŸ”Ø Compiling contract ${contractName} from package ${packageName}...`); + + // Determine the package path - look for packages directory in current or parent directories + let projectRoot = resolve(process.cwd(), '..'); + while (!existsSync(join(projectRoot, 'packages')) && projectRoot !== '/') { + projectRoot = resolve(projectRoot, '..'); + } + + if (!existsSync(join(projectRoot, 'packages'))) { + throw new Error('Could not find packages directory. Please run from project root or a subdirectory.'); + } + + // Check if Scarb.toml exists in the package + const scarbTomlPath = join(projectRoot, 'Scarb.toml'); + if (!existsSync(scarbTomlPath)) { + throw new Error(`Scarb.toml not found in package ${packageName}`); + } + + try { + // Compile the contract using Scarb + console.log(`šŸ“¦ Building package ${packageName} with profile ${profile}...`); + const buildCommand = `scarb --profile ${profile} build --package ${packageName}`; + execSync(buildCommand, { + stdio: 'pipe', + cwd: projectRoot + }); + + console.log('āœ… Contract compiled successfully'); + + // Find the compiled contract file + const targetDir = join(projectRoot, 'target', profile); + + const compiledSierra = json.parse( + readFileSync(`${targetDir}/${packageName}_${contractName}.contract_class.json`).toString("ascii") + ) + const compiledCasm = json.parse( + readFileSync(`${targetDir}/${packageName}_${contractName}.compiled_contract_class.json`).toString("ascii") + ) + + // Read and parse the contract class + const payload = { + contract: compiledSierra, + casm: compiledCasm + }; + + const result = extractContractHashes(payload); + + console.log(`šŸŽÆ Class hash: ${result.classHash}`); + return result.classHash; + + } catch (error) { + if (error instanceof Error) { + throw new Error(`Compilation failed: ${error.message}`); + } + throw error; + } +} + +/** + * List available packages and contracts + */ +function listPackages(): void { + console.log('šŸ“¦ Available packages:'); + + // Find project root + let projectRoot = process.cwd(); + while (!existsSync(join(projectRoot, 'packages')) && projectRoot !== '/') { + projectRoot = resolve(projectRoot, '..'); + } + + if (!existsSync(join(projectRoot, 'packages'))) { + console.log(' No packages directory found'); + return; + } + + const packagesDir = join(projectRoot, 'packages'); + + if (!existsSync(packagesDir)) { + console.log(' No packages directory found'); + return; + } + + try { + const packages = execSync('ls packages', { encoding: 'utf8', cwd: projectRoot }).trim().split('\n'); + + packages.forEach(pkg => { + const packagePath = join(packagesDir, pkg); + const scarbTomlPath = join(packagePath, 'Scarb.toml'); + + if (existsSync(scarbTomlPath)) { + console.log(` šŸ“ ${pkg}`); + + // Try to find contract files + const srcPath = join(packagePath, 'src'); + if (existsSync(srcPath)) { + try { + const contractFiles = execSync(`find ${srcPath} -name "*.cairo" -type f`, { encoding: 'utf8' }) + .trim() + .split('\n') + .filter(file => file.includes('contract') || file.includes('interface')); + + if (contractFiles.length > 0) { + contractFiles.forEach(file => { + const fileName = file.split('/').pop()?.replace('.cairo', '') || ''; + console.log(` šŸ“„ ${fileName}`); + }); + } + } catch { + // Ignore errors when searching for contract files + } + } + } + }); + } catch (error) { + console.log(' Error listing packages'); + } +} + +// CLI setup +program + .name('starknet-compiler') + .description('Compile Cairo contracts and get their class hashes') + .version('1.0.0'); + +program + .command('compile') + .description('Compile a contract and return its class hash') + .requiredOption('-p, --package ', 'Package name (e.g., vault, vault_allocator)') + .requiredOption('-c, --contract ', 'Contract name') + .option('--profile ', 'Build profile (dev, release)', 'release') + .action(async (options) => { + try { + const classHash = await compileContract(options); + console.log(`\nšŸŽ‰ Success! Class hash: ${classHash}`); + } catch (error) { + console.error(`āŒ Error: ${error instanceof Error ? error.message : 'Unknown error'}`); + process.exit(1); + } + }); + +program + .command('list') + .description('List available packages and contracts') + .action(() => { + listPackages(); + }); + +program.parse(); + +// Show help if no command provided +if (!process.argv.slice(2).length) { + program.outputHelp(); +} diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 00000000..f9477bd6 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,24 @@ +{ + "name": "scripts", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "compile": "tsx cli.ts compile", + "list": "tsx cli.ts list", + "cli": "tsx cli.ts" + }, + "keywords": [], + "author": "akiraonstarknet", + "license": "ISC", + "dependencies": { + "starknet": "^8.5.5" + }, + "devDependencies": { + "@types/node": "^24.7.2", + "commander": "^14.0.1", + "tsx": "^4.20.6", + "typescript": "^5.9.3" + } +} diff --git a/scripts/pnpm-lock.yaml b/scripts/pnpm-lock.yaml new file mode 100644 index 00000000..3fac8e04 --- /dev/null +++ b/scripts/pnpm-lock.yaml @@ -0,0 +1,619 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + starknet: + specifier: ^8.5.5 + version: 8.5.5 + devDependencies: + '@types/node': + specifier: ^24.7.2 + version: 24.7.2 + commander: + specifier: ^14.0.1 + version: 14.0.1 + tsx: + specifier: ^4.20.6 + version: 4.20.6 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + +packages: + + '@esbuild/aix-ppc64@0.25.10': + resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.10': + resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.10': + resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.10': + resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.10': + resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.10': + resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.10': + resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.10': + resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.10': + resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.10': + resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.10': + resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.10': + resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.10': + resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.10': + resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.10': + resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.10': + resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.10': + resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.10': + resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.10': + resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.10': + resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.10': + resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.10': + resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.10': + resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.10': + resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.10': + resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.10': + resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@noble/curves@1.7.0': + resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.6.0': + resolution: {integrity: sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.6.1': + resolution: {integrity: sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==} + engines: {node: ^14.21.3 || >=16} + + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/starknet@1.1.0': + resolution: {integrity: sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ==} + + '@starknet-io/types-js@0.8.4': + resolution: {integrity: sha512-0RZ3TZHcLsUTQaq1JhDSCM8chnzO4/XNsSCozwDET64JK5bjFDIf2ZUkta+tl5Nlbf4usoU7uZiDI/Q57kt2SQ==} + + '@starknet-io/types-js@0.9.2': + resolution: {integrity: sha512-vWOc0FVSn+RmabozIEWcEny1I73nDGTvOrLYJsR1x7LGA3AZmqt4i/aW69o/3i2NN5CVP8Ok6G1ayRQJKye3Wg==} + + '@types/node@24.7.2': + resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==} + + abi-wan-kanabi@2.2.4: + resolution: {integrity: sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg==} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansicolors@0.3.2: + resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} + + cardinal@2.1.1: + resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} + hasBin: true + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@14.0.1: + resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==} + engines: {node: '>=20'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + esbuild@0.25.10: + resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-tsconfig@4.12.0: + resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + lossless-json@4.3.0: + resolution: {integrity: sha512-ToxOC+SsduRmdSuoLZLYAr5zy1Qu7l5XhmPWM3zefCZ5IcrzW/h108qbJUKfOlDlhvhjUK84+8PSVX0kxnit0g==} + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + redeyed@2.1.1: + resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + starknet@8.5.5: + resolution: {integrity: sha512-YbAoOUx8cGcpaCPt/KuIYNyQIEWNr1iUJFVibpaxg/C8+ubAClqy6U498HgCVbxMbTg6UaYMc6xnh0RpeMYlcA==} + engines: {node: '>=22'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + ts-mixer@6.0.4: + resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==} + + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.14.0: + resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + +snapshots: + + '@esbuild/aix-ppc64@0.25.10': + optional: true + + '@esbuild/android-arm64@0.25.10': + optional: true + + '@esbuild/android-arm@0.25.10': + optional: true + + '@esbuild/android-x64@0.25.10': + optional: true + + '@esbuild/darwin-arm64@0.25.10': + optional: true + + '@esbuild/darwin-x64@0.25.10': + optional: true + + '@esbuild/freebsd-arm64@0.25.10': + optional: true + + '@esbuild/freebsd-x64@0.25.10': + optional: true + + '@esbuild/linux-arm64@0.25.10': + optional: true + + '@esbuild/linux-arm@0.25.10': + optional: true + + '@esbuild/linux-ia32@0.25.10': + optional: true + + '@esbuild/linux-loong64@0.25.10': + optional: true + + '@esbuild/linux-mips64el@0.25.10': + optional: true + + '@esbuild/linux-ppc64@0.25.10': + optional: true + + '@esbuild/linux-riscv64@0.25.10': + optional: true + + '@esbuild/linux-s390x@0.25.10': + optional: true + + '@esbuild/linux-x64@0.25.10': + optional: true + + '@esbuild/netbsd-arm64@0.25.10': + optional: true + + '@esbuild/netbsd-x64@0.25.10': + optional: true + + '@esbuild/openbsd-arm64@0.25.10': + optional: true + + '@esbuild/openbsd-x64@0.25.10': + optional: true + + '@esbuild/openharmony-arm64@0.25.10': + optional: true + + '@esbuild/sunos-x64@0.25.10': + optional: true + + '@esbuild/win32-arm64@0.25.10': + optional: true + + '@esbuild/win32-ia32@0.25.10': + optional: true + + '@esbuild/win32-x64@0.25.10': + optional: true + + '@noble/curves@1.7.0': + dependencies: + '@noble/hashes': 1.6.0 + + '@noble/hashes@1.6.0': {} + + '@noble/hashes@1.6.1': {} + + '@scure/base@1.2.6': {} + + '@scure/starknet@1.1.0': + dependencies: + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + + '@starknet-io/types-js@0.8.4': {} + + '@starknet-io/types-js@0.9.2': {} + + '@types/node@24.7.2': + dependencies: + undici-types: 7.14.0 + + abi-wan-kanabi@2.2.4: + dependencies: + ansicolors: 0.3.2 + cardinal: 2.1.1 + fs-extra: 10.1.0 + yargs: 17.7.2 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansicolors@0.3.2: {} + + cardinal@2.1.1: + dependencies: + ansicolors: 0.3.2 + redeyed: 2.1.1 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@14.0.1: {} + + emoji-regex@8.0.0: {} + + esbuild@0.25.10: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.10 + '@esbuild/android-arm': 0.25.10 + '@esbuild/android-arm64': 0.25.10 + '@esbuild/android-x64': 0.25.10 + '@esbuild/darwin-arm64': 0.25.10 + '@esbuild/darwin-x64': 0.25.10 + '@esbuild/freebsd-arm64': 0.25.10 + '@esbuild/freebsd-x64': 0.25.10 + '@esbuild/linux-arm': 0.25.10 + '@esbuild/linux-arm64': 0.25.10 + '@esbuild/linux-ia32': 0.25.10 + '@esbuild/linux-loong64': 0.25.10 + '@esbuild/linux-mips64el': 0.25.10 + '@esbuild/linux-ppc64': 0.25.10 + '@esbuild/linux-riscv64': 0.25.10 + '@esbuild/linux-s390x': 0.25.10 + '@esbuild/linux-x64': 0.25.10 + '@esbuild/netbsd-arm64': 0.25.10 + '@esbuild/netbsd-x64': 0.25.10 + '@esbuild/openbsd-arm64': 0.25.10 + '@esbuild/openbsd-x64': 0.25.10 + '@esbuild/openharmony-arm64': 0.25.10 + '@esbuild/sunos-x64': 0.25.10 + '@esbuild/win32-arm64': 0.25.10 + '@esbuild/win32-ia32': 0.25.10 + '@esbuild/win32-x64': 0.25.10 + + escalade@3.2.0: {} + + esprima@4.0.1: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + get-caller-file@2.0.5: {} + + get-tsconfig@4.12.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + graceful-fs@4.2.11: {} + + is-fullwidth-code-point@3.0.0: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + lossless-json@4.3.0: {} + + pako@2.1.0: {} + + redeyed@2.1.1: + dependencies: + esprima: 4.0.1 + + require-directory@2.1.1: {} + + resolve-pkg-maps@1.0.0: {} + + starknet@8.5.5: + dependencies: + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/base': 1.2.6 + '@scure/starknet': 1.1.0 + '@starknet-io/starknet-types-08': '@starknet-io/types-js@0.8.4' + '@starknet-io/starknet-types-09': '@starknet-io/types-js@0.9.2' + abi-wan-kanabi: 2.2.4 + lossless-json: 4.3.0 + pako: 2.1.0 + ts-mixer: 6.0.4 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + ts-mixer@6.0.4: {} + + tsx@4.20.6: + dependencies: + esbuild: 0.25.10 + get-tsconfig: 4.12.0 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.9.3: {} + + undici-types@7.14.0: {} + + universalify@2.0.1: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 00000000..af8b5d73 --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": [ + "*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] +}