perf: smart Contract Performance Improvement ⚡
This commit is contained in:
49
contracts/lib/auth/Owned.sol
Normal file
49
contracts/lib/auth/Owned.sol
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.4;
|
||||
|
||||
/// @notice Simple single owner authorization mixin.
|
||||
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Owned.sol)
|
||||
abstract contract Owned {
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
EVENTS
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
event OwnerUpdated(
|
||||
address indexed user,
|
||||
address indexed newOwner
|
||||
);
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
OWNERSHIP STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
address public owner;
|
||||
|
||||
modifier onlyOwner() virtual {
|
||||
require(msg.sender == owner, "UNAUTHORIZED");
|
||||
|
||||
_;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
CONSTRUCTOR
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
constructor(address _owner) {
|
||||
owner = _owner;
|
||||
|
||||
emit OwnerUpdated(address(0), _owner);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
OWNERSHIP LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function setOwner(
|
||||
address newOwner
|
||||
) public virtual onlyOwner {
|
||||
owner = newOwner;
|
||||
|
||||
emit OwnerUpdated(msg.sender, newOwner);
|
||||
}
|
||||
}
|
||||
10
contracts/lib/mock/mockToken.sol
Normal file
10
contracts/lib/mock/mockToken.sol
Normal file
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.9;
|
||||
|
||||
import { ERC20 } from "../tokens/ERC20.sol";
|
||||
|
||||
contract MockToken is ERC20 {
|
||||
constructor(uint256 supply) ERC20("MockBRL", "MBRL", 18) {
|
||||
_mint(msg.sender, supply);
|
||||
}
|
||||
}
|
||||
250
contracts/lib/tokens/ERC20.sol
Normal file
250
contracts/lib/tokens/ERC20.sol
Normal file
@@ -0,0 +1,250 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.4;
|
||||
|
||||
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
|
||||
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
|
||||
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
|
||||
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
|
||||
abstract contract ERC20 {
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
EVENTS
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
event Transfer(
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
event Approval(
|
||||
address indexed owner,
|
||||
address indexed spender,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
METADATA STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
string public name;
|
||||
|
||||
string public symbol;
|
||||
|
||||
uint8 public immutable decimals;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC20 STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
uint256 public totalSupply;
|
||||
|
||||
mapping(address => uint256) public balanceOf;
|
||||
|
||||
mapping(address => mapping(address => uint256))
|
||||
public allowance;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
EIP-2612 STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
uint256 internal immutable INITIAL_CHAIN_ID;
|
||||
|
||||
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
|
||||
|
||||
mapping(address => uint256) public nonces;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
CONSTRUCTOR
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
constructor(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
uint8 _decimals
|
||||
) {
|
||||
name = _name;
|
||||
symbol = _symbol;
|
||||
decimals = _decimals;
|
||||
|
||||
INITIAL_CHAIN_ID = block.chainid;
|
||||
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC20 LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function approve(
|
||||
address spender,
|
||||
uint256 amount
|
||||
) public virtual returns (bool) {
|
||||
allowance[msg.sender][spender] = amount;
|
||||
|
||||
emit Approval(msg.sender, spender, amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function transfer(
|
||||
address to,
|
||||
uint256 amount
|
||||
) public virtual returns (bool) {
|
||||
balanceOf[msg.sender] -= amount;
|
||||
|
||||
// Cannot overflow because the sum of all user
|
||||
// balances can't exceed the max uint256 value.
|
||||
unchecked {
|
||||
balanceOf[to] += amount;
|
||||
}
|
||||
|
||||
emit Transfer(msg.sender, to, amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function transferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) public virtual returns (bool) {
|
||||
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
|
||||
|
||||
if (allowed != type(uint256).max)
|
||||
allowance[from][msg.sender] = allowed - amount;
|
||||
|
||||
balanceOf[from] -= amount;
|
||||
|
||||
// Cannot overflow because the sum of all user
|
||||
// balances can't exceed the max uint256 value.
|
||||
unchecked {
|
||||
balanceOf[to] += amount;
|
||||
}
|
||||
|
||||
emit Transfer(from, to, amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
EIP-2612 LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function permit(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 value,
|
||||
uint256 deadline,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) public virtual {
|
||||
require(
|
||||
deadline >= block.timestamp,
|
||||
"PERMIT_DEADLINE_EXPIRED"
|
||||
);
|
||||
|
||||
// Unchecked because the only math done is incrementing
|
||||
// the owner's nonce which cannot realistically overflow.
|
||||
unchecked {
|
||||
address recoveredAddress = ecrecover(
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
DOMAIN_SEPARATOR(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
keccak256(
|
||||
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
|
||||
),
|
||||
owner,
|
||||
spender,
|
||||
value,
|
||||
nonces[owner]++,
|
||||
deadline
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
|
||||
require(
|
||||
recoveredAddress != address(0) &&
|
||||
recoveredAddress == owner,
|
||||
"INVALID_SIGNER"
|
||||
);
|
||||
|
||||
allowance[recoveredAddress][spender] = value;
|
||||
}
|
||||
|
||||
emit Approval(owner, spender, value);
|
||||
}
|
||||
|
||||
function DOMAIN_SEPARATOR()
|
||||
public
|
||||
view
|
||||
virtual
|
||||
returns (bytes32)
|
||||
{
|
||||
return
|
||||
block.chainid == INITIAL_CHAIN_ID
|
||||
? INITIAL_DOMAIN_SEPARATOR
|
||||
: computeDomainSeparator();
|
||||
}
|
||||
|
||||
function computeDomainSeparator()
|
||||
internal
|
||||
view
|
||||
virtual
|
||||
returns (bytes32)
|
||||
{
|
||||
return
|
||||
keccak256(
|
||||
abi.encode(
|
||||
keccak256(
|
||||
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
||||
),
|
||||
keccak256(bytes(name)),
|
||||
keccak256("1"),
|
||||
block.chainid,
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
INTERNAL MINT/BURN LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function _mint(
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal virtual {
|
||||
totalSupply += amount;
|
||||
|
||||
// Cannot overflow because the sum of all user
|
||||
// balances can't exceed the max uint256 value.
|
||||
unchecked {
|
||||
balanceOf[to] += amount;
|
||||
}
|
||||
|
||||
emit Transfer(address(0), to, amount);
|
||||
}
|
||||
|
||||
function _burn(
|
||||
address from,
|
||||
uint256 amount
|
||||
) internal virtual {
|
||||
balanceOf[from] -= amount;
|
||||
|
||||
// Cannot underflow because a user's balance
|
||||
// will never be larger than the total supply.
|
||||
unchecked {
|
||||
totalSupply -= amount;
|
||||
}
|
||||
|
||||
emit Transfer(from, address(0), amount);
|
||||
}
|
||||
}
|
||||
52
contracts/lib/utils/Counters.sol
Normal file
52
contracts/lib/utils/Counters.sol
Normal file
@@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
pragma solidity >=0.8.4;
|
||||
|
||||
/// @title Counters
|
||||
/// @author buf0t9
|
||||
/// @author Modified from OpenZeppelin Contracts
|
||||
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Counters.sol)
|
||||
/// @notice Provides counters that can only be incremented, decrementedor reset.
|
||||
/// @dev Include with `using Counters for Counters.Counter;`
|
||||
library Counters {
|
||||
// solhint-disable no-inline-assembly
|
||||
struct Counter {
|
||||
/// @dev Interactions must be restricted to the library's function.
|
||||
uint256 _val; // := 0
|
||||
}
|
||||
|
||||
/// @dev 0xce3a3d37
|
||||
error DecOverflow();
|
||||
|
||||
function current(
|
||||
Counter storage counter
|
||||
) internal view returns (uint256 _val) {
|
||||
assembly {
|
||||
_val := sload(counter.slot)
|
||||
}
|
||||
}
|
||||
|
||||
function increment(Counter storage counter) internal {
|
||||
assembly {
|
||||
let _val := sload(counter.slot)
|
||||
sstore(counter.slot, add(_val, 0x01))
|
||||
}
|
||||
}
|
||||
|
||||
function decrement(Counter storage counter) internal {
|
||||
assembly {
|
||||
let _val := sload(counter.slot)
|
||||
if or(iszero(_val), lt(_val, 0x00)) {
|
||||
mstore(0x00, 0xce3a3d37)
|
||||
revert(0x1c, 0x04)
|
||||
}
|
||||
sstore(counter.slot, sub(_val, 0x01))
|
||||
}
|
||||
}
|
||||
|
||||
function reset(Counter storage counter) internal {
|
||||
assembly {
|
||||
sstore(counter.slot, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
34
contracts/lib/utils/ReentrancyGuard.sol
Normal file
34
contracts/lib/utils/ReentrancyGuard.sol
Normal file
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.4;
|
||||
|
||||
/// @notice Reentrancy protection for smart contracts.
|
||||
/// @author z0r0z.eth
|
||||
/// @author Modified from Seaport
|
||||
/// (https://github.com/ProjectOpenSea/seaport/blob/main/contracts/lib/ReentrancyGuard.sol)
|
||||
/// @author Modified from Solmate
|
||||
/// (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
|
||||
abstract contract ReentrancyGuard {
|
||||
error Reentrancy();
|
||||
|
||||
uint256 private guard = 1;
|
||||
|
||||
modifier nonReentrant() virtual {
|
||||
setReentrancyGuard();
|
||||
|
||||
_;
|
||||
|
||||
clearReentrancyGuard();
|
||||
}
|
||||
|
||||
/// @dev Check guard sentinel value and set it.
|
||||
function setReentrancyGuard() internal virtual {
|
||||
if (guard == 2) revert Reentrancy();
|
||||
|
||||
guard = 2;
|
||||
}
|
||||
|
||||
/// @dev Unset sentinel value.
|
||||
function clearReentrancyGuard() internal virtual {
|
||||
guard = 1;
|
||||
}
|
||||
}
|
||||
159
contracts/lib/utils/SafeTransferLib.sol
Normal file
159
contracts/lib/utils/SafeTransferLib.sol
Normal file
@@ -0,0 +1,159 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.4;
|
||||
|
||||
import { ERC20 } from "../tokens/ERC20.sol";
|
||||
|
||||
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
|
||||
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
|
||||
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
|
||||
library SafeTransferLib {
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ETH OPERATIONS
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function safeTransferETH(
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
bool success;
|
||||
|
||||
assembly {
|
||||
// Transfer the ETH and store if it succeeded or not.
|
||||
success := call(gas(), to, amount, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
require(success, "ETH_TRANSFER_FAILED");
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC20 OPERATIONS
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function safeTransferFrom(
|
||||
ERC20 token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
bool success;
|
||||
|
||||
assembly {
|
||||
// We'll write our calldata to this slot below, but restore it later.
|
||||
let memPointer := mload(0x40)
|
||||
|
||||
// Write the abi-encoded calldata into memory, beginning with the function selector.
|
||||
mstore(
|
||||
0,
|
||||
0x23b872dd00000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
mstore(4, from) // Append the "from" argument.
|
||||
mstore(36, to) // Append the "to" argument.
|
||||
mstore(68, amount) // Append the "amount" argument.
|
||||
|
||||
success := and(
|
||||
// Set success to whether the call reverted, if not we check it either
|
||||
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
||||
or(
|
||||
and(
|
||||
eq(mload(0), 1),
|
||||
gt(returndatasize(), 31)
|
||||
),
|
||||
iszero(returndatasize())
|
||||
),
|
||||
// We use 100 because that's the total length of our calldata (4 + 32 * 3)
|
||||
// Counterintuitively, this call() must be positioned after the or() in the
|
||||
// surrounding and() because and() evaluates its arguments from right to left.
|
||||
call(gas(), token, 0, 0, 100, 0, 32)
|
||||
)
|
||||
|
||||
mstore(0x60, 0) // Restore the zero slot to zero.
|
||||
mstore(0x40, memPointer) // Restore the memPointer.
|
||||
}
|
||||
|
||||
require(success, "TRANSFER_FROM_FAILED");
|
||||
}
|
||||
|
||||
function safeTransfer(
|
||||
ERC20 token,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
bool success;
|
||||
|
||||
assembly {
|
||||
// We'll write our calldata to this slot below, but restore it later.
|
||||
let memPointer := mload(0x40)
|
||||
|
||||
// Write the abi-encoded calldata into memory, beginning with the function selector.
|
||||
mstore(
|
||||
0,
|
||||
0xa9059cbb00000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
mstore(4, to) // Append the "to" argument.
|
||||
mstore(36, amount) // Append the "amount" argument.
|
||||
|
||||
success := and(
|
||||
// Set success to whether the call reverted, if not we check it either
|
||||
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
||||
or(
|
||||
and(
|
||||
eq(mload(0), 1),
|
||||
gt(returndatasize(), 31)
|
||||
),
|
||||
iszero(returndatasize())
|
||||
),
|
||||
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
|
||||
// Counterintuitively, this call() must be positioned after the or() in the
|
||||
// surrounding and() because and() evaluates its arguments from right to left.
|
||||
call(gas(), token, 0, 0, 68, 0, 32)
|
||||
)
|
||||
|
||||
mstore(0x60, 0) // Restore the zero slot to zero.
|
||||
mstore(0x40, memPointer) // Restore the memPointer.
|
||||
}
|
||||
|
||||
require(success, "TRANSFER_FAILED");
|
||||
}
|
||||
|
||||
function safeApprove(
|
||||
ERC20 token,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
bool success;
|
||||
|
||||
assembly {
|
||||
// We'll write our calldata to this slot below, but restore it later.
|
||||
let memPointer := mload(0x40)
|
||||
|
||||
// Write the abi-encoded calldata into memory, beginning with the function selector.
|
||||
mstore(
|
||||
0,
|
||||
0x095ea7b300000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
mstore(4, to) // Append the "to" argument.
|
||||
mstore(36, amount) // Append the "amount" argument.
|
||||
|
||||
success := and(
|
||||
// Set success to whether the call reverted, if not we check it either
|
||||
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
||||
or(
|
||||
and(
|
||||
eq(mload(0), 1),
|
||||
gt(returndatasize(), 31)
|
||||
),
|
||||
iszero(returndatasize())
|
||||
),
|
||||
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
|
||||
// Counterintuitively, this call() must be positioned after the or() in the
|
||||
// surrounding and() because and() evaluates its arguments from right to left.
|
||||
call(gas(), token, 0, 0, 68, 0, 32)
|
||||
)
|
||||
|
||||
mstore(0x60, 0) // Restore the zero slot to zero.
|
||||
mstore(0x40, memPointer) // Restore the memPointer.
|
||||
}
|
||||
|
||||
require(success, "APPROVE_FAILED");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user