Skip to content

Commit

Permalink
Merge pull request #7 from symbioticfi/self-register-sqrt-task
Browse files Browse the repository at this point in the history
feat: self register sqrt task middleware example
  • Loading branch information
alrxy authored Dec 12, 2024
2 parents bf262d8 + a75ae35 commit 8145c5e
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Middleware Development Guide [**!!!WIP!!!**]
# Middleware Development Guide

**Warning: The SDK is a work in progress and is currently under audits. Use with caution.**

Expand Down
179 changes: 179 additions & 0 deletions src/examples/sqrt-task-network/SelfRegisterSqrtTaskMiddleware.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {IVault} from "@symbiotic/interfaces/vault/IVault.sol";
import {IBaseDelegator} from "@symbiotic/interfaces/delegator/IBaseDelegator.sol";
import {Subnetwork} from "@symbiotic/contracts/libraries/Subnetwork.sol";

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

import {BaseMiddleware} from "../../middleware/BaseMiddleware.sol";
import {SharedVaults} from "../../extensions/SharedVaults.sol";
import {SelfRegisterOperators} from "../../extensions/operators/SelfRegisterOperators.sol";

import {ECDSASig} from "../../extensions/managers/sigs/ECDSASig.sol";
import {OwnableAccessManager} from "../../extensions/managers/access/OwnableAccessManager.sol";
import {KeyManager256} from "../../extensions/managers/keys/KeyManager256.sol";
import {TimestampCapture} from "../../extensions/managers/capture-timestamps/TimestampCapture.sol";
import {EqualStakePower} from "../../extensions/managers/stake-powers/EqualStakePower.sol";

contract SelfRegisterSqrtTaskMiddleware is
SharedVaults,
SelfRegisterOperators,
ECDSASig,
KeyManager256,
OwnableAccessManager,
TimestampCapture,
EqualStakePower
{
using Subnetwork for address;
using Math for uint256;

error InvalidHints();
error TaskCompleted();

event CreateTask(uint256 indexed taskIndex);
event CompleteTask(uint256 indexed taskIndex, bool isValidAnswer);

struct Task {
uint48 captureTimestamp;
uint256 value;
address operator;
bool completed;
}

bytes32 private constant COMPLETE_TASK_TYPEHASH = keccak256("CompleteTask(uint256 taskIndex,uint256 answer)");

Task[] public tasks;

constructor(
address network,
uint48 slashingWindow,
address operatorRegistry,
address vaultRegistry,
address operatorNetOptin,
address reader,
address owner
) {
initialize(network, slashingWindow, vaultRegistry, operatorRegistry, operatorNetOptin, reader, owner);
}

function initialize(
address network,
uint48 slashingWindow,
address vaultRegistry,
address operatorRegistry,
address operatorNetOptin,
address reader,
address owner
) internal initializer {
__BaseMiddleware_init(network, slashingWindow, vaultRegistry, operatorRegistry, operatorNetOptin, reader);
__OwnableAccessManager_init(owner);
__SelfRegisterOperators_init("SelfRegisterSqrtTaskMiddleware");
}

function createTask(uint256 value, address operator) external returns (uint256 taskIndex) {
taskIndex = tasks.length;
tasks.push(Task({captureTimestamp: getCaptureTimestamp(), value: value, operator: operator, completed: false}));

emit CreateTask(taskIndex);
}

function completeTask(
uint256 taskIndex,
uint256 answer,
bytes calldata signature,
bytes[] calldata stakeHints,
bytes[] calldata slashHints
) external returns (bool isValidAnswer) {
isValidAnswer = _verify(taskIndex, answer, signature);

tasks[taskIndex].completed = true;

if (!isValidAnswer) {
_slash(taskIndex, stakeHints, slashHints);
}

emit CompleteTask(taskIndex, isValidAnswer);
}

function _verify(uint256 taskIndex, uint256 answer, bytes calldata signature) private view returns (bool) {
if (tasks[taskIndex].completed) {
revert TaskCompleted();
}
_verifySignature(taskIndex, answer, signature);
return _verifyAnswer(taskIndex, answer);
}

function _verifySignature(uint256 taskIndex, uint256 answer, bytes calldata signature) private view {
Task storage task = tasks[taskIndex];

bytes32 hash_ = _hashTypedDataV4(keccak256(abi.encode(COMPLETE_TASK_TYPEHASH, taskIndex, answer)));

if (!SignatureChecker.isValidSignatureNow(task.operator, hash_, signature)) {
revert InvalidSignature();
}
}

function _verifyAnswer(uint256 taskIndex, uint256 answer) private view returns (bool) {
uint256 value = tasks[taskIndex].value;
uint256 square = answer ** 2;
if (square == value) {
return true;
}

if (square < value) {
uint256 difference = value - square;
uint256 nextSquare = (answer + 1) ** 2;
uint256 nextDifference = nextSquare > value ? nextSquare - value : value - nextSquare;
if (difference <= nextDifference) {
return true;
}
} else {
uint256 difference = square - value;
uint256 prevSquare = (answer - 1) ** 2;
uint256 prevDifference = prevSquare > value ? prevSquare - value : value - prevSquare;
if (difference <= prevDifference) {
return true;
}
}

return false;
}

function _slash(uint256 taskIndex, bytes[] calldata stakeHints, bytes[] calldata slashHints) private {
Task storage task = tasks[taskIndex];
address[] memory vaults = _activeVaultsAt(task.captureTimestamp, task.operator);
uint256 vaultsLength = vaults.length;

if (stakeHints.length != slashHints.length || stakeHints.length != vaultsLength) {
revert InvalidHints();
}

bytes32 subnetwork = _NETWORK().subnetwork(0);
for (uint256 i; i < vaultsLength; ++i) {
address vault = vaults[i];
uint256 slashAmount = IBaseDelegator(IVault(vault).delegator()).stakeAt(
subnetwork, task.operator, task.captureTimestamp, stakeHints[i]
);

if (slashAmount == 0) {
continue;
}

_slashVault(task.captureTimestamp, vault, subnetwork, task.operator, slashAmount, slashHints[i]);
}
}

function executeSlash(
uint48 epochStart,
address vault,
bytes32 subnetwork,
address operator,
uint256 amount,
bytes memory hints
) external checkAccess {
_slashVault(epochStart, vault, subnetwork, operator, amount, hints);
}
}

0 comments on commit 8145c5e

Please sign in to comment.