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

Payable delegate funcs for possible tip functionality #59

Merged
merged 13 commits into from
Aug 25, 2023
19 changes: 10 additions & 9 deletions gasbenchmark10mil
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
No files changed, compilation skipped

Running 1 test for test/GasBenchmark.t.sol:GasBenchmark
[PASS] testGas(address,bytes32) (runs: 256, μ: 12620571, ~: 12621045)
Test result: ok. 1 passed; 0 failed; finished in 1.15s
[PASS] testGas(address,bytes32) (runs: 256, μ: 12884784, ~: 12885163)
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 239.15ms
| src/DelegateRegistry.sol:DelegateRegistry contract | | | | | |
|----------------------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1804898 | 9047 | | | | |
| 1848948 | 9267 | | | | |
| Function Name | min | avg | median | max | # calls |
| checkDelegateForAll | 2940 | 3151 | 3151 | 3363 | 2 |
| checkDelegateForContract | 5459 | 5897 | 5897 | 6336 | 2 |
| checkDelegateForERC1155 | 8003 | 8715 | 8715 | 9428 | 2 |
| checkDelegateForERC20 | 7942 | 8642 | 8642 | 9343 | 2 |
| checkDelegateForERC721 | 8006 | 8683 | 8683 | 9361 | 2 |
| delegateAll | 135846 | 135846 | 135846 | 135846 | 2 |
| delegateContract | 114436 | 125386 | 125386 | 136336 | 2 |
| delegateERC1155 | 159360 | 170310 | 170310 | 181260 | 2 |
| delegateERC20 | 136909 | 147859 | 147859 | 158809 | 2 |
| delegateERC721 | 136903 | 147853 | 147853 | 158803 | 2 |
| multicall | 689549 | 689549 | 689549 | 689549 | 1 |
| delegateAll | 135800 | 135800 | 135800 | 135800 | 2 |
| delegateContract | 114412 | 125362 | 125362 | 136312 | 2 |
| delegateERC1155 | 159336 | 170286 | 170286 | 181236 | 2 |
| delegateERC20 | 136885 | 147835 | 147835 | 158785 | 2 |
| delegateERC721 | 136879 | 147829 | 147829 | 158779 | 2 |
| multicall | 689383 | 689383 | 689383 | 689383 | 1 |



Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)
3 changes: 2 additions & 1 deletion hashbenchmark10mil
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ No files changed, compilation skipped

Running 1 test for test/HashBenchmark.t.sol:HashBenchmark
[PASS] testHashGas(address,bytes32,address,uint256,address,bytes32) (runs: 256, μ: 19906, ~: 19906)
Test result: ok. 1 passed; 0 failed; finished in 102.76ms
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 21.20ms
| test/HashBenchmark.t.sol:HashHarness contract | | | | | |
|-----------------------------------------------|-----------------|-----|--------|-----|---------|
| Deployment Cost | Deployment Size | | | | |
Expand All @@ -23,3 +23,4 @@ Test result: ok. 1 passed; 0 failed; finished in 102.76ms



Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)
31 changes: 23 additions & 8 deletions src/DelegateRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ contract DelegateRegistry is IDelegateRegistry {
*/

/// @inheritdoc IDelegateRegistry
function multicall(bytes[] calldata data) external override returns (bytes[] memory results) {
function multicall(bytes[] calldata data) external payable override returns (bytes[] memory results) {
results = new bytes[](data.length);
bool success;
unchecked {
for (uint256 i = 0; i < data.length; ++i) {
//slither-disable-next-line calls-loop
//slither-disable-next-line calls-loop,delegatecall-loop
(success, results[i]) = address(this).delegatecall(data[i]);
if (!success) revert MulticallFailed();
}
}
}

/// @inheritdoc IDelegateRegistry
function delegateAll(address to, bytes32 rights, bool enable) external override returns (bytes32 hash) {
function delegateAll(address to, bytes32 rights, bool enable) external payable override returns (bytes32 hash) {
hash = Hashes.allHash(msg.sender, rights, to);
bytes32 location = Hashes.location(hash);
if (_loadFrom(location) == DELEGATION_EMPTY) _pushDelegationHashes(msg.sender, to, hash);
Expand All @@ -60,7 +60,7 @@ contract DelegateRegistry is IDelegateRegistry {
}

/// @inheritdoc IDelegateRegistry
function delegateContract(address to, address contract_, bytes32 rights, bool enable) external override returns (bytes32 hash) {
function delegateContract(address to, address contract_, bytes32 rights, bool enable) external payable override returns (bytes32 hash) {
hash = Hashes.contractHash(msg.sender, rights, to, contract_);
bytes32 location = Hashes.location(hash);
if (_loadFrom(location) == DELEGATION_EMPTY) _pushDelegationHashes(msg.sender, to, hash);
Expand All @@ -75,7 +75,7 @@ contract DelegateRegistry is IDelegateRegistry {
}

/// @inheritdoc IDelegateRegistry
function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable) external override returns (bytes32 hash) {
function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable) external payable override returns (bytes32 hash) {
hash = Hashes.erc721Hash(msg.sender, rights, to, tokenId, contract_);
bytes32 location = Hashes.location(hash);
if (_loadFrom(location) == DELEGATION_EMPTY) _pushDelegationHashes(msg.sender, to, hash);
Expand All @@ -92,7 +92,7 @@ contract DelegateRegistry is IDelegateRegistry {
}

// @inheritdoc IDelegateRegistry
function delegateERC20(address to, address contract_, uint256 amount, bytes32 rights, bool enable) external override returns (bytes32 hash) {
function delegateERC20(address to, address contract_, uint256 amount, bytes32 rights, bool enable) external payable override returns (bytes32 hash) {
hash = Hashes.erc20Hash(msg.sender, rights, to, contract_);
bytes32 location = Hashes.location(hash);
if (_loadFrom(location) == DELEGATION_EMPTY) _pushDelegationHashes(msg.sender, to, hash);
Expand All @@ -109,7 +109,12 @@ contract DelegateRegistry is IDelegateRegistry {
}

/// @inheritdoc IDelegateRegistry
function delegateERC1155(address to, address contract_, uint256 tokenId, uint256 amount, bytes32 rights, bool enable) external override returns (bytes32 hash) {
function delegateERC1155(address to, address contract_, uint256 tokenId, uint256 amount, bytes32 rights, bool enable)
external
payable
override
returns (bytes32 hash)
{
hash = Hashes.erc1155Hash(msg.sender, rights, to, tokenId, contract_);
bytes32 location = Hashes.location(hash);
if (_loadFrom(location) == DELEGATION_EMPTY) _pushDelegationHashes(msg.sender, to, hash);
Expand All @@ -127,6 +132,16 @@ contract DelegateRegistry is IDelegateRegistry {
emit DelegateERC1155(msg.sender, to, contract_, tokenId, amount, rights, enable);
}

/// @dev Transfer native token out
function sweep() external {
// TODO: Replace this with correct address
// This hardcoded address is a CREATE2 factory counterfactual smart contract wallet that will always accept native token transfers
uint256 sc = uint256(uint160(0x0000000000000000000000000000000000000000));
assembly ("memory-safe") {
let result := call(gas(), sc, selfbalance(), 0, 0, 0, 0)
}
}

/**
* ----------- CHECKS -----------
*/
Expand All @@ -137,7 +152,7 @@ contract DelegateRegistry is IDelegateRegistry {
if (!Ops.or(rights == "", valid)) valid = _validateDelegation(Hashes.allLocation(from, rights, to), from);
assembly ("memory-safe") {
// Only first 32 bytes of scratch space is accessed
mstore(0, iszero(iszero(valid))) // Compiler cleans ditry booleans on the stack to 1, so we're doing the same here
mstore(0, iszero(iszero(valid))) // Compiler cleans dirty booleans on the stack to 1, so we're doing the same here
return(0, 32) // Direct return. Skips Solidity's redundant copying to save gas.
}
}
Expand Down
15 changes: 9 additions & 6 deletions src/IDelegateRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ interface IDelegateRegistry {
* @param data The encoded function data for each of the calls to make to this contract
* @return results The results from each of the calls passed in via data
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results);
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for all contracts
Expand All @@ -65,7 +65,7 @@ interface IDelegateRegistry {
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateAll(address to, bytes32 rights, bool enable) external returns (bytes32 delegationHash);
function delegateAll(address to, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific contract
Expand All @@ -75,7 +75,7 @@ interface IDelegateRegistry {
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateContract(address to, address contract_, bytes32 rights, bool enable) external returns (bytes32 delegationHash);
function delegateContract(address to, address contract_, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific ERC721 token
Expand All @@ -86,7 +86,7 @@ interface IDelegateRegistry {
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable) external returns (bytes32 delegationHash);
function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC20 tokens
Expand All @@ -98,7 +98,7 @@ interface IDelegateRegistry {
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC20(address to, address contract_, uint256 amount, bytes32 rights, bool enable) external returns (bytes32 delegationHash);
function delegateERC20(address to, address contract_, uint256 amount, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC1155 tokens
Expand All @@ -111,7 +111,10 @@ interface IDelegateRegistry {
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC1155(address to, address contract_, uint256 tokenId, uint256 amount, bytes32 rights, bool enable) external returns (bytes32 delegationHash);
function delegateERC1155(address to, address contract_, uint256 tokenId, uint256 amount, bytes32 rights, bool enable)
external
payable
returns (bytes32 delegationHash);

/**
* ----------- CHECKS -----------
Expand Down
56 changes: 56 additions & 0 deletions src/singlesig/Singlesig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @dev This does not include receiver callbacks for "safe" transfer methods of ERC721, ERC1155, etc
0xfoobar marked this conversation as resolved.
Show resolved Hide resolved
contract Singlesig {
address public owner;
address public pendingOwner;

event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

/// @dev Initializes the contract setting the address provided by the deployer as the initial owner
/// @dev Distinct constructor args will lead to distinct CREATE2 deployment bytecode, so no collision risk here
constructor(address initialOwner) {
owner = initialOwner;
emit OwnershipTransferred(address(0), initialOwner);
}

// @dev Function to receive Ether. msg.data must be empty
receive() external payable {}

// @dev Fallback function is called when msg.data is not empty
fallback() external payable {}

/// @dev Throws if called by any account other than the owner
modifier onlyOwner() {
require(owner == msg.sender, "Ownable2Step: caller is not the owner");
_;
}

/// @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one
function transferOwnership(address newOwner) external onlyOwner {
pendingOwner = newOwner;
emit OwnershipTransferStarted(owner, newOwner);
}

/// @dev The new owner accepts the ownership transfer
function acceptOwnership() external {
require(pendingOwner == msg.sender, "Ownable2Step: caller is not the new owner");
emit OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
}

/**
* @notice Executes a call with provided parameters
* @dev This method doesn't perform any sanity check of the transaction
* @param to Destination address
* @param value Ether value in wei
* @param data Data payload
* @return success Boolean flag indicating if the call succeeded
*/
function execute(address to, uint256 value, bytes memory data) public onlyOwner returns (bool success) {
(success,) = to.call{value: value}(data);
}
}
23 changes: 23 additions & 0 deletions test/DelegateRegistry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,27 @@ contract DelegateRegistryTest is Test {
bytes32[] memory vaultDelegationHashes = reg.getOutgoingDelegationHashes(address(this));
assertEq(vaultDelegationHashes.length, 5 * hashesLimit);
}

function testSweep(address to, address contract_, uint256 tokenId, uint256 amount, bytes32 rights_, bool enable) public {
address sc = address(0);
uint256 regBalanceBefore = address(reg).balance;
uint256 scBalanceBefore = address(sc).balance;
bytes[] memory data = new bytes[](1);
data[0] = abi.encodeWithSelector(reg.delegateAll.selector, to, rights_, enable);
reg.multicall{value: 0.2 ether}(data);
assertEq(regBalanceBefore + 0.2 ether, address(reg).balance);
reg.delegateAll{value: 0.2 ether}(to, rights_, enable);
assertEq(regBalanceBefore + 0.4 ether, address(reg).balance);
reg.delegateContract{value: 0.2 ether}(to, contract_, rights_, enable);
assertEq(regBalanceBefore + 0.6 ether, address(reg).balance);
reg.delegateERC721{value: 0.2 ether}(to, contract_, tokenId, rights_, enable);
assertEq(regBalanceBefore + 0.8 ether, address(reg).balance);
reg.delegateERC20{value: 0.1 ether}(to, contract_, amount, rights_, enable);
assertEq(regBalanceBefore + 0.9 ether, address(reg).balance);
reg.delegateERC1155{value: 0.1 ether}(to, contract_, tokenId, amount, rights_, enable);
assertEq(regBalanceBefore + 1 ether, address(reg).balance);
reg.sweep();
assertEq(address(reg).balance, 0);
assertEq(scBalanceBefore + 1 ether, sc.balance);
}
}
Loading