Added ERC2771 support & Increased pixTarget max size

This commit is contained in:
PedroCailleret
2023-05-21 02:05:43 -03:00
parent 2129566a27
commit 32469e2480
53 changed files with 1571 additions and 368 deletions

View File

@@ -12,7 +12,7 @@ contract Reputation is IReputation {
// prettier-ignore
// solhint-disable no-inline-assembly
// solhint-disable-next-line no-empty-blocks
constructor(/* */) {/* */}
constructor(/* */) payable {/* */}
function limiter(
uint256 _userCredit

View File

@@ -2,10 +2,15 @@
pragma solidity 0.8.19;
import { OwnerSettings } from "./OwnerSettings.sol";
import { ECDSA } from "../lib/utils/ECDSA.sol";
import { MerkleProofLib as Merkle } from "../lib/utils/MerkleProofLib.sol";
import { ReentrancyGuard } from "../lib/utils/ReentrancyGuard.sol";
abstract contract BaseUtils is OwnerSettings {
abstract contract BaseUtils is
OwnerSettings,
ReentrancyGuard
{
/// ███ Storage ████████████████████████████████████████████████████████████
/// @dev List of Pix transactions already signed.
@@ -67,26 +72,26 @@ abstract contract BaseUtils is OwnerSettings {
) revert AddressDenied();
}
function _castToUint(
uint96 _amount,
uint160 _pixTarget,
function _castBool(
bool _valid
)
internal
pure
returns (
uint256 _amountCasted,
uint256 _pixTargetCasted,
uint256 _validCasted
)
{
) internal pure returns (uint256 _validCasted) {
assembly {
_amountCasted := _amount
_pixTargetCasted := _pixTarget
_validCasted := _valid
}
}
function getStr(
string memory str
) public pure returns (bytes32 strEnc) {
bytes memory enc = bytes(abi.encodePacked(str));
assembly {
if lt(0x20, mload(enc)) {
invalid()
}
strEnc := mload(add(enc, 0x20))
}
}
/// @notice Public method that handles `address`
/// to `uint256` safe type casting.
/// @dev Function sighash: 0x4b2ae980.

View File

@@ -8,6 +8,8 @@ abstract contract Constants {
0x0b294da292f26e55fd442b5c0164fbb9013036ff00c5cfdde0efd01c1baaf632;
uint256 constant _ALLOWED_ERC20_UPDATED_EVENT_SIGNATURE =
0x5d6e86e5341d57a92c49934296c51542a25015c9b1782a1c2722a940131c3d9a;
uint256 constant _TRUSTED_FORWARDER_UPDATED_EVENT_SIGNATURE =
0xbee55516e29d3969d3cb8eb01351eb3c52d06f9e2435bd5a8bfe3647e185df92;
/// @dev Seller casted to key => Seller's allowlist merkleroot.
/// mapping(uint256 => bytes32) public sellerAllowList;
@@ -18,12 +20,12 @@ abstract contract Constants {
/// @dev `balance` max. value = 10**26.
/// @dev `pixTarget` keys are restricted to 160 bits.
/// mapping(uint256 => mapping(ERC20 => uint256)) public sellerBalance;
/// mapping(uint256 => mapping(ERC20 => { `uint256`, `uint96` } )) public sellerBalance;
/// @dev Bits layout:
/// `bytes32` [0...255] := pixTarget
/// `uint96` [0...94] := balance
/// `uint160` [95...254] := pixTarget
/// `bool` [255] := valid
/// `bool` [95] := valid
/// @dev Value in custom storage slot given by:
/// mstore(0x20, token)
@@ -34,12 +36,10 @@ abstract contract Constants {
/// @dev The bitmask of `sellerBalance` entry.
uint256 constant BITMASK_SB_ENTRY = (1 << 94) - 1;
/// @dev The bit position of `pixTarget` in `sellerBalance`.
uint256 constant BITPOS_PIXTARGET = 95;
/// @dev The bit position of `valid` in `sellerBalance`.
uint256 constant BITPOS_VALID = 255;
uint256 constant BITPOS_VALID = 95;
/// @dev The bitmask of all 256 bits of `sellerBalance` except for the last one.
uint256 constant BITMASK_VALID = (1 << 255) - 1;
// uint256 constant BITMASK_VALID = (1 << 255) - 1;
/// @dev The scalar of BRZ token.
uint256 constant WAD = 1e18;

View File

@@ -3,15 +3,13 @@ pragma solidity 0.8.19;
library DataTypes {
struct Lock {
uint80 amount;
uint160 pixTarget;
address token;
/// @dev Amount to be tranfered via PIX.
address buyerAddress;
uint256 sellerKey;
uint256 counter;
/// @dev If not paid at this block will be expired.
uint256 expirationBlock;
bytes32 pixTarget;
uint80 amount;
address token;
address buyerAddress;
}
// prettier-ignore

View File

@@ -57,6 +57,11 @@ interface EventAndErrors {
address indexed token,
bool indexed state
);
/// @dev 0xbee55516e29d3969d3cb8eb01351eb3c52d06f9e2435bd5a8bfe3647e185df92
event TrustedForwarderUpdated(
address indexed forwarder,
bool indexed state
);
/// @dev 0xe127cf589a3879da0156d4a24f43b44f65cfa3570de594806b0bfa2fcf06884f
event ReputationUpdated(address reputation);
/// @dev 0x70fa43ca70216ad905ade86b9e650a691b2ce5a01980d0a81bdd8324141b8511
@@ -64,6 +69,7 @@ interface EventAndErrors {
/// @dev 0x14a422d2412784a5749d03da98921fe468c98577b767851389a9f58ea5a363d7
event ValidSignersUpdated(address[] signers);
/// ███ Errors ████████████████████████████████████████████████████████████
/// @dev Only seller could call this function.

View File

@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import { ERC2771Context as ERC2771 } from "../lib/metatx/ERC2771Context.sol";
import { ERC20, SafeTransferLib } from "../lib/utils/SafeTransferLib.sol";
import { IReputation } from "../lib/interfaces/IReputation.sol";
import { EventAndErrors } from "./EventAndErrors.sol";
@@ -10,7 +11,8 @@ import { Owned } from "../lib/auth/Owned.sol";
abstract contract OwnerSettings is
Constants,
EventAndErrors,
Owned(msg.sender)
Owned(msg.sender),
ERC2771
{
/// ███ Storage ████████████████████████████████████████████████████████████
@@ -40,6 +42,48 @@ abstract contract OwnerSettings is
/// ███ Owner Only █████████████████████████████████████████████████████████
function setTrustedFowarders(
address[] memory forwarders,
bool[] memory states
) external onlyOwner {
assembly {
// first 32 bytes eq to array's length
let fLen := mload(forwarders)
// halts execution if forwarders.length eq 0
if iszero(fLen) {
invalid()
}
// revert with `LengthMismatch()`
if iszero(eq(fLen, mload(states))) {
mstore(0x00, 0xff633a38)
revert(0x1c, 0x04)
}
let fLoc := add(forwarders, 0x20)
let sLoc := add(states, 0x20)
for {
let end := add(fLoc, shl(5, fLen))
} iszero(eq(fLoc, end)) {
fLoc := add(fLoc, 0x20)
sLoc := add(sLoc, 0x20)
} {
// cache hashmap entry in scratch space
mstore(0x20, isTrustedForwarder.slot)
mstore(0x00, mload(fLoc))
// let mapSlot := keccak256(0x00, 0x40)
sstore(keccak256(0x00, 0x40), mload(sLoc))
// emit TrustedForwarderUpdated(address, bool)
log3(
0,
0,
_TRUSTED_FORWARDER_UPDATED_EVENT_SIGNATURE,
mload(fLoc),
mload(sLoc)
)
}
}
}
/// @dev Contract's underlying balance withdraw method.
/// @dev Function sighash: 0x5fd8c710.
function withdrawBalance() external onlyOwner {

View File

@@ -0,0 +1,94 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/// @author OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Context.sol)
/// @dev Provides information about the current execution context, including the
/// sender of the transaction and its data. While these are generally available
/// via msg.sender and msg.data, they should not be accessed in such a direct
/// manner, since when dealing with meta-transactions the account sending and
/// paying for execution may not be the actual sender (as far as an application
/// is concerned).
///
/// This contract is only required for intermediate, library-like contracts.
abstract contract Context {
function _msgSender()
internal
view
virtual
returns (address)
{
return msg.sender;
}
function _msgData()
internal
view
virtual
returns (bytes calldata)
{
return msg.data;
}
}
/// @author Modified from OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/metatx/ERC2771Context.sol
/// @dev Context variant with ERC2771 support.
abstract contract ERC2771Context is Context {
// address private immutable _trustedForwarder;
mapping(address => bool) public isTrustedForwarder;
/// @custom:oz-upgrades-unsafe-allow constructor
// constructor(address trustedForwarder) {
// _trustedForwarder = trustedForwarder;
// }
function _msgSender()
internal
view
virtual
override
returns (address sender)
{
if (isTrustedForwarder[msg.sender]) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
/// @solidity memory-safe-assembly
assembly {
sender := shr(
96,
calldataload(sub(calldatasize(), 20))
)
}
} else {
return super._msgSender();
}
}
function _isTrustedForwarder()
internal
view
returns (address _sender, uint256 _forwarder)
{
_sender = _msgSender();
_forwarder = (_sender != msg.sender)
? uint256(1)
: uint256(0);
}
function _msgData()
internal
view
virtual
override
returns (bytes calldata)
{
if (isTrustedForwarder[msg.sender]) {
return msg.data[:msg.data.length - 20];
} else {
return super._msgData();
}
}
}

View File

@@ -11,11 +11,11 @@ pragma solidity 0.8.19;
import { OwnerSettings, ERC20, SafeTransferLib } from "./core/OwnerSettings.sol";
import { BaseUtils } from "./core/BaseUtils.sol";
import { ReentrancyGuard } from "./lib/utils/ReentrancyGuard.sol";
import { DataTypes as DT } from "./core/DataTypes.sol";
contract P2PIX is BaseUtils, ReentrancyGuard {
contract P2PIX is BaseUtils {
// solhint-disable use-forbidden-name
// solhint-disable no-inline-assembly
// solhint-disable no-empty-blocks
@@ -62,19 +62,20 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
function deposit(
address _token,
uint96 _amount,
uint160 _pixTarget,
string memory _pixTarget,
bool _valid,
bytes32 allowlistRoot
) public {
ERC20 t = ERC20(_token);
uint256 k = _castAddrToKey(msg.sender);
if (_pixTarget == 0) revert EmptyPixTarget();
if (bytes(_pixTarget).length == 0) revert EmptyPixTarget();
if (!allowedERC20s(t)) revert TokenDenied();
uint256 _sellerBalance = sellerBalance(k,t);
uint256 _sellerBalance = __sellerBalance(k,t);
uint256 currBal = _sellerBalance & BITMASK_SB_ENTRY;
if ((currBal + _amount) > MAXBALANCE_UPPERBOUND)
uint256 _newBal = uint256(currBal + _amount);
if (_newBal > MAXBALANCE_UPPERBOUND)
revert MaxBalExceeded();
setReentrancyGuard();
@@ -83,21 +84,14 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
setRoot(msg.sender, allowlistRoot);
}
uint256 amountCasted;
uint256 pixTargetCasted;
uint256 validCasted;
(
amountCasted,
pixTargetCasted,
validCasted
) = _castToUint(_amount, _pixTarget, _valid);
bytes32 pixTargetCasted = getStr(_pixTarget);
uint256 validCasted = _castBool(_valid);
_setSellerBalance(
k,
t,
((currBal + amountCasted) |
(pixTargetCasted << BITPOS_PIXTARGET) |
(validCasted << BITPOS_VALID))
(_newBal | (validCasted << BITPOS_VALID)),
pixTargetCasted
);
SafeTransferLib.safeTransferFrom(
@@ -118,19 +112,16 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
/// @dev Function sighash: 0x72fada5c.
function setValidState(ERC20 token, bool state) public {
uint256 key = _castAddrToKey(msg.sender);
uint256 _sellerBalance = sellerBalance(key, token);
uint256 _sellerBalance = __sellerBalance(key, token);
if (_sellerBalance != 0) {
uint256 _valid;
assembly {
_valid := state
}
uint256 _valid = _castBool(state);
_sellerBalance =
(_sellerBalance & BITMASK_VALID) |
(_sellerBalance & BITMASK_SB_ENTRY) |
(_valid << BITPOS_VALID);
_setSellerBalance(key, token, _sellerBalance);
_setValidState(key, token, _sellerBalance);
emit ValidSet(msg.sender, address(token), state);
} else revert NotInitialized();
@@ -175,33 +166,43 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
mapLocks[cCounter].expirationBlock >= block.number
) revert NotExpired();
address sender; uint256 forwarder;
(sender, forwarder) = _isTrustedForwarder();
DT.Lock memory l = DT.Lock(
_amount,
uint160(sellerBalance(k, t) >> BITPOS_PIXTARGET),
address(t),
msg.sender,
k,
cCounter,
(block.number + defaultLockBlocks)
(block.number + defaultLockBlocks),
getPixTarget(_seller, t),
_amount,
address(t),
sender
);
// transaction forwarding must leave `merkleProof` empty;
// otherwise, the trustedForwarder must be previously added
// to a seller whitelist.
if (merkleProof.length != 0) {
_merkleVerify(merkleProof, sellerAllowList(k), msg.sender);
_merkleVerify(merkleProof, sellerAllowList(k), sender);
lockID = _addLock(bal, _amount, cCounter, l, t, k);
} else {
if (l.amount <= REPUTATION_LOWERBOUND) {
lockID = _addLock(bal, _amount, cCounter, l, t, k);
} else {
if (forwarder != 0) {
lockID = _addLock(bal, _amount, cCounter, l, t, k);
} else {
uint256 userCredit = userRecord[_castAddrToKey(msg.sender)];
uint256 spendLimit; (spendLimit) = _limiter(userCredit / WAD);
if (
l.amount > (spendLimit * WAD) || l.amount > LOCKAMOUNT_UPPERBOUND
l.amount > (spendLimit * WAD) ||
l.amount > LOCKAMOUNT_UPPERBOUND
) revert AmountNotAllowed();
lockID = _addLock(bal, _amount, cCounter, l, t, k);
/* */}/* */}
/* */}/* */}/* */}
}
/// @notice Lock release method that liquidate lock
@@ -241,20 +242,23 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
ERC20 t = ERC20(l.token);
// We cache values before zeroing them out.
// We cache lockAmount value before zeroing it out.
uint256 lockAmount = l.amount;
// uint256 totalAmount = (lockAmount - l.relayerPremium);
l.amount = 0;
l.expirationBlock = 0;
_setUsedTransactions(message);
address sender; uint256 forwarder;
(sender, forwarder) = _isTrustedForwarder();
if (forwarder == 0) {
if (msg.sender != l.buyerAddress) {
userRecord[_castAddrToKey(msg.sender)] += (lockAmount >> 1);
userRecord[_castAddrToKey(l.buyerAddress)] += (lockAmount >> 1);
} else {
userRecord[_castAddrToKey(msg.sender)] += lockAmount;
}
}}
SafeTransferLib.safeTransfer(
t,
@@ -283,7 +287,7 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
_notExpired(l);
uint256 _sellerBalance =
sellerBalance(l.sellerKey, ERC20(l.token)) & BITMASK_SB_ENTRY;
__sellerBalance(l.sellerKey, ERC20(l.token)) & BITMASK_SB_ENTRY;
if ((_sellerBalance + l.amount) > MAXBALANCE_UPPERBOUND)
revert MaxBalExceeded();
@@ -335,7 +339,7 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
uint256 key = _castAddrToKey(msg.sender);
_decBal(
(sellerBalance(key, token) & BITMASK_SB_ENTRY),
(__sellerBalance(key, token) & BITMASK_SB_ENTRY),
amount,
token,
key
@@ -407,7 +411,7 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
mapLocks[_lockID] = _l;
_decBal(_bal, _amount, _t, _k);
lockCounter++;
++lockCounter;
counter = _lockID;
emit LockAdded(
@@ -448,9 +452,6 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
view
returns (uint256 bal)
{
// bal =
// sellerBalance[_castAddrToKey(seller)][token] &
// BITMASK_SB_ENTRY;
assembly {
for {
/* */
@@ -462,7 +463,7 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
mstore(0x00, seller)
bal := and(
BITMASK_SB_ENTRY,
sload(keccak256(0x0c, 0x34))
sload(add(keccak256(0x0c, 0x34), 0x01))
)
break
}
@@ -474,10 +475,6 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
view
returns (bool valid)
{
// uint256 b = sellerBalance[
// _castAddrToKey(seller)
// ][token];
// ] >> BITPOS_VALID) & BITMASK_SB_ENTRY;
assembly {
for {
/* */
@@ -491,7 +488,7 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
BITMASK_SB_ENTRY,
shr(
BITPOS_VALID,
sload(keccak256(0x0c, 0x34))
sload(add(keccak256(0x0c, 0x34), 0x01))
)
)
break
@@ -502,12 +499,8 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
function getPixTarget(address seller, ERC20 token)
public
view
returns (uint160 pixTarget)
returns (bytes32 pixTarget)
{
// pixTarget = uint160(
// sellerBalance[_castAddrToKey(seller)][token] >>
// BITPOS_PIXTARGET
// );
assembly {
for {
/* */
@@ -517,15 +510,17 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
mstore(0x20, token)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, seller)
pixTarget := shr(
BITPOS_PIXTARGET,
sload(keccak256(0x0c, 0x34))
)
pixTarget := sload(keccak256(0x0c, 0x34))
break
}
}
}
function getPixTargetString(address seller, ERC20 token) public view returns (string memory pixTarget) {
bytes32 _pixEnc = getPixTarget(seller, token);
pixTarget = string(abi.encodePacked(_pixEnc));
}
function getBalances(
address[] memory sellers,
ERC20 token
@@ -593,42 +588,53 @@ contract P2PIX is BaseUtils, ReentrancyGuard {
return (sortedIDs, status);
}
function _setSellerBalance(uint256 sellerKey, ERC20 erc20, uint256 packed) private {
function _setSellerBalance(uint256 _sellerKey, ERC20 _erc20, uint256 _packed, bytes32 _pixTarget) private {
assembly {
mstore(0x20, erc20)
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, shr(12, sellerKey))
sstore(keccak256(0x0c, 0x34), packed)
mstore(0x00, shr(12, _sellerKey))
let _loc := keccak256(0x0c, 0x34)
sstore(add(_loc, 0x01), _packed)
sstore(_loc, _pixTarget)
}
}
function _addSellerBalance(uint256 sellerKey, ERC20 erc20, uint256 amount) private {
function _setValidState(uint256 _sellerKey, ERC20 _erc20, uint256 _packed) private {
assembly {
mstore(0x20, erc20)
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, shr(12, sellerKey))
let slot := keccak256(0x0c, 0x34)
sstore(slot, add(sload(slot), amount))
mstore(0x00, shr(12, _sellerKey))
let _loc := keccak256(0x0c, 0x34)
sstore(add(_loc, 0x01), _packed)
}
}
function _decSellerBalance(uint256 sellerKey, ERC20 erc20, uint256 amount) private {
function _addSellerBalance(uint256 _sellerKey, ERC20 _erc20, uint256 _amount) private {
assembly {
mstore(0x20, erc20)
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, shr(12, sellerKey))
let slot := keccak256(0x0c, 0x34)
sstore(slot, sub(sload(slot), amount))
mstore(0x00, shr(12, _sellerKey))
let _loc := add(keccak256(0x0c, 0x34), 0x01)
sstore(_loc, add(sload(_loc), _amount))
}
}
function sellerBalance(uint256 sellerKey, ERC20 erc20) public view returns(uint256 packed) {
function _decSellerBalance(uint256 _sellerKey, ERC20 _erc20, uint256 _amount) private {
assembly {
mstore(0x20, erc20)
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, shr(12, sellerKey))
packed := sload(keccak256(0x0c, 0x34))
mstore(0x00, shr(12, _sellerKey))
let _loc := add(keccak256(0x0c, 0x34), 0x01)
sstore(_loc, sub(sload(_loc), _amount))
}
}
function __sellerBalance(uint256 _sellerKey, ERC20 _erc20) private view returns(uint256 _packed) {
assembly {
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, shr(12, _sellerKey))
_packed := sload(add(keccak256(0x0c, 0x34), 0x01))
}
}