From 8884f99d759992aeb69aea4b9218915b28d30731 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 17:50:39 +0300 Subject: [PATCH 01/23] Add multisig module --- packages/deeptrade-core/sources/multisig.move | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 packages/deeptrade-core/sources/multisig.move diff --git a/packages/deeptrade-core/sources/multisig.move b/packages/deeptrade-core/sources/multisig.move new file mode 100644 index 0000000..3f7986b --- /dev/null +++ b/packages/deeptrade-core/sources/multisig.move @@ -0,0 +1,110 @@ +module deeptrade_core::multisig; + +use multisig::multisig; +use sui::event; + +// === Errors === +const ENewAddressIsOldAddress: u64 = 1; + +// === Structs === +/// Configuration of the protocol's administrator multisig. Only a multisig account matching these +/// parameters can perform administrative actions requiring `AdminCap` +public struct MultisigConfig has key { + id: UID, + public_keys: vector>, + weights: vector, + threshold: u16, +} + +/// Capability to update the multisig config +public struct MultisigAdminCap has key, store { + id: UID, +} + +// === Events === +public struct MultisigConfigUpdated has copy, drop { + config_id: ID, + old_public_keys: vector>, + new_public_keys: vector>, + old_weights: vector, + new_weights: vector, + old_threshold: u16, + new_threshold: u16, + old_address: address, + new_address: address, +} + +// Initialize multisig config object and send multisig admin cap to publisher +fun init(ctx: &mut TxContext) { + let multisig_config = MultisigConfig { + id: object::new(ctx), + public_keys: vector::empty(), + weights: vector::empty(), + threshold: 0, + }; + let multisig_admin_cap = MultisigAdminCap { + id: object::new(ctx), + }; + + transfer::share_object(multisig_config); + transfer::transfer(multisig_admin_cap, ctx.sender()); +} + +// === Public-Mutative Functions === +public fun update_multisig_config( + config: &mut MultisigConfig, + _admin: &MultisigAdminCap, + new_public_keys: vector>, + new_weights: vector, + new_threshold: u16, +) { + let old_public_keys = config.public_keys; + let old_weights = config.weights; + let old_threshold = config.threshold; + + let old_address = multisig::derive_multisig_address_quiet( + old_public_keys, + old_weights, + old_threshold, + ); + // Validates passed multisig parameters, aborting if they are invalid + let new_address = multisig::derive_multisig_address_quiet( + new_public_keys, + new_weights, + new_threshold, + ); + + assert!(old_address != new_address, ENewAddressIsOldAddress); + + config.public_keys = new_public_keys; + config.weights = new_weights; + config.threshold = new_threshold; + + event::emit(MultisigConfigUpdated { + config_id: config.id.to_inner(), + old_public_keys, + new_public_keys, + old_weights, + new_weights, + old_threshold, + new_threshold, + old_address, + new_address, + }); +} + +// === Public-View Functions === +public fun public_keys(config: &MultisigConfig): vector> { config.public_keys } + +public fun weights(config: &MultisigConfig): vector { config.weights } + +public fun threshold(config: &MultisigConfig): u16 { config.threshold } + +// === Test Functions === +#[test_only] +public fun init_for_testing(ctx: &mut TxContext) { init(ctx); } + +#[test_only] +public fun get_multisig_admin_cap_for_testing(ctx: &mut TxContext): MultisigAdminCap { + MultisigAdminCap { id: object::new(ctx) } +} From 4fca9e33315400b659f1dc1a6aeeee5f2928ab67 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 17:57:08 +0300 Subject: [PATCH 02/23] Update modules to use MultisigConfig For all functions requiring AdminCap and multisig params before, use MultisigConfig instead of externally passed multisig params. Update ESenderIsNotMultisig to ESenderIsNotValidMultisig. Update functions docs. --- .../deeptrade-core/sources/fee_manager.move | 29 ++++++++----- packages/deeptrade-core/sources/loyalty.move | 42 +++++++++++------- packages/deeptrade-core/sources/ticket.move | 25 +++++------ packages/deeptrade-core/sources/treasury.move | 43 ++++++++++--------- 4 files changed, 79 insertions(+), 60 deletions(-) diff --git a/packages/deeptrade-core/sources/fee_manager.move b/packages/deeptrade-core/sources/fee_manager.move index 2a6bd9f..0b26046 100644 --- a/packages/deeptrade-core/sources/fee_manager.move +++ b/packages/deeptrade-core/sources/fee_manager.move @@ -6,6 +6,7 @@ use deepbook::order_info::OrderInfo; use deepbook::pool::Pool; use deeptrade_core::admin::AdminCap; use deeptrade_core::math; +use deeptrade_core::multisig::MultisigConfig; use deeptrade_core::treasury::{Treasury, join_protocol_fee}; use multisig::multisig; use sui::bag::{Self, Bag}; @@ -29,7 +30,7 @@ const EFilledQuantityGreaterThanOrderQuantity: u64 = 7; /// Error when the user unsettled fee is not empty to be destroyed const EUserUnsettledFeeNotEmpty: u64 = 8; const EProtocolUnsettledFeeNotEmpty: u64 = 9; -const ESenderIsNotMultisig: u64 = 10; +const ESenderIsNotValidMultisig: u64 = 10; const EInvalidFeeManagerShareTicket: u64 = 11; // === Structs === @@ -282,16 +283,19 @@ public fun claim_user_unsettled_fee_storage_rebate_admin, balance_manager: &BalanceManager, + multisig_config: &MultisigConfig, _admin: &AdminCap, order_id: u128, - pks: vector>, - weights: vector, - threshold: u16, ctx: &mut TxContext, ) { assert!( - multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + multisig::check_if_sender_is_multisig_address( + multisig_config.public_keys(), + multisig_config.weights(), + multisig_config.threshold(), + ctx, + ), + ESenderIsNotValidMultisig, ); treasury.verify_version(); @@ -325,15 +329,18 @@ public fun claim_protocol_unsettled_fee_storage_rebate( public fun claim_protocol_unsettled_fee_storage_rebate_admin( treasury: &Treasury, fee_manager: &mut FeeManager, + multisig_config: &MultisigConfig, _admin: &AdminCap, - pks: vector>, - weights: vector, - threshold: u16, ctx: &mut TxContext, ) { assert!( - multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + multisig::check_if_sender_is_multisig_address( + multisig_config.public_keys(), + multisig_config.weights(), + multisig_config.threshold(), + ctx, + ), + ESenderIsNotValidMultisig, ); treasury.verify_version(); diff --git a/packages/deeptrade-core/sources/loyalty.move b/packages/deeptrade-core/sources/loyalty.move index c6e3364..15eb3a6 100644 --- a/packages/deeptrade-core/sources/loyalty.move +++ b/packages/deeptrade-core/sources/loyalty.move @@ -8,6 +8,7 @@ module deeptrade_core::loyalty; use deeptrade_core::admin::AdminCap; +use deeptrade_core::multisig::MultisigConfig; use multisig::multisig; use sui::event; use sui::table::{Self, Table}; @@ -19,7 +20,7 @@ const ELoyaltyLevelHasUsers: u64 = 3; const EUserAlreadyHasLoyaltyLevel: u64 = 4; const EUserHasNoLoyaltyLevel: u64 = 5; const EInvalidFeeDiscountRate: u64 = 6; -const ESenderIsNotMultisig: u64 = 7; +const ESenderIsNotValidMultisig: u64 = 7; const ESenderIsNotLoyaltyAdmin: u64 = 8; // === Constants === @@ -100,16 +101,19 @@ fun init(ctx: &mut TxContext) { /// A protocol admin operation. public fun update_loyalty_admin_cap_owner( loyalty_admin_cap: &mut LoyaltyAdminCap, + multisig_config: &MultisigConfig, _admin_cap: &AdminCap, new_owner: address, - pks: vector>, - weights: vector, - threshold: u16, ctx: &mut TxContext, ) { assert!( - multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + multisig::check_if_sender_is_multisig_address( + multisig_config.public_keys(), + multisig_config.weights(), + multisig_config.threshold(), + ctx, + ), + ESenderIsNotValidMultisig, ); let old_owner = loyalty_admin_cap.owner; @@ -191,18 +195,21 @@ public fun revoke_user_level( /// Add a new loyalty level with fee discount rate public fun add_loyalty_level( loyalty_program: &mut LoyaltyProgram, + multisig_config: &MultisigConfig, _admin: &AdminCap, level: u8, fee_discount_rate: u64, - pks: vector>, - weights: vector, - threshold: u16, ctx: &mut TxContext, ) { // Validate multisig assert!( - multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + multisig::check_if_sender_is_multisig_address( + multisig_config.public_keys(), + multisig_config.weights(), + multisig_config.threshold(), + ctx, + ), + ESenderIsNotValidMultisig, ); // Validate fee discount rate @@ -228,17 +235,20 @@ public fun add_loyalty_level( /// Remove a loyalty level (only if no users have this level) public fun remove_loyalty_level( loyalty_program: &mut LoyaltyProgram, + multisig_config: &MultisigConfig, _admin: &AdminCap, level: u8, - pks: vector>, - weights: vector, - threshold: u16, ctx: &mut TxContext, ) { // Validate multisig assert!( - multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + multisig::check_if_sender_is_multisig_address( + multisig_config.public_keys(), + multisig_config.weights(), + multisig_config.threshold(), + ctx, + ), + ESenderIsNotValidMultisig, ); // Validate level exists diff --git a/packages/deeptrade-core/sources/ticket.move b/packages/deeptrade-core/sources/ticket.move index 96a4d1c..01344cd 100644 --- a/packages/deeptrade-core/sources/ticket.move +++ b/packages/deeptrade-core/sources/ticket.move @@ -1,6 +1,7 @@ module deeptrade_core::ticket; use deeptrade_core::admin::AdminCap; +use deeptrade_core::multisig::MultisigConfig; use multisig::multisig; use sui::clock::Clock; use sui::event; @@ -10,7 +11,7 @@ const ETicketOwnerMismatch: u64 = 1; const ETicketTypeMismatch: u64 = 2; const ETicketExpired: u64 = 3; const ETicketNotReady: u64 = 4; -const ESenderIsNotMultisig: u64 = 5; +const ESenderIsNotValidMultisig: u64 = 5; const ETicketNotExpired: u64 = 6; // === Constants === @@ -52,14 +53,12 @@ public struct TicketDestroyed has copy, drop { // === Public Functions === /// Create an admin ticket for timelock mechanism with multi-signature verification -/// Verifies sender matches the multi-sig address, then creates a ticket for future execution +/// Verifies sender matches the admin multi-sig address, then creates a ticket for future execution /// /// Parameters: +/// - multisig_config: Protocol's admin multisig config /// - _admin: Admin capability /// - ticket_type: Type of operation this ticket authorizes -/// - pks: Vector of public keys of the multi-sig signers -/// - weights: Vector of weights for each corresponding signer (must match pks length) -/// - threshold: Minimum sum of weights required to authorize transactions (must be > 0 and <= sum of weights) /// - clock: Clock for timestamp recording /// - ctx: Mutable transaction context for ticket creation and sender verification /// @@ -67,20 +66,22 @@ public struct TicketDestroyed has copy, drop { /// - AdminTicket: The created ticket bound to the sender address /// /// Aborts: -/// - With ESenderIsNotMultisig if the transaction sender is not the expected multi-signature address -/// derived from the provided pks, weights, and threshold parameters +/// - With ESenderIsNotValidMultisig if the transaction sender is not the protocol's admin multisig public fun create_ticket( + multisig_config: &MultisigConfig, _admin: &AdminCap, ticket_type: u8, - pks: vector>, - weights: vector, - threshold: u16, clock: &Clock, ctx: &mut TxContext, ) { assert!( - multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + multisig::check_if_sender_is_multisig_address( + multisig_config.public_keys(), + multisig_config.weights(), + multisig_config.threshold(), + ctx, + ), + ESenderIsNotValidMultisig, ); let created_at = clock.timestamp_ms(); diff --git a/packages/deeptrade-core/sources/treasury.move b/packages/deeptrade-core/sources/treasury.move index 8f7b5bf..1d9e5d5 100644 --- a/packages/deeptrade-core/sources/treasury.move +++ b/packages/deeptrade-core/sources/treasury.move @@ -2,6 +2,7 @@ module deeptrade_core::treasury; use deeptrade_core::admin::AdminCap; use deeptrade_core::helper::current_version; +use deeptrade_core::multisig::MultisigConfig; use deeptrade_core::ticket::{ AdminTicket, validate_ticket, @@ -28,7 +29,7 @@ const ECannotDisableNewerVersion: u64 = 3; const EVersionNotEnabled: u64 = 4; /// Error when trying to use shared object in a package whose version is not enabled const EPackageVersionNotEnabled: u64 = 5; -const ESenderIsNotMultisig: u64 = 6; +const ESenderIsNotValidMultisig: u64 = 6; /// Error when trying to enable a version that has been permanently disabled const EVersionPermanentlyDisabled: u64 = 7; @@ -237,29 +238,29 @@ public fun withdraw_deep_reserves( /// /// Parameters: /// - treasury: Treasury object +/// - multisig_config: Protocol's admin multisig config /// - _admin: Admin capability /// - version: Package version to enable -/// - pks: Vector of public keys of the multi-sig signers -/// - weights: Vector of weights for each corresponding signer (must match pks length) -/// - threshold: Minimum sum of weights required to authorize transactions /// - ctx: Mutable transaction context for sender verification /// /// Aborts: -/// - With ESenderIsNotMultisig if the transaction sender is not the expected multi-signature address -/// derived from the provided pks, weights, and threshold parameters +/// - With ESenderIsNotValidMultisig if the transaction sender is not the protocol's admin multisig /// - With EVersionAlreadyEnabled if the version is already enabled public fun enable_version( treasury: &mut Treasury, + multisig_config: &MultisigConfig, _admin: &AdminCap, version: u16, - pks: vector>, - weights: vector, - threshold: u16, ctx: &mut TxContext, ) { assert!( - multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + multisig::check_if_sender_is_multisig_address( + multisig_config.public_keys(), + multisig_config.weights(), + multisig_config.threshold(), + ctx, + ), + ESenderIsNotValidMultisig, ); // Check if the version has been permanently disabled @@ -280,30 +281,30 @@ public fun enable_version( /// /// Parameters: /// - treasury: Treasury object +/// - multisig_config: Protocol's admin multisig config /// - _admin: Admin capability /// - version: Package version to disable -/// - pks: Vector of public keys of the multi-sig signers -/// - weights: Vector of weights for each corresponding signer (must match pks length) -/// - threshold: Minimum sum of weights required to authorize transactions /// - ctx: Mutable transaction context for sender verification /// /// Aborts: -/// - With ESenderIsNotMultisig if the transaction sender is not the expected multi-signature address -/// derived from the provided pks, weights, and threshold parameters +/// - With ESenderIsNotValidMultisig if the transaction sender is not the protocol's admin multisig /// - With ECannotDisableNewerVersion if trying to disable a newer version /// - With EVersionNotEnabled if the version is not currently enabled public fun disable_version( treasury: &mut Treasury, + multisig_config: &MultisigConfig, _admin: &AdminCap, version: u16, - pks: vector>, - weights: vector, - threshold: u16, ctx: &mut TxContext, ) { assert!( - multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + multisig::check_if_sender_is_multisig_address( + multisig_config.public_keys(), + multisig_config.weights(), + multisig_config.threshold(), + ctx, + ), + ESenderIsNotValidMultisig, ); assert!(version <= current_version(), ECannotDisableNewerVersion); assert!(treasury.allowed_versions.contains(&version), EVersionNotEnabled); From 8f8d03c3b38771363d8b314abdc04d8086b589ed Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 17:59:08 +0300 Subject: [PATCH 03/23] Use updated ESenderIsNotValidMultisig error in tests & doc --- docs/multisig.md | 2 +- .../deeptrade-core/tests/loyalty/add_loyalty_level.move | 4 ++-- .../deeptrade-core/tests/loyalty/remove_loyalty_level.move | 4 ++-- .../tests/loyalty/update_loyalty_admin_cap_owner.move | 2 +- .../deeptrade-core/tests/ticket/create_ticket_tests.move | 4 ++-- .../tests/treasury/version_management_tests.move | 6 +++--- .../claim_protocol_unsettled_fee_storage_rebate.move | 4 ++-- .../claim_user_unsettled_fee_storage_rebate.move | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/multisig.md b/docs/multisig.md index c8b6d25..54bcb18 100644 --- a/docs/multisig.md +++ b/docs/multisig.md @@ -32,7 +32,7 @@ public fun update_pool_creation_protocol_fee( // This assertion guarantees the sender is the expected multi-sig wallet. assert!( multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotMultisig, + ESenderIsNotValidMultisig, ); config.protocol_fee = new_fee; diff --git a/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move b/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move index 48cee98..0a69328 100644 --- a/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move +++ b/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move @@ -7,7 +7,7 @@ use deeptrade_core::loyalty::{ LoyaltyProgram, ELoyaltyLevelAlreadyExists, EInvalidFeeDiscountRate, - ESenderIsNotMultisig + ESenderIsNotValidMultisig }; use multisig::multisig_test_utils::{ get_test_multisig_address, @@ -349,7 +349,7 @@ fun add_level_with_invalid_discount_rate_fails() { end(scenario); } -#[test, expected_failure(abort_code = ESenderIsNotMultisig)] +#[test, expected_failure(abort_code = ESenderIsNotValidMultisig)] fun non_multisig_sender_fails() { let mut scenario = setup_test_environment(); diff --git a/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move b/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move index c752654..e0fbe4f 100644 --- a/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move +++ b/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move @@ -8,7 +8,7 @@ use deeptrade_core::loyalty::{ LoyaltyProgram, ELoyaltyLevelNotFound, ELoyaltyLevelHasUsers, - ESenderIsNotMultisig + ESenderIsNotValidMultisig }; use multisig::multisig_test_utils::{ get_test_multisig_address, @@ -379,7 +379,7 @@ fun remove_level_with_members_fails() { end(scenario); } -#[test, expected_failure(abort_code = ESenderIsNotMultisig)] +#[test, expected_failure(abort_code = ESenderIsNotValidMultisig)] fun non_multisig_sender_fails() { let (mut scenario, loyalty_program_id) = setup_test_environment(); diff --git a/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move b/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move index 41ebff7..7187f05 100644 --- a/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move +++ b/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move @@ -141,7 +141,7 @@ fun old_owner_cannot_grant_level() { end(scenario); } -#[test, expected_failure(abort_code = loyalty::ESenderIsNotMultisig)] +#[test, expected_failure(abort_code = loyalty::ESenderIsNotValidMultisig)] fun non_multisig_sender_fails() { let mut scenario = setup_test_environment(); diff --git a/packages/deeptrade-core/tests/ticket/create_ticket_tests.move b/packages/deeptrade-core/tests/ticket/create_ticket_tests.move index 844b1cc..192329c 100644 --- a/packages/deeptrade-core/tests/ticket/create_ticket_tests.move +++ b/packages/deeptrade-core/tests/ticket/create_ticket_tests.move @@ -6,7 +6,7 @@ use deeptrade_core::admin_init_tests::setup_with_admin_cap; use deeptrade_core::ticket::{ Self, AdminTicket, - ESenderIsNotMultisig, + ESenderIsNotValidMultisig, TicketCreated, ticket_delay_duration }; @@ -78,7 +78,7 @@ fun create_ticket_success_with_multisig() { scenario.end(); } -#[test, expected_failure(abort_code = ESenderIsNotMultisig)] +#[test, expected_failure(abort_code = ESenderIsNotValidMultisig)] /// Test that ticket creation fails if the sender is not the derived multisig address. fun create_ticket_fails_if_sender_not_multisig() { let owner = @0xDEED; diff --git a/packages/deeptrade-core/tests/treasury/version_management_tests.move b/packages/deeptrade-core/tests/treasury/version_management_tests.move index b8ed46f..39a58d0 100644 --- a/packages/deeptrade-core/tests/treasury/version_management_tests.move +++ b/packages/deeptrade-core/tests/treasury/version_management_tests.move @@ -16,7 +16,7 @@ use deeptrade_core::treasury::{ EVersionAlreadyEnabled, EVersionNotEnabled, ECannotDisableNewerVersion, - ESenderIsNotMultisig + ESenderIsNotValidMultisig }; use multisig::multisig_test_utils::{ get_test_multisig_address, @@ -267,7 +267,7 @@ fun test_disable_version_fails_newer_version() { } #[test] -#[expected_failure(abort_code = ESenderIsNotMultisig)] +#[expected_failure(abort_code = ESenderIsNotValidMultisig)] fun test_version_management_fails_not_multisig() { let (mut scenario, _, admin_cap, mut treasury, _) = setup(); @@ -292,7 +292,7 @@ fun test_version_management_fails_not_multisig() { } #[test] -#[expected_failure(abort_code = ESenderIsNotMultisig)] +#[expected_failure(abort_code = ESenderIsNotValidMultisig)] fun test_version_management_fails_not_multisig_disable() { let (mut scenario, _, admin_cap, mut treasury, _) = setup(); diff --git a/packages/deeptrade-core/tests/unsettled-fees/claim_protocol_unsettled_fee_storage_rebate.move b/packages/deeptrade-core/tests/unsettled-fees/claim_protocol_unsettled_fee_storage_rebate.move index 0d66790..4d397bc 100644 --- a/packages/deeptrade-core/tests/unsettled-fees/claim_protocol_unsettled_fee_storage_rebate.move +++ b/packages/deeptrade-core/tests/unsettled-fees/claim_protocol_unsettled_fee_storage_rebate.move @@ -10,7 +10,7 @@ use deeptrade_core::fee_manager::{ start_protocol_fee_settlement, EInvalidOwner, EProtocolUnsettledFeeNotEmpty, - ESenderIsNotMultisig + ESenderIsNotValidMultisig }; use deeptrade_core::settle_user_fees_tests::setup_test_environment; use deeptrade_core::treasury::Treasury; @@ -176,7 +176,7 @@ fun admin_claims_rebate_successfully() { end(scenario); } -#[test, expected_failure(abort_code = ESenderIsNotMultisig)] +#[test, expected_failure(abort_code = ESenderIsNotValidMultisig)] /// Test that a non-multisig sender cannot claim a rebate via the admin function. fun non_multisig_admin_claim_fails() { let (mut scenario, fee_manager_id) = setup_protocol_fee_for_rebate(); diff --git a/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move b/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move index 6490850..da13e46 100644 --- a/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move +++ b/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move @@ -9,7 +9,7 @@ use deeptrade_core::fee_manager::{ claim_user_unsettled_fee_storage_rebate_admin, settle_filled_order_fee_and_record, start_protocol_fee_settlement, - ESenderIsNotMultisig + ESenderIsNotValidMultisig }; use deeptrade_core::settle_user_fees_tests::setup_test_environment; use deeptrade_core::treasury::Treasury; @@ -256,7 +256,7 @@ fun admin_claims_rebate_successfully() { end(scenario); } -#[test, expected_failure(abort_code = ESenderIsNotMultisig)] +#[test, expected_failure(abort_code = ESenderIsNotValidMultisig)] /// Test that a non-multisig sender cannot claim a rebate via the admin function. fun non_multisig_admin_claim_fails() { let ( From 4f842faa1437d1d9c24c91eab5ff49a483aaa0bb Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 18:25:13 +0300 Subject: [PATCH 04/23] Rename multisig module to multisig_config --- packages/deeptrade-core/sources/fee_manager.move | 2 +- packages/deeptrade-core/sources/loyalty.move | 2 +- .../sources/{multisig.move => multisig_config.move} | 2 +- packages/deeptrade-core/sources/ticket.move | 2 +- packages/deeptrade-core/sources/treasury.move | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename packages/deeptrade-core/sources/{multisig.move => multisig_config.move} (98%) diff --git a/packages/deeptrade-core/sources/fee_manager.move b/packages/deeptrade-core/sources/fee_manager.move index 0b26046..21eb563 100644 --- a/packages/deeptrade-core/sources/fee_manager.move +++ b/packages/deeptrade-core/sources/fee_manager.move @@ -6,7 +6,7 @@ use deepbook::order_info::OrderInfo; use deepbook::pool::Pool; use deeptrade_core::admin::AdminCap; use deeptrade_core::math; -use deeptrade_core::multisig::MultisigConfig; +use deeptrade_core::multisig_config::MultisigConfig; use deeptrade_core::treasury::{Treasury, join_protocol_fee}; use multisig::multisig; use sui::bag::{Self, Bag}; diff --git a/packages/deeptrade-core/sources/loyalty.move b/packages/deeptrade-core/sources/loyalty.move index 15eb3a6..e948938 100644 --- a/packages/deeptrade-core/sources/loyalty.move +++ b/packages/deeptrade-core/sources/loyalty.move @@ -8,7 +8,7 @@ module deeptrade_core::loyalty; use deeptrade_core::admin::AdminCap; -use deeptrade_core::multisig::MultisigConfig; +use deeptrade_core::multisig_config::MultisigConfig; use multisig::multisig; use sui::event; use sui::table::{Self, Table}; diff --git a/packages/deeptrade-core/sources/multisig.move b/packages/deeptrade-core/sources/multisig_config.move similarity index 98% rename from packages/deeptrade-core/sources/multisig.move rename to packages/deeptrade-core/sources/multisig_config.move index 3f7986b..237ff82 100644 --- a/packages/deeptrade-core/sources/multisig.move +++ b/packages/deeptrade-core/sources/multisig_config.move @@ -1,4 +1,4 @@ -module deeptrade_core::multisig; +module deeptrade_core::multisig_config; use multisig::multisig; use sui::event; diff --git a/packages/deeptrade-core/sources/ticket.move b/packages/deeptrade-core/sources/ticket.move index 01344cd..cf0bbc2 100644 --- a/packages/deeptrade-core/sources/ticket.move +++ b/packages/deeptrade-core/sources/ticket.move @@ -1,7 +1,7 @@ module deeptrade_core::ticket; use deeptrade_core::admin::AdminCap; -use deeptrade_core::multisig::MultisigConfig; +use deeptrade_core::multisig_config::MultisigConfig; use multisig::multisig; use sui::clock::Clock; use sui::event; diff --git a/packages/deeptrade-core/sources/treasury.move b/packages/deeptrade-core/sources/treasury.move index 1d9e5d5..ae0633a 100644 --- a/packages/deeptrade-core/sources/treasury.move +++ b/packages/deeptrade-core/sources/treasury.move @@ -2,7 +2,7 @@ module deeptrade_core::treasury; use deeptrade_core::admin::AdminCap; use deeptrade_core::helper::current_version; -use deeptrade_core::multisig::MultisigConfig; +use deeptrade_core::multisig_config::MultisigConfig; use deeptrade_core::ticket::{ AdminTicket, validate_ticket, From 409cddd3ca09875abb3662589b607c50c2ca6b2d Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 18:33:34 +0300 Subject: [PATCH 05/23] Reduce code duplication Add validate_sender_is_admin_multisig to multisig_config module and use it everywhere needed instead of direct multisig::check_if_sender_is_multisig_address usage. Remove unused errors from modules. Remove unused public-view multisig config functions. --- .../deeptrade-core/sources/fee_manager.move | 24 ++----------- packages/deeptrade-core/sources/loyalty.move | 34 +++---------------- .../sources/multisig_config.move | 22 ++++++++---- packages/deeptrade-core/sources/ticket.move | 14 ++------ packages/deeptrade-core/sources/treasury.move | 24 ++----------- 5 files changed, 28 insertions(+), 90 deletions(-) diff --git a/packages/deeptrade-core/sources/fee_manager.move b/packages/deeptrade-core/sources/fee_manager.move index 21eb563..ffa16d6 100644 --- a/packages/deeptrade-core/sources/fee_manager.move +++ b/packages/deeptrade-core/sources/fee_manager.move @@ -8,7 +8,6 @@ use deeptrade_core::admin::AdminCap; use deeptrade_core::math; use deeptrade_core::multisig_config::MultisigConfig; use deeptrade_core::treasury::{Treasury, join_protocol_fee}; -use multisig::multisig; use sui::bag::{Self, Bag}; use sui::balance::Balance; use sui::coin::{Self, Coin}; @@ -30,8 +29,7 @@ const EFilledQuantityGreaterThanOrderQuantity: u64 = 7; /// Error when the user unsettled fee is not empty to be destroyed const EUserUnsettledFeeNotEmpty: u64 = 8; const EProtocolUnsettledFeeNotEmpty: u64 = 9; -const ESenderIsNotValidMultisig: u64 = 10; -const EInvalidFeeManagerShareTicket: u64 = 11; +const EInvalidFeeManagerShareTicket: u64 = 10; // === Structs === /// A shared object that manages a user's fee-related operations. Required for trading @@ -288,15 +286,7 @@ public fun claim_user_unsettled_fee_storage_rebate_admin( @@ -333,15 +323,7 @@ public fun claim_protocol_unsettled_fee_storage_rebate_admin( _admin: &AdminCap, ctx: &mut TxContext, ) { - assert!( - multisig::check_if_sender_is_multisig_address( - multisig_config.public_keys(), - multisig_config.weights(), - multisig_config.threshold(), - ctx, - ), - ESenderIsNotValidMultisig, - ); + multisig_config.validate_sender_is_admin_multisig(ctx); treasury.verify_version(); claim_protocol_unsettled_fee_rebate_core(fee_manager); diff --git a/packages/deeptrade-core/sources/loyalty.move b/packages/deeptrade-core/sources/loyalty.move index e948938..825c9aa 100644 --- a/packages/deeptrade-core/sources/loyalty.move +++ b/packages/deeptrade-core/sources/loyalty.move @@ -9,7 +9,6 @@ module deeptrade_core::loyalty; use deeptrade_core::admin::AdminCap; use deeptrade_core::multisig_config::MultisigConfig; -use multisig::multisig; use sui::event; use sui::table::{Self, Table}; @@ -20,8 +19,7 @@ const ELoyaltyLevelHasUsers: u64 = 3; const EUserAlreadyHasLoyaltyLevel: u64 = 4; const EUserHasNoLoyaltyLevel: u64 = 5; const EInvalidFeeDiscountRate: u64 = 6; -const ESenderIsNotValidMultisig: u64 = 7; -const ESenderIsNotLoyaltyAdmin: u64 = 8; +const ESenderIsNotLoyaltyAdmin: u64 = 7; // === Constants === const MAX_FEE_DISCOUNT_RATE: u64 = 1_000_000_000; // 100% in billionths @@ -106,15 +104,7 @@ public fun update_loyalty_admin_cap_owner( new_owner: address, ctx: &mut TxContext, ) { - assert!( - multisig::check_if_sender_is_multisig_address( - multisig_config.public_keys(), - multisig_config.weights(), - multisig_config.threshold(), - ctx, - ), - ESenderIsNotValidMultisig, - ); + multisig_config.validate_sender_is_admin_multisig(ctx); let old_owner = loyalty_admin_cap.owner; loyalty_admin_cap.owner = new_owner; @@ -202,15 +192,7 @@ public fun add_loyalty_level( ctx: &mut TxContext, ) { // Validate multisig - assert!( - multisig::check_if_sender_is_multisig_address( - multisig_config.public_keys(), - multisig_config.weights(), - multisig_config.threshold(), - ctx, - ), - ESenderIsNotValidMultisig, - ); + multisig_config.validate_sender_is_admin_multisig(ctx); // Validate fee discount rate assert!( @@ -241,15 +223,7 @@ public fun remove_loyalty_level( ctx: &mut TxContext, ) { // Validate multisig - assert!( - multisig::check_if_sender_is_multisig_address( - multisig_config.public_keys(), - multisig_config.weights(), - multisig_config.threshold(), - ctx, - ), - ESenderIsNotValidMultisig, - ); + multisig_config.validate_sender_is_admin_multisig(ctx); // Validate level exists assert!(loyalty_program.levels.contains(level), ELoyaltyLevelNotFound); diff --git a/packages/deeptrade-core/sources/multisig_config.move b/packages/deeptrade-core/sources/multisig_config.move index 237ff82..0dd6df8 100644 --- a/packages/deeptrade-core/sources/multisig_config.move +++ b/packages/deeptrade-core/sources/multisig_config.move @@ -5,6 +5,7 @@ use sui::event; // === Errors === const ENewAddressIsOldAddress: u64 = 1; +const ESenderIsNotValidMultisig: u64 = 2; // === Structs === /// Configuration of the protocol's administrator multisig. Only a multisig account matching these @@ -93,12 +94,21 @@ public fun update_multisig_config( }); } -// === Public-View Functions === -public fun public_keys(config: &MultisigConfig): vector> { config.public_keys } - -public fun weights(config: &MultisigConfig): vector { config.weights } - -public fun threshold(config: &MultisigConfig): u16 { config.threshold } +// === Public-Package Functions === +public(package) fun validate_sender_is_admin_multisig( + config: &MultisigConfig, + ctx: &mut TxContext, +) { + assert!( + multisig::check_if_sender_is_multisig_address( + config.public_keys, + config.weights, + config.threshold, + ctx, + ), + ESenderIsNotValidMultisig, + ); +} // === Test Functions === #[test_only] diff --git a/packages/deeptrade-core/sources/ticket.move b/packages/deeptrade-core/sources/ticket.move index cf0bbc2..75fefe5 100644 --- a/packages/deeptrade-core/sources/ticket.move +++ b/packages/deeptrade-core/sources/ticket.move @@ -2,7 +2,6 @@ module deeptrade_core::ticket; use deeptrade_core::admin::AdminCap; use deeptrade_core::multisig_config::MultisigConfig; -use multisig::multisig; use sui::clock::Clock; use sui::event; @@ -11,8 +10,7 @@ const ETicketOwnerMismatch: u64 = 1; const ETicketTypeMismatch: u64 = 2; const ETicketExpired: u64 = 3; const ETicketNotReady: u64 = 4; -const ESenderIsNotValidMultisig: u64 = 5; -const ETicketNotExpired: u64 = 6; +const ETicketNotExpired: u64 = 5; // === Constants === const MILLISECONDS_PER_DAY: u64 = 86_400_000; @@ -74,15 +72,7 @@ public fun create_ticket( clock: &Clock, ctx: &mut TxContext, ) { - assert!( - multisig::check_if_sender_is_multisig_address( - multisig_config.public_keys(), - multisig_config.weights(), - multisig_config.threshold(), - ctx, - ), - ESenderIsNotValidMultisig, - ); + multisig_config.validate_sender_is_admin_multisig(ctx); let created_at = clock.timestamp_ms(); diff --git a/packages/deeptrade-core/sources/treasury.move b/packages/deeptrade-core/sources/treasury.move index ae0633a..4c9eed6 100644 --- a/packages/deeptrade-core/sources/treasury.move +++ b/packages/deeptrade-core/sources/treasury.move @@ -11,7 +11,6 @@ use deeptrade_core::ticket::{ withdraw_protocol_fee_ticket_type, withdraw_deep_reserves_ticket_type }; -use multisig::multisig; use sui::bag::{Self, Bag}; use sui::balance::{Self, Balance}; use sui::clock::Clock; @@ -29,10 +28,9 @@ const ECannotDisableNewerVersion: u64 = 3; const EVersionNotEnabled: u64 = 4; /// Error when trying to use shared object in a package whose version is not enabled const EPackageVersionNotEnabled: u64 = 5; -const ESenderIsNotValidMultisig: u64 = 6; /// Error when trying to enable a version that has been permanently disabled -const EVersionPermanentlyDisabled: u64 = 7; +const EVersionPermanentlyDisabled: u64 = 6; // === Structs === public struct Treasury has key, store { @@ -253,15 +251,7 @@ public fun enable_version( version: u16, ctx: &mut TxContext, ) { - assert!( - multisig::check_if_sender_is_multisig_address( - multisig_config.public_keys(), - multisig_config.weights(), - multisig_config.threshold(), - ctx, - ), - ESenderIsNotValidMultisig, - ); + multisig_config.validate_sender_is_admin_multisig(ctx); // Check if the version has been permanently disabled assert!(!treasury.disabled_versions.contains(&version), EVersionPermanentlyDisabled); @@ -297,15 +287,7 @@ public fun disable_version( version: u16, ctx: &mut TxContext, ) { - assert!( - multisig::check_if_sender_is_multisig_address( - multisig_config.public_keys(), - multisig_config.weights(), - multisig_config.threshold(), - ctx, - ), - ESenderIsNotValidMultisig, - ); + multisig_config.validate_sender_is_admin_multisig(ctx); assert!(version <= current_version(), ECannotDisableNewerVersion); assert!(treasury.allowed_versions.contains(&version), EVersionNotEnabled); From fa421edae55584df23531692ae9d90e2cd1634a9 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 18:36:49 +0300 Subject: [PATCH 06/23] Revert multisig doc changes --- docs/multisig.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/multisig.md b/docs/multisig.md index 54bcb18..c8b6d25 100644 --- a/docs/multisig.md +++ b/docs/multisig.md @@ -32,7 +32,7 @@ public fun update_pool_creation_protocol_fee( // This assertion guarantees the sender is the expected multi-sig wallet. assert!( multisig::check_if_sender_is_multisig_address(pks, weights, threshold, ctx), - ESenderIsNotValidMultisig, + ESenderIsNotMultisig, ); config.protocol_fee = new_fee; From 441075abaec3613ab031c46d60e2930544c8f849 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 21:20:53 +0300 Subject: [PATCH 07/23] Add initialization step for multisig config & add test utils --- .../sources/multisig_config.move | 83 ++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/packages/deeptrade-core/sources/multisig_config.move b/packages/deeptrade-core/sources/multisig_config.move index 0dd6df8..97a3e4e 100644 --- a/packages/deeptrade-core/sources/multisig_config.move +++ b/packages/deeptrade-core/sources/multisig_config.move @@ -6,6 +6,8 @@ use sui::event; // === Errors === const ENewAddressIsOldAddress: u64 = 1; const ESenderIsNotValidMultisig: u64 = 2; +const EMultisigConfigAlreadyInitialized: u64 = 3; +const EMultisigConfigNotInitialized: u64 = 4; // === Structs === /// Configuration of the protocol's administrator multisig. Only a multisig account matching these @@ -15,6 +17,7 @@ public struct MultisigConfig has key { public_keys: vector>, weights: vector, threshold: u16, + initialized: bool, } /// Capability to update the multisig config @@ -23,6 +26,14 @@ public struct MultisigAdminCap has key, store { } // === Events === +public struct MultisigConfigInitialized has copy, drop { + config_id: ID, + public_keys: vector>, + weights: vector, + threshold: u16, + multisig_address: address, +} + public struct MultisigConfigUpdated has copy, drop { config_id: ID, old_public_keys: vector>, @@ -35,13 +46,14 @@ public struct MultisigConfigUpdated has copy, drop { new_address: address, } -// Initialize multisig config object and send multisig admin cap to publisher +// Share multisig config object and transfer multisig admin cap to publisher fun init(ctx: &mut TxContext) { let multisig_config = MultisigConfig { id: object::new(ctx), public_keys: vector::empty(), weights: vector::empty(), threshold: 0, + initialized: false, }; let multisig_admin_cap = MultisigAdminCap { id: object::new(ctx), @@ -52,6 +64,38 @@ fun init(ctx: &mut TxContext) { } // === Public-Mutative Functions === +/// Multisig config can be initialized only once. `update_multisig_config` should be used for subsequent updates +public fun initialize_multisig_config( + config: &mut MultisigConfig, + _admin_cap: &MultisigAdminCap, + public_keys: vector>, + weights: vector, + threshold: u16, +) { + assert!(!config.initialized, EMultisigConfigAlreadyInitialized); + + // Validates passed multisig parameters, aborting if they are invalid + let multisig_address = multisig::derive_multisig_address_quiet( + public_keys, + weights, + threshold, + ); + + config.public_keys = public_keys; + config.weights = weights; + config.threshold = threshold; + config.initialized = true; + + event::emit(MultisigConfigInitialized { + config_id: config.id.to_inner(), + public_keys, + weights, + threshold, + multisig_address, + }); +} + +/// Multisig config can be updated only after it has been initialized by `initialize_multisig_config` public fun update_multisig_config( config: &mut MultisigConfig, _admin: &MultisigAdminCap, @@ -59,6 +103,8 @@ public fun update_multisig_config( new_weights: vector, new_threshold: u16, ) { + assert!(config.initialized, EMultisigConfigNotInitialized); + let old_public_keys = config.public_keys; let old_weights = config.weights; let old_threshold = config.threshold; @@ -99,6 +145,7 @@ public(package) fun validate_sender_is_admin_multisig( config: &MultisigConfig, ctx: &mut TxContext, ) { + assert!(config.initialized, EMultisigConfigNotInitialized); assert!( multisig::check_if_sender_is_multisig_address( config.public_keys, @@ -118,3 +165,37 @@ public fun init_for_testing(ctx: &mut TxContext) { init(ctx); } public fun get_multisig_admin_cap_for_testing(ctx: &mut TxContext): MultisigAdminCap { MultisigAdminCap { id: object::new(ctx) } } + +#[test_only] +public fun get_multisig_config_params( + config: &MultisigConfig, +): (vector>, vector, u16) { + (config.public_keys, config.weights, config.threshold) +} + +#[test_only] +public fun unwrap_multisig_config_updated_event( + event: &MultisigConfigUpdated, +): ( + ID, + vector>, + vector>, + vector, + vector, + u16, + u16, + address, + address, +) { + ( + event.config_id, + event.old_public_keys, + event.new_public_keys, + event.old_weights, + event.new_weights, + event.old_threshold, + event.new_threshold, + event.old_address, + event.new_address, + ) +} From 2df8912205aecf4e0c206a38008038de73c6f15e Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 22:14:21 +0300 Subject: [PATCH 08/23] Add tests for initialize_multisig_config & update_multisig_config funs --- .../sources/multisig_config.move | 7 + .../initialize_multisig_config.move | 244 +++++++++++++++ .../update_multisig_config.move | 285 ++++++++++++++++++ 3 files changed, 536 insertions(+) create mode 100644 packages/deeptrade-core/tests/multisig_config/initialize_multisig_config.move create mode 100644 packages/deeptrade-core/tests/multisig_config/update_multisig_config.move diff --git a/packages/deeptrade-core/sources/multisig_config.move b/packages/deeptrade-core/sources/multisig_config.move index 97a3e4e..f8db5b6 100644 --- a/packages/deeptrade-core/sources/multisig_config.move +++ b/packages/deeptrade-core/sources/multisig_config.move @@ -199,3 +199,10 @@ public fun unwrap_multisig_config_updated_event( event.new_address, ) } + +#[test_only] +public fun unwrap_multisig_config_initialized_event( + event: &MultisigConfigInitialized, +): (ID, vector>, vector, u16, address) { + (event.config_id, event.public_keys, event.weights, event.threshold, event.multisig_address) +} diff --git a/packages/deeptrade-core/tests/multisig_config/initialize_multisig_config.move b/packages/deeptrade-core/tests/multisig_config/initialize_multisig_config.move new file mode 100644 index 0000000..8a0bde0 --- /dev/null +++ b/packages/deeptrade-core/tests/multisig_config/initialize_multisig_config.move @@ -0,0 +1,244 @@ +#[test_only] +module deeptrade_core::initialize_multisig_config_tests; + +use deeptrade_core::multisig_config::{ + Self, + MultisigConfig, + MultisigConfigInitialized, + EMultisigConfigAlreadyInitialized +}; +use multisig::multisig::{ + Self, + ELengthsOfPksAndWeightsAreNotEqual, + EThresholdIsPositiveAndNotGreaterThanTheSumOfWeights +}; +use multisig::multisig_test_utils::{ + get_test_multisig_pks, + get_test_multisig_weights, + get_test_multisig_threshold, + get_test_multisig_address +}; +use std::unit_test::assert_eq; +use sui::event; +use sui::test_scenario::{Self, Scenario, end, return_shared}; +use sui::test_utils; + +const OWNER: address = @0x1; + +// === Tests === + +#[test] +fun success() { + let mut scenario = setup(); + + let pks = get_test_multisig_pks(); + let weights = get_test_multisig_weights(); + let threshold = get_test_multisig_threshold(); + let multisig_address = get_test_multisig_address(); + + let config_id; + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + config_id = object::id(&config); + + multisig_config::initialize_multisig_config( + &mut config, + &admin_cap, + pks, + weights, + threshold, + ); + + let ( + updated_pks, + updated_weights, + updated_threshold, + ) = multisig_config::get_multisig_config_params(&config); + + let new_address_derived = multisig::derive_multisig_address_quiet( + updated_pks, + updated_weights, + updated_threshold, + ); + + assert_eq!(updated_pks, pks); + assert_eq!(updated_weights, weights); + assert_eq!(updated_threshold, threshold); + assert_eq!(new_address_derived, multisig_address); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + let events = event::events_by_type(); + assert_eq!(events.length(), 1); + let event = events[0]; + + let ( + event_config_id, + event_pks, + event_weights, + event_threshold, + event_address, + ) = multisig_config::unwrap_multisig_config_initialized_event(&event); + + assert_eq!(config_id, event_config_id); + assert_eq!(pks, event_pks); + assert_eq!(weights, event_weights); + assert_eq!(threshold, event_threshold); + assert_eq!(multisig_address, event_address); + + end(scenario); +} + +#[test, expected_failure(abort_code = EMultisigConfigAlreadyInitialized)] +fun already_initialized_fails() { + let mut scenario = setup(); + + let pks = get_test_multisig_pks(); + let weights = get_test_multisig_weights(); + let threshold = get_test_multisig_threshold(); + + // First call, successful initialization + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::initialize_multisig_config( + &mut config, + &admin_cap, + pks, + weights, + threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + // Second call, should fail + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::initialize_multisig_config( + &mut config, + &admin_cap, + pks, + weights, + threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +#[test, expected_failure(abort_code = ELengthsOfPksAndWeightsAreNotEqual)] +fun mismatched_pks_and_weights_fails() { + let mut scenario = setup(); + + let pks = get_test_multisig_pks(); + let mut weights = get_test_multisig_weights(); + let threshold = get_test_multisig_threshold(); + + // Remove one weight to create a mismatch + weights.pop_back(); + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::initialize_multisig_config( + &mut config, + &admin_cap, + pks, + weights, + threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +#[test, expected_failure(abort_code = EThresholdIsPositiveAndNotGreaterThanTheSumOfWeights)] +fun zero_threshold_fails() { + let mut scenario = setup(); + + let pks = get_test_multisig_pks(); + let weights = get_test_multisig_weights(); + let threshold = 0; + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::initialize_multisig_config( + &mut config, + &admin_cap, + pks, + weights, + threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +#[test, expected_failure(abort_code = EThresholdIsPositiveAndNotGreaterThanTheSumOfWeights)] +fun unachievable_threshold_fails() { + let mut scenario = setup(); + + let pks = get_test_multisig_pks(); + let weights = get_test_multisig_weights(); + // Sum of weights is 3, so 4 is unachievable + let threshold = 4; + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::initialize_multisig_config( + &mut config, + &admin_cap, + pks, + weights, + threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +// === Helpers === + +#[test_only] +public(package) fun setup(): Scenario { + let mut scenario = test_scenario::begin(OWNER); + + scenario.next_tx(OWNER); + { + multisig_config::init_for_testing(scenario.ctx()); + }; + + scenario +} diff --git a/packages/deeptrade-core/tests/multisig_config/update_multisig_config.move b/packages/deeptrade-core/tests/multisig_config/update_multisig_config.move new file mode 100644 index 0000000..a406c30 --- /dev/null +++ b/packages/deeptrade-core/tests/multisig_config/update_multisig_config.move @@ -0,0 +1,285 @@ +#[test_only] +module deeptrade_core::update_multisig_config_tests; + +use deeptrade_core::initialize_multisig_config_tests::setup; +use deeptrade_core::multisig_config::{ + Self, + MultisigConfig, + MultisigConfigUpdated, + ENewAddressIsOldAddress, + EMultisigConfigNotInitialized +}; +use multisig::multisig::{ + Self, + ELengthsOfPksAndWeightsAreNotEqual, + EThresholdIsPositiveAndNotGreaterThanTheSumOfWeights +}; +use multisig::multisig_test_utils::{ + get_test_multisig_pks, + get_test_multisig_weights, + get_test_multisig_threshold, + get_test_multisig_address +}; +use std::unit_test::assert_eq; +use sui::event; +use sui::test_scenario::{Scenario, return_shared, end}; +use sui::test_utils; + +const OWNER: address = @0x1; + +// === Tests === + +#[test] +fun success() { + let mut scenario = setup_with_initialized_config(); + + let old_pks = get_test_multisig_pks(); + let old_weights = get_test_multisig_weights(); + let old_threshold = get_test_multisig_threshold(); + let old_address = get_test_multisig_address(); + + // Create a new valid config + let new_pks = vector[b"pk1_new", b"pk2_new", b"pk3_new"]; + let new_weights = vector[1, 1, 1]; + let new_threshold = 2; + let new_address = multisig::derive_multisig_address_quiet( + new_pks, + new_weights, + new_threshold, + ); + let config_id: ID; + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + config_id = object::id(&config); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::update_multisig_config( + &mut config, + &admin_cap, + new_pks, + new_weights, + new_threshold, + ); + + let ( + updated_pks, + updated_weights, + updated_threshold, + ) = multisig_config::get_multisig_config_params(&config); + + let new_address_derived = multisig::derive_multisig_address_quiet( + updated_pks, + updated_weights, + updated_threshold, + ); + + assert_eq!(updated_pks, new_pks); + assert_eq!(updated_weights, new_weights); + assert_eq!(updated_threshold, new_threshold); + assert_eq!(new_address_derived, new_address); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + let events = event::events_by_type(); + assert_eq!(events.length(), 1); + let event = events[0]; + + let ( + event_config_id, + event_old_pks, + event_new_pks, + event_old_weights, + event_new_weights, + event_old_threshold, + event_new_threshold, + event_old_address, + event_new_address, + ) = multisig_config::unwrap_multisig_config_updated_event(&event); + + assert_eq!(config_id, event_config_id); + assert_eq!(old_pks, event_old_pks); + assert_eq!(new_pks, event_new_pks); + assert_eq!(old_weights, event_old_weights); + assert_eq!(new_weights, event_new_weights); + assert_eq!(old_threshold, event_old_threshold); + assert_eq!(new_threshold, event_new_threshold); + assert_eq!(old_address, event_old_address); + assert_eq!(new_address, event_new_address); + + end(scenario); +} + +#[test, expected_failure(abort_code = EMultisigConfigNotInitialized)] +fun not_initialized_fails() { + let mut scenario = setup(); + + let new_pks = get_test_multisig_pks(); + let new_weights = get_test_multisig_weights(); + let new_threshold = get_test_multisig_threshold(); + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::update_multisig_config( + &mut config, + &admin_cap, + new_pks, + new_weights, + new_threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +#[test, expected_failure(abort_code = ENewAddressIsOldAddress)] +fun new_address_is_old_address_fails() { + let mut scenario = setup_with_initialized_config(); + + // Attempt to update with the same parameters used for initialization + let pks = get_test_multisig_pks(); + let weights = get_test_multisig_weights(); + let threshold = get_test_multisig_threshold(); + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::update_multisig_config( + &mut config, + &admin_cap, + pks, + weights, + threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +#[test, expected_failure(abort_code = ELengthsOfPksAndWeightsAreNotEqual)] +fun mismatched_pks_and_weights_fails() { + let mut scenario = setup_with_initialized_config(); + + let mut new_pks = get_test_multisig_pks(); + let new_weights = get_test_multisig_weights(); + let new_threshold = get_test_multisig_threshold(); + + // Create a mismatch + new_pks.pop_back(); + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::update_multisig_config( + &mut config, + &admin_cap, + new_pks, + new_weights, + new_threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +#[test, expected_failure(abort_code = EThresholdIsPositiveAndNotGreaterThanTheSumOfWeights)] +fun zero_threshold_fails() { + let mut scenario = setup_with_initialized_config(); + + let new_pks = get_test_multisig_pks(); + let new_weights = get_test_multisig_weights(); + let new_threshold = 0; + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::update_multisig_config( + &mut config, + &admin_cap, + new_pks, + new_weights, + new_threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +#[test, expected_failure(abort_code = EThresholdIsPositiveAndNotGreaterThanTheSumOfWeights)] +fun unachievable_threshold_fails() { + let mut scenario = setup_with_initialized_config(); + + let new_pks = get_test_multisig_pks(); + let new_weights = get_test_multisig_weights(); + // Sum of weights is 3, so 4 is unachievable + let new_threshold = 4; + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::update_multisig_config( + &mut config, + &admin_cap, + new_pks, + new_weights, + new_threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + +// === Helpers === +#[test_only] +public(package) fun setup_with_initialized_config(): Scenario { + let mut scenario = setup(); + + // Initialize it + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::initialize_multisig_config( + &mut config, + &admin_cap, + get_test_multisig_pks(), + get_test_multisig_weights(), + get_test_multisig_threshold(), + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + scenario +} From 2f321b6e9ac519a35e2d4d9fd420254d5141fd5c Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 23:29:28 +0300 Subject: [PATCH 09/23] Upd loyalty tests to use multisig config --- .../tests/loyalty/add_loyalty_level.move | 100 +++++++------- .../tests/loyalty/get_user_discount_rate.move | 16 +-- .../tests/loyalty/grant_user_level.move | 23 ++-- .../tests/loyalty/remove_loyalty_level.move | 125 ++++++++---------- .../update_loyalty_admin_cap_owner.move | 51 +++---- 5 files changed, 140 insertions(+), 175 deletions(-) diff --git a/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move b/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move index 0a69328..474b791 100644 --- a/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move +++ b/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move @@ -6,17 +6,15 @@ use deeptrade_core::loyalty::{ LoyaltyAdminCap, LoyaltyProgram, ELoyaltyLevelAlreadyExists, - EInvalidFeeDiscountRate, - ESenderIsNotValidMultisig + EInvalidFeeDiscountRate }; +use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use multisig::multisig_test_utils::{ get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; use std::unit_test::assert_eq; -use sui::test_scenario::{Scenario, begin, end, return_shared}; +use sui::test_scenario::{Scenario, end, return_shared}; use sui::test_utils::destroy; // === Constants === @@ -49,16 +47,15 @@ fun successful_add_loyalty_level() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, PLATINUM_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -76,6 +73,7 @@ fun successful_add_loyalty_level() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -89,39 +87,34 @@ fun add_multiple_loyalty_levels() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add multiple levels loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, BRONZE_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_SILVER, SILVER_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_GOLD, GOLD_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -153,6 +146,7 @@ fun add_multiple_loyalty_levels() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -166,16 +160,15 @@ fun add_level_with_max_discount_rate() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, MAX_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -189,6 +182,7 @@ fun add_level_with_max_discount_rate() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -202,22 +196,22 @@ fun add_level_with_zero_discount_rate_fails() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Try to add level with zero discount rate (should fail) loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, ZERO_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -231,29 +225,26 @@ fun add_level_with_different_level_ids() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add level with ID 0 loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_ZERO, BRONZE_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); // Add level with max u8 value loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_MAX, GOLD_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -274,6 +265,7 @@ fun add_level_with_different_level_ids() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -287,34 +279,32 @@ fun add_duplicate_level_fails() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add level first time loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_SILVER, SILVER_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); // Try to add the same level again loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_SILVER, GOLD_DISCOUNT, // Different discount rate, but same level ID - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -328,22 +318,22 @@ fun add_level_with_invalid_discount_rate_fails() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Try to add level with discount rate > 100% loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, INVALID_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -356,22 +346,22 @@ fun non_multisig_sender_fails() { scenario.next_tx(OWNER); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Use invalid multisig parameters to trigger failure loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_GOLD, GOLD_DISCOUNT, - vector[vector[1u8, 2u8, 3u8]], // Invalid public key - vector[1u8], // Invalid weight - 1u16, // Invalid threshold scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -387,57 +377,56 @@ fun add_then_remove_then_add_again() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, PLATINUM_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Remove level scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Add level again scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, GOLD_DISCOUNT, // Different discount rate this time - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -451,6 +440,7 @@ fun add_then_remove_then_add_again() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -466,21 +456,21 @@ fun add_level_then_grant_to_user() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, PLATINUM_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Grant the new level to a user @@ -519,7 +509,7 @@ fun add_level_then_grant_to_user() { /// Returns (scenario) ready for testing. #[test_only] public(package) fun setup_test_environment(): Scenario { - let mut scenario = begin(OWNER); + let mut scenario = setup_with_initialized_config(); // Initialize loyalty program scenario.next_tx(OWNER); diff --git a/packages/deeptrade-core/tests/loyalty/get_user_discount_rate.move b/packages/deeptrade-core/tests/loyalty/get_user_discount_rate.move index 4773c92..51c643c 100644 --- a/packages/deeptrade-core/tests/loyalty/get_user_discount_rate.move +++ b/packages/deeptrade-core/tests/loyalty/get_user_discount_rate.move @@ -3,11 +3,9 @@ module deeptrade_core::get_user_discount_rate_tests; use deeptrade_core::grant_user_level_tests::setup_test_environment; use deeptrade_core::loyalty::{Self, LoyaltyAdminCap, LoyaltyProgram}; +use deeptrade_core::multisig_config::MultisigConfig; use multisig::multisig_test_utils::{ get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; use std::unit_test::assert_eq; use sui::test_scenario::{end, return_shared}; @@ -227,22 +225,22 @@ fun get_discount_rate_for_nonexistent_level_edge_case() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add a new level loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, PLATINUM_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Grant the new level to ALICE @@ -293,20 +291,20 @@ fun get_discount_rate_for_nonexistent_level_edge_case() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Verify discount rate is now 0 (user has no level) diff --git a/packages/deeptrade-core/tests/loyalty/grant_user_level.move b/packages/deeptrade-core/tests/loyalty/grant_user_level.move index 946923c..643552f 100644 --- a/packages/deeptrade-core/tests/loyalty/grant_user_level.move +++ b/packages/deeptrade-core/tests/loyalty/grant_user_level.move @@ -9,14 +9,13 @@ use deeptrade_core::loyalty::{ EUserAlreadyHasLoyaltyLevel, ESenderIsNotLoyaltyAdmin }; +use deeptrade_core::multisig_config::MultisigConfig; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use multisig::multisig_test_utils::{ get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; use std::unit_test::assert_eq; -use sui::test_scenario::{Scenario, begin, end, return_shared}; +use sui::test_scenario::{Scenario, end, return_shared}; use sui::test_utils::destroy; // === Constants === @@ -323,7 +322,7 @@ fun grant_to_zero_address_succeeds() { /// Returns (scenario, loyalty_program_id) ready for testing. #[test_only] public(package) fun setup_test_environment(): (Scenario, ID) { - let mut scenario = begin(OWNER); + let mut scenario = setup_with_initialized_config(); // Initialize loyalty program scenario.next_tx(OWNER); @@ -336,45 +335,41 @@ public(package) fun setup_test_environment(): (Scenario, ID) { scenario.next_tx(multisig_address); let loyalty_program_id = { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add test loyalty levels loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, BRONZE_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_SILVER, SILVER_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_GOLD, GOLD_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); let loyalty_program_id = object::id(&loyalty_program); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); loyalty_program_id }; diff --git a/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move b/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move index e0fbe4f..ab85b34 100644 --- a/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move +++ b/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move @@ -7,14 +7,11 @@ use deeptrade_core::loyalty::{ LoyaltyAdminCap, LoyaltyProgram, ELoyaltyLevelNotFound, - ELoyaltyLevelHasUsers, - ESenderIsNotValidMultisig + ELoyaltyLevelHasUsers }; +use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; use multisig::multisig_test_utils::{ - get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold + get_test_multisig_address }; use std::unit_test::assert_eq; use sui::test_scenario::{end, return_shared}; @@ -49,37 +46,36 @@ fun successful_remove_loyalty_level() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add a new level first loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, PLATINUM_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Now remove the level scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -96,6 +92,7 @@ fun successful_remove_loyalty_level() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -109,79 +106,70 @@ fun remove_multiple_empty_levels() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add multiple new levels loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, PLATINUM_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, 10u8, SILVER_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, 20u8, GOLD_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Remove all the new levels scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, 10u8, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, 20u8, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -217,6 +205,7 @@ fun remove_multiple_empty_levels() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -230,58 +219,53 @@ fun remove_level_with_different_ids() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add levels with different IDs loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_ZERO, BRONZE_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_MAX, GOLD_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Remove the levels scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_ZERO, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_MAX, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -300,6 +284,7 @@ fun remove_level_with_different_ids() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -313,21 +298,21 @@ fun remove_nonexistent_level_fails() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Try to remove a level that doesn't exist loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, 99u8, // Non-existent level - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -360,20 +345,20 @@ fun remove_level_with_members_fails() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -386,21 +371,21 @@ fun non_multisig_sender_fails() { scenario.next_tx(OWNER); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Use invalid multisig parameters to trigger failure loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, - vector[vector[1u8, 2u8, 3u8]], // Invalid public key - vector[1u8], // Invalid weight - 1u16, // Invalid threshold scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -466,15 +451,14 @@ fun remove_level_after_revoking_all_users() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -487,6 +471,7 @@ fun remove_level_after_revoking_all_users() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -502,36 +487,31 @@ fun remove_last_level() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Remove all levels (they start with 0 members) loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_SILVER, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_GOLD, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -555,6 +535,7 @@ fun remove_last_level() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); @@ -568,48 +549,49 @@ fun remove_level_then_verify_state() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Add a new level loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, PLATINUM_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Remove the level scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::remove_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Verify level is completely removed and can't be accessed scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared_by_id(loyalty_program_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); // Verify level doesn't exist @@ -626,12 +608,10 @@ fun remove_level_then_verify_state() { // Verify we can add the same level again (proving it was completely removed) loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_PLATINUM, GOLD_DISCOUNT, // Different discount rate - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -645,6 +625,7 @@ fun remove_level_then_verify_state() { destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; end(scenario); diff --git a/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move b/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move index 7187f05..f788d0c 100644 --- a/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move +++ b/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move @@ -3,13 +3,14 @@ module deeptrade_core::update_loyalty_admin_cap_owner_tests; use deeptrade_core::add_loyalty_level_tests::setup_test_environment; use deeptrade_core::admin; -use deeptrade_core::loyalty::{Self, LoyaltyAdminCap, LoyaltyProgram, ESenderIsNotLoyaltyAdmin}; -use multisig::multisig_test_utils::{ - get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold +use deeptrade_core::loyalty::{ + Self, + LoyaltyAdminCap, + LoyaltyProgram, + ESenderIsNotLoyaltyAdmin, }; +use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::test_scenario::{end, return_shared}; use sui::test_utils::destroy; @@ -28,15 +29,14 @@ fun successful_owner_update() { scenario.next_tx(multisig_address); { let mut loyalty_admin_cap = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::update_loyalty_admin_cap_owner( &mut loyalty_admin_cap, + &config, &admin_cap, ALICE, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -44,6 +44,7 @@ fun successful_owner_update() { destroy(admin_cap); return_shared(loyalty_admin_cap); + return_shared(config); }; // 2. Verify new owner (ALICE) can grant levels @@ -51,19 +52,19 @@ fun successful_owner_update() { scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, 1, 100_000_000, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; scenario.next_tx(ALICE); @@ -91,39 +92,39 @@ fun old_owner_cannot_grant_level() { scenario.next_tx(multisig_address); { let mut loyalty_admin_cap = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::update_loyalty_admin_cap_owner( &mut loyalty_admin_cap, + &config, &admin_cap, ALICE, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_admin_cap); + return_shared(config); }; // 2. Add a dummy level scenario.next_tx(multisig_address); { let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, 1, 100_000_000, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // 3. Verify old owner (OWNER) fails to grant level @@ -141,27 +142,27 @@ fun old_owner_cannot_grant_level() { end(scenario); } -#[test, expected_failure(abort_code = loyalty::ESenderIsNotValidMultisig)] +#[test, expected_failure(abort_code = ESenderIsNotValidMultisig)] fun non_multisig_sender_fails() { let mut scenario = setup_test_environment(); scenario.next_tx(OWNER); { let mut loyalty_admin_cap = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::update_loyalty_admin_cap_owner( &mut loyalty_admin_cap, + &config, &admin_cap, ALICE, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_admin_cap); + return_shared(config); }; end(scenario); @@ -175,15 +176,14 @@ fun update_to_same_owner() { scenario.next_tx(multisig_address); { let mut loyalty_admin_cap = scenario.take_shared(); + let config = scenario.take_shared(); let admin_cap = admin::get_admin_cap_for_testing(scenario.ctx()); loyalty::update_loyalty_admin_cap_owner( &mut loyalty_admin_cap, + &config, &admin_cap, OWNER, // Update to the same owner - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -191,6 +191,7 @@ fun update_to_same_owner() { destroy(admin_cap); return_shared(loyalty_admin_cap); + return_shared(config); }; end(scenario); From 20edef21bcdfe62990d5ca20a5f4aff6c5b0f0be Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 23:35:39 +0300 Subject: [PATCH 10/23] Upd unsettled fees tests to use multisig config --- ...protocol_unsettled_fee_storage_rebate.move | 23 ++++++++----------- ...aim_user_unsettled_fee_storage_rebate.move | 19 +++++++-------- .../unsettled-fees/settle_user_fees.move | 5 ++-- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/packages/deeptrade-core/tests/unsettled-fees/claim_protocol_unsettled_fee_storage_rebate.move b/packages/deeptrade-core/tests/unsettled-fees/claim_protocol_unsettled_fee_storage_rebate.move index 4d397bc..7ba898d 100644 --- a/packages/deeptrade-core/tests/unsettled-fees/claim_protocol_unsettled_fee_storage_rebate.move +++ b/packages/deeptrade-core/tests/unsettled-fees/claim_protocol_unsettled_fee_storage_rebate.move @@ -9,17 +9,12 @@ use deeptrade_core::fee_manager::{ settle_protocol_fee_and_record, start_protocol_fee_settlement, EInvalidOwner, - EProtocolUnsettledFeeNotEmpty, - ESenderIsNotValidMultisig + EProtocolUnsettledFeeNotEmpty }; +use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; use deeptrade_core::settle_user_fees_tests::setup_test_environment; use deeptrade_core::treasury::Treasury; -use multisig::multisig_test_utils::{ - get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold -}; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::balance; use sui::sui::SUI; @@ -153,15 +148,14 @@ fun admin_claims_rebate_successfully() { { let treasury = scenario.take_shared(); let mut fee_manager = scenario.take_shared_by_id(fee_manager_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); claim_protocol_unsettled_fee_storage_rebate_admin( &treasury, &mut fee_manager, + &config, &admin_cap, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -171,6 +165,7 @@ fun admin_claims_rebate_successfully() { destroy(admin_cap); return_shared(treasury); return_shared(fee_manager); + return_shared(config); }; end(scenario); @@ -186,21 +181,21 @@ fun non_multisig_admin_claim_fails() { { let treasury = scenario.take_shared(); let mut fee_manager = scenario.take_shared_by_id(fee_manager_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); claim_protocol_unsettled_fee_storage_rebate_admin( &treasury, &mut fee_manager, + &config, &admin_cap, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(treasury); return_shared(fee_manager); + return_shared(config); }; end(scenario); diff --git a/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move b/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move index da13e46..a3882ca 100644 --- a/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move +++ b/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move @@ -8,16 +8,13 @@ use deeptrade_core::fee_manager::{ claim_user_unsettled_fee_storage_rebate, claim_user_unsettled_fee_storage_rebate_admin, settle_filled_order_fee_and_record, - start_protocol_fee_settlement, - ESenderIsNotValidMultisig + start_protocol_fee_settlement }; +use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; use deeptrade_core::settle_user_fees_tests::setup_test_environment; use deeptrade_core::treasury::Treasury; use multisig::multisig_test_utils::{ get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; use std::unit_test::assert_eq; use sui::balance; @@ -225,6 +222,7 @@ fun admin_claims_rebate_successfully() { let mut fee_manager = scenario.take_shared_by_id(fee_manager_id); let pool = scenario.take_shared_by_id(pool_id); let balance_manager = scenario.take_shared_by_id(balance_manager_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); claim_user_unsettled_fee_storage_rebate_admin( @@ -232,11 +230,9 @@ fun admin_claims_rebate_successfully() { &mut fee_manager, &pool, &balance_manager, + &config, &admin_cap, order_id, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -251,6 +247,7 @@ fun admin_claims_rebate_successfully() { return_shared(fee_manager); return_shared(pool); return_shared(balance_manager); + return_shared(config); }; end(scenario); @@ -274,6 +271,7 @@ fun non_multisig_admin_claim_fails() { let mut fee_manager = scenario.take_shared_by_id(fee_manager_id); let pool = scenario.take_shared_by_id(pool_id); let balance_manager = scenario.take_shared_by_id(balance_manager_id); + let config = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); claim_user_unsettled_fee_storage_rebate_admin( @@ -281,11 +279,9 @@ fun non_multisig_admin_claim_fails() { &mut fee_manager, &pool, &balance_manager, + &config, &admin_cap, order_id, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -294,6 +290,7 @@ fun non_multisig_admin_claim_fails() { return_shared(fee_manager); return_shared(pool); return_shared(balance_manager); + return_shared(config); }; end(scenario); diff --git a/packages/deeptrade-core/tests/unsettled-fees/settle_user_fees.move b/packages/deeptrade-core/tests/unsettled-fees/settle_user_fees.move index 69662ec..b8dce15 100644 --- a/packages/deeptrade-core/tests/unsettled-fees/settle_user_fees.move +++ b/packages/deeptrade-core/tests/unsettled-fees/settle_user_fees.move @@ -12,11 +12,12 @@ use deepbook::pool_tests::{ }; use deeptrade_core::fee_manager::{Self, FeeManager, settle_user_fees}; use deeptrade_core::treasury; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use std::unit_test::assert_eq; use sui::balance; use sui::clock::Clock; use sui::sui::SUI; -use sui::test_scenario::{Self, Scenario, begin, end, return_shared}; +use sui::test_scenario::{Self, Scenario, end, return_shared}; use sui::test_utils::destroy; use token::deep::DEEP; @@ -1607,7 +1608,7 @@ fun settlement_with_maximum_precision_amounts() { /// - Creates FeeManager for ALICE #[test_only] public(package) fun setup_test_environment(): (Scenario, ID, ID, ID) { - let mut scenario = begin(OWNER); + let mut scenario = setup_with_initialized_config(); // Setup treasury scenario.next_tx(OWNER); From 2da5555ba0dbc6eaa3d2f23181c1f8fb1b4c63e1 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 23:45:58 +0300 Subject: [PATCH 11/23] Upd admin, ticket & treasury tests to use multisig config --- .../tests/admin/admin_init_tests.move | 4 +- .../tests/ticket/create_ticket_tests.move | 23 +++--- .../treasury/version_management_tests.move | 73 +++++++++---------- 3 files changed, 46 insertions(+), 54 deletions(-) diff --git a/packages/deeptrade-core/tests/admin/admin_init_tests.move b/packages/deeptrade-core/tests/admin/admin_init_tests.move index 5d3b045..10f5eb4 100644 --- a/packages/deeptrade-core/tests/admin/admin_init_tests.move +++ b/packages/deeptrade-core/tests/admin/admin_init_tests.move @@ -2,6 +2,7 @@ module deeptrade_core::admin_init_tests; use deeptrade_core::admin::{Self, AdminCap}; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use std::unit_test::assert_eq; use sui::test_scenario; @@ -33,7 +34,8 @@ fun init_logic_creates_and_transfers_admin_cap_to_publisher() { /// Initializes admin and gives the AdminCap to the OWNER. #[test_only] public fun setup_with_admin_cap(owner: address): test_scenario::Scenario { - let mut scenario = test_scenario::begin(owner); + let mut scenario = setup_with_initialized_config(); + scenario.next_tx(owner); { admin::init_for_testing(scenario.ctx()); }; diff --git a/packages/deeptrade-core/tests/ticket/create_ticket_tests.move b/packages/deeptrade-core/tests/ticket/create_ticket_tests.move index 192329c..79960db 100644 --- a/packages/deeptrade-core/tests/ticket/create_ticket_tests.move +++ b/packages/deeptrade-core/tests/ticket/create_ticket_tests.move @@ -3,18 +3,15 @@ module deeptrade_core::create_ticket_tests; use deeptrade_core::admin::AdminCap; use deeptrade_core::admin_init_tests::setup_with_admin_cap; +use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; use deeptrade_core::ticket::{ Self, AdminTicket, - ESenderIsNotValidMultisig, TicketCreated, ticket_delay_duration }; use multisig::multisig_test_utils::{ get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; use std::unit_test::assert_eq; use sui::clock::{Self, Clock}; @@ -39,13 +36,12 @@ fun create_ticket_success_with_multisig() { let mut clock = clock::create_for_testing(scenario.ctx()); clock.set_for_testing(CLOCK_TIMESTAMP_MS); let admin_cap = scenario.take_from_sender(); + let config = scenario.take_shared(); ticket::create_ticket( + &config, &admin_cap, TICKET_TYPE, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), &clock, scenario.ctx(), ); @@ -60,6 +56,7 @@ fun create_ticket_success_with_multisig() { clock::destroy_for_testing(clock); scenario.return_to_sender(admin_cap); + return_shared(config); }; scenario.next_tx(multisig_address); @@ -90,20 +87,20 @@ fun create_ticket_fails_if_sender_not_multisig() { { let clock = clock::create_for_testing(scenario.ctx()); let admin_cap = scenario.take_from_sender(); + let config = scenario.take_shared(); // This should abort ticket::create_ticket( + &config, &admin_cap, TICKET_TYPE, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), &clock, scenario.ctx(), ); clock::destroy_for_testing(clock); scenario.return_to_sender(admin_cap); + return_shared(config); }; scenario.end(); @@ -117,19 +114,19 @@ public fun create_ticket_with_multisig(scenario: &mut Scenario, ticket_type: u8) let clock = clock::create_for_testing(scenario.ctx()); let admin_cap = scenario.take_from_sender(); + let config = scenario.take_shared(); ticket::create_ticket( + &config, &admin_cap, ticket_type, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), &clock, scenario.ctx(), ); clock::destroy_for_testing(clock); scenario.return_to_sender(admin_cap); + return_shared(config); // We keep it here to make sure the ticket is available from Global Inventory in the next test scenario.next_tx(multisig_address); diff --git a/packages/deeptrade-core/tests/treasury/version_management_tests.move b/packages/deeptrade-core/tests/treasury/version_management_tests.move index 39a58d0..5640ceb 100644 --- a/packages/deeptrade-core/tests/treasury/version_management_tests.move +++ b/packages/deeptrade-core/tests/treasury/version_management_tests.move @@ -4,6 +4,7 @@ module deeptrade_core::version_management_tests; use deeptrade_core::admin::AdminCap; use deeptrade_core::admin_init_tests::setup_with_admin_cap; use deeptrade_core::helper::current_version; +use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; use deeptrade_core::treasury::{ Self, Treasury, @@ -16,14 +17,8 @@ use deeptrade_core::treasury::{ EVersionAlreadyEnabled, EVersionNotEnabled, ECannotDisableNewerVersion, - ESenderIsNotValidMultisig -}; -use multisig::multisig_test_utils::{ - get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; +use multisig::multisig_test_utils::get_test_multisig_address; use sui::event; use sui::test_scenario::{Self, Scenario}; @@ -38,14 +33,13 @@ fun test_enable_new_version_success() { { let new_version = NEW_VERSION; assert!(!treasury.allowed_versions().contains(&new_version), 1); + let config = scenario.take_shared(); treasury::enable_version( &mut treasury, + &config, &admin_cap, NEW_VERSION, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -59,6 +53,7 @@ fun test_enable_new_version_success() { ); assert!(event_treasury_id == treasury_id, 4); assert!(event_version == NEW_VERSION, 5); + test_scenario::return_shared(config); }; test_scenario::return_shared(treasury); @@ -74,17 +69,16 @@ fun test_disable_current_version_success() { scenario.next_tx(multisig_address); { let version_to_disable = current_version(); + let config = scenario.take_shared(); assert!(treasury.allowed_versions().contains(&version_to_disable), 1); assert!(!treasury.disabled_versions().contains(&version_to_disable), 2); treasury::disable_version( &mut treasury, + &config, &admin_cap, version_to_disable, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); @@ -99,6 +93,7 @@ fun test_disable_current_version_success() { ); assert!(event_treasury_id == treasury_id, 6); assert!(event_version == version_to_disable, 7); + test_scenario::return_shared(config); }; test_scenario::return_shared(treasury); @@ -116,15 +111,15 @@ fun test_action_fails_on_disabled_version() { // Disable the current version scenario.next_tx(multisig_address); { + let config = scenario.take_shared(); treasury::disable_version( &mut treasury, + &config, &admin_cap, version_to_disable, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); + test_scenario::return_shared(config); }; // Attempt to perform a version-checked action @@ -151,30 +146,30 @@ fun test_reenable_disabled_version_fails() { // Disable the current version scenario.next_tx(multisig_address); { + let config = scenario.take_shared(); treasury::disable_version( &mut treasury, + &config, &admin_cap, version_to_disable, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); + test_scenario::return_shared(config); }; // Attempt to re-enable the disabled version scenario.next_tx(multisig_address); { + let config = scenario.take_shared(); // This should fail treasury::enable_version( &mut treasury, + &config, &admin_cap, version_to_disable, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); + test_scenario::return_shared(config); }; scenario.return_to_sender(admin_cap); @@ -190,16 +185,16 @@ fun test_enable_version_fails_already_enabled() { scenario.next_tx(multisig_address); { + let config = scenario.take_shared(); // Attempt to enable the current version, which is already enabled by default treasury::enable_version( &mut treasury, + &config, &admin_cap, current_version(), - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); + test_scenario::return_shared(config); }; scenario.return_to_sender(admin_cap); @@ -214,27 +209,25 @@ fun test_disable_version_fails_not_enabled() { scenario.next_tx(multisig_address); { + let config = scenario.take_shared(); // Disable the current version treasury::disable_version( &mut treasury, + &config, &admin_cap, current_version(), - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); // Disable the version that is not enabled (we already disabled the current version) treasury::disable_version( &mut treasury, + &config, &admin_cap, current_version(), - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); + test_scenario::return_shared(config); }; scenario.return_to_sender(admin_cap); @@ -249,16 +242,16 @@ fun test_disable_version_fails_newer_version() { scenario.next_tx(multisig_address); { + let config = scenario.take_shared(); // Attempt to disable a future version treasury::disable_version( &mut treasury, + &config, &admin_cap, current_version() + 1, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); + test_scenario::return_shared(config); }; scenario.return_to_sender(admin_cap); @@ -274,16 +267,16 @@ fun test_version_management_fails_not_multisig() { // Switch to a non-multisig user scenario.next_tx(FAKE_USER); { + let config = scenario.take_shared(); // Attempt to enable a version from an unauthorized address treasury::enable_version( &mut treasury, + &config, &admin_cap, NEW_VERSION, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); + test_scenario::return_shared(config); }; scenario.return_to_sender(admin_cap); @@ -299,16 +292,16 @@ fun test_version_management_fails_not_multisig_disable() { // Switch to a non-multisig user scenario.next_tx(FAKE_USER); { + let config = scenario.take_shared(); // Attempt to enable a version from an unauthorized address treasury::disable_version( &mut treasury, + &config, &admin_cap, current_version(), - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); + test_scenario::return_shared(config); }; scenario.return_to_sender(admin_cap); From 404e26781c6fc36603be8a2c5eef2e80ecdcf4cd Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 23:50:52 +0300 Subject: [PATCH 12/23] Upd remaining tests to use multisig config --- .../fee/estimate_full_fee_market_tests.move | 21 +++++++---------- .../calculate_market_order_params_tests.move | 23 ++++++++----------- .../get_quantity_out_input_fee_tests.move | 23 ++++++++----------- 3 files changed, 26 insertions(+), 41 deletions(-) diff --git a/packages/deeptrade-core/tests/fee/estimate_full_fee_market_tests.move b/packages/deeptrade-core/tests/fee/estimate_full_fee_market_tests.move index 033897c..0f387b7 100644 --- a/packages/deeptrade-core/tests/fee/estimate_full_fee_market_tests.move +++ b/packages/deeptrade-core/tests/fee/estimate_full_fee_market_tests.move @@ -20,12 +20,11 @@ use deeptrade_core::get_sui_per_deep_from_oracle_tests::{ new_sui_price_object }; use deeptrade_core::loyalty::{Self, LoyaltyAdminCap, LoyaltyProgram}; +use deeptrade_core::multisig_config::MultisigConfig; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use deeptrade_core::treasury; use multisig::multisig_test_utils::{ get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; use pyth::price_info::{Self, PriceInfoObject}; use sui::clock; @@ -183,7 +182,7 @@ public(package) fun setup_test_environment(): ( PriceInfoObject, ID, ) { - let mut scenario = begin(OWNER); + let mut scenario = setup_with_initialized_config(); // Setup treasury scenario.next_tx(OWNER); @@ -294,44 +293,40 @@ public(package) fun setup_test_environment(): ( let loyalty_program_id = { let mut loyalty_program = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); + let config = scenario.take_shared(); // Add test loyalty levels loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, BRONZE_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_SILVER, SILVER_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_GOLD, GOLD_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); let loyalty_program_id = object::id(&loyalty_program); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); loyalty_program_id }; diff --git a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move index 1b2298b..fc9a815 100644 --- a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move +++ b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move @@ -21,19 +21,18 @@ use deeptrade_core::get_sui_per_deep_from_oracle_tests::{ }; use deeptrade_core::helper; use deeptrade_core::loyalty::{Self, LoyaltyProgram}; +use deeptrade_core::multisig_config::MultisigConfig; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use deeptrade_core::treasury; use multisig::multisig_test_utils::{ get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; use pyth::price_info::{Self, PriceInfoObject}; use std::unit_test::assert_eq; use sui::clock; use sui::coin; use sui::sui::SUI; -use sui::test_scenario::{Self, Scenario, begin, end, return_shared}; +use sui::test_scenario::{Self, Scenario, end, return_shared}; use sui::test_utils::destroy; use token::deep::DEEP; @@ -60,7 +59,7 @@ public(package) fun setup_test_environment(): ( PriceInfoObject, ID, ) { - let mut scenario = begin(OWNER); + let mut scenario = setup_with_initialized_config(); // Setup treasury scenario.next_tx(OWNER); @@ -171,44 +170,40 @@ public(package) fun setup_test_environment(): ( let loyalty_program_id = { let mut loyalty_program = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); + let config = scenario.take_shared(); // Add test loyalty levels loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, BRONZE_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_SILVER, SILVER_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_GOLD, GOLD_DISCOUNT, - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); let loyalty_program_id = object::id(&loyalty_program); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); loyalty_program_id }; diff --git a/packages/deeptrade-core/tests/swap/get_quantity_out_input_fee_tests.move b/packages/deeptrade-core/tests/swap/get_quantity_out_input_fee_tests.move index ec13e6a..c159dc4 100644 --- a/packages/deeptrade-core/tests/swap/get_quantity_out_input_fee_tests.move +++ b/packages/deeptrade-core/tests/swap/get_quantity_out_input_fee_tests.move @@ -12,18 +12,17 @@ use deepbook::pool_tests::{ use deeptrade_core::fee::{Self, TradingFeeConfig}; use deeptrade_core::fee_manager::{Self, FeeManager}; use deeptrade_core::loyalty::{Self, LoyaltyAdminCap, LoyaltyProgram}; +use deeptrade_core::multisig_config::MultisigConfig; use deeptrade_core::swap::get_quantity_out_input_fee; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use deeptrade_core::treasury; use multisig::multisig_test_utils::{ get_test_multisig_address, - get_test_multisig_pks, - get_test_multisig_weights, - get_test_multisig_threshold }; use std::unit_test::assert_eq; use sui::clock::Clock; use sui::sui::SUI; -use sui::test_scenario::{Self, Scenario, begin, end, return_shared}; +use sui::test_scenario::{Self, Scenario, end, return_shared}; use sui::test_utils::destroy; use token::deep::DEEP; @@ -333,7 +332,7 @@ fun no_loyalty_level() { /// Returns (scenario, pool_id, balance_manager_id, fee_manager_id) ready for testing. #[test_only] public(package) fun setup_test_environment(): (Scenario, ID, ID, ID) { - let mut scenario = begin(OWNER); + let mut scenario = setup_with_initialized_config(); // Setup treasury scenario.next_tx(OWNER); @@ -379,43 +378,39 @@ public(package) fun setup_test_environment(): (Scenario, ID, ID, ID) { { let mut loyalty_program = scenario.take_shared(); let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); + let config = scenario.take_shared(); // Add test loyalty levels loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_BRONZE, 100_000_000, // 10% discount - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_SILVER, 250_000_000, // 25% discount - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); loyalty::add_loyalty_level( &mut loyalty_program, + &config, &admin_cap, LEVEL_GOLD, 500_000_000, // 50% discount - get_test_multisig_pks(), - get_test_multisig_weights(), - get_test_multisig_threshold(), scenario.ctx(), ); destroy(admin_cap); return_shared(loyalty_program); + return_shared(config); }; // Add liquidity to the pool From abf6b4587a556a0b5389e076aa21740b42c1ba39 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 29 Sep 2025 23:59:34 +0300 Subject: [PATCH 13/23] Add add_loyalty_level_fails_uninitialized_config test --- .../tests/loyalty/grant_user_level.move | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/deeptrade-core/tests/loyalty/grant_user_level.move b/packages/deeptrade-core/tests/loyalty/grant_user_level.move index 643552f..a3f447d 100644 --- a/packages/deeptrade-core/tests/loyalty/grant_user_level.move +++ b/packages/deeptrade-core/tests/loyalty/grant_user_level.move @@ -9,8 +9,9 @@ use deeptrade_core::loyalty::{ EUserAlreadyHasLoyaltyLevel, ESenderIsNotLoyaltyAdmin }; -use deeptrade_core::multisig_config::MultisigConfig; +use deeptrade_core::multisig_config::{MultisigConfig, EMultisigConfigNotInitialized}; use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; +use deeptrade_core::initialize_multisig_config_tests::setup; use multisig::multisig_test_utils::{ get_test_multisig_address, }; @@ -316,6 +317,41 @@ fun grant_to_zero_address_succeeds() { end(scenario); } +#[test, expected_failure(abort_code = EMultisigConfigNotInitialized)] +fun add_loyalty_level_fails_uninitialized_config() { + let mut scenario = setup(); + let multisig_address = get_test_multisig_address(); + + // Initialize loyalty program + scenario.next_tx(OWNER); + { + loyalty::init_for_testing(scenario.ctx()); + }; + + scenario.next_tx(multisig_address); + { + let mut loyalty_program = scenario.take_shared(); + let config = scenario.take_shared(); + let admin_cap = deeptrade_core::admin::get_admin_cap_for_testing(scenario.ctx()); + + // This should fail because the config is not initialized + loyalty::add_loyalty_level( + &mut loyalty_program, + &config, + &admin_cap, + LEVEL_BRONZE, + BRONZE_DISCOUNT, + scenario.ctx(), + ); + + destroy(admin_cap); + return_shared(loyalty_program); + return_shared(config); + }; + + end(scenario); +} + // === Helper Functions === /// Sets up a complete test environment with loyalty program. From f2846cfd3eb5baf58ae929b1ed157643e420db80 Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 00:06:29 +0300 Subject: [PATCH 14/23] Prettify --- .../tests/fee/estimate_full_fee_market_tests.move | 6 ++---- .../helpers/calculate_market_order_params_tests.move | 6 ++---- .../tests/loyalty/add_loyalty_level.move | 4 +--- .../tests/loyalty/get_user_discount_rate.move | 4 +--- .../tests/loyalty/grant_user_level.move | 6 ++---- .../tests/loyalty/remove_loyalty_level.move | 4 +--- .../tests/loyalty/update_loyalty_admin_cap_owner.move | 7 +------ .../tests/swap/get_quantity_out_input_fee_tests.move | 6 ++---- .../tests/ticket/create_ticket_tests.move | 11 ++--------- .../tests/treasury/version_management_tests.move | 2 +- .../claim_user_unsettled_fee_storage_rebate.move | 4 +--- 11 files changed, 16 insertions(+), 44 deletions(-) diff --git a/packages/deeptrade-core/tests/fee/estimate_full_fee_market_tests.move b/packages/deeptrade-core/tests/fee/estimate_full_fee_market_tests.move index 0f387b7..ae42295 100644 --- a/packages/deeptrade-core/tests/fee/estimate_full_fee_market_tests.move +++ b/packages/deeptrade-core/tests/fee/estimate_full_fee_market_tests.move @@ -21,11 +21,9 @@ use deeptrade_core::get_sui_per_deep_from_oracle_tests::{ }; use deeptrade_core::loyalty::{Self, LoyaltyAdminCap, LoyaltyProgram}; use deeptrade_core::multisig_config::MultisigConfig; -use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use deeptrade_core::treasury; -use multisig::multisig_test_utils::{ - get_test_multisig_address, -}; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; +use multisig::multisig_test_utils::get_test_multisig_address; use pyth::price_info::{Self, PriceInfoObject}; use sui::clock; use sui::coin; diff --git a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move index fc9a815..854ab75 100644 --- a/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move +++ b/packages/deeptrade-core/tests/helpers/calculate_market_order_params_tests.move @@ -22,11 +22,9 @@ use deeptrade_core::get_sui_per_deep_from_oracle_tests::{ use deeptrade_core::helper; use deeptrade_core::loyalty::{Self, LoyaltyProgram}; use deeptrade_core::multisig_config::MultisigConfig; -use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use deeptrade_core::treasury; -use multisig::multisig_test_utils::{ - get_test_multisig_address, -}; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; +use multisig::multisig_test_utils::get_test_multisig_address; use pyth::price_info::{Self, PriceInfoObject}; use std::unit_test::assert_eq; use sui::clock; diff --git a/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move b/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move index 474b791..23e097c 100644 --- a/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move +++ b/packages/deeptrade-core/tests/loyalty/add_loyalty_level.move @@ -10,9 +10,7 @@ use deeptrade_core::loyalty::{ }; use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; -use multisig::multisig_test_utils::{ - get_test_multisig_address, -}; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::test_scenario::{Scenario, end, return_shared}; use sui::test_utils::destroy; diff --git a/packages/deeptrade-core/tests/loyalty/get_user_discount_rate.move b/packages/deeptrade-core/tests/loyalty/get_user_discount_rate.move index 51c643c..05f298a 100644 --- a/packages/deeptrade-core/tests/loyalty/get_user_discount_rate.move +++ b/packages/deeptrade-core/tests/loyalty/get_user_discount_rate.move @@ -4,9 +4,7 @@ module deeptrade_core::get_user_discount_rate_tests; use deeptrade_core::grant_user_level_tests::setup_test_environment; use deeptrade_core::loyalty::{Self, LoyaltyAdminCap, LoyaltyProgram}; use deeptrade_core::multisig_config::MultisigConfig; -use multisig::multisig_test_utils::{ - get_test_multisig_address, -}; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::test_scenario::{end, return_shared}; use sui::test_utils::destroy; diff --git a/packages/deeptrade-core/tests/loyalty/grant_user_level.move b/packages/deeptrade-core/tests/loyalty/grant_user_level.move index a3f447d..3c94f0a 100644 --- a/packages/deeptrade-core/tests/loyalty/grant_user_level.move +++ b/packages/deeptrade-core/tests/loyalty/grant_user_level.move @@ -1,6 +1,7 @@ #[test_only] module deeptrade_core::grant_user_level_tests; +use deeptrade_core::initialize_multisig_config_tests::setup; use deeptrade_core::loyalty::{ Self, LoyaltyProgram, @@ -11,10 +12,7 @@ use deeptrade_core::loyalty::{ }; use deeptrade_core::multisig_config::{MultisigConfig, EMultisigConfigNotInitialized}; use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; -use deeptrade_core::initialize_multisig_config_tests::setup; -use multisig::multisig_test_utils::{ - get_test_multisig_address, -}; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::test_scenario::{Scenario, end, return_shared}; use sui::test_utils::destroy; diff --git a/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move b/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move index ab85b34..b1d3e53 100644 --- a/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move +++ b/packages/deeptrade-core/tests/loyalty/remove_loyalty_level.move @@ -10,9 +10,7 @@ use deeptrade_core::loyalty::{ ELoyaltyLevelHasUsers }; use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; -use multisig::multisig_test_utils::{ - get_test_multisig_address -}; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::test_scenario::{end, return_shared}; use sui::test_utils::destroy; diff --git a/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move b/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move index f788d0c..b36ac3b 100644 --- a/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move +++ b/packages/deeptrade-core/tests/loyalty/update_loyalty_admin_cap_owner.move @@ -3,12 +3,7 @@ module deeptrade_core::update_loyalty_admin_cap_owner_tests; use deeptrade_core::add_loyalty_level_tests::setup_test_environment; use deeptrade_core::admin; -use deeptrade_core::loyalty::{ - Self, - LoyaltyAdminCap, - LoyaltyProgram, - ESenderIsNotLoyaltyAdmin, -}; +use deeptrade_core::loyalty::{Self, LoyaltyAdminCap, LoyaltyProgram, ESenderIsNotLoyaltyAdmin}; use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; diff --git a/packages/deeptrade-core/tests/swap/get_quantity_out_input_fee_tests.move b/packages/deeptrade-core/tests/swap/get_quantity_out_input_fee_tests.move index c159dc4..605952a 100644 --- a/packages/deeptrade-core/tests/swap/get_quantity_out_input_fee_tests.move +++ b/packages/deeptrade-core/tests/swap/get_quantity_out_input_fee_tests.move @@ -14,11 +14,9 @@ use deeptrade_core::fee_manager::{Self, FeeManager}; use deeptrade_core::loyalty::{Self, LoyaltyAdminCap, LoyaltyProgram}; use deeptrade_core::multisig_config::MultisigConfig; use deeptrade_core::swap::get_quantity_out_input_fee; -use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; use deeptrade_core::treasury; -use multisig::multisig_test_utils::{ - get_test_multisig_address, -}; +use deeptrade_core::update_multisig_config_tests::setup_with_initialized_config; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::clock::Clock; use sui::sui::SUI; diff --git a/packages/deeptrade-core/tests/ticket/create_ticket_tests.move b/packages/deeptrade-core/tests/ticket/create_ticket_tests.move index 79960db..9e8bd55 100644 --- a/packages/deeptrade-core/tests/ticket/create_ticket_tests.move +++ b/packages/deeptrade-core/tests/ticket/create_ticket_tests.move @@ -4,15 +4,8 @@ module deeptrade_core::create_ticket_tests; use deeptrade_core::admin::AdminCap; use deeptrade_core::admin_init_tests::setup_with_admin_cap; use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; -use deeptrade_core::ticket::{ - Self, - AdminTicket, - TicketCreated, - ticket_delay_duration -}; -use multisig::multisig_test_utils::{ - get_test_multisig_address, -}; +use deeptrade_core::ticket::{Self, AdminTicket, TicketCreated, ticket_delay_duration}; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::clock::{Self, Clock}; use sui::event; diff --git a/packages/deeptrade-core/tests/treasury/version_management_tests.move b/packages/deeptrade-core/tests/treasury/version_management_tests.move index 5640ceb..118c87a 100644 --- a/packages/deeptrade-core/tests/treasury/version_management_tests.move +++ b/packages/deeptrade-core/tests/treasury/version_management_tests.move @@ -16,7 +16,7 @@ use deeptrade_core::treasury::{ EVersionPermanentlyDisabled, EVersionAlreadyEnabled, EVersionNotEnabled, - ECannotDisableNewerVersion, + ECannotDisableNewerVersion }; use multisig::multisig_test_utils::get_test_multisig_address; use sui::event; diff --git a/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move b/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move index a3882ca..24be142 100644 --- a/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move +++ b/packages/deeptrade-core/tests/unsettled-fees/claim_user_unsettled_fee_storage_rebate.move @@ -13,9 +13,7 @@ use deeptrade_core::fee_manager::{ use deeptrade_core::multisig_config::{MultisigConfig, ESenderIsNotValidMultisig}; use deeptrade_core::settle_user_fees_tests::setup_test_environment; use deeptrade_core::treasury::Treasury; -use multisig::multisig_test_utils::{ - get_test_multisig_address, -}; +use multisig::multisig_test_utils::get_test_multisig_address; use std::unit_test::assert_eq; use sui::balance; use sui::sui::SUI; From 01c7742456b057d3902c97994b0e6e0d98eb2562 Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 14:51:35 +0300 Subject: [PATCH 15/23] Upd fee manager examples --- ...claim-protocol-unsettled-fee-storage-rebate.ts | 13 ++++++++----- .../claim-user-unsettled-fee-storage-rebate.ts | 15 ++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/examples/fee-manager/claim-protocol-unsettled-fee-storage-rebate.ts b/examples/fee-manager/claim-protocol-unsettled-fee-storage-rebate.ts index 5f5b7d7..cc8509d 100644 --- a/examples/fee-manager/claim-protocol-unsettled-fee-storage-rebate.ts +++ b/examples/fee-manager/claim-protocol-unsettled-fee-storage-rebate.ts @@ -1,7 +1,11 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID, DEEPTRADE_CORE_PACKAGE_ID, TREASURY_OBJECT_ID } from "../constants"; +import { + ADMIN_CAP_OBJECT_ID, + DEEPTRADE_CORE_PACKAGE_ID, + MULTISIG_CONFIG_OBJECT_ID, + TREASURY_OBJECT_ID, +} from "../constants"; import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; const FEE_MANAGER_ID = "0x4cef4ff8a75cc0e926fa57c99a3477d92b829b3a26b21cde5ea8b64041b75fba"; const FEE_COIN_TYPE = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"; @@ -13,11 +17,10 @@ const FEE_COIN_TYPE = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb84 tx.moveCall({ target: `${DEEPTRADE_CORE_PACKAGE_ID}::fee_manager::claim_protocol_unsettled_fee_storage_rebate_admin`, arguments: [ + tx.object(TREASURY_OBJECT_ID), tx.object(FEE_MANAGER_ID), + tx.object(MULTISIG_CONFIG_OBJECT_ID), tx.object(ADMIN_CAP_OBJECT_ID), - tx.pure.vector("vector", MULTISIG_CONFIG.publicKeysSuiBytes), - tx.pure.vector("u8", MULTISIG_CONFIG.weights), - tx.pure.u16(MULTISIG_CONFIG.threshold), ], typeArguments: [FEE_COIN_TYPE], }); diff --git a/examples/fee-manager/claim-user-unsettled-fee-storage-rebate.ts b/examples/fee-manager/claim-user-unsettled-fee-storage-rebate.ts index d288a78..932aef6 100644 --- a/examples/fee-manager/claim-user-unsettled-fee-storage-rebate.ts +++ b/examples/fee-manager/claim-user-unsettled-fee-storage-rebate.ts @@ -1,7 +1,11 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID, DEEPTRADE_CORE_PACKAGE_ID, TREASURY_OBJECT_ID } from "../constants"; +import { + ADMIN_CAP_OBJECT_ID, + DEEPTRADE_CORE_PACKAGE_ID, + MULTISIG_CONFIG_OBJECT_ID, + TREASURY_OBJECT_ID, +} from "../constants"; import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; const FEE_MANAGER_ID = "0x4cef4ff8a75cc0e926fa57c99a3477d92b829b3a26b21cde5ea8b64041b75fba"; const POOL_ID = "0xfacee57bc356dae0d9958c653253893dfb24e24e8871f53e69b7dccb3ffbf945"; @@ -18,16 +22,13 @@ const FEE_COIN_TYPE = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb84 tx.moveCall({ target: `${DEEPTRADE_CORE_PACKAGE_ID}::fee_manager::claim_user_unsettled_fee_storage_rebate_admin`, arguments: [ - // TODO: Uncomment, when upgraded to new version of contract - // tx.object(TREASURY_OBJECT_ID), + tx.object(TREASURY_OBJECT_ID), tx.object(FEE_MANAGER_ID), tx.object(POOL_ID), tx.object(BALANCE_MANAGER_ID), + tx.object(MULTISIG_CONFIG_OBJECT_ID), tx.object(ADMIN_CAP_OBJECT_ID), tx.pure.u128(ORDER_ID), - tx.pure.vector("vector", MULTISIG_CONFIG.publicKeysSuiBytes), - tx.pure.vector("u8", MULTISIG_CONFIG.weights), - tx.pure.u16(MULTISIG_CONFIG.threshold), ], typeArguments: [BASE_COIN_TYPE, QUOTE_COIN_TYPE, FEE_COIN_TYPE], }); From be9954f32e7ff51f3804d32687d2f1e8bbb34538 Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 14:58:29 +0300 Subject: [PATCH 16/23] Update loyalty examples --- examples/loyalty/add-loyalty-level.ts | 12 ++++---- .../loyalty/grant-multiple-users-level.ts | 9 ++++-- examples/loyalty/grant-user-level.ts | 9 ++++-- examples/loyalty/remove-loyalty-level.ts | 12 ++++---- examples/loyalty/revoke-user-level.ts | 17 +++++------ .../loyalty/update-loyalty-admin-cap-owner.ts | 29 +++++++++++++++++++ examples/loyalty/utils.ts | 8 ++--- 7 files changed, 65 insertions(+), 31 deletions(-) create mode 100644 examples/loyalty/update-loyalty-admin-cap-owner.ts diff --git a/examples/loyalty/add-loyalty-level.ts b/examples/loyalty/add-loyalty-level.ts index 1e932cf..7743df8 100644 --- a/examples/loyalty/add-loyalty-level.ts +++ b/examples/loyalty/add-loyalty-level.ts @@ -1,7 +1,11 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID, LOYALTY_PROGRAM_OBJECT_ID, DEEPTRADE_CORE_PACKAGE_ID } from "../constants"; +import { + ADMIN_CAP_OBJECT_ID, + DEEPTRADE_CORE_PACKAGE_ID, + LOYALTY_PROGRAM_OBJECT_ID, + MULTISIG_CONFIG_OBJECT_ID, +} from "../constants"; import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; import { percentageInBillionths } from "../utils"; const LEVEL = 1; // Level ID to create @@ -16,12 +20,10 @@ const FEE_DISCOUNT_RATE = percentageInBillionths(FEE_DISCOUNT_PERCENTAGE); target: `${DEEPTRADE_CORE_PACKAGE_ID}::loyalty::add_loyalty_level`, arguments: [ tx.object(LOYALTY_PROGRAM_OBJECT_ID), + tx.object(MULTISIG_CONFIG_OBJECT_ID), tx.object(ADMIN_CAP_OBJECT_ID), tx.pure.u8(LEVEL), tx.pure.u64(FEE_DISCOUNT_RATE), - tx.pure.vector("vector", MULTISIG_CONFIG.publicKeysSuiBytes), - tx.pure.vector("u8", MULTISIG_CONFIG.weights), - tx.pure.u16(MULTISIG_CONFIG.threshold), ], }); diff --git a/examples/loyalty/grant-multiple-users-level.ts b/examples/loyalty/grant-multiple-users-level.ts index fbb100f..5375d4b 100644 --- a/examples/loyalty/grant-multiple-users-level.ts +++ b/examples/loyalty/grant-multiple-users-level.ts @@ -1,5 +1,5 @@ import { Transaction } from "@mysten/sui/transactions"; -import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; +import { keypair, provider } from "../common"; import { grantUserLevelTx } from "./utils"; const USER_ADDRESSES: string[] = []; // Addresses of the users to grant the level to @@ -13,7 +13,10 @@ const LEVEL = 1; // Level to grant to the users grantUserLevelTx(userAddress, LEVEL, tx); }); - console.warn(`Building transaction to grant users ${USER_ADDRESSES} loyalty level ${LEVEL}`); + console.warn(`Executing transaction to grant users ${USER_ADDRESSES} loyalty level ${LEVEL}`); - await buildAndLogMultisigTransaction(tx); + // const res = await provider.devInspectTransactionBlock({ transactionBlock: tx, sender: user }); + const res = await provider.signAndExecuteTransaction({ transaction: tx, signer: keypair }); + + console.log("res:", JSON.stringify(res, null, 2)); })(); diff --git a/examples/loyalty/grant-user-level.ts b/examples/loyalty/grant-user-level.ts index 39ecb78..d48ece2 100644 --- a/examples/loyalty/grant-user-level.ts +++ b/examples/loyalty/grant-user-level.ts @@ -1,5 +1,5 @@ import { Transaction } from "@mysten/sui/transactions"; -import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; +import { keypair, provider } from "../common"; import { grantUserLevelTx } from "./utils"; const USER_ADDRESS = ""; // Address of the user to grant the level to @@ -11,7 +11,10 @@ const LEVEL = 1; // Level to grant to the user grantUserLevelTx(USER_ADDRESS, LEVEL, tx); - console.warn(`Building transaction to grant user ${USER_ADDRESS} loyalty level ${LEVEL}`); + console.warn(`Executing transaction to grant user ${USER_ADDRESS} loyalty level ${LEVEL}`); - await buildAndLogMultisigTransaction(tx); + // const res = await provider.devInspectTransactionBlock({ transactionBlock: tx, sender: user }); + const res = await provider.signAndExecuteTransaction({ transaction: tx, signer: keypair }); + + console.log("res:", JSON.stringify(res, null, 2)); })(); diff --git a/examples/loyalty/remove-loyalty-level.ts b/examples/loyalty/remove-loyalty-level.ts index 5751441..95a54c9 100644 --- a/examples/loyalty/remove-loyalty-level.ts +++ b/examples/loyalty/remove-loyalty-level.ts @@ -1,7 +1,11 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID, DEEPTRADE_CORE_PACKAGE_ID, LOYALTY_PROGRAM_OBJECT_ID } from "../constants"; +import { + ADMIN_CAP_OBJECT_ID, + DEEPTRADE_CORE_PACKAGE_ID, + LOYALTY_PROGRAM_OBJECT_ID, + MULTISIG_CONFIG_OBJECT_ID, +} from "../constants"; import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; const LEVEL = 2; // Level ID to remove (must have no members) @@ -13,11 +17,9 @@ const LEVEL = 2; // Level ID to remove (must have no members) target: `${DEEPTRADE_CORE_PACKAGE_ID}::loyalty::remove_loyalty_level`, arguments: [ tx.object(LOYALTY_PROGRAM_OBJECT_ID), + tx.object(MULTISIG_CONFIG_OBJECT_ID), tx.object(ADMIN_CAP_OBJECT_ID), tx.pure.u8(LEVEL), - tx.pure.vector("vector", MULTISIG_CONFIG.publicKeysSuiBytes), - tx.pure.vector("u8", MULTISIG_CONFIG.weights), - tx.pure.u16(MULTISIG_CONFIG.threshold), ], }); diff --git a/examples/loyalty/revoke-user-level.ts b/examples/loyalty/revoke-user-level.ts index 3bf8111..6a8c7ff 100644 --- a/examples/loyalty/revoke-user-level.ts +++ b/examples/loyalty/revoke-user-level.ts @@ -1,7 +1,6 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID, DEEPTRADE_CORE_PACKAGE_ID, LOYALTY_PROGRAM_OBJECT_ID } from "../constants"; -import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; +import { keypair, provider } from "../common"; +import { DEEPTRADE_CORE_PACKAGE_ID, LOYALTY_ADMIN_CAP_OBJECT_ID, LOYALTY_PROGRAM_OBJECT_ID } from "../constants"; const USER_ADDRESS = ""; // Address of the user to revoke the level from @@ -13,15 +12,15 @@ const USER_ADDRESS = ""; // Address of the user to revoke the level from target: `${DEEPTRADE_CORE_PACKAGE_ID}::loyalty::revoke_user_level`, arguments: [ tx.object(LOYALTY_PROGRAM_OBJECT_ID), - tx.object(ADMIN_CAP_OBJECT_ID), + tx.object(LOYALTY_ADMIN_CAP_OBJECT_ID), tx.pure.address(USER_ADDRESS), - tx.pure.vector("vector", MULTISIG_CONFIG.publicKeysSuiBytes), - tx.pure.vector("u8", MULTISIG_CONFIG.weights), - tx.pure.u16(MULTISIG_CONFIG.threshold), ], }); - console.warn(`Building transaction to revoke loyalty level from user ${USER_ADDRESS}`); + console.warn(`Executing transaction to revoke loyalty level from user ${USER_ADDRESS}`); - await buildAndLogMultisigTransaction(tx); + // const res = await provider.devInspectTransactionBlock({ transactionBlock: tx, sender: user }); + const res = await provider.signAndExecuteTransaction({ transaction: tx, signer: keypair }); + + console.log("res:", JSON.stringify(res, null, 2)); })(); diff --git a/examples/loyalty/update-loyalty-admin-cap-owner.ts b/examples/loyalty/update-loyalty-admin-cap-owner.ts new file mode 100644 index 0000000..999ce60 --- /dev/null +++ b/examples/loyalty/update-loyalty-admin-cap-owner.ts @@ -0,0 +1,29 @@ +import { Transaction } from "@mysten/sui/transactions"; +import { + ADMIN_CAP_OBJECT_ID, + DEEPTRADE_CORE_PACKAGE_ID, + LOYALTY_ADMIN_CAP_OBJECT_ID, + MULTISIG_CONFIG_OBJECT_ID, +} from "../constants"; +import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; + +const NEW_OWNER = ""; // New owner of the loyalty admin cap + +// Usage: yarn ts-node examples/loyalty/update-loyalty-admin-cap-owner.ts > update-loyalty-admin-cap-owner.log 2>&1 +(async () => { + const tx = new Transaction(); + + tx.moveCall({ + target: `${DEEPTRADE_CORE_PACKAGE_ID}::loyalty::update_loyalty_admin_cap_owner`, + arguments: [ + tx.object(LOYALTY_ADMIN_CAP_OBJECT_ID), + tx.object(MULTISIG_CONFIG_OBJECT_ID), + tx.object(ADMIN_CAP_OBJECT_ID), + tx.pure.address(NEW_OWNER), + ], + }); + + console.warn(`Building transaction to update loyalty admin cap owner to ${NEW_OWNER}`); + + await buildAndLogMultisigTransaction(tx); +})(); diff --git a/examples/loyalty/utils.ts b/examples/loyalty/utils.ts index 6a68e5c..43c5373 100644 --- a/examples/loyalty/utils.ts +++ b/examples/loyalty/utils.ts @@ -1,6 +1,5 @@ import { Transaction } from "@mysten/sui/transactions"; -import { DEEPTRADE_CORE_PACKAGE_ID, LOYALTY_PROGRAM_OBJECT_ID, ADMIN_CAP_OBJECT_ID } from "../constants"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; +import { DEEPTRADE_CORE_PACKAGE_ID, LOYALTY_ADMIN_CAP_OBJECT_ID, LOYALTY_PROGRAM_OBJECT_ID } from "../constants"; export function grantUserLevelTx(userAddress: string, level: number, transaction?: Transaction) { const tx = transaction ?? new Transaction(); @@ -9,12 +8,9 @@ export function grantUserLevelTx(userAddress: string, level: number, transaction target: `${DEEPTRADE_CORE_PACKAGE_ID}::loyalty::grant_user_level`, arguments: [ tx.object(LOYALTY_PROGRAM_OBJECT_ID), - tx.object(ADMIN_CAP_OBJECT_ID), + tx.object(LOYALTY_ADMIN_CAP_OBJECT_ID), tx.pure.address(userAddress), tx.pure.u8(level), - tx.pure.vector("vector", MULTISIG_CONFIG.publicKeysSuiBytes), - tx.pure.vector("u8", MULTISIG_CONFIG.weights), - tx.pure.u16(MULTISIG_CONFIG.threshold), ], }); From 8b84937d62427d3b20443a98446f1ef99127ebb3 Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 15:01:45 +0300 Subject: [PATCH 17/23] Upd create ticket example --- examples/ticket/create-ticket.ts | 7 ++----- examples/ticket/utils/createTicketTx.ts | 17 ++++------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/examples/ticket/create-ticket.ts b/examples/ticket/create-ticket.ts index 7649503..70a45df 100644 --- a/examples/ticket/create-ticket.ts +++ b/examples/ticket/create-ticket.ts @@ -1,6 +1,5 @@ -import { ADMIN_CAP_OBJECT_ID } from "../constants"; +import { ADMIN_CAP_OBJECT_ID, MULTISIG_CONFIG_OBJECT_ID } from "../constants"; import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; import { createTicketTx, TicketType } from "./utils/createTicketTx"; // yarn ts-node examples/ticket/create-ticket.ts > create-ticket.log 2>&1 @@ -12,9 +11,7 @@ import { createTicketTx, TicketType } from "./utils/createTicketTx"; const { tx, ticket } = createTicketTx({ ticketType, adminCapId: ADMIN_CAP_OBJECT_ID, - pks: MULTISIG_CONFIG.publicKeysSuiBytes, - weights: MULTISIG_CONFIG.weights, - threshold: MULTISIG_CONFIG.threshold, + multisigConfigId: MULTISIG_CONFIG_OBJECT_ID, }); await buildAndLogMultisigTransaction(tx); diff --git a/examples/ticket/utils/createTicketTx.ts b/examples/ticket/utils/createTicketTx.ts index 2ca5577..e33082b 100644 --- a/examples/ticket/utils/createTicketTx.ts +++ b/examples/ticket/utils/createTicketTx.ts @@ -1,6 +1,6 @@ import { Transaction } from "@mysten/sui/transactions"; -import { DEEPTRADE_CORE_PACKAGE_ID } from "../../constants"; import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils"; +import { DEEPTRADE_CORE_PACKAGE_ID, MULTISIG_CONFIG_OBJECT_ID } from "../../constants"; export enum TicketType { WithdrawDeepReserves = "WithdrawDeepReserves", @@ -14,13 +14,11 @@ export enum TicketType { export interface CreateTicketParams { ticketType: TicketType; adminCapId: string; - pks: number[][]; - weights: number[]; - threshold: number; + multisigConfigId: string; transaction?: Transaction; } -export function createTicketTx({ ticketType, adminCapId, pks, weights, threshold, transaction }: CreateTicketParams): { +export function createTicketTx({ ticketType, adminCapId, multisigConfigId, transaction }: CreateTicketParams): { tx: Transaction; ticket: any; } { @@ -34,14 +32,7 @@ export function createTicketTx({ ticketType, adminCapId, pks, weights, threshold const ticket = tx.moveCall({ target: `${DEEPTRADE_CORE_PACKAGE_ID}::ticket::create_ticket`, - arguments: [ - tx.object(adminCapId), - ticketTypeArg, - tx.pure.vector("vector", pks), - tx.pure.vector("u8", weights), - tx.pure.u16(threshold), - tx.object(SUI_CLOCK_OBJECT_ID), - ], + arguments: [tx.object(multisigConfigId), tx.object(adminCapId), ticketTypeArg, tx.object(SUI_CLOCK_OBJECT_ID)], }); return { tx, ticket }; From d5c5ed8167298f8e34e316f2641abaf7d5d71c5f Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 15:03:17 +0300 Subject: [PATCH 18/23] Upd enable disable version examples --- examples/treasury/versions/disable-version.ts | 12 +++++++----- examples/treasury/versions/enable-version.ts | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/treasury/versions/disable-version.ts b/examples/treasury/versions/disable-version.ts index cd0b16e..cbfa37e 100644 --- a/examples/treasury/versions/disable-version.ts +++ b/examples/treasury/versions/disable-version.ts @@ -1,6 +1,10 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID, TREASURY_OBJECT_ID, DEEPTRADE_CORE_PACKAGE_ID } from "../../constants"; -import { MULTISIG_CONFIG } from "../../multisig/multisig"; +import { + ADMIN_CAP_OBJECT_ID, + DEEPTRADE_CORE_PACKAGE_ID, + MULTISIG_CONFIG_OBJECT_ID, + TREASURY_OBJECT_ID, +} from "../../constants"; import { buildAndLogMultisigTransaction } from "../../multisig/buildAndLogMultisigTransaction"; // Set the version to disable here @@ -14,11 +18,9 @@ const VERSION = 1; target: `${DEEPTRADE_CORE_PACKAGE_ID}::treasury::disable_version`, arguments: [ tx.object(TREASURY_OBJECT_ID), + tx.object(MULTISIG_CONFIG_OBJECT_ID), tx.object(ADMIN_CAP_OBJECT_ID), tx.pure.u16(VERSION), - tx.pure.vector("vector", MULTISIG_CONFIG.publicKeysSuiBytes), - tx.pure.vector("u8", MULTISIG_CONFIG.weights), - tx.pure.u16(MULTISIG_CONFIG.threshold), ], }); diff --git a/examples/treasury/versions/enable-version.ts b/examples/treasury/versions/enable-version.ts index 5eb61b8..ffbc45f 100644 --- a/examples/treasury/versions/enable-version.ts +++ b/examples/treasury/versions/enable-version.ts @@ -1,6 +1,10 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID, TREASURY_OBJECT_ID, DEEPTRADE_CORE_PACKAGE_ID } from "../../constants"; -import { MULTISIG_CONFIG } from "../../multisig/multisig"; +import { + ADMIN_CAP_OBJECT_ID, + DEEPTRADE_CORE_PACKAGE_ID, + MULTISIG_CONFIG_OBJECT_ID, + TREASURY_OBJECT_ID, +} from "../../constants"; import { buildAndLogMultisigTransaction } from "../../multisig/buildAndLogMultisigTransaction"; // Set the version to enable here @@ -14,11 +18,9 @@ const VERSION = 2; target: `${DEEPTRADE_CORE_PACKAGE_ID}::treasury::enable_version`, arguments: [ tx.object(TREASURY_OBJECT_ID), + tx.object(MULTISIG_CONFIG_OBJECT_ID), tx.object(ADMIN_CAP_OBJECT_ID), tx.pure.u16(VERSION), - tx.pure.vector("vector", MULTISIG_CONFIG.publicKeysSuiBytes), - tx.pure.vector("u8", MULTISIG_CONFIG.weights), - tx.pure.u16(MULTISIG_CONFIG.threshold), ], }); From 164d3e0bbe845b54ef03375bda0b21d1fb061e47 Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 15:03:35 +0300 Subject: [PATCH 19/23] Add empty LOYALTY_ADMIN_CAP_OBJECT_ID & MULTISIG_CONFIG_OBJECT_ID constants --- examples/constants.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/constants.ts b/examples/constants.ts index 2cd3bd4..6f006db 100644 --- a/examples/constants.ts +++ b/examples/constants.ts @@ -6,6 +6,8 @@ export const UPGRADE_CAP_OBJECT_ID = "0xae8c80532528977c531c7ee477d55d9e8618320e export const POOL_CREATION_CONFIG_OBJECT_ID = "0xa5e401f7311aa3a58bf0de4a893ef5684e9f994d2400d02eb0ea74c8cd88504c"; export const TRADING_FEE_CONFIG_OBJECT_ID = ""; export const LOYALTY_PROGRAM_OBJECT_ID = ""; +export const LOYALTY_ADMIN_CAP_OBJECT_ID = ""; +export const MULTISIG_CONFIG_OBJECT_ID = ""; // Coin types export const DEEP_COIN_TYPE = "0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP"; From fb731bdb2c94f54253107702b60a27ba1462560f Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 15:10:00 +0300 Subject: [PATCH 20/23] Add multisig config examples --- .../initialize-multisig-config.ts | 31 +++++++++++++++++++ .../multisig-config/update-multisig-config.ts | 31 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 examples/multisig-config/initialize-multisig-config.ts create mode 100644 examples/multisig-config/update-multisig-config.ts diff --git a/examples/multisig-config/initialize-multisig-config.ts b/examples/multisig-config/initialize-multisig-config.ts new file mode 100644 index 0000000..f033661 --- /dev/null +++ b/examples/multisig-config/initialize-multisig-config.ts @@ -0,0 +1,31 @@ +import { Transaction } from "@mysten/sui/transactions"; +import { keypair, provider } from "../common"; +import { DEEPTRADE_CORE_PACKAGE_ID, MULTISIG_ADMIN_CAP_OBJECT_ID, MULTISIG_CONFIG_OBJECT_ID } from "../constants"; +import { MultisigConfig } from "../multisig/types"; + +const NEW_PUBLIC_KEYS: MultisigConfig["publicKeysSuiBytes"] = []; +const NEW_WEIGHTS: MultisigConfig["weights"] = []; +const NEW_THRESHOLD: MultisigConfig["threshold"] = 0; + +// yarn ts-node examples/multisig-config/initialize-multisig-config.ts > initialize-multisig-config.log 2>&1 +(async () => { + const tx = new Transaction(); + + tx.moveCall({ + target: `${DEEPTRADE_CORE_PACKAGE_ID}::multisig_config::initialize_multisig_config`, + arguments: [ + tx.object(MULTISIG_CONFIG_OBJECT_ID), + tx.object(MULTISIG_ADMIN_CAP_OBJECT_ID), + tx.pure.vector("vector", NEW_PUBLIC_KEYS), + tx.pure.vector("u8", NEW_WEIGHTS), + tx.pure.u16(NEW_THRESHOLD), + ], + }); + + console.warn(`Executing transaction to initialize multisig config`); + + // const res = await provider.devInspectTransactionBlock({ transactionBlock: tx, sender: user }); + const res = await provider.signAndExecuteTransaction({ transaction: tx, signer: keypair }); + + console.log("res:", JSON.stringify(res, null, 2)); +})(); diff --git a/examples/multisig-config/update-multisig-config.ts b/examples/multisig-config/update-multisig-config.ts new file mode 100644 index 0000000..28f87a5 --- /dev/null +++ b/examples/multisig-config/update-multisig-config.ts @@ -0,0 +1,31 @@ +import { Transaction } from "@mysten/sui/transactions"; +import { keypair, provider } from "../common"; +import { DEEPTRADE_CORE_PACKAGE_ID, MULTISIG_ADMIN_CAP_OBJECT_ID, MULTISIG_CONFIG_OBJECT_ID } from "../constants"; +import { MultisigConfig } from "../multisig/types"; + +const NEW_PUBLIC_KEYS: MultisigConfig["publicKeysSuiBytes"] = []; +const NEW_WEIGHTS: MultisigConfig["weights"] = []; +const NEW_THRESHOLD: MultisigConfig["threshold"] = 0; + +// yarn ts-node examples/multisig-config/update-multisig-config.ts > update-multisig-config.log 2>&1 +(async () => { + const tx = new Transaction(); + + tx.moveCall({ + target: `${DEEPTRADE_CORE_PACKAGE_ID}::multisig_config::update_multisig_config`, + arguments: [ + tx.object(MULTISIG_CONFIG_OBJECT_ID), + tx.object(MULTISIG_ADMIN_CAP_OBJECT_ID), + tx.pure.vector("vector", NEW_PUBLIC_KEYS), + tx.pure.vector("u8", NEW_WEIGHTS), + tx.pure.u16(NEW_THRESHOLD), + ], + }); + + console.warn(`Executing transaction to update multisig config`); + + // const res = await provider.devInspectTransactionBlock({ transactionBlock: tx, sender: user }); + const res = await provider.signAndExecuteTransaction({ transaction: tx, signer: keypair }); + + console.log("res:", JSON.stringify(res, null, 2)); +})(); From 8f7eaf247aa6fbd4312043c4085e7c6679604228 Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 15:10:12 +0300 Subject: [PATCH 21/23] Add empty MULTISIG_ADMIN_CAP_OBJECT_ID constant --- examples/constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/constants.ts b/examples/constants.ts index 6f006db..f8c5600 100644 --- a/examples/constants.ts +++ b/examples/constants.ts @@ -8,6 +8,7 @@ export const TRADING_FEE_CONFIG_OBJECT_ID = ""; export const LOYALTY_PROGRAM_OBJECT_ID = ""; export const LOYALTY_ADMIN_CAP_OBJECT_ID = ""; export const MULTISIG_CONFIG_OBJECT_ID = ""; +export const MULTISIG_ADMIN_CAP_OBJECT_ID = ""; // Coin types export const DEEP_COIN_TYPE = "0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP"; From f7d3f9225fc3d1deb8d3ce9a07a964297a80ca85 Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 30 Sep 2025 15:27:38 +0300 Subject: [PATCH 22/23] Upd create multiple tickets examples --- .../create-multiple-tickets-withdraw-protocol-fees.ts | 9 +++------ examples/ticket/create-multiple-tickets.ts | 7 ++----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/examples/ticket/create-multiple-tickets-withdraw-protocol-fees.ts b/examples/ticket/create-multiple-tickets-withdraw-protocol-fees.ts index 79a7c79..4c99f35 100644 --- a/examples/ticket/create-multiple-tickets-withdraw-protocol-fees.ts +++ b/examples/ticket/create-multiple-tickets-withdraw-protocol-fees.ts @@ -1,10 +1,9 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID } from "../constants"; +import { ADMIN_CAP_OBJECT_ID, MULTISIG_CONFIG_OBJECT_ID } from "../constants"; import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; -import { createTicketTx, TicketType } from "./utils/createTicketTx"; import { getTreasuryBags } from "../treasury/utils/getTreasuryBags"; import { processFeesBag } from "../treasury/utils/processFeeBag"; +import { createTicketTx, TicketType } from "./utils/createTicketTx"; // yarn ts-node examples/ticket/create-multiple-tickets-withdraw-protocol-fees.ts > create-multiple-tickets-withdraw-protocol-fees.log 2>&1 (async () => { @@ -25,9 +24,7 @@ import { processFeesBag } from "../treasury/utils/processFeeBag"; transaction: tx, ticketType, adminCapId: ADMIN_CAP_OBJECT_ID, - pks: MULTISIG_CONFIG.publicKeysSuiBytes, - weights: MULTISIG_CONFIG.weights, - threshold: MULTISIG_CONFIG.threshold, + multisigConfigId: MULTISIG_CONFIG_OBJECT_ID, }); } diff --git a/examples/ticket/create-multiple-tickets.ts b/examples/ticket/create-multiple-tickets.ts index 29c9b49..944ce78 100644 --- a/examples/ticket/create-multiple-tickets.ts +++ b/examples/ticket/create-multiple-tickets.ts @@ -1,7 +1,6 @@ import { Transaction } from "@mysten/sui/transactions"; -import { ADMIN_CAP_OBJECT_ID } from "../constants"; +import { ADMIN_CAP_OBJECT_ID, MULTISIG_CONFIG_OBJECT_ID } from "../constants"; import { buildAndLogMultisigTransaction } from "../multisig/buildAndLogMultisigTransaction"; -import { MULTISIG_CONFIG } from "../multisig/multisig"; import { createTicketTx, TicketType } from "./utils/createTicketTx"; // yarn ts-node examples/ticket/create-multiple-tickets.ts > create-multiple-tickets.log 2>&1 @@ -24,9 +23,7 @@ import { createTicketTx, TicketType } from "./utils/createTicketTx"; transaction: tx, ticketType, adminCapId: ADMIN_CAP_OBJECT_ID, - pks: MULTISIG_CONFIG.publicKeysSuiBytes, - weights: MULTISIG_CONFIG.weights, - threshold: MULTISIG_CONFIG.threshold, + multisigConfigId: MULTISIG_CONFIG_OBJECT_ID, }); } From 2b735e4d33a0b9758bb8a3074d3fcf714b3c5d53 Mon Sep 17 00:00:00 2001 From: bathord Date: Wed, 1 Oct 2025 17:55:20 +0300 Subject: [PATCH 23/23] Enforce signers count to be >= 2 --- .../sources/multisig_config.move | 6 +++ .../initialize_multisig_config.move | 38 +++++++++++++++- .../update_multisig_config.move | 44 +++++++++++++++++-- 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/packages/deeptrade-core/sources/multisig_config.move b/packages/deeptrade-core/sources/multisig_config.move index f8db5b6..9c87642 100644 --- a/packages/deeptrade-core/sources/multisig_config.move +++ b/packages/deeptrade-core/sources/multisig_config.move @@ -8,6 +8,10 @@ const ENewAddressIsOldAddress: u64 = 1; const ESenderIsNotValidMultisig: u64 = 2; const EMultisigConfigAlreadyInitialized: u64 = 3; const EMultisigConfigNotInitialized: u64 = 4; +const ETooFewSigners: u64 = 5; + +// === Constants === +const MIN_SIGNERS: u64 = 2; // === Structs === /// Configuration of the protocol's administrator multisig. Only a multisig account matching these @@ -73,6 +77,7 @@ public fun initialize_multisig_config( threshold: u16, ) { assert!(!config.initialized, EMultisigConfigAlreadyInitialized); + assert!(public_keys.length() >= MIN_SIGNERS, ETooFewSigners); // Validates passed multisig parameters, aborting if they are invalid let multisig_address = multisig::derive_multisig_address_quiet( @@ -104,6 +109,7 @@ public fun update_multisig_config( new_threshold: u16, ) { assert!(config.initialized, EMultisigConfigNotInitialized); + assert!(new_public_keys.length() >= MIN_SIGNERS, ETooFewSigners); let old_public_keys = config.public_keys; let old_weights = config.weights; diff --git a/packages/deeptrade-core/tests/multisig_config/initialize_multisig_config.move b/packages/deeptrade-core/tests/multisig_config/initialize_multisig_config.move index 8a0bde0..5b0dcda 100644 --- a/packages/deeptrade-core/tests/multisig_config/initialize_multisig_config.move +++ b/packages/deeptrade-core/tests/multisig_config/initialize_multisig_config.move @@ -5,7 +5,8 @@ use deeptrade_core::multisig_config::{ Self, MultisigConfig, MultisigConfigInitialized, - EMultisigConfigAlreadyInitialized + EMultisigConfigAlreadyInitialized, + ETooFewSigners }; use multisig::multisig::{ Self, @@ -229,6 +230,41 @@ fun unachievable_threshold_fails() { end(scenario); } +#[test, expected_failure(abort_code = ETooFewSigners)] +fun too_few_signers_fails() { + let mut scenario = setup(); + + let mut pks = get_test_multisig_pks(); + let mut weights = get_test_multisig_weights(); + // With one signer, threshold must be 1 + let threshold = 1; + + // Remove two signers to have only one + pks.pop_back(); + pks.pop_back(); + weights.pop_back(); + weights.pop_back(); + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::initialize_multisig_config( + &mut config, + &admin_cap, + pks, + weights, + threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + // === Helpers === #[test_only] diff --git a/packages/deeptrade-core/tests/multisig_config/update_multisig_config.move b/packages/deeptrade-core/tests/multisig_config/update_multisig_config.move index a406c30..bab775b 100644 --- a/packages/deeptrade-core/tests/multisig_config/update_multisig_config.move +++ b/packages/deeptrade-core/tests/multisig_config/update_multisig_config.move @@ -7,7 +7,8 @@ use deeptrade_core::multisig_config::{ MultisigConfig, MultisigConfigUpdated, ENewAddressIsOldAddress, - EMultisigConfigNotInitialized + EMultisigConfigNotInitialized, + ETooFewSigners }; use multisig::multisig::{ Self, @@ -174,12 +175,12 @@ fun new_address_is_old_address_fails() { fun mismatched_pks_and_weights_fails() { let mut scenario = setup_with_initialized_config(); - let mut new_pks = get_test_multisig_pks(); - let new_weights = get_test_multisig_weights(); + let new_pks = get_test_multisig_pks(); + let mut new_weights = get_test_multisig_weights(); let new_threshold = get_test_multisig_threshold(); // Create a mismatch - new_pks.pop_back(); + new_weights.pop_back(); scenario.next_tx(OWNER); { @@ -258,6 +259,41 @@ fun unachievable_threshold_fails() { end(scenario); } +#[test, expected_failure(abort_code = ETooFewSigners)] +fun too_few_signers_fails() { + let mut scenario = setup_with_initialized_config(); + + let mut new_pks = get_test_multisig_pks(); + let mut new_weights = get_test_multisig_weights(); + // With one signer, threshold must be 1 + let new_threshold = 1; + + // Remove two signers to have only one + new_pks.pop_back(); + new_pks.pop_back(); + new_weights.pop_back(); + new_weights.pop_back(); + + scenario.next_tx(OWNER); + { + let mut config = scenario.take_shared(); + let admin_cap = multisig_config::get_multisig_admin_cap_for_testing(scenario.ctx()); + + multisig_config::update_multisig_config( + &mut config, + &admin_cap, + new_pks, + new_weights, + new_threshold, + ); + + test_utils::destroy(admin_cap); + return_shared(config); + }; + + end(scenario); +} + // === Helpers === #[test_only] public(package) fun setup_with_initialized_config(): Scenario {