V2 Release

This commit is contained in:
PedroCailleret
2023-01-26 21:40:17 -03:00
parent 0d612572c6
commit 6fe1e794d0
45 changed files with 3195 additions and 2866 deletions

View File

@@ -23,18 +23,18 @@ interface EventAndErrors {
);
event LockAdded(
address indexed buyer,
bytes32 indexed lockID,
uint256 indexed lockID,
uint256 seller,
uint256 amount
);
event LockReleased(
address indexed buyer,
bytes32 lockId,
uint256 lockId,
uint256 amount
);
event LockReturned(
address indexed buyer,
bytes32 lockId
uint256 lockId
);
event FundsWithdrawn(
address owner,
@@ -108,8 +108,10 @@ interface EventAndErrors {
/// @dev 0xce3a3d37
error DecOverflow();
/// @dev 0x
/// @dev 0xf3fb0eb9
error MaxBalExceeded();
/// @dev 0x6a3bc53e
error EmptyPixTarget();
/// @dev 0x87138d5c
error NotInitialized();
}

View File

@@ -1,52 +0,0 @@
// SPDX-License-Identifier: MIT
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)
}
}
}

View File

@@ -9,7 +9,6 @@ pragma solidity 0.8.9;
///
import { Owned } from "./lib/auth/Owned.sol";
// import { Counters } from "./lib/utils/Counters.sol";
import { ERC20, SafeTransferLib } from "./lib/utils/SafeTransferLib.sol";
import { IReputation } from "./lib/interfaces/IReputation.sol";
import { MerkleProofLib as Merkle } from "./lib/utils/MerkleProofLib.sol";
@@ -24,22 +23,22 @@ contract P2PIX is
{
// solhint-disable use-forbidden-name
// solhint-disable no-inline-assembly
// solhint-disable no-empty-blocks
// using Counters for Counters.Counter;
using DT for DT.Lock;
/// ███ Constants ██████████████████████████████████████████████████████████
/// @dev The bitmask of `sellerBalance` entry.
uint256 constant private BITMASK_SB_ENTRY = (1 << 94) - 1;
/// @dev The bitmask of `sellerBalance` entry.
uint256 private constant BITMASK_SB_ENTRY = (1 << 94) - 1;
/// @dev The bit position of `pixTarget` in `sellerBalance`.
uint256 constant private BITPOS_PIXTARGET = 95;
uint256 private constant BITPOS_PIXTARGET = 95;
/// @dev The bit position of `valid` in `sellerBalance`.
uint256 constant private BITPOS_VALID = 255;
uint256 private constant BITPOS_VALID = 255;
/// @dev The bitmask of all 256 bits of `sellerBalance` except for the last one.
uint256 constant private BITMASK_VALID = (1 << 255) - 1;
uint256 private constant BITMASK_VALID = (1 << 255) - 1;
/// @dev The scalar of BRZ token.
uint256 constant public WAD = 1e18;
uint256 public constant WAD = 1e18;
/// ███ Storage ████████████████████████████████████████████████████████████
@@ -51,7 +50,7 @@ contract P2PIX is
uint256 public lockCounter;
/// @dev List of Locks.
mapping(bytes32 => DT.Lock) public mapLocks;
mapping(uint256 => DT.Lock) public mapLocks;
/// @dev Seller casted to key => Seller's allowlist merkleroot.
mapping(uint256 => bytes32) public sellerAllowList;
/// @dev Stores an relayer's last computed credit.
@@ -66,11 +65,12 @@ contract P2PIX is
// BITS LAYOUT
// `uint96` [0...94] := balance
// `uint160` [95...254] := pixTarget
// `bool` [255] := valid
// `bool` [255] := valid
/// @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))
public sellerBalance;
/// ███ Constructor ████████████████████████████████████████████████████████
@@ -105,17 +105,17 @@ contract P2PIX is
) public {
ERC20 t = ERC20(_token);
uint256 k = _castAddrToKey(msg.sender);
if(_pixTarget == 0)
if (_pixTarget == 0)
revert EmptyPixTarget();
if (!allowedERC20s[t])
revert TokenDenied();
uint256 _sellerBalance =
sellerBalance[k][t];
uint256 currBal =
_sellerBalance & BITMASK_SB_ENTRY;
if ((currBal + _amount) > 1e8 ether)
_sellerBalance & BITMASK_SB_ENTRY;
if ((currBal + _amount) > 1e8 ether)
revert MaxBalExceeded();
setReentrancyGuard();
@@ -123,17 +123,25 @@ contract P2PIX is
if (allowlistRoot != 0) {
setRoot(msg.sender, allowlistRoot);
}
uint256 amountCasted;
uint256 pixTargetCasted;
uint256 validCasted;
(amountCasted, pixTargetCasted, validCasted) =
_castToUint(_amount, _pixTarget, _valid);
sellerBalance[k][t] =
(currBal + amountCasted) |
(pixTargetCasted << BITPOS_PIXTARGET) |
(validCasted << BITPOS_VALID);
(
amountCasted,
pixTargetCasted,
validCasted
) = _castToUint(
_amount,
_pixTarget,
_valid
);
sellerBalance[k][t] =
(currBal + amountCasted) |
(pixTargetCasted << BITPOS_PIXTARGET) |
(validCasted << BITPOS_VALID);
SafeTransferLib.safeTransferFrom(
t,
msg.sender,
@@ -144,8 +152,8 @@ contract P2PIX is
clearReentrancyGuard();
emit DepositAdded(
msg.sender,
_token,
msg.sender,
_token,
_amount
);
}
@@ -155,23 +163,27 @@ contract P2PIX is
/// @dev This function does not affect any ongoing active locks.
/// @dev Function sighash: 0x72fada5c.
function setValidState(ERC20 token, bool state) public {
uint256 key = _castAddrToKey(msg.sender);
uint256 key =
_castAddrToKey(msg.sender);
uint256 _sellerBalance =
sellerBalance[key][token];
if (_sellerBalance != 0) {
uint256 _valid;
assembly {
_valid := state
}
_sellerBalance = (_sellerBalance & BITMASK_VALID) |
(_valid << BITPOS_VALID);
sellerBalance[key][token] = _sellerBalance;
assembly { _valid := state }
emit ValidSet(
msg.sender,
address(token),
state
);
_sellerBalance =
(_sellerBalance & BITMASK_VALID) |
(_valid << BITPOS_VALID);
sellerBalance[key][token] =
_sellerBalance;
emit ValidSet(
msg.sender,
address(token),
state
);
} else
revert NotInitialized();
}
@@ -203,39 +215,38 @@ contract P2PIX is
uint256 _relayerPremium,
uint256 _amount,
bytes32[] calldata merkleProof,
bytes32[] calldata expiredLocks
) public nonReentrant returns (bytes32 lockID) {
uint256[] calldata expiredLocks
) public nonReentrant returns (uint256) {
unlockExpired(expiredLocks);
ERC20 t = ERC20(_token);
if (!getValid(_seller, t))
revert InvalidDeposit();
uint256 bal = getBalance(_seller, t);
uint256 bal =
getBalance(_seller, t);
if (bal < _amount)
revert NotEnoughTokens();
uint256 k =
_castAddrToKey(_seller);
uint256 cachedCounter =
lockCounter + 1;
uint256 cCounter =
lockCounter + 1;
lockID = keccak256(
abi.encodePacked(
lockCounter,
_amount,
_buyerAddress
)
);
if (mapLocks[lockID].expirationBlock >= block.number)
revert NotExpired();
if (mapLocks[cCounter].expirationBlock
>= block.number)
revert NotExpired();
DT.Lock memory l = DT.Lock(
k,
cachedCounter,
cCounter,
_relayerPremium,
_amount,
(block.number + defaultLockBlocks),
uint160(sellerBalance[k][t] >> BITPOS_PIXTARGET),
(block.number +
defaultLockBlocks),
uint160(sellerBalance[k][t]
>> BITPOS_PIXTARGET),
_buyerAddress,
_relayerTarget,
msg.sender,
@@ -250,56 +261,62 @@ contract P2PIX is
);
_addLock(
bal,
_amount,
lockID,
bal,
_amount,
cCounter,
l,
t,
k);
k
);
lockCounter++;
// Halt execution and output `lockID`.
return lockID;
return cCounter;
} else {
if (l.amount <= 1e2 ether) {
_addLock(
bal,
_amount,
lockID,
l,
t,
k);
lockCounter++;
bal,
_amount,
cCounter,
l,
t,
k
);
// Halt execution and output `lockID`.
return lockID;
lockCounter++;
// Halt execution and output `lockID`.
return cCounter;
} else {
uint256 userCredit = userRecord[
_castAddrToKey(msg.sender)
];
uint256 spendLimit;
(spendLimit) = _limiter(userCredit / WAD);
(spendLimit) =
_limiter(userCredit / WAD);
if (l.amount > (spendLimit * WAD) ||
l.amount > 1e6 ether)
revert AmountNotAllowed();
if (
l.amount > (spendLimit * WAD) ||
l.amount > 1e6 ether
) revert AmountNotAllowed();
_addLock(
bal,
_amount,
lockID,
l,
t,
k);
lockCounter++;
_addLock(
bal,
_amount,
cCounter,
l,
t,
k
);
// Halt execution and output `lockID`.
return lockID;
lockCounter++;
// Halt execution and output `lockID`.
return cCounter;
}
}
}
@@ -318,7 +335,7 @@ contract P2PIX is
/// @param _relayerTarget Target address entitled to the `relayerPremim`.
/// @dev Function sighash: 0x4e1389ed.
function release(
bytes32 lockID,
uint256 lockID,
address _relayerTarget,
bytes32 pixTimestamp,
bytes32 r,
@@ -358,22 +375,25 @@ contract P2PIX is
ERC20 t = ERC20(l.token);
// We cache values before zeroing them out.
uint256 lockAmount = l.amount;
uint256 totalAmount = (lockAmount - l.relayerPremium);
uint256 lockAmount =
l.amount;
uint256 totalAmount =
(lockAmount - l.relayerPremium);
l.amount = 0;
l.expirationBlock = 0;
usedTransactions[message] = true;
if (msg.sender != l.relayerAddress) {
userRecord[_castAddrToKey(msg.sender)] += l
.relayerPremium;
userRecord[
_castAddrToKey(msg.sender)
] += l.relayerPremium;
userRecord[
_castAddrToKey(l.relayerAddress)
] += lockAmount;
} else {
userRecord[_castAddrToKey(msg.sender)] += (l
.relayerPremium + lockAmount);
userRecord[_castAddrToKey(msg.sender)]
+= (l.relayerPremium + lockAmount);
}
SafeTransferLib.safeTransfer(
@@ -404,7 +424,11 @@ contract P2PIX is
}
}
emit LockReleased(l.buyerAddress, lockID, lockAmount);
emit LockReleased(
l.buyerAddress,
lockID,
lockAmount
);
}
/// @notice Unlocks expired locks.
@@ -413,38 +437,49 @@ contract P2PIX is
/// @dev For each successfull unexpired lock recovered,
/// `userRecord[_castAddrToKey(l.relayerAddress)]` is decreased by half of its value.
/// @dev Function sighash: 0x8e2749d6.
function unlockExpired(
bytes32[] calldata lockIDs
) public {
function unlockExpired(uint256[] calldata lockIDs)
public
{
uint256 i;
uint256 locksSize = lockIDs.length;
for (i; i < locksSize; ) {
DT.Lock storage l = mapLocks[lockIDs[i]];
DT.Lock storage l =
mapLocks[lockIDs[i]];
_notExpired(l);
uint256 _sellerBalance =
sellerBalance[l.sellerKey][ERC20(l.token)] & BITMASK_SB_ENTRY;
if((_sellerBalance + l.amount) > 1e8 ether)
sellerBalance[
l.sellerKey][ERC20(l.token)
] & BITMASK_SB_ENTRY;
if (
(_sellerBalance + l.amount)
> 1e8 ether
)
revert MaxBalExceeded();
sellerBalance[l.sellerKey][ERC20(l.token)] += l.amount;
sellerBalance[
l.sellerKey][ERC20(l.token)
] += l.amount;
l.amount = 0;
uint256 userKey = _castAddrToKey(
l.relayerAddress
);
uint256 _newUserRecord = (userRecord[userKey] >>
1);
uint256 userKey =
_castAddrToKey(l.relayerAddress);
uint256 _newUserRecord =
(userRecord[userKey] >> 1);
if (_newUserRecord <= 1e2 ether) {
userRecord[userKey] = 1e2 ether;
} else {
userRecord[userKey] = _newUserRecord;
}
emit LockReturned(l.buyerAddress, lockIDs[i]);
emit LockReturned(
l.buyerAddress,
lockIDs[i]
);
unchecked {
++i;
@@ -466,11 +501,11 @@ contract P2PIX is
/// @dev Function sighash: 0x36317972.
function withdraw(
ERC20 token,
bytes32[] calldata expiredLocks
)
public
nonReentrant
returns(uint256 _sellerBalance)
uint256 amount,
uint256[] calldata expiredLocks
)
public
nonReentrant
{
unlockExpired(expiredLocks);
@@ -478,37 +513,35 @@ contract P2PIX is
== true
) {
setValidState(token, false);
}
}
uint256 key =
_castAddrToKey(msg.sender);
_decBal(
(sellerBalance[key][token]
& BITMASK_SB_ENTRY),
amount,
token,
key
);
sellerBalance[key][token] -=
(_sellerBalance =
sellerBalance[key][token]
& BITMASK_SB_ENTRY);
if(_sellerBalance == 0)
revert InvalidDeposit();
// safeTransfer tokens to seller
SafeTransferLib.safeTransfer(
token,
msg.sender,
amount
);
// safeTransfer tokens to seller
SafeTransferLib.safeTransfer(
token,
msg.sender,
_sellerBalance
);
emit DepositWithdrawn(
msg.sender,
address(token),
_sellerBalance
);
emit DepositWithdrawn(
msg.sender,
address(token),
amount
);
}
function setRoot(
address addr,
bytes32 merkleroot
) public {
function setRoot(address addr, bytes32 merkleroot)
public
{
if (addr == msg.sender) {
sellerAllowList[
_castAddrToKey(addr)
@@ -527,27 +560,30 @@ contract P2PIX is
emit FundsWithdrawn(msg.sender, balance);
}
function setReputation(
IReputation _reputation
) public onlyOwner {
function setReputation(IReputation _reputation)
public
onlyOwner
{
assembly {
sstore(reputation.slot, _reputation)
}
emit ReputationUpdated(address(_reputation));
}
function setDefaultLockBlocks(
uint256 _blocks
) public onlyOwner {
function setDefaultLockBlocks(uint256 _blocks)
public
onlyOwner
{
assembly {
sstore(defaultLockBlocks.slot, _blocks)
}
emit LockBlocksUpdated(_blocks);
}
function setValidSigners(
address[] memory _validSigners
) public onlyOwner {
function setValidSigners(address[] memory _validSigners)
public
onlyOwner
{
unchecked {
uint256 i;
uint256 len = _validSigners.length;
@@ -623,33 +659,14 @@ contract P2PIX is
function _addLock(
uint256 _bal,
uint256 _amount,
bytes32 _lockID,
uint256 _lockID,
DT.Lock memory _l,
ERC20 _t,
uint256 _k
) internal {
mapLocks[_lockID] = _l;
assembly {
if iszero(
iszero(
or(
iszero(_bal),
lt(
sub(
_bal,
_amount),
0x0
)))) {
// DecOverflow()
mstore(0x00, 0xce3a3d37)
revert(0x1c, 0x04)
}
}
// we can directly dec from packed uint entry value
sellerBalance[_k][_t] -= _amount;
_decBal(_bal, _amount, _t, _k);
emit LockAdded(
_l.buyerAddress,
@@ -657,61 +674,8 @@ contract P2PIX is
_l.sellerKey,
_l.amount
);
/// @todo
// assembly {
// // sstore(mp.slot, _l)
// mstore(0x00, _k)
// mstore(0x20, sellerBalance.slot)
// let sbkslot := keccak256(0x00, 0x40)
// mstore(0x00, _t)
// mstore(0x20, sbkslot)
// let sbslot := keccak256(0x00,0x40)
// let oldsb := sload(sbslot)
// sstore(
// sbslot,
// or(
// or(
// _newBal,
// shr(
// oldsb,
// BITPOS_PIXTARGET
// )),
// and(
// shr(
// oldsb,
// BITPOS_VALID
// ),
// BITMASK_SB_ENTRY
// )
// )
// )
// }
}
/// @notice Private view auxiliar logic that encodes/returns
/// the `bytes32` identifier of an lock.
/// @dev reverts on a not expired lock with the same ID passed
/// as argument of the function.
/// @dev Called exclusively by the `lock` method.
/// @dev Function sighash: 0x3fc5fb52.
// function _encodeLockID(
// uint256 _lockCounter,
// uint256 _amount,
// address _buyerAddress
// ) private view returns (bytes32 _lockID) {
// _lockID = keccak256(
// abi.encodePacked(
// _lockCounter,
// _amount,
// _buyerAddress
// )
// );
// if (mapLocks[_lockID].expirationBlock >= block.number)
// revert NotExpired();
// }
function merkleVerify(
bytes32[] calldata _merkleProof,
bytes32 root,
@@ -726,9 +690,11 @@ contract P2PIX is
) revert AddressDenied();
}
function _limiter(
uint256 _userCredit
) internal view returns (uint256 _spendLimit) {
function _limiter(uint256 _userCredit)
internal
view
returns (uint256 _spendLimit)
{
// enconde the fx sighash and args
bytes memory encodedParams = abi.encodeWithSelector(
IReputation.limiter.selector,
@@ -766,80 +732,164 @@ contract P2PIX is
}
function _castToUint(
uint96 _amount,
uint160 _pixTarget,
bool _valid)
private
pure
uint96 _amount,
uint160 _pixTarget,
bool _valid
)
private
pure
returns (
uint256 _amountCasted,
uint256 _pixTargetCasted,
uint256 _amountCasted,
uint256 _pixTargetCasted,
uint256 _validCasted
) {
assembly {
_amountCasted := _amount
_pixTargetCasted := _pixTarget
_validCasted := _valid
)
{
assembly {
_amountCasted := _amount
_pixTargetCasted := _pixTarget
_validCasted := _valid
}
}
function _decBal(
uint256 _bal,
uint256 _amount,
ERC20 _t,
uint256 _k
) private {
assembly {
if iszero(
iszero(
or(
iszero(_bal),
lt(sub(_bal, _amount), 0x0)
)
)
) {
// DecOverflow()
mstore(0x00, 0xce3a3d37)
revert(0x1c, 0x04)
}
}
function getBalance(
address seller,
ERC20 token
)
public
view
returns(uint256 bal)
{
bal =
sellerBalance[
_castAddrToKey(seller)
][token] & BITMASK_SB_ENTRY;
// we can directly dec from packed uint entry value
sellerBalance[_k][_t] -= _amount;
}
function getValid(
address seller,
ERC20 token
)
public
view
returns(bool valid)
function getBalance(address seller, ERC20 token)
public
view
returns (uint256 bal)
{
uint256 b =
(sellerBalance[
_castAddrToKey(seller)
][token] >> BITPOS_VALID) & BITMASK_SB_ENTRY;
assembly { valid := b }
// bal =
// sellerBalance[_castAddrToKey(seller)][token] &
// BITMASK_SB_ENTRY;
assembly {
for {/* */} iszero(0x0) {/* */} {
mstore(0x00, shl(0xC,seller))
mstore(0x20, sellerBalance.slot)
let sbkslot := keccak256(0x00, 0x40)
mstore(0x00, token)
mstore(0x20, sbkslot)
bal := and(
BITMASK_SB_ENTRY,
sload(keccak256(0x00,0x40)
)) break
}}
}
function getPixTarget(
address seller,
function getValid(address seller, ERC20 token)
public
view
returns (bool valid)
{
// uint256 b = sellerBalance[
// _castAddrToKey(seller)
// ][token];
// ] >> BITPOS_VALID) & BITMASK_SB_ENTRY;
assembly {
for {/* */} iszero(0x0) {/* */} {
mstore(0x00, shl(0xC,seller))
mstore(0x20, sellerBalance.slot)
let sbkslot := keccak256(0x00, 0x40)
mstore(0x00, token)
mstore(0x20, sbkslot)
valid := and(
BITMASK_SB_ENTRY,
shr(
BITPOS_VALID,
sload(keccak256(0x00,0x40)
))) break
}}
}
function getPixTarget(address seller, ERC20 token)
public
view
returns (uint160 pixTarget)
{
// pixTarget = uint160(
// sellerBalance[_castAddrToKey(seller)][token] >>
// BITPOS_PIXTARGET
// );
assembly {
for {/* */} iszero(0) {/* */} {
mstore(0,shl(12,seller))
mstore(32,sellerBalance.slot)
let sbkslot := keccak256(0,64)
mstore(0,token)
mstore(32,sbkslot)
pixTarget := shr(
BITPOS_PIXTARGET,
sload(keccak256(0,64)
)) break
}}
}
function getBalances(
address[] memory sellers,
ERC20 token
)
public
external
view
returns(uint160 pixTarget)
returns(uint256[] memory balances)
{
pixTarget =
uint160(
sellerBalance[
_castAddrToKey(seller)
][token] >> BITPOS_PIXTARGET
);
if(address(token) == address(0x0))
revert NoTokens();
if(sellers.length == 0)
revert LengthMismatch();
uint256 j;
uint256 len = sellers.length;
while (j < len) { balances[j] =
getBalance(sellers[j], token);
}
return balances;
}
/// @notice Public method that handles `address`
/// to `uint256` safe type casting.
/// @dev Function sighash: 0x4b2ae980.
function _castAddrToKey(
address _addr
) public pure returns (uint256 _key) {
_key = uint256(uint160(address(_addr))) << 12;
function _castAddrToKey(address _addr)
public
pure
returns (uint256 _key)
{
// _key = uint256(uint160(address(_addr))) << 12;
assembly {
_key := shl(12,_addr)
}
}
function _castKeyToAddr(
uint256 _key
) public pure returns (address _addr) {
_addr = address(uint160(uint256(_key >> 12)));
function _castKeyToAddr(uint256 _key)
public
pure
returns (address _addr)
{
// _addr = address(uint160(uint256(_key >> 12)));
assembly {
_addr := shr(12,_key)
}
}
}