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

feat: add ITS utils #84

Merged
merged 15 commits into from
Oct 19, 2023
Merged
9 changes: 9 additions & 0 deletions contracts/interfaces/IImplementation.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IContractIdentifier } from './IContractIdentifier.sol';

interface IImplementation is IContractIdentifier {
error NotProxy();
}
21 changes: 21 additions & 0 deletions contracts/interfaces/IMulticall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
* @title IMulticall
* @notice This contract is a multi-functional smart contract which allows for multiple
* contract calls in a single transaction.
*/
interface IMulticall {
error MulticallFailed();

/**
* @notice Performs multiple delegate calls and returns the results of all calls as an array
* @dev This function requires that the contract has sufficient balance for the delegate calls.
* If any of the calls fail, the function will revert with the failure message.
* @param data An array of encoded function calls
* @return results An bytes array with the return data of each function call
*/
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}
22 changes: 22 additions & 0 deletions contracts/interfaces/IPausable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
* @title Pausable
* @notice This contract provides a mechanism to halt the execution of specific functions
* if a pause condition is activated.
*/
interface IPausable {
event Paused(address indexed account);
event Unpaused(address indexed account);

error Pause();
error NotPaused();

/**
* @notice Check if the contract is paused
* @return paused A boolean representing the pause status. True if paused, false otherwise.
*/
function paused() external view returns (bool);
}
12 changes: 12 additions & 0 deletions contracts/interfaces/IReentrancyGuard.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
* @title ReentrancyGuard
* @notice This contract provides a mechanism to halt the execution of specific functions
* if a pause condition is activated.
*/
interface IReentrancyGuard {
error ReentrantCall();
}
7 changes: 2 additions & 5 deletions contracts/interfaces/IUpgradable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
pragma solidity ^0.8.0;

import { IOwnable } from './IOwnable.sol';
import { IContractIdentifier } from './IContractIdentifier.sol';
import { IImplementation } from './IImplementation.sol';

// General interface for upgradable contracts
interface IUpgradable is IOwnable, IContractIdentifier {
interface IUpgradable is IOwnable, IImplementation {
error InvalidCodeHash();
error InvalidImplementation();
error SetupFailed();
error NotProxy();

event Upgraded(address indexed newImplementation);

Expand All @@ -21,6 +20,4 @@ interface IUpgradable is IOwnable, IContractIdentifier {
bytes32 newImplementationCodeHash,
bytes calldata params
) external;

function setup(bytes calldata data) external;
}
38 changes: 38 additions & 0 deletions contracts/libs/AddressBytes.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
* @title AddressBytesUtils
* @dev This library provides utility functions to convert between `address` and `bytes`.
*/
library AddressBytes {
error InvalidBytesLength(bytes bytesAddress);

/**
* @dev Converts a bytes address to an address type.
* @param bytesAddress The bytes representation of an address
* @return addr The converted address
*/
function toAddress(bytes memory bytesAddress) internal pure returns (address addr) {
if (bytesAddress.length != 20) revert InvalidBytesLength(bytesAddress);

assembly {
addr := mload(add(bytesAddress, 20))
}
}

/**
* @dev Converts an address to bytes.
* @param addr The address to be converted
* @return bytesAddress The bytes representation of the address
*/
function toBytes(address addr) internal pure returns (bytes memory bytesAddress) {
bytesAddress = new bytes(20);
// we can test if using a single 32 byte variable that is the address with the length together and using one mstore would be slightly cheaper.
assembly {
mstore(add(bytesAddress, 20), addr)
mstore(bytesAddress, 20)
}
}
}
18 changes: 18 additions & 0 deletions contracts/test/libs/TestAddressBytes.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { AddressBytes } from '../../libs/AddressBytes.sol';

contract TestAddressBytes {
using AddressBytes for address;
using AddressBytes for bytes;

function toAddress(bytes memory bytesAddress) external pure returns (address addr) {
return bytesAddress.toAddress();
}

function toBytes(address addr) external pure returns (bytes memory bytesAddress) {
return addr.toBytes();
}
}
17 changes: 17 additions & 0 deletions contracts/test/upgradable/TestImplementation.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Implementation } from '../../upgradable/Implementation.sol';

contract TestImplementation is Implementation {
uint256 public val;

function setup(bytes calldata params) external override onlyProxy {
val = abi.decode(params, (uint256));
}

function contractId() external pure override returns (bytes32) {
return keccak256('TestImplementation');
}
}
32 changes: 32 additions & 0 deletions contracts/test/utils/TestMulticall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Multicall } from '../../utils/Multicall.sol';

contract TestMulticall is Multicall {
uint256 public nonce;
bytes[] public lastMulticallReturns;
event Function1Called(uint256 nonce_);
event Function2Called(uint256 nonce_);

function function1() external returns (uint256) {
uint256 nonce_ = nonce++;
emit Function1Called(nonce_);
return nonce_;
}

function function2() external returns (uint256) {
uint256 nonce_ = nonce++;
emit Function2Called(nonce_);
return nonce_;
}

function multicallTest(bytes[] calldata data) external {
lastMulticallReturns = multicall(data);
}

function getLastMulticallReturns() external view returns (bytes[] memory r) {
return lastMulticallReturns;
}
}
21 changes: 21 additions & 0 deletions contracts/test/utils/TestPausable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Pausable } from '../../utils/Pausable.sol';

contract TestPausable is Pausable {
event TestEvent();

function pause() external {
_pause();
}

function unpause() external {
_unpause();
}

function testPaused() external whenNotPaused {
emit TestEvent();
}
}
21 changes: 21 additions & 0 deletions contracts/test/utils/TestReentrancyGuard.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { ReentrancyGuard } from '../../utils/ReentrancyGuard.sol';

contract TestReentrancyGuard is ReentrancyGuard {
uint256 public value;

constructor() {
require(ENTERED_SLOT == uint256(keccak256('ReentrancyGuard:entered')) - 1, 'invalid constant');
}

function testFunction() external noReEntrancy {
value = 1;
this.callback();
value = 2;
}

function callback() external noReEntrancy {}
}
38 changes: 38 additions & 0 deletions contracts/upgradable/Implementation.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IImplementation } from '../interfaces/IImplementation.sol';

/**
* @title Implementation
* @notice This contract serves as a base for other contracts and enforces a proxy-first access restriction.
* @dev Derived contracts must implement the setup function.
*/
abstract contract Implementation is IImplementation {
address private immutable implementationAddress;

/**
* @dev Contract constructor that sets the implementation address to the address of this contract.
*/
constructor() {
implementationAddress = address(this);
}

/**
* @dev Modifier to require the caller to be the proxy contract.
* Reverts if the caller is the current contract (i.e., the implementation contract itself).
*/
modifier onlyProxy() {
if (implementationAddress == address(this)) revert NotProxy();
_;
}

/**
* @notice Initializes contract parameters.
* This function is intended to be overridden by derived contracts.
* The overriding function must have the onlyProxy modifier.
* @param params The parameters to be used for initialization
*/
function setup(bytes calldata params) external virtual;
}
17 changes: 3 additions & 14 deletions contracts/upgradable/Upgradable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ pragma solidity ^0.8.0;

import { IUpgradable } from '../interfaces/IUpgradable.sol';
import { Ownable } from '../utils/Ownable.sol';
import { Implementation } from './Implementation.sol';

/**
* @title Upgradable Contract
* @notice This contract provides an interface for upgradable smart contracts and includes the functionality to perform upgrades.
*/
abstract contract Upgradable is Ownable, IUpgradable {
abstract contract Upgradable is Ownable, Implementation, IUpgradable {
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
address internal immutable implementationAddress;

/**
* @notice Constructor sets the implementation address to the address of the contract itself
Expand All @@ -21,18 +21,7 @@ abstract contract Upgradable is Ownable, IUpgradable {
* @dev The owner is initially set as address(1) because the actual owner is set within the proxy. It is not
* set as the zero address because Ownable is designed to throw an error for ownership transfers to the zero address.
*/
constructor() Ownable(address(1)) {
implementationAddress = address(this);
}

/**
* @notice Modifier to ensure that a function can only be called by the proxy
*/
modifier onlyProxy() {
// Prevent setup from being called on the implementation
if (address(this) == implementationAddress) revert NotProxy();
_;
}
constructor() Ownable(address(1)) {}

/**
* @notice Returns the address of the current implementation
Expand Down
38 changes: 38 additions & 0 deletions contracts/utils/Multicall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IMulticall } from '../interfaces/IMulticall.sol';

/**
* @title Multicall
* @notice This contract is a multi-functional smart contract which allows for multiple
* contract calls in a single transaction.
*/
contract Multicall is IMulticall {
/**
* @notice Performs multiple delegate calls and returns the results of all calls as an array
* @dev This function requires that the contract has sufficient balance for the delegate calls.
* If any of the calls fail, the function will revert with the failure message.
* @param data An array of encoded function calls
* @return results An bytes array with the return data of each function call
*/
function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {
results = new bytes[](data.length);
bool success;
bytes memory result;
for (uint256 i = 0; i < data.length; ++i) {
// slither-disable-next-line calls-loop,delegatecall-loop
(success, result) = address(this).delegatecall(data[i]);

if (!success) {
if (result.length == 0) revert MulticallFailed();
assembly {
revert(add(32, result), mload(result))
}
}

results[i] = result;
}
}
}
Loading
Loading