Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add opt in signatures #53

Merged
merged 2 commits into from
Oct 8, 2024
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
6 changes: 4 additions & 2 deletions script/deploy/Core.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ contract CoreScript is Script {
MetadataService operatorMetadataService = new MetadataService(address(operatorRegistry));
MetadataService networkMetadataService = new MetadataService(address(networkRegistry));
NetworkMiddlewareService networkMiddlewareService = new NetworkMiddlewareService(address(networkRegistry));
OptInService operatorVaultOptInService = new OptInService(address(operatorRegistry), address(vaultFactory));
OptInService operatorNetworkOptInService = new OptInService(address(operatorRegistry), address(networkRegistry));
OptInService operatorVaultOptInService =
new OptInService(address(operatorRegistry), address(vaultFactory), "OperatorVaultOptInService");
OptInService operatorNetworkOptInService =
new OptInService(address(operatorRegistry), address(networkRegistry), "OperatorNetworkOptInService");

address vaultImpl =
address(new Vault(address(delegatorFactory), address(slasherFactory), address(vaultFactory)));
Expand Down
4 changes: 2 additions & 2 deletions script/deploy/OptInService.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import "forge-std/Script.sol";
import {OptInService} from "../../src/contracts/service/OptInService.sol";

contract OptInServiceScript is Script {
function run(address whoRegistry, address whereRegistry) public {
function run(address whoRegistry, address whereRegistry, string calldata name) public {
vm.startBroadcast();

new OptInService(whoRegistry, whereRegistry);
new OptInService(whoRegistry, whereRegistry, name);

vm.stopBroadcast();
}
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/hints/OptInServiceHints.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {Checkpoints} from "../libraries/Checkpoints.sol";
contract OptInServiceHints is Hints, OptInService {
using Checkpoints for Checkpoints.Trace208;

constructor() OptInService(address(0), address(0)) {}
constructor() OptInService(address(0), address(0), "") {}

function optInHintInternal(
address who,
Expand Down
121 changes: 106 additions & 15 deletions src/contracts/service/OptInService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {IRegistry} from "../../interfaces/common/IRegistry.sol";

import {Checkpoints} from "../libraries/Checkpoints.sol";

import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";

contract OptInService is StaticDelegateCallable, IOptInService {
contract OptInService is StaticDelegateCallable, EIP712, IOptInService {
using Checkpoints for Checkpoints.Trace208;

/**
Expand All @@ -23,9 +25,29 @@ contract OptInService is StaticDelegateCallable, IOptInService {
*/
address public immutable WHERE_REGISTRY;

bytes32 private constant OPT_IN_TYPEHASH =
keccak256("OptIn(address who,address where,uint256 nonce,uint48 deadline)");

bytes32 private constant OPT_OUT_TYPEHASH =
keccak256("OptOut(address who,address where,uint256 nonce,uint48 deadline)");

/**
* @inheritdoc IOptInService
*/
mapping(address who => mapping(address where => uint256 nonce)) public nonces;

mapping(address who => mapping(address where => Checkpoints.Trace208 value)) internal _isOptedIn;

constructor(address whoRegistry, address whereRegistry) {
modifier checkDeadline(
uint48 deadline
) {
if (deadline < Time.timestamp()) {
revert ExpiredSignature();
}
_;
}

constructor(address whoRegistry, address whereRegistry, string memory name) EIP712(name, "1") {
WHO_REGISTRY = whoRegistry;
WHERE_REGISTRY = whereRegistry;
}
Expand Down Expand Up @@ -55,30 +77,81 @@ contract OptInService is StaticDelegateCallable, IOptInService {
function optIn(
address where
) external {
if (!IRegistry(WHO_REGISTRY).isEntity(msg.sender)) {
_optIn(msg.sender, where);
}

/**
* @inheritdoc IOptInService
*/
function optIn(
address who,
address where,
uint48 deadline,
bytes calldata signature
) external checkDeadline(deadline) {
if (!SignatureChecker.isValidSignatureNow(who, _hash(true, who, where, deadline), signature)) {
revert InvalidSignature();
}

_optIn(who, where);
}

/**
* @inheritdoc IOptInService
*/
function optOut(
address where
) external {
_optOut(msg.sender, where);
}

/**
* @inheritdoc IOptInService
*/
function optOut(
address who,
address where,
uint48 deadline,
bytes calldata signature
) external checkDeadline(deadline) {
if (!SignatureChecker.isValidSignatureNow(who, _hash(false, who, where, deadline), signature)) {
revert InvalidSignature();
}

_optOut(who, where);
}

/**
* @inheritdoc IOptInService
*/
function increaseNonce(
address where
) external {
_increaseNonce(msg.sender, where);
}

function _optIn(address who, address where) internal {
if (!IRegistry(WHO_REGISTRY).isEntity(who)) {
revert NotWho();
}

if (!IRegistry(WHERE_REGISTRY).isEntity(where)) {
revert NotWhereEntity();
}

if (isOptedIn(msg.sender, where)) {
if (isOptedIn(who, where)) {
revert AlreadyOptedIn();
}

_isOptedIn[msg.sender][where].push(Time.timestamp(), 1);
_isOptedIn[who][where].push(Time.timestamp(), 1);

_increaseNonce(who, where);

emit OptIn(msg.sender, where);
emit OptIn(who, where);
}

/**
* @inheritdoc IOptInService
*/
function optOut(
address where
) external {
(, uint48 latestTimestamp, uint208 latestValue) = _isOptedIn[msg.sender][where].latestCheckpoint();
function _optOut(address who, address where) internal {
(, uint48 latestTimestamp, uint208 latestValue) = _isOptedIn[who][where].latestCheckpoint();

if (latestValue == 0) {
revert NotOptedIn();
Expand All @@ -88,8 +161,26 @@ contract OptInService is StaticDelegateCallable, IOptInService {
revert OptOutCooldown();
}

_isOptedIn[msg.sender][where].push(Time.timestamp(), 0);
_isOptedIn[who][where].push(Time.timestamp(), 0);

_increaseNonce(who, where);

emit OptOut(who, where);
}

function _hash(bool ifOptIn, address who, address where, uint48 deadline) internal view returns (bytes32) {
return _hashTypedDataV4(
keccak256(
abi.encode(ifOptIn ? OPT_IN_TYPEHASH : OPT_OUT_TYPEHASH, who, where, nonces[who][where], deadline)
)
);
}

function _increaseNonce(address who, address where) internal {
unchecked {
++nonces[who][where];
}

emit OptOut(msg.sender, where);
emit IncreaseNonce(who, where);
}
}
52 changes: 48 additions & 4 deletions src/interfaces/service/IOptInService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pragma solidity ^0.8.0;

interface IOptInService {
error AlreadyOptedIn();
error ExpiredSignature();
error InvalidSignature();
error NotOptedIn();
error NotWhereEntity();
error NotWho();
Expand All @@ -22,6 +24,13 @@ interface IOptInService {
*/
event OptOut(address indexed who, address indexed where);

/**
* @notice Emitted when the nonce of a "who" to a "where" entity is increased.
* @param who address of the "who"
* @param where address of the "where" entity
*/
event IncreaseNonce(address indexed who, address indexed where);

/**
* @notice Get the "who" registry's address.
* @return address of the "who" registry
Expand All @@ -37,7 +46,7 @@ interface IOptInService {
/**
* @notice Get if a given "who" is opted-in to a particular "where" entity at a given timestamp using a hint.
* @param who address of the "who"
* @param where address of the "where" registry
* @param where address of the "where" entity
* @param timestamp time point to get if the "who" is opted-in at
* @param hint hint for the checkpoint index
* @return if the "who" is opted-in at the given timestamp
Expand All @@ -52,24 +61,59 @@ interface IOptInService {
/**
* @notice Check if a given "who" is opted-in to a particular "where" entity.
* @param who address of the "who"
* @param where address of the "where" registry
* @param where address of the "where" entity
* @return if the "who" is opted-in
*/
function isOptedIn(address who, address where) external view returns (bool);

/**
* @notice Get the nonce of a given "who" to a particular "where" entity.
* @param who address of the "who"
* @param where address of the "where" entity
* @return nonce
*/
function nonces(address who, address where) external view returns (uint256);

/**
* @notice Opt-in a calling "who" to a particular "where" entity.
* @param where address of the "where" registry
* @param where address of the "where" entity
*/
function optIn(
address where
) external;

/**
* @notice Opt-in a "who" to a particular "where" entity with a signature.
* @param who address of the "who"
* @param where address of the "where" entity
* @param deadline time point until the signature is valid (inclusively)
* @param signature signature of the "who"
*/
function optIn(address who, address where, uint48 deadline, bytes calldata signature) external;

/**
* @notice Opt-out a calling "who" from a particular "where" entity.
* @param where address of the "where" registry
* @param where address of the "where" entity
*/
function optOut(
address where
) external;

/**
* @notice Opt-out a "who" from a particular "where" entity with a signature.
* @param who address of the "who"
* @param where address of the "where" entity
* @param deadline time point until the signature is valid (inclusively)
* @param signature signature of the "who"
*/
function optOut(address who, address where, uint48 deadline, bytes calldata signature) external;

/**
* @notice Increase the nonce of a given "who" to a particular "where" entity.
* @param where address of the "where" entity
* @dev It can be used to invalidate a given signature.
*/
function increaseNonce(
address where
) external;
}
6 changes: 4 additions & 2 deletions test/DelegatorFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ contract DelegatorFactoryTest is Test {
operatorMetadataService = new MetadataService(address(operatorRegistry));
networkMetadataService = new MetadataService(address(networkRegistry));
networkMiddlewareService = new NetworkMiddlewareService(address(networkRegistry));
operatorVaultOptInService = new OptInService(address(operatorRegistry), address(vaultFactory));
operatorNetworkOptInService = new OptInService(address(operatorRegistry), address(networkRegistry));
operatorVaultOptInService =
new OptInService(address(operatorRegistry), address(vaultFactory), "OperatorVaultOptInService");
operatorNetworkOptInService =
new OptInService(address(operatorRegistry), address(networkRegistry), "OperatorNetworkOptInService");

address vaultImpl =
address(new Vault(address(delegatorFactory), address(slasherFactory), address(vaultFactory)));
Expand Down
6 changes: 4 additions & 2 deletions test/POCBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ contract POCBaseTest is Test {
operatorMetadataService = new MetadataService(address(operatorRegistry));
networkMetadataService = new MetadataService(address(networkRegistry));
networkMiddlewareService = new NetworkMiddlewareService(address(networkRegistry));
operatorVaultOptInService = new OptInService(address(operatorRegistry), address(vaultFactory));
operatorNetworkOptInService = new OptInService(address(operatorRegistry), address(networkRegistry));
operatorVaultOptInService =
new OptInService(address(operatorRegistry), address(vaultFactory), "OperatorVaultOptInService");
operatorNetworkOptInService =
new OptInService(address(operatorRegistry), address(networkRegistry), "OperatorNetworkOptInService");

address vaultImpl =
address(new Vault(address(delegatorFactory), address(slasherFactory), address(vaultFactory)));
Expand Down
6 changes: 4 additions & 2 deletions test/SlasherFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ contract SlasherFactoryTest is Test {
operatorMetadataService = new MetadataService(address(operatorRegistry));
networkMetadataService = new MetadataService(address(networkRegistry));
networkMiddlewareService = new NetworkMiddlewareService(address(networkRegistry));
operatorVaultOptInService = new OptInService(address(operatorRegistry), address(vaultFactory));
operatorNetworkOptInService = new OptInService(address(operatorRegistry), address(networkRegistry));
operatorVaultOptInService =
new OptInService(address(operatorRegistry), address(vaultFactory), "OperatorVaultOptInService");
operatorNetworkOptInService =
new OptInService(address(operatorRegistry), address(networkRegistry), "OperatorNetworkOptInService");

address vaultImpl =
address(new Vault(address(delegatorFactory), address(slasherFactory), address(vaultFactory)));
Expand Down
6 changes: 4 additions & 2 deletions test/VaultConfigurator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ contract VaultConfiguratorTest is Test {
operatorMetadataService = new MetadataService(address(operatorRegistry));
networkMetadataService = new MetadataService(address(networkRegistry));
networkMiddlewareService = new NetworkMiddlewareService(address(networkRegistry));
operatorVaultOptInService = new OptInService(address(operatorRegistry), address(vaultFactory));
operatorNetworkOptInService = new OptInService(address(operatorRegistry), address(networkRegistry));
operatorVaultOptInService =
new OptInService(address(operatorRegistry), address(vaultFactory), "OperatorVaultOptInService");
operatorNetworkOptInService =
new OptInService(address(operatorRegistry), address(networkRegistry), "OperatorNetworkOptInService");

address vaultImpl =
address(new Vault(address(delegatorFactory), address(slasherFactory), address(vaultFactory)));
Expand Down
6 changes: 4 additions & 2 deletions test/VaultFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ contract VaultFactoryTest is Test {
operatorMetadataService = new MetadataService(address(operatorRegistry));
networkMetadataService = new MetadataService(address(networkRegistry));
networkMiddlewareService = new NetworkMiddlewareService(address(networkRegistry));
operatorVaultOptInService = new OptInService(address(operatorRegistry), address(vaultFactory));
operatorNetworkOptInService = new OptInService(address(operatorRegistry), address(networkRegistry));
operatorVaultOptInService =
new OptInService(address(operatorRegistry), address(vaultFactory), "OperatorVaultOptInService");
operatorNetworkOptInService =
new OptInService(address(operatorRegistry), address(networkRegistry), "OperatorNetworkOptInService");

address vaultImpl =
address(new Vault(address(delegatorFactory), address(slasherFactory), address(vaultFactory)));
Expand Down
6 changes: 4 additions & 2 deletions test/delegator/FullRestakeDelegator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ contract FullRestakeDelegatorTest is Test {
operatorMetadataService = new MetadataService(address(operatorRegistry));
networkMetadataService = new MetadataService(address(networkRegistry));
networkMiddlewareService = new NetworkMiddlewareService(address(networkRegistry));
operatorVaultOptInService = new OptInService(address(operatorRegistry), address(vaultFactory));
operatorNetworkOptInService = new OptInService(address(operatorRegistry), address(networkRegistry));
operatorVaultOptInService =
new OptInService(address(operatorRegistry), address(vaultFactory), "OperatorVaultOptInService");
operatorNetworkOptInService =
new OptInService(address(operatorRegistry), address(networkRegistry), "OperatorNetworkOptInService");

address vaultImpl =
address(new Vault(address(delegatorFactory), address(slasherFactory), address(vaultFactory)));
Expand Down
6 changes: 4 additions & 2 deletions test/delegator/NetworkRestakeDelegator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ contract NetworkRestakeDelegatorTest is Test {
operatorMetadataService = new MetadataService(address(operatorRegistry));
networkMetadataService = new MetadataService(address(networkRegistry));
networkMiddlewareService = new NetworkMiddlewareService(address(networkRegistry));
operatorVaultOptInService = new OptInService(address(operatorRegistry), address(vaultFactory));
operatorNetworkOptInService = new OptInService(address(operatorRegistry), address(networkRegistry));
operatorVaultOptInService =
new OptInService(address(operatorRegistry), address(vaultFactory), "OperatorVaultOptInService");
operatorNetworkOptInService =
new OptInService(address(operatorRegistry), address(networkRegistry), "OperatorNetworkOptInService");

address vaultImpl =
address(new Vault(address(delegatorFactory), address(slasherFactory), address(vaultFactory)));
Expand Down
Loading
Loading