diff --git a/aptos-move/framework/move-stdlib/sources/fixed_point32.move b/aptos-move/framework/move-stdlib/sources/fixed_point32.move index 96409a9ac4dfd..9eb68e92c57e6 100644 --- a/aptos-move/framework/move-stdlib/sources/fixed_point32.move +++ b/aptos-move/framework/move-stdlib/sources/fixed_point32.move @@ -56,6 +56,16 @@ module std::fixed_point32 { (val * multiplier.value) >> 32 } + public fun multiply_u64_return_fixpoint32(val: u64, multiplier: FixedPoint32): FixedPoint32 { + // The product of two 64 bit values has 128 bits, so perform the + // multiplication with u128 types and keep the full 128 bit product + // to avoid losing accuracy. + let unscaled_product = (val as u128) * (multiplier.value as u128); + // Check whether the value is too large. + assert!(unscaled_product <= MAX_U64, EMULTIPLICATION); + create_from_raw_value((product as u64)); + } + /// Divide a u64 integer by a fixed-point number, truncating any /// fractional part of the quotient. This will abort if the divisor /// is zero or if the quotient overflows. diff --git a/aptos-move/framework/supra-framework/sources/pbo_delegation_pool.move b/aptos-move/framework/supra-framework/sources/pbo_delegation_pool.move index 68cab4bc556fe..af5009f4c445c 100644 --- a/aptos-move/framework/supra-framework/sources/pbo_delegation_pool.move +++ b/aptos-move/framework/supra-framework/sources/pbo_delegation_pool.move @@ -1772,34 +1772,22 @@ module supra_framework::pbo_delegation_pool { let last_unlocked_period = unlock_schedule.last_unlock_period; let schedule_length = vector::length(&unlock_schedule.schedule); let cfraction = unlock_schedule.cumulative_unlocked_fraction; - while (last_unlocked_period < unlock_periods_passed - && fixed_point64::less(cfraction, one)) { - let next_fraction = - if (schedule_length <= last_unlocked_period) { - *vector::borrow(&unlock_schedule.schedule, schedule_length - 1) - } else { - *vector::borrow(&unlock_schedule.schedule, last_unlocked_period) - }; - let next_fraction_unchanged = next_fraction == *vector::borrow(&unlock_schedule.schedule, schedule_length - 1); - // If next fraction is same as last unlocked period, then no need to fetch from schedule - if (next_fraction_unchanged) { - while (last_unlocked_period < unlock_periods_passed - && fixed_point64::less(cfraction, one)) { - cfraction = fixed_point64::add(cfraction, next_fraction); - last_unlocked_period = last_unlocked_period + 1; - }; - } else { - cfraction = fixed_point64::add(cfraction, next_fraction); - - last_unlocked_period = last_unlocked_period + 1; - }; + while (last_unlocked_period < unlock_periods_passed && fixed_point64::less(cfraction, one) + && last_unlocked_period < schedule_length) { + let next_fraction = *vector::borrow(&unlock_schedule.schedule, last_unlocked_period); + cfraction = fixed_point64::add(cfraction, next_fraction); + last_unlocked_period = last_unlocked_period + 1; }; + let final_fraction= *vector::borrow(&unlock_schedule.schedule, schedule_length - 1); + // Fast foward calculation to current period and don't update last_unlocked_period since it is not used anymore + cfraction = fixed_point64::add(cfraction, fixed_point64::multiply_u128_return_fixpoint64((unlock_periods_passed - last_unlocked_period as u128), final_fraction)); + cfraction = fixed_point64::min(cfraction, one); + unlock_schedule.cumulative_unlocked_fraction = cfraction; unlock_schedule.last_unlock_period = unlock_periods_passed; let unlockable_amount = cached_unlockable_balance(delegator_addr, pool_address); amount <= unlockable_amount - } /// Unlock `amount` from the active + pending_active stake of `delegator` or diff --git a/aptos-move/framework/supra-framework/sources/vesting_without_staking.move b/aptos-move/framework/supra-framework/sources/vesting_without_staking.move index 20aa10d312454..2be38d29d95e6 100644 --- a/aptos-move/framework/supra-framework/sources/vesting_without_staking.move +++ b/aptos-move/framework/supra-framework/sources/vesting_without_staking.move @@ -495,42 +495,35 @@ module supra_framework::vesting_without_staking { / vesting_schedule.period_duration; // Index is 0-based while period is 1-based so we need to subtract 1. - while (last_completed_period >= next_period_to_vest && vesting_record.left_amount > 0) { - let schedule_index = next_period_to_vest - 1; - let vesting_fraction = if (schedule_index < vector::length(schedule)) { - *vector::borrow(schedule, schedule_index) - } else { - // Last vesting schedule fraction will repeat until the grant runs out. - *vector::borrow(schedule, vector::length(schedule) - 1) - }; - let vesting_fraction_unchange = vesting_fraction == *vector::borrow(schedule, vector::length(schedule) - 1); - if (vesting_fraction_unchange) { - while (last_completed_period >= next_period_to_vest && vesting_record.left_amount > 0) { - vest_transfer(vesting_record, signer_cap, beneficiary, vesting_fraction); - emit_event(&mut vesting_contract.vest_events, - VestEvent { - admin: vesting_contract.admin, - shareholder_address: shareholder_address, - vesting_contract_address: contract_address, - period_vested: next_period_to_vest, - }, - ); - next_period_to_vest = next_period_to_vest + 1; - } - } else { - vest_transfer(vesting_record, signer_cap, beneficiary, vesting_fraction); - emit_event(&mut vesting_contract.vest_events, - VestEvent { - admin: vesting_contract.admin, - shareholder_address: shareholder_address, - vesting_contract_address: contract_address, - period_vested: next_period_to_vest, - }, - ); - next_period_to_vest = next_period_to_vest + 1; - }; + while (last_completed_period >= next_period_to_vest && vesting_record.left_amount > 0 && next_period_to_vest <= vector::length(schedule)) { + // let schedule_index = next_period_to_vest - 1; + let vesting_fraction = *vector::borrow(schedule, vector::length(schedule) - 1); + vest_transfer(vesting_record, signer_cap, beneficiary, vesting_fraction); + emit_event(&mut vesting_contract.vest_events, + VestEvent { + admin: vesting_contract.admin, + shareholder_address: shareholder_address, + vesting_contract_address: contract_address, + period_vested: next_period_to_vest, + }, + ); + next_period_to_vest = next_period_to_vest + 1; }; + let final_fraction = *vector::borrow(schedule, vector::length(schedule) - 1); + let total_fraction = fixed_point32::multiply_u64_return_fixpoint32((last_completed_period - next_period_to_vest ), final_fraction); + // We don't need to check vesting_record.left_amount > 0 because vest_transfer will handle that. + vest_transfer(vesting_record, signer_cap, beneficiary, total_fraction); + next_period_to_vest = last_completed_period + 1; + emit_event(&mut vesting_contract.vest_events, + VestEvent { + admin: vesting_contract.admin, + shareholder_address: shareholder_address, + vesting_contract_address: contract_address, + period_vested: next_period_to_vest, + }, + ); + //update last_vested_period for the shareholder vesting_record.last_vested_period = next_period_to_vest - 1; }