This repository has been archived by the owner on Dec 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 70
[feat] Collection Offers v1.0 #125
Open
kulkarohan
wants to merge
14
commits into
main
Choose a base branch
from
collection-offers
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
469faf1
[feat] Collection Offers v1.0
f943878
[feat] update for community review
78ce19a
Merge branch 'main' into collection-offers
e3cb5a3
snapshot
joshieDo b5bd260
no need for uint32 on createOffers
joshieDo ddb8b45
use uint32 only for storage
joshieDo 65e8d93
_offerCount to save sloads
joshieDo bd2daaf
Merge branch 'main' into collection-offers
f4a66d3
Merge pull request #143 from joshieDo/changes
kulkarohan 7c3d955
Merge branch 'collection-offers' of https://github.com/ourzora/v3 int…
1cfeb54
[fix] add gas limit
ebbebd4
[fix] events param id -> offerId
4e6b08a
test: add assertions when protocol fee is non-zero
almndbtr 04f674b
Merge pull request #144 from almndbtr/collection-offers-nonzero-proto…
kulkarohan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
430 changes: 430 additions & 0 deletions
430
contracts/modules/CollectionOffers/V1/CollectionOfferBookV1.sol
Large diffs are not rendered by default.
Oops, something went wrong.
401 changes: 401 additions & 0 deletions
401
contracts/modules/CollectionOffers/V1/CollectionOffersV1.sol
Large diffs are not rendered by default.
Oops, something went wrong.
168 changes: 168 additions & 0 deletions
168
contracts/test/modules/CollectionOffers/V1/CollectionOffers.integration.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity 0.8.10; | ||
|
||
import {DSTest} from "ds-test/test.sol"; | ||
|
||
import {CollectionOffersV1} from "../../../../modules/CollectionOffers/V1/CollectionOffersV1.sol"; | ||
import {Zorb} from "../../../utils/users/Zorb.sol"; | ||
import {ZoraRegistrar} from "../../../utils/users/ZoraRegistrar.sol"; | ||
import {ZoraModuleManager} from "../../../../ZoraModuleManager.sol"; | ||
import {ZoraProtocolFeeSettings} from "../../../../auxiliary/ZoraProtocolFeeSettings/ZoraProtocolFeeSettings.sol"; | ||
import {ERC20TransferHelper} from "../../../../transferHelpers/ERC20TransferHelper.sol"; | ||
import {ERC721TransferHelper} from "../../../../transferHelpers/ERC721TransferHelper.sol"; | ||
import {RoyaltyEngine} from "../../../utils/modules/RoyaltyEngine.sol"; | ||
|
||
import {TestERC721} from "../../../utils/tokens/TestERC721.sol"; | ||
import {WETH} from "../../../utils/tokens/WETH.sol"; | ||
import {VM} from "../../../utils/VM.sol"; | ||
|
||
/// @title CollectionOffersV1IntegrationTest | ||
/// @notice Integration Tests for CollectionOffersV1 | ||
contract CollectionOffersV1IntegrationTest is DSTest { | ||
VM internal vm; | ||
|
||
ZoraRegistrar internal registrar; | ||
ZoraProtocolFeeSettings internal ZPFS; | ||
ZoraModuleManager internal ZMM; | ||
ERC20TransferHelper internal erc20TransferHelper; | ||
ERC721TransferHelper internal erc721TransferHelper; | ||
RoyaltyEngine internal royaltyEngine; | ||
|
||
CollectionOffersV1 internal offers; | ||
TestERC721 internal token; | ||
WETH internal weth; | ||
|
||
Zorb internal seller; | ||
Zorb internal buyer; | ||
Zorb internal finder; | ||
Zorb internal royaltyRecipient; | ||
|
||
function setUp() public { | ||
// Cheatcodes | ||
vm = VM(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); | ||
|
||
// Deploy V3 | ||
registrar = new ZoraRegistrar(); | ||
ZPFS = new ZoraProtocolFeeSettings(); | ||
ZMM = new ZoraModuleManager(address(registrar), address(ZPFS)); | ||
erc20TransferHelper = new ERC20TransferHelper(address(ZMM)); | ||
erc721TransferHelper = new ERC721TransferHelper(address(ZMM)); | ||
|
||
// Init V3 | ||
registrar.init(ZMM); | ||
ZPFS.init(address(ZMM), address(0)); | ||
|
||
// Create users | ||
seller = new Zorb(address(ZMM)); | ||
buyer = new Zorb(address(ZMM)); | ||
finder = new Zorb(address(ZMM)); | ||
royaltyRecipient = new Zorb(address(ZMM)); | ||
|
||
// Deploy mocks | ||
royaltyEngine = new RoyaltyEngine(address(royaltyRecipient)); | ||
token = new TestERC721(); | ||
weth = new WETH(); | ||
|
||
// Deploy Collection Offers v1.0 | ||
offers = new CollectionOffersV1( | ||
address(erc20TransferHelper), | ||
address(erc721TransferHelper), | ||
address(royaltyEngine), | ||
address(ZPFS), | ||
address(weth) | ||
); | ||
registrar.registerModule(address(offers)); | ||
|
||
// Set seller balance | ||
vm.deal(address(seller), 100 ether); | ||
|
||
// Mint buyer token | ||
token.mint(address(buyer), 0); | ||
|
||
// Users approve Collection Offers module | ||
seller.setApprovalForModule(address(offers), true); | ||
buyer.setApprovalForModule(address(offers), true); | ||
|
||
// Buyer approve ERC721TransferHelper | ||
vm.prank(address(buyer)); | ||
token.setApprovalForAll(address(erc721TransferHelper), true); | ||
} | ||
|
||
/// ------------ ETH COLLECTION OFFER ------------ /// | ||
|
||
function offer() public { | ||
vm.prank(address(seller)); | ||
offers.createOffer{value: 1 ether}(address(token)); | ||
} | ||
|
||
function fill() public { | ||
vm.prank(address(seller)); | ||
offers.createOffer{value: 1 ether}(address(token)); | ||
|
||
vm.prank(address(buyer)); | ||
offers.fillOffer(address(token), 0, 1 ether, address(finder)); | ||
} | ||
|
||
function test_WithdrawOfferFromSeller() public { | ||
uint256 beforeBalance = address(seller).balance; | ||
offer(); | ||
uint256 afterBalance = address(seller).balance; | ||
|
||
require(beforeBalance - afterBalance == 1 ether); | ||
} | ||
|
||
function test_WithdrawOfferIncreaseFromSeller() public { | ||
uint256 beforeBalance = address(seller).balance; | ||
|
||
offer(); | ||
|
||
// Increase initial offer to 2 ETH | ||
vm.prank(address(seller)); | ||
offers.setOfferAmount{value: 1 ether}(address(token), 1, 2 ether); | ||
|
||
uint256 afterBalance = address(seller).balance; | ||
|
||
require(beforeBalance - afterBalance == 2 ether); | ||
} | ||
|
||
function test_RefundOfferDecreaseToSeller() public { | ||
uint256 beforeBalance = address(seller).balance; | ||
|
||
offer(); | ||
|
||
// Decrease initial offer to 0.5 ETH | ||
vm.prank(address(seller)); | ||
offers.setOfferAmount(address(token), 1, 0.5 ether); | ||
|
||
uint256 afterBalance = address(seller).balance; | ||
|
||
require(beforeBalance - afterBalance == 0.5 ether); | ||
} | ||
|
||
function test_ETHIntegration() public { | ||
uint256 beforeSellerBalance = address(seller).balance; | ||
uint256 beforeBuyerBalance = address(buyer).balance; | ||
uint256 beforeRoyaltyRecipientBalance = address(royaltyRecipient).balance; | ||
uint256 beforeFinderBalance = address(finder).balance; | ||
address beforeTokenOwner = token.ownerOf(0); | ||
|
||
fill(); | ||
|
||
uint256 afterBuyerBalance = address(buyer).balance; | ||
uint256 afterSellerBalance = address(seller).balance; | ||
uint256 afterRoyaltyRecipientBalance = address(royaltyRecipient).balance; | ||
uint256 afterFinderBalance = address(finder).balance; | ||
address afterTokenOwner = token.ownerOf(0); | ||
|
||
// 1 ETH withdrawn from seller | ||
require((beforeSellerBalance - afterSellerBalance) == 1 ether); | ||
// 0.05 ETH creator royalty | ||
require((afterRoyaltyRecipientBalance - beforeRoyaltyRecipientBalance) == 0.05 ether); | ||
// 100 bps finders fee (Remaining 0.95 ETH * 10% finders fee = 0.0095 ETH) | ||
require((afterFinderBalance - beforeFinderBalance) == 0.0095 ether); | ||
// Remaining 0.9405 ETH paid to buyer | ||
require((afterBuyerBalance - beforeBuyerBalance) == 0.9405 ether); | ||
// NFT transferred to seller | ||
require((beforeTokenOwner == address(buyer)) && afterTokenOwner == address(seller)); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A default finders fee is configured in
CollectionOffersV1
's constructor:v3/contracts/modules/CollectionOffers/V1/CollectionOffersV1.sol
Line 91 in 78ce19a
Since the maximum finders fee is
10000
, a100
bps finders fee is1%
, not10%
.It follows that
0.95 ETH
*1%
is0.0095 ETH
as it's expressed in the assertion.Given this context, the following annotation should be updated accordingly for correctness: