Skip to content
Open
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
58 changes: 55 additions & 3 deletions examples/automation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
from supra_sdk.clients.rest import SupraClient
from supra_sdk.clients.rest.supra_client import TransactionFailedError
from supra_sdk.clients.rest.transaction_defaults import MaxGasEstimationError
from supra_sdk.transactions import EntryFunction, TransactionArgument
from supra_sdk.transactions import (
EntryFunction,
TransactionArgument,
TransactionPayload,
)

TASK_DURATION_SECONDS = 3600

Expand All @@ -37,6 +41,51 @@ async def outside_cycle_transition(action: Callable[[], Awaitable[str]]) -> str:
raise AssertionError("unreachable")


# Max gas of the simulated transaction. The node checks that the sender's balance covers
# it at the gas price, so it stays below what a faucet-funded account holds; raise it for
# a function that uses more.
SIMULATION_MAX_GAS = 5_000
# The automated transaction runs in a later block, on state that may differ from the
# simulated one, so the task's max gas is this many times the simulated gas.
MAX_GAS_FACTOR = 2
# The registry's fee estimate rises with the gas other tasks have committed, which can
# change before the registration is included, so the fee cap is this many times the
# estimate.
FEE_CAP_FACTOR = 2


async def automation_task_limits(
supra_client: SupraClient, sender: Account, automated_function: EntryFunction
) -> tuple[int, int]:
"""Returns the max gas and the fee cap for the epoch to register a task with.

The max gas is `MAX_GAS_FACTOR` times the gas that `automated_function` uses when
`sender` sends it as a user transaction. The fee cap is `FEE_CAP_FACTOR` times
the registry's estimate of the epoch fee for that max gas
(`0x1::automation_registry::estimate_automation_fee`). The registry refuses a
registration whose cap is below its estimate, and charges the cap at registration.
"""
simulation = await supra_client.simulate_transaction(
await supra_client.create_signed_transaction(
sender,
TransactionPayload(automated_function),
max_gas_amount=SIMULATION_MAX_GAS,
)
)
move_output = simulation["output"]["Move"]
if simulation["status"] != "Success":
raise RuntimeError(f"The automated function does not succeed: {move_output}")
max_gas = MAX_GAS_FACTOR * int(move_output["gas_used"])
estimated_fee = int(
(
await supra_client.view(
"0x1::automation_registry::estimate_automation_fee", [], [str(max_gas)]
)
)[0]
)
return max_gas, FEE_CAP_FACTOR * estimated_fee


async def main() -> None:
supra_client = SupraClient(RPC_NODE_URL)
sender = Account.generate()
Expand All @@ -54,6 +103,9 @@ async def main() -> None:
TransactionArgument(1, Serializer.u64),
],
)
max_gas, fee_cap_for_epoch = await automation_task_limits(
supra_client, sender, automated_function
)
# Without a task type the task is registered with version 1 of the registration
# parameters. `automation_task_type` selects version 2. `automation_priority` is
# accepted only with `AutomationTaskType.SYSTEM`, from an account the registry has
Expand All @@ -76,9 +128,9 @@ async def main() -> None:
lambda: supra_client.register_automation_task(
sender,
automated_function,
9,
max_gas,
gas_price_cap,
1 * (10**8),
fee_cap_for_epoch,
expiry_time,
[],
)
Expand Down
7 changes: 5 additions & 2 deletions supra_sdk/clients/rest/supra_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,9 +882,12 @@ async def register_automation_task(
owner_account (Account): Account registering an automation task.
It will be eligible to cancel or stop task.
automated_function (str): Automated entry function payload.
automation_max_gas_amount (int): Max gas allowed for automation.
automation_max_gas_amount (int): Max gas allowed for automation. An automated transaction that needs
more gas than this fails, and its owner pays for the gas it used. Simulate the automated function
as a user transaction and use its gas with a margin (see `examples/automation.py`).
automation_gas_price_cap (int): Gas price cap for automation execution.
automation_fee_cap_for_epoch (int): Maximum total fee for the epoch.
automation_fee_cap_for_epoch (int): Maximum total fee for the epoch. The registry refuses a cap below
its estimate for the max gas (the view `0x1::automation_registry::estimate_automation_fee`).
automation_expiration_timestamp_secs (int): Expiration time for automation.
automation_aux_data (list[bytes]): Auxiliary data for automation.
automation_task_type (AutomationTaskType | None): ``USER`` or ``SYSTEM``.
Expand Down
8 changes: 6 additions & 2 deletions tests/unit/test_supra_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import unittest
from typing import Any, cast

from examples.automation import automation_task_limits
from supra_sdk.account import Account
from supra_sdk.account_address import AccountAddress
from supra_sdk.bcs import Serializer
Expand Down Expand Up @@ -510,14 +511,17 @@ async def register_test_automation_task(self) -> str:
TransactionArgument(1, Serializer.u64),
],
)
max_gas, fee_cap_for_epoch = await automation_task_limits(
self.supra_client, self.sender, automated_function
)
# The node refuses a cap below its minimum gas price. These tests do not wait for
# the task to execute, so the minimum is enough.
tx_hash = await self.supra_client.register_automation_task(
self.sender,
automated_function,
9,
max_gas,
(await self.supra_client.estimate_gas_price())["min_configured_gas_price"],
1 * (10**8),
fee_cap_for_epoch,
int(time.time()) + 7200,
[],
)
Expand Down
Loading