We propose a x/cw-callback module which enables CosmWasm smart contracts to receive callbacks at the end of each block.
This is useful for scheduling actions to happen at an expected time by reserving block execution in advance.
Context
Currently on Archway, the smart contracts can only be executed when called on by a user or by other contracts. The decentralized nature of ecosystem should allow enforcement of predefined protocols without the need for intermediaries.
However, for some applications, dapps still rely on trusted third parties to instigate the executions or state changes. Examples include, epochs for defi primitives, price balancing arbitrage bots, vesting distribution of funds, auto compounding of rewards. In case of Archway, with its unique rewards mechanism, the withdrawal of contract rewards would need to happen on a recurring basis.
The aim of this AIP is to solve the reliance on the trusted third parties for these types of scripting reasons. The protocol should enable a minimal scheduler service which can be used to handle such operations in a permissionless and trust minimized way.
Decision
We will implement a new module called x/cw-callback which solves the specified problem and enables automated, time triggered actions. With this, a dapp will be able to ask the chain to give it a callback at a specific height so its can perform any desired operations. The field of job_id is added to allow the contract to have context on the callback.
The module relies on the ABCI EndBlocker to execute these callbacks. All the callbacks for the given height will be executed in a First-In-First-Out basis. Each callback will be called with a custom context and any errors thrown by it during the execution, will be collected and thrown as events. This is due to the fact that a wallet is not instigating this execution, to return the error using the execution context.
As the execution of the callbacks is done by the chain, it is the validators who are paying for the execution of the callback. Since they will not be receiving the transaction fees for the executions, explicit value of transaction fees will be paid by the contract when requesting the callback. Because the module will not be aware of how much gas a callback will consume, the contract is expected to overpay the transaction fees in advance and the contract will receive any leftover gas fees post callback execution. If the callback is cancelled, this amount is returned completely.
The total amount to be paid for a callback is calculated as:
This is the maximum gas that can be consumed by a callback. If the gas consumed exceeds this value, the callback will fail automatically. This ensures, that the contracts aren't abusing the callback feature to perform high gas computations. This value is expected to be large enough to allow meaningful executions but small enough to prevent abuse.
max_block_reservation_limit
This is the maximum number of callbacks which can be registered in a given block. If this value is 5, maximum of 5 callbacks can be registered for a given block height. This prevents too many callbacks from slowing down the performance of the chain.
max_future_reservation_limit
This is the maximum number of blocks in the future that a contract can request a callback in. If this value is 100, the contracts can only request callbacks in the next 100 blocks. The callback feature provides guaranteed block execution; therefore, this limit is necessary to ensure the callbacks aren't reserved without meaningful intention.
block_reservation_fee_multiplier
This is a value which calculates a part of the reservation fees which will need to be paid when requesting the callback. As the number of callbacks for a given block approach max_block_reservation_limit, this multiplier is applied as it will disincentivize too many callbacks from being registered in a single block.
future_reservation_fee_multiplier
This is a value which calculates a part of the reservation fees which will need to be paid while requesting the callback. The difference between callback request height and current block height will be multiplied by this value to calculate the reservation fees for the callback. This reduces the incentives to request callbacks far in the future, as a way to hedge against any raising gas prices.
The module will expose the following msg services:
Since the feature is being added as a new module, this should not cause any backwards compatibility issues.
Positive
Allows contracts to be triggered without dependencies on third party bot solutions which could lead to unique protocols being built on top of Archway
Sets groundwork for more comprehensive cron like solutions to be built as a service on the contract layer by other contracts
Increases protocol revenue for the validators and delegators
Negative
Increases the exposure of custom wasm bindings of the protocol
In case, the fees value increases significantly in the future, the validators encounter a loss as they were paid lesser fees when the callback was reserved
Further Discussions
Future iterations of the module could include the following:
Allow a contract to pay extra "incentives" to prioritize their callback at the given height. Allows bypassing FIFO ordering.
Store the contract errors in the state for n blocks such that the developers will not need to set up event monitoring to access what the error was.
Algorithmically set the multipliers which are used in reservation fee calculation instead of relying on governance to set the params in a market efficient way.
Test Cases
With the development of the module, there needs to be extensive testing and simulations to arrive at the ideal values for the module parameters.
Testing should be done with recommended system specs and mainnet params to see how many callbacks can be handled in a block reasonably.
Discussed in https://github.com/orgs/archway-network/discussions/25
Originally posted by spoo-bar October 10, 2023
Abstract
We propose a x/cw-callback module which enables CosmWasm smart contracts to receive callbacks at the end of each block. This is useful for scheduling actions to happen at an expected time by reserving block execution in advance.
Context
Currently on Archway, the smart contracts can only be executed when called on by a user or by other contracts. The decentralized nature of ecosystem should allow enforcement of predefined protocols without the need for intermediaries.
However, for some applications, dapps still rely on trusted third parties to instigate the executions or state changes. Examples include, epochs for defi primitives, price balancing arbitrage bots, vesting distribution of funds, auto compounding of rewards. In case of Archway, with its unique rewards mechanism, the withdrawal of contract rewards would need to happen on a recurring basis.
The aim of this AIP is to solve the reliance on the trusted third parties for these types of scripting reasons. The protocol should enable a minimal scheduler service which can be used to handle such operations in a permissionless and trust minimized way.
Decision
We will implement a new module called x/cw-callback which solves the specified problem and enables automated, time triggered actions. With this, a dapp will be able to ask the chain to give it a callback at a specific height so its can perform any desired operations. The field of job_id is added to allow the contract to have context on the callback.
The module relies on the ABCI
EndBlocker
to execute these callbacks. All the callbacks for the given height will be executed in a First-In-First-Out basis. Each callback will be called with a custom context and any errors thrown by it during the execution, will be collected and thrown as events. This is due to the fact that a wallet is not instigating this execution, to return the error using the execution context.As the execution of the callbacks is done by the chain, it is the validators who are paying for the execution of the callback. Since they will not be receiving the transaction fees for the executions, explicit value of transaction fees will be paid by the contract when requesting the callback. Because the module will not be aware of how much gas a callback will consume, the contract is expected to overpay the transaction fees in advance and the contract will receive any leftover gas fees post callback execution. If the callback is cancelled, this amount is returned completely.
The total amount to be paid for a callback is calculated as:
$callbackFee = transactionExecutionFee + totalReservationFee$
$transactionExecutionFee = callbackGasLimit_{params} \times estimateFees(1)$
$totalReservationFee = (resevationFee{blockReservation} + resevationFee{futureReservation})$
$resevationFee{blockReservation}= (maxBlockReservationLimit{params} - count(callbacks{currentHeight})) \times blockReservationFeeMultiplier{params}$
$resevationFee{futureReservation}= (blockHeight{callback} - blockHeight{current}) \times futureReservationFeeMultiplier{params}$
The module will have the following module params:
callback_gas_limit
This is the maximum gas that can be consumed by a callback. If the gas consumed exceeds this value, the callback will fail automatically. This ensures, that the contracts aren't abusing the callback feature to perform high gas computations. This value is expected to be large enough to allow meaningful executions but small enough to prevent abuse.
max_block_reservation_limit
This is the maximum number of callbacks which can be registered in a given block. If this value is 5, maximum of 5 callbacks can be registered for a given block height. This prevents too many callbacks from slowing down the performance of the chain.
max_future_reservation_limit
This is the maximum number of blocks in the future that a contract can request a callback in. If this value is 100, the contracts can only request callbacks in the next 100 blocks. The callback feature provides guaranteed block execution; therefore, this limit is necessary to ensure the callbacks aren't reserved without meaningful intention.
block_reservation_fee_multiplier
This is a value which calculates a part of the reservation fees which will need to be paid when requesting the callback. As the number of callbacks for a given block approach
max_block_reservation_limit
, this multiplier is applied as it will disincentivize too many callbacks from being registered in a single block.future_reservation_fee_multiplier
This is a value which calculates a part of the reservation fees which will need to be paid while requesting the callback. The difference between callback request height and current block height will be multiplied by this value to calculate the reservation fees for the callback. This reduces the incentives to request callbacks far in the future, as a way to hedge against any raising gas prices.
The module will expose the following msg services:
The module will expose the following queries:
The module will expose the following wasm bindings:
Consequences
Backwards Compatibility
Since the feature is being added as a new module, this should not cause any backwards compatibility issues.
Positive
Negative
Further Discussions
Future iterations of the module could include the following:
n
blocks such that the developers will not need to set up event monitoring to access what the error was.Test Cases
With the development of the module, there needs to be extensive testing and simulations to arrive at the ideal values for the module parameters.
Testing should be done with recommended system specs and mainnet params to see how many callbacks can be handled in a block reasonably.