Overview
The Othentic Stack enables developers to create a FeeCalculator
contract, which functions as a hook for calculating task submission rewards on-chain, offering flexible and customizable logic tailored to specific needs.
Interface
Copy // SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.25;
/*______ __ __ __ __
/ \ / | / | / | / |
/$$$$$$ | _$$ |_ $$ |____ ______ _______ _$$ |_ $$/ _______
$$ | $$ |/ $$ | $$ \ / \ / \ / $$ | / | / |
$$ | $$ |$$$$$$/ $$$$$$$ |/$$$$$$ |$$$$$$$ |$$$$$$/ $$ |/$$$$$$$/
$$ | $$ | $$ | __ $$ | $$ |$$ $$ |$$ | $$ | $$ | __ $$ |$$ |
$$ \__$$ | $$ |/ |$$ | $$ |$$$$$$$$/ $$ | $$ | $$ |/ |$$ |$$ \_____
$$ $$/ $$ $$/ $$ | $$ |$$ |$$ | $$ | $$ $$/ $$ |$$ |
$$$$$$/ $$$$/ $$/ $$/ $$$$$$$/ $$/ $$/ $$$$/ $$/ $$$$$$$/
*/
/**
* @author Othentic Labs LTD.
* @notice Terms of Service: https://www.othentic.xyz/terms-of-service
* @notice Depending on the application, it may be necessary to add reentrancy gaurds to hooks
*/
import { IAttestationCenter } from "@othentic/NetworkManagement/L2/interfaces/IAttestationCenter.sol" ;
interface IFeeCalculator {
struct FeeCalculatorData {
IAttestationCenter.TaskInfo data;
uint256 aggregatorId;
uint256 performerId;
uint256 [] attestersIds;
}
struct FeePerId {
uint256 index;
uint256 fee;
}
function calculateBaseRewardFees(FeeCalculatorData calldata _feeCalculatorData) external returns (uint256 baseRewardFeeForAttesters, uint256 baseRewardFeeForAggregator, uint256 baseRewardFeeForPerformer);
function calculateFeesPerId(FeeCalculatorData calldata _feeCalculatorData) external returns (FeePerId[] memory feesPerId);
function isBaseRewardFee () external pure returns ( bool );
}
The interface defines two functions: calculateBaseRewardFees
and calculateFeesPerId
.
You should select one of these options.
These functions can be called in the AttestationCenter
inside the submitTask
function by the Task Aggregator.
calculateBaseRewardFees
example:
Copy function _calculateBaseRewardFees(AttestationCenterStorageData storage _sd, uint16 _taskDefinitionId, TaskDefinition memory _taskDefinition, IFeeCalculator.FeeCalculatorData memory _feeCalculatorData) private returns (uint256 _baseRewardFeeForAttesters, uint256 _baseRewardFeeForPerformer, uint256 _baseRewardFeeForAggregator) {
IFeeCalculator _feeCalculator = _sd.feeCalculator;
bool _isFeeCalculatorSet = address (_feeCalculator) != address ( 0 );
if (_isFeeCalculatorSet){
(_baseRewardFeeForAttesters ,
_baseRewardFeeForPerformer ,
_baseRewardFeeForAggregator) = _feeCalculator. calculateBaseRewardFees (_feeCalculatorData);
}
...
}
calculateFeesPerId
example:
Copy function _submitTaskBusinessLogic(AttestationCenterStorageData storage _sd, TaskInfo calldata _taskInfo, TaskDefinition memory _taskDefinition, bool _isApproved, uint256[] memory _attestersIds) private {
...
IFeeCalculator _feeCalculator = _sd.feeCalculator;
bool _isFeeCalculatorSet = address(_feeCalculator) != address(0);
if (!_isFeeCalculatorSet || (_isFeeCalculatorSet && _feeCalculator.isBaseRewardFee())) {
...
}
else {
IFeeCalculator.FeePerId[] memory _feesPerId = _feeCalculator.calculateFeesPerId(_feeCalculatorData);
_accumulateRewardsForOperatorsById(_sd, _feesPerId, _taskNumber);
}
Deployment and setup
After AVS implementation, you can deploy the contract on the same L2 network as the AttestationCenter
.
You should deploy the AVS contracts before deploying the IFeeCalculator.
Copy // SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
/*______ __ __ __ __
/ \ / | / | / | / |
/$$$$$$ | _$$ |_ $$ |____ ______ _______ _$$ |_ $$/ _______
$$ | $$ |/ $$ | $$ \ / \ / \ / $$ | / | / |
$$ | $$ |$$$$$$/ $$$$$$$ |/$$$$$$ |$$$$$$$ |$$$$$$/ $$ |/$$$$$$$/
$$ | $$ | $$ | __ $$ | $$ |$$ $$ |$$ | $$ | $$ | __ $$ |$$ |
$$ \__$$ | $$ |/ |$$ | $$ |$$$$$$$$/ $$ | $$ | $$ |/ |$$ |$$ \_____
$$ $$/ $$ $$/ $$ | $$ |$$ |$$ | $$ | $$ $$/ $$ |$$ |
$$$$$$/ $$$$/ $$/ $$/ $$$$$$$/ $$/ $$/ $$$$/ $$/ $$$$$$$/
*/
/**
* @author Othentic Labs LTD.
* @notice Terms of Service: https://www.othentic.xyz/terms-of-service
*/
import { Script , console } from "forge-std/Script.sol" ;
import "../src/IAttestationCenter.sol" ;
import "../src/FeeCalculator.sol" ;
// forge script FeeCalculatorScript --rpc-url $L2_RPC --private-key $PRIVATE_KEY
// --broadcast -vvvv --verify --etherscan-api-key $L2_ETHERSCAN_API_KEY --chain
// $L2_CHAIN --verifier-url $L2_VERIFIER_URL --sig="run(address)" $ATTESTATION_CENTER_ADDRESS
contract FeeCalculatorScript is Script {
function setUp () public {}
function run ( address attestationCenter) public {
vm. broadcast ();
FeeCalculator feeCalculator = new FeeCalculator (attestationCenter);
IAttestationCenter (attestationCenter). setFeeCalculator ( address (feeCalculator));
}
}
After deploying, you need to call the setFeeCalculator
function on the AttestationCenter
and pass in the address of the FeeCalculator
as the parameter.
Now, the contracts are set and ready to be used.