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

Sequential commit #569

Merged
merged 53 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
22fe29f
1st iteration
zajck Jan 25, 2023
3c69e06
buy order
zajck Feb 2, 2023
6678165
fulfilSellOrder
zajck Feb 2, 2023
a6d14f3
releaseFundsToIntermediateSellers
zajck Feb 3, 2023
b0909b4
Bump eslint-config-prettier from 8.5.0 to 8.6.0 (#547)
dependabot[bot] Jan 25, 2023
7626fae
Protocol Initalization Handler Facet documentation (#538)
zajck Jan 25, 2023
7c706af
chore: add forwarder address in deploy suite script
albertfolch-redeemeum Jan 25, 2023
df22455
chore: add forwarder address in .env.example
albertfolch-redeemeum Jan 26, 2023
fc9f878
Rename operator to assistant (#556)
zajck Feb 2, 2023
45705ba
PriceDIscovery domain test
zajck Feb 14, 2023
b87bd1b
sequential commits test, first batch
zajck Feb 15, 2023
47d40ad
revert reasons
zajck Feb 15, 2023
ea54644
Merge branch 'main' into sequential-commit
zajck Feb 15, 2023
b13ce9c
escrow amount
zajck Feb 16, 2023
6ad5f11
Return overpaid to buyer
zajck Feb 17, 2023
701720d
sell order
zajck Feb 17, 2023
3d9fb9f
releaseFunds - COMPLETED
zajck Feb 20, 2023
eba75f7
releaseFunds - REVOKED
zajck Feb 21, 2023
c113d2a
releaseFunds - CANCELED
zajck Feb 21, 2023
091154e
releaseFunds - RETRACTED
zajck Feb 21, 2023
31e16fa
releaseFunds - DISPUTED - RESOLVED
zajck Feb 21, 2023
8a71b3a
releaseFunds - ESCALATED
zajck Feb 21, 2023
c71f3b5
releaseFunds - REFUSED
zajck Feb 21, 2023
bbc3e19
changing fee + royalties
zajck Feb 21, 2023
c992cc9
Refactor
zajck Feb 21, 2023
628ed83
refactor into separate facet
zajck Feb 28, 2023
741b079
sequentialCommitToOffer tests
zajck Feb 28, 2023
5b61620
verify incoming voucher
zajck Feb 28, 2023
d967270
Fix failing tests
zajck Mar 1, 2023
3d3e14f
Protocol Diamond diagram
zajck Mar 2, 2023
f4bee26
review fixes
zajck Mar 3, 2023
c66b00f
additional tests
zajck Mar 3, 2023
1f8314d
remove RoyaltyRecipient + fix typos
zajck Mar 3, 2023
30344ab
Fix typo SequentialCommitHandler.sol
zajck Apr 6, 2023
8b051bf
Merge branch 'main' into sequential-commit-2
zajck Apr 21, 2023
86bcb9e
New chunks
zajck Aug 7, 2023
2518ffd
correctly toggle preprocessing
zajck Aug 7, 2023
babf4f0
Merge branch 'main' into sequential-commit-2
zajck Aug 14, 2023
40e7a1f
Merge branch 'main' into sequential-commit-2
zajck Sep 8, 2023
18097af
SequentialCommitHandlerFacet constructor arg in deployment
zajck Sep 8, 2023
bfefe83
fix unit tests
zajck Sep 11, 2023
c5ff0be
test isEligibleToCommit
zajck Oct 24, 2023
7ad553d
review fixes
zajck Nov 9, 2023
4685fbd
add events for encumbering/releasing funds
zajck Nov 9, 2023
69e249d
add tests for events for encumbering/releasing funds
zajck Nov 10, 2023
03d65bf
use safeerc20 for approval
zajck Nov 10, 2023
0ca1b01
extend test timeout
zajck Nov 10, 2023
7d36468
Merge branch 'main' into sequential-commit-2
zajck Nov 10, 2023
db883cc
Bump code coverage
zajck Nov 10, 2023
f6afca0
refactor + disable viaIR compilation
zajck Nov 10, 2023
e0d4660
Price discovery (#578)
anajuliabit Nov 20, 2023
e6c4621
Merge branch 'update-test-chunks' into sequential-commit-2
zajck Nov 20, 2023
bc18cc3
New chunks + bump line coverage
zajck Nov 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions contracts/domain/BosonConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ string constant EXCHANGE_IS_NOT_IN_A_FINAL_STATE = "Exchange is not in a final s
string constant EXCHANGE_ALREADY_EXISTS = "Exchange already exists";
string constant INVALID_RANGE_LENGTH = "Range length is too large or zero";

// Revert Reasons: Sequential commit related
string constant UNEXPECTED_ERC721_RECEIVED = "Unexpected ERC721 received";
string constant FEE_AMOUNT_TOO_HIGH = "Fee amount is too high";
string constant VOUCHER_NOT_RECEIVED = "Voucher not received";

// Revert Reasons: Twin related
uint256 constant SINGLE_TWIN_RESERVED_GAS = 160000;
uint256 constant MINIMAL_RESIDUAL_GAS = 230000;
Expand Down Expand Up @@ -157,6 +162,7 @@ string constant TOKEN_TRANSFER_FAILED = "Token transfer failed";
string constant INSUFFICIENT_VALUE_RECEIVED = "Insufficient value received";
string constant INSUFFICIENT_AVAILABLE_FUNDS = "Insufficient available funds";
string constant NATIVE_NOT_ALLOWED = "Transfer of native currency not allowed";
string constant ZERO_DEPOSIT_NOT_ALLOWED = "Zero deposit not allowed";

// Revert Reasons: Meta-Transactions related
string constant NONCE_USED_ALREADY = "Nonce used already";
Expand Down
19 changes: 19 additions & 0 deletions contracts/domain/BosonTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ contract BosonTypes {
ExchangeState state;
}

struct SequentialCommit {
uint256 resellerId;
uint256 price;
uint256 protocolFeeAmount;
uint256 royaltyAmount;
}

struct Voucher {
uint256 committedDate;
uint256 validUntilDate;
Expand Down Expand Up @@ -299,4 +306,16 @@ contract BosonTypes {
address collectionAddress;
string externalId;
}

struct PriceDiscovery {
uint256 price;
address priceDiscoveryContract;
bytes priceDiscoveryData;
Side side;
}

enum Side {
Ask,
Bid
}
}
2 changes: 1 addition & 1 deletion contracts/example/SnapshotGate/support/ERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
pragma solidity 0.8.21;

import "../../../interfaces/IERC721.sol";
import "./IERC721Receiver.sol";
import "../../../interfaces/IERC721Receiver.sol";
import "./IERC721Metadata.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
Expand Down
17 changes: 17 additions & 0 deletions contracts/interfaces/IWETH9Like.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/**
* @title IWETH9Like
*
* @notice Provides the minimum interface for native token wrapper
*/
interface IWETH9Like {
levalleux-ludo marked this conversation as resolved.
Show resolved Hide resolved
function withdraw(uint256) external;

function deposit() external payable;

function transfer(address, uint256) external returns (bool);

function transferFrom(address, address, uint256) external returns (bool);
}
2 changes: 2 additions & 0 deletions contracts/interfaces/handlers/IBosonFundsHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ interface IBosonFundsHandler is IBosonFundsEvents, IBosonFundsLibEvents {
*
* Reverts if:
* - The funds region of protocol is paused
* - Amount to deposit is zero
* - Seller id does not exist
* - It receives some native currency (e.g. ETH), but token address is not zero
* - It receives some native currency (e.g. ETH), and the amount does not match msg.value
* - It receives no native currency, but token address is zero
* - Contract at token address does not support ERC20 function transferFrom
* - Calling transferFrom on token fails for some reason (e.g. protocol is not approved to transfer)
* - Received ERC20 token amount differs from the expected value
Expand Down
55 changes: 55 additions & 0 deletions contracts/interfaces/handlers/IBosonSequentialCommitHandler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.21;
Dismissed Show dismissed Hide dismissed

import { BosonTypes } from "../../domain/BosonTypes.sol";
import { IBosonExchangeEvents } from "../events/IBosonExchangeEvents.sol";
import { IBosonFundsLibEvents } from "../events/IBosonFundsEvents.sol";
import { IERC721Receiver } from "../IERC721Receiver.sol";

/**
* @title ISequentialCommitHandler
*
* @notice Handles sequential commits.
*
* The ERC-165 identifier for this interface is: 0x1566334a
*/
interface IBosonSequentialCommitHandler is IBosonExchangeEvents, IBosonFundsLibEvents, IERC721Receiver {
/**
* @notice Commits to an existing exchange. Price discovery is oflaoaded to external contract.
albertfolch-redeemeum marked this conversation as resolved.
Show resolved Hide resolved
*
* Emits a BuyerCommitted event if successful.
* Transfers voucher to the buyer address.
*
* Reverts if:
* - The exchanges region of protocol is paused
* - The buyers region of protocol is paused
* - Buyer address is zero
* - Exchange does not exist
* - Exchange is not in Committed state
* - Voucher has expired
* - It is a bid order and:
* - Caller is not the voucher holder
* - Voucher owner did not approve protocol to transfer the voucher
* - Price received from price discovery is lower than the expected price
* - It is a ask order and:
* - Offer price is in native token and caller does not send enough
* - Offer price is in some ERC20 token and caller also sends native currency
* - Calling transferFrom on token fails for some reason (e.g. protocol is not approved to transfer)
* - Received ERC20 token amount differs from the expected value
* - Protocol does not receive the voucher
* - Transfer of voucher to the buyer fails for some reasong (e.g. buyer is contract that doesn't accept voucher)
* - Reseller did not approve protocol to transfer exchange token in escrow
* - Call to price discovery contract fails
* - Protocol fee and royalties combined exceed the secondary price
* - Transfer of exchange token fails
*
* @param _buyer - the buyer's address (caller can commit on behalf of a buyer)
* @param _exchangeId - the id of the exchange to commit to
* @param _priceDiscovery - the fully populated BosonTypes.PriceDiscovery struct
*/
function sequentialCommitToOffer(
address payable _buyer,
uint256 _exchangeId,
BosonTypes.PriceDiscovery calldata _priceDiscovery
) external payable;
}
153 changes: 153 additions & 0 deletions contracts/mock/PriceDiscovery.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (metatx/MockForwarder.sol)
pragma solidity 0.8.21;

import "../interfaces/IERC20.sol";
import "../interfaces/IERC721.sol";
import "./Foreign721.sol";

/**
* @dev Simple price discovery contract used in tests
*
* This contract simluates external price discovery mechanism.
albertfolch-redeemeum marked this conversation as resolved.
Show resolved Hide resolved
* When user commits to an offer, protocol talks to this contract to validate the exchange.
*/
contract PriceDiscovery {
struct Order {
address seller;
address buyer;
address voucherContract; // sold by seller
uint256 tokenId; // is exchange id
address exchangeToken;
uint256 price;
}

/**
* @dev simple fulfillOrder that does not perform any checks
* It just transfers the voucher and exchange token to the buyer
levalleux-ludo marked this conversation as resolved.
Show resolved Hide resolved
* If any of the transfers fail, the whole transaction will revert
*/
function fulfilBuyOrder(Order memory _order) public payable virtual {
// transfer voucher
try IERC721(_order.voucherContract).safeTransferFrom(_order.seller, msg.sender, _order.tokenId) {} catch (
bytes memory reason
) {
if (reason.length == 0) {
revert("Voucher transfer failed");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}

// transfer exchange token
if (_order.exchangeToken == address(0)) {
(bool success, ) = payable(_order.seller).call{ value: _order.price }("");
require(success, "Token transfer failed");

// return any extra ETH to the buyer
if (msg.value > _order.price) {
(success, ) = payable(msg.sender).call{ value: msg.value - _order.price }("");
require(success, "ETH return failed");
}
} else
try IERC20(_order.exchangeToken).transferFrom(msg.sender, _order.seller, _order.price) {} catch (
bytes memory reason
) {
if (reason.length == 0) {
revert("Token transfer failed");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}

function fulfilSellOrder(Order memory _order) public payable virtual {
// transfer voucher
try IERC721(_order.voucherContract).safeTransferFrom(msg.sender, _order.buyer, _order.tokenId) {} catch (
bytes memory reason
) {
if (reason.length == 0) {
revert("Voucher transfer failed");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}

// transfer exchange token
try IERC20(_order.exchangeToken).transferFrom(_order.buyer, msg.sender, _order.price) {} catch (
bytes memory reason
) {
if (reason.length == 0) {
revert("Token transfer failed");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}

/**
* @dev Simple bad price discovery contract used in tests
*
* This contract modifies the token id, simulates bad/malicious contract
*/
contract PriceDiscoveryModifyTokenId is PriceDiscovery {
/**
* @dev simple fulfillOrder that does not perform any checks
* Bump token id by 1
*/
function fulfilBuyOrder(Order memory _order) public payable override {
_order.tokenId++;
super.fulfilBuyOrder(_order);
}
}

/**
* @dev Simple bad price discovery contract used in tests
*
* This contract modifies the erc721 token, simulates bad/malicious contract
*/
contract PriceDiscoveryModifyVoucherContract is PriceDiscovery {
Foreign721 private erc721;

constructor(address _erc721) {
erc721 = Foreign721(_erc721);
}

/**
* @dev simple fulfillOrder that does not perform any checks
* Change order voucher address with custom erc721
* Mint tokenId on custom erc721
*/
function fulfilBuyOrder(Order memory _order) public payable override {
erc721.mint(_order.tokenId, 1);

_order.seller = address(this);
_order.voucherContract = address(erc721);
super.fulfilBuyOrder(_order);
}
}

/**
* @dev Simple bad price discovery contract used in tests
*
* This contract modifies simply does not transfer the voucher to the caller
albertfolch-redeemeum marked this conversation as resolved.
Show resolved Hide resolved
*/
contract PriceDiscoveryNoTransfer is PriceDiscovery {
/**
* @dev do nothing
*/
function fulfilBuyOrder(Order memory _order) public payable override {}
}
69 changes: 69 additions & 0 deletions contracts/mock/WETH9.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @title WETH
*
* @notice Mock WETH used for testing
* source: https://github.com/gnosis/canonical-weth/blob/master/contracts/WETH9.sol
*/

// solhint-disable-next-line compiler-version
pragma solidity >=0.4.22 <0.6;

contract WETH9 {
string public name = "Wrapped Ether";
string public symbol = "WETH";
uint8 public decimals = 18;

event Approval(address indexed src, address indexed guy, uint256 wad);
event Transfer(address indexed src, address indexed dst, uint256 wad);
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);

mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

function() external payable {
deposit();
}

function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}

function withdraw(uint256 wad) public {
require(balanceOf[msg.sender] >= wad);
balanceOf[msg.sender] -= wad;
msg.sender.transfer(wad);
emit Withdrawal(msg.sender, wad);
}

function totalSupply() public view returns (uint256) {
return address(this).balance;
}

function approve(address guy, uint256 wad) public returns (bool) {
allowance[msg.sender][guy] = wad;
emit Approval(msg.sender, guy, wad);
return true;
}

function transfer(address dst, uint256 wad) public returns (bool) {
return transferFrom(msg.sender, dst, wad);
}

function transferFrom(address src, address dst, uint256 wad) public returns (bool) {
require(balanceOf[src] >= wad);

if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) {
require(allowance[src][msg.sender] >= wad);
allowance[src][msg.sender] -= wad;
}

balanceOf[src] -= wad;
balanceOf[dst] += wad;

emit Transfer(src, dst, wad);

return true;
}
}
Loading