Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions aptos-move/framework/supra-framework/doc/automation_registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -2668,6 +2668,16 @@ Invalid number of auxiliary data.



<a id="0x1_automation_registry_EINVALID_CYCLE_REFUND_FEE"></a>

The refund fee for remaining cycle time is greater than total cycle fee for the task.


<pre><code><b>const</b> <a href="automation_registry.md#0x1_automation_registry_EINVALID_CYCLE_REFUND_FEE">EINVALID_CYCLE_REFUND_FEE</a>: u64 = 48;
</code></pre>



<a id="0x1_automation_registry_EINVALID_EXPIRY_TIME"></a>

Invalid expiry time: it cannot be earlier than the current time
Expand Down Expand Up @@ -4663,7 +4673,7 @@ by the max gas amount of the stopped task. Half of the remaining task fee is ref

<b>let</b> stopped_task_details = <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>[];
<b>let</b> total_refund_fee = 0;
<b>let</b> epoch_locked_fees = <a href="automation_registry.md#0x1_automation_registry">automation_registry</a>.epoch_locked_fees;
<b>let</b> total_cycle_locked_fees = <a href="automation_registry.md#0x1_automation_registry">automation_registry</a>.epoch_locked_fees;

// Calculate refundable fee for this remaining time task in current epoch
<b>let</b> current_time = <a href="timestamp.md#0x1_timestamp_now_seconds">timestamp::now_seconds</a>();
Expand Down Expand Up @@ -4700,36 +4710,43 @@ by the max gas amount of the stopped task. Half of the remaining task fee is ref
<a href="automation_registry.md#0x1_automation_registry">automation_registry</a>.gas_committed_for_next_epoch = <a href="automation_registry.md#0x1_automation_registry">automation_registry</a>.gas_committed_for_next_epoch - task.max_gas_amount;
};

<b>let</b> (epoch_fee_refund, deposit_refund) = <b>if</b> (task.state != <a href="automation_registry.md#0x1_automation_registry_PENDING">PENDING</a>) {
<b>let</b> task_fee = <a href="automation_registry.md#0x1_automation_registry_calculate_task_fee">calculate_task_fee</a>(
<b>let</b> (cycle_locked_fee_for_task, cycle_fee_refund, deposit_refund) = <b>if</b> (task.state != <a href="automation_registry.md#0x1_automation_registry_PENDING">PENDING</a>) {
<b>let</b> task_fee_for_full_cycle =
<a href="automation_registry.md#0x1_automation_registry_calculate_automation_fee_for_interval">calculate_automation_fee_for_interval</a>(
cycle_info.duration_secs,
task.max_gas_amount,
automation_fee_per_sec,
arc.registry_max_gas_cap);
<b>let</b> task_fee_for_residual_time = <a href="automation_registry.md#0x1_automation_registry_calculate_task_fee">calculate_task_fee</a>(
&arc,
&task,
residual_interval,
current_time,
automation_fee_per_sec
);
// Refund full deposit and the half of the remaining run-time fee when task is active or cancelled stage
(task_fee / <a href="automation_registry.md#0x1_automation_registry_REFUND_FRACTION">REFUND_FRACTION</a>, task.locked_fee_for_next_epoch)
(task_fee_for_full_cycle, task_fee_for_residual_time / <a href="automation_registry.md#0x1_automation_registry_REFUND_FRACTION">REFUND_FRACTION</a>, task.locked_fee_for_next_epoch)
} <b>else</b> {
(0, (task.locked_fee_for_next_epoch / <a href="automation_registry.md#0x1_automation_registry_REFUND_FRACTION">REFUND_FRACTION</a>))
(0, 0, (task.locked_fee_for_next_epoch / <a href="automation_registry.md#0x1_automation_registry_REFUND_FRACTION">REFUND_FRACTION</a>))
};
<b>let</b> result = <a href="automation_registry.md#0x1_automation_registry_safe_unlock_locked_deposit">safe_unlock_locked_deposit</a>(
refund_bookkeeping,
task.locked_fee_for_next_epoch,
task.task_index);
<b>assert</b>!(result, <a href="automation_registry.md#0x1_automation_registry_EDEPOSIT_REFUND">EDEPOSIT_REFUND</a>);
<b>let</b> (result, remaining_epoch_locked_fees) = <a href="automation_registry.md#0x1_automation_registry_safe_unlock_locked_epoch_fee">safe_unlock_locked_epoch_fee</a>(
epoch_locked_fees,
epoch_fee_refund,
<b>assert</b>!(cycle_locked_fee_for_task &gt;= cycle_fee_refund, <a href="automation_registry.md#0x1_automation_registry_EINVALID_CYCLE_REFUND_FEE">EINVALID_CYCLE_REFUND_FEE</a>);
<b>let</b> (result, remaining_cycle_locked_fees) = <a href="automation_registry.md#0x1_automation_registry_safe_unlock_locked_epoch_fee">safe_unlock_locked_epoch_fee</a>(
total_cycle_locked_fees,
cycle_locked_fee_for_task,
task.task_index);
<b>assert</b>!(result, <a href="automation_registry.md#0x1_automation_registry_EEPOCH_FEE_REFUND">EEPOCH_FEE_REFUND</a>);
epoch_locked_fees = remaining_epoch_locked_fees;
total_cycle_locked_fees = remaining_cycle_locked_fees;

total_refund_fee = total_refund_fee + (epoch_fee_refund + deposit_refund);
total_refund_fee = total_refund_fee + (cycle_fee_refund + deposit_refund);

<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector_push_back">vector::push_back</a>(
&<b>mut</b> stopped_task_details,
<a href="automation_registry.md#0x1_automation_registry_TaskStoppedV2">TaskStoppedV2</a> { task_index, deposit_refund, epoch_fee_refund, registration_hash: task.tx_hash }
<a href="automation_registry.md#0x1_automation_registry_TaskStoppedV2">TaskStoppedV2</a> { task_index, deposit_refund, epoch_fee_refund: cycle_fee_refund, registration_hash: task.tx_hash }
);
}
});
Expand All @@ -4743,6 +4760,7 @@ by the max gas amount of the stopped task. Half of the remaining task fee is ref
<b>let</b> resource_account_balance = <a href="coin.md#0x1_coin_balance">coin::balance</a>&lt;SupraCoin&gt;(<a href="automation_registry.md#0x1_automation_registry">automation_registry</a>.registry_fee_address);
<b>assert</b>!(resource_account_balance &gt;= total_refund_fee, <a href="automation_registry.md#0x1_automation_registry_EINSUFFICIENT_BALANCE_FOR_REFUND">EINSUFFICIENT_BALANCE_FOR_REFUND</a>);
<a href="coin.md#0x1_coin_transfer">coin::transfer</a>&lt;SupraCoin&gt;(&resource_signer, owner, total_refund_fee);
<a href="automation_registry.md#0x1_automation_registry">automation_registry</a>.epoch_locked_fees = total_cycle_locked_fees;

// Emit task stopped <a href="event.md#0x1_event">event</a>
<a href="event.md#0x1_event_emit">event::emit</a>(<a href="automation_registry.md#0x1_automation_registry_TasksStoppedV2">TasksStoppedV2</a> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ module supra_framework::automation_registry {
const EREGISTRY_SYSTEM_MAX_GAS_CAP_NON_ZERO: u64 = 46;
/// The input address is not identified as multisig account.
const EUNKNOWN_MULTISIG_ADDRESS: u64 = 47;
/// The refund fee for remaining cycle time is greater than total cycle fee for the task.
const EINVALID_CYCLE_REFUND_FEE: u64 = 48;

/// The length of the transaction hash.
const TXN_HASH_LENGTH: u64 = 32;
Expand Down Expand Up @@ -1286,7 +1288,7 @@ module supra_framework::automation_registry {

let stopped_task_details = vector[];
let total_refund_fee = 0;
let epoch_locked_fees = automation_registry.epoch_locked_fees;
let total_cycle_locked_fees = automation_registry.epoch_locked_fees;

// Calculate refundable fee for this remaining time task in current epoch
let current_time = timestamp::now_seconds();
Expand Down Expand Up @@ -1323,36 +1325,43 @@ module supra_framework::automation_registry {
automation_registry.gas_committed_for_next_epoch = automation_registry.gas_committed_for_next_epoch - task.max_gas_amount;
};

let (epoch_fee_refund, deposit_refund) = if (task.state != PENDING) {
let task_fee = calculate_task_fee(
let (cycle_locked_fee_for_task, cycle_fee_refund, deposit_refund) = if (task.state != PENDING) {
let task_fee_for_full_cycle =
calculate_automation_fee_for_interval(
cycle_info.duration_secs,
task.max_gas_amount,
automation_fee_per_sec,
arc.registry_max_gas_cap);
let task_fee_for_residual_time = calculate_task_fee(
&arc,
&task,
residual_interval,
current_time,
automation_fee_per_sec
);
// Refund full deposit and the half of the remaining run-time fee when task is active or cancelled stage
(task_fee / REFUND_FRACTION, task.locked_fee_for_next_epoch)
(task_fee_for_full_cycle, task_fee_for_residual_time / REFUND_FRACTION, task.locked_fee_for_next_epoch)
} else {
(0, (task.locked_fee_for_next_epoch / REFUND_FRACTION))
(0, 0, (task.locked_fee_for_next_epoch / REFUND_FRACTION))
};
let result = safe_unlock_locked_deposit(
refund_bookkeeping,
task.locked_fee_for_next_epoch,
task.task_index);
assert!(result, EDEPOSIT_REFUND);
let (result, remaining_epoch_locked_fees) = safe_unlock_locked_epoch_fee(
epoch_locked_fees,
epoch_fee_refund,
assert!(cycle_locked_fee_for_task >= cycle_fee_refund, EINVALID_CYCLE_REFUND_FEE);
let (result, remaining_cycle_locked_fees) = safe_unlock_locked_epoch_fee(
total_cycle_locked_fees,
cycle_locked_fee_for_task,
task.task_index);
assert!(result, EEPOCH_FEE_REFUND);
epoch_locked_fees = remaining_epoch_locked_fees;
total_cycle_locked_fees = remaining_cycle_locked_fees;

total_refund_fee = total_refund_fee + (epoch_fee_refund + deposit_refund);
total_refund_fee = total_refund_fee + (cycle_fee_refund + deposit_refund);

vector::push_back(
&mut stopped_task_details,
TaskStoppedV2 { task_index, deposit_refund, epoch_fee_refund, registration_hash: task.tx_hash }
TaskStoppedV2 { task_index, deposit_refund, epoch_fee_refund: cycle_fee_refund, registration_hash: task.tx_hash }
);
}
});
Expand All @@ -1366,6 +1375,7 @@ module supra_framework::automation_registry {
let resource_account_balance = coin::balance<SupraCoin>(automation_registry.registry_fee_address);
assert!(resource_account_balance >= total_refund_fee, EINSUFFICIENT_BALANCE_FOR_REFUND);
coin::transfer<SupraCoin>(&resource_signer, owner, total_refund_fee);
automation_registry.epoch_locked_fees = total_cycle_locked_fees;

// Emit task stopped event
event::emit(TasksStoppedV2 {
Expand Down Expand Up @@ -3334,6 +3344,14 @@ module supra_framework::automation_registry {
automation_registry.main.epoch_locked_fees = locked_fee;
}

#[test_only]
public(friend) fun check_locked_fee(
locked_fee: u64,
) acquires AutomationRegistryV2 {
let automation_registry = borrow_global_mut<AutomationRegistryV2>(@supra_framework);
assert!(automation_registry.main.epoch_locked_fees == locked_fee, locked_fee);
}

#[test_only]
public(friend) fun set_total_deposited_automation_fee(
fee: u64,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module std::automation_registry_tests {
use supra_framework::multisig_account;
use supra_framework::automation_registry::{
check_task_priority, check_cycle_state_and_duration, check_next_task_index_to_be_processed,
calculate_automation_fee_multiplier_for_committed_occupancy, AutomationRegistryConfigV2,
calculate_automation_fee_multiplier_for_committed_occupancy, AutomationRegistryConfigV2, check_locked_fee,
};
use supra_framework::coin;
use supra_framework::config_buffer;
Expand Down Expand Up @@ -2841,11 +2841,12 @@ module std::automation_registry_tests {
});

// 0.002 (*4) - automation_epoch_fee_per_second, 7200 epoch duration
let expected_automation_fee = 4 * (max_gas_amount * EPOCH_INTERVAL_FOR_TEST_IN_SECS / 100000);
expected_current_balance = expected_current_balance - expected_automation_fee;
expected_registry_balance = expected_registry_balance + expected_automation_fee;
check_account_balance(user_account, expected_current_balance );
check_account_balance( registry_fee_address, expected_registry_balance );
let expected_automation_fees = 4 * (max_gas_amount * EPOCH_INTERVAL_FOR_TEST_IN_SECS / 100000);
expected_current_balance = expected_current_balance - expected_automation_fees;
expected_registry_balance = expected_registry_balance + expected_automation_fees;
check_account_balance(user_account, expected_current_balance);
check_account_balance(registry_fee_address, expected_registry_balance);
check_locked_fee(expected_automation_fees);

timestamp::update_global_time_for_test_secs(
EPOCH_INTERVAL_FOR_TEST_IN_SECS + (EPOCH_INTERVAL_FOR_TEST_IN_SECS / 2)
Expand All @@ -2869,6 +2870,8 @@ module std::automation_registry_tests {
expected_registry_balance = expected_registry_balance - expected_refund;
check_account_balance(user_account, expected_current_balance);
check_account_balance(registry_fee_address, expected_registry_balance);
// locked cycle fees are deducted by the full cycle fee amount for the user
check_locked_fee((expected_automation_fees * 3) / 4);

// Add and stop the task in the same epoch. Task index will be 4
assert!(automation_registry::get_next_task_index() == 4, 1);
Expand Down Expand Up @@ -2903,6 +2906,8 @@ module std::automation_registry_tests {
expected_refund = automation_fee_cap / REFUND_FACTOR;
check_account_balance(user_account, expected_current_balance + expected_refund);
check_account_balance(registry_fee_address, expected_registry_balance - expected_refund);
// As long as the task was not active, no locked fee for it will be unlocked
check_locked_fee((expected_automation_fees * 3) / 4);
}

#[test(framework = @supra_framework, user = @0x1cafe, user2 = @0x1cafa)]
Expand Down
Loading