Skip to content

Commit

Permalink
Merge branch 'main' into audit_v2_4_0_flb_01c
Browse files Browse the repository at this point in the history
  • Loading branch information
zajck committed Jan 9, 2024
2 parents d0a9a4e + 06209b1 commit 2920848
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ interface IBosonSequentialCommitHandler is BosonErrors, IBosonExchangeEvents, IB
* - 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)
* - Transfer of voucher to the buyer fails for some reason (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
Expand Down
40 changes: 40 additions & 0 deletions contracts/mock/BuyerContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.22;

import { IERC721Receiver } from "../interfaces/IERC721Receiver.sol";

/**
* @title BuyerContract
*
* @notice Contract that acts as a buyer for testing purposes
*/
contract BuyerContract is IERC721Receiver {
enum FailType {
None,
Revert,
ReturnWrongSelector
}

FailType public failType;

/**
* @dev Set fail type
*/
function setFailType(FailType _failType) external {
failType = _failType;
}

/**
* @dev Return wrong selector to test revert
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4) {
if (failType == FailType.Revert) revert("BuyerContract: revert");
if (failType == FailType.ReturnWrongSelector) return 0x12345678;
return IERC721Receiver.onERC721Received.selector;
}
}
2 changes: 1 addition & 1 deletion contracts/protocol/bases/PriceDiscoveryBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ contract PriceDiscoveryBase is ProtocolBase {
if (_bosonVoucher.ownerOf(_tokenId) != address(this)) revert VoucherNotReceived();

// Transfer voucher to buyer
_bosonVoucher.transferFrom(address(this), _buyer, _tokenId);
_bosonVoucher.safeTransferFrom(address(this), _buyer, _tokenId);
}

uint256 overchargedAmount = _priceDiscovery.price - actualPrice;
Expand Down
2 changes: 2 additions & 0 deletions contracts/protocol/facets/SellerHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ contract SellerHandlerFacet is SellerBase {
1;
}
royaltyRecipients.pop();

delete royaltyRecipientIndexBySellerAndRecipient[_seller.treasury];
}

// Update treasury
Expand Down
4 changes: 2 additions & 2 deletions contracts/protocol/facets/SequentialCommitHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ contract SequentialCommitHandlerFacet is IBosonSequentialCommitHandler, PriceDis
* - 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)
* - Transfer of voucher to the buyer fails for some reason (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
Expand Down Expand Up @@ -174,7 +174,7 @@ contract SequentialCommitHandlerFacet is IBosonSequentialCommitHandler, PriceDis
}

if (payout > 0) {
FundsLib.transferFundsFromProtocol(exchangeToken, payable(seller), payout); // also emits FundsWithdrawn
FundsLib.transferFundsFromProtocol(exchangeToken, payable(seller), payout);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions scripts/config/revert-reasons.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ exports.RevertReasons = {
SAFE_ERC20_LOW_LEVEL_CALL: "SafeERC20: low-level call failed",
SAFE_ERC20_OPERATION_FAILED: "SafeERC20: ERC20 operation did not succeed",
INITIALIZABLE_ALREADY_INITIALIZED: "Initializable: contract is already initialized",
ERC721_NON_RECEIVER: "ERC721: transfer to non ERC721Receiver implementer",
BUYER_CONTRACT_REVERT: "BuyerContract: revert",

// Meta-Transactions related
NONCE_USED_ALREADY: "NonceUsedAlready",
Expand Down
67 changes: 66 additions & 1 deletion test/protocol/SellerHandlerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -3885,7 +3885,72 @@ describe("SellerHandler", function () {
);
expect(returnedRoyaltyRecipientList).to.deep.equal(
expectedRoyaltyRecipientList,
"Default royalty recipient mismatch"
"Royalty recipient mismatch"
);
});

it("correctly handle treasury during the seller update", async function () {
// Add royalty recipients
await accountHandler.connect(admin).addRoyaltyRecipients(seller.id, royaltyRecipientList.toStruct());

// Update the seller, so one of the recipients is removed
seller.treasury = other1.address;
await accountHandler.connect(admin).updateSeller(seller, emptyAuthToken);

// other1 is not a recipient anymore
let returnedRoyaltyRecipientList = RoyaltyRecipientList.fromStruct(
await accountHandler.connect(rando).getRoyaltyRecipients(seller.id)
);

royaltyRecipientList = new RoyaltyRecipientList([
new RoyaltyRecipient(other3.address, "300", "other3"),
new RoyaltyRecipient(other2.address, "200", "other2"),
]);

expectedRoyaltyRecipientList = new RoyaltyRecipientList([
new RoyaltyRecipient(ZeroAddress, voucherInitValues.royaltyPercentage, DEFAULT_ROYALTY_RECIPIENT),
...royaltyRecipientList.royaltyRecipients,
]);

expect(returnedRoyaltyRecipientList).to.deep.equal(
expectedRoyaltyRecipientList,
"Royalty recipient mismatch"
);

// other 1 now cannot be added as another recipient
royaltyRecipientList = new RoyaltyRecipientList([new RoyaltyRecipient(other1.address, "100", "other1")]);

// Adding other 1 should fail
await expect(
accountHandler.connect(admin).addRoyaltyRecipients(seller.id, royaltyRecipientList.toStruct())
).to.revertedWithCustomError(bosonErrors, RevertReasons.RECIPIENT_NOT_UNIQUE);

// Update the seller again, so other 1 can later be added as a recipient
seller.treasury = rando.address;
await accountHandler.connect(admin).updateSeller(seller, emptyAuthToken);

// Now adding should succeed
await accountHandler.connect(admin).addRoyaltyRecipients(seller.id, royaltyRecipientList.toStruct());

// other1 is back on the list
returnedRoyaltyRecipientList = RoyaltyRecipientList.fromStruct(
await accountHandler.connect(rando).getRoyaltyRecipients(seller.id)
);

royaltyRecipientList = new RoyaltyRecipientList([
new RoyaltyRecipient(other3.address, "300", "other3"),
new RoyaltyRecipient(other2.address, "200", "other2"),
new RoyaltyRecipient(other1.address, "100", "other1"),
]);

expectedRoyaltyRecipientList = new RoyaltyRecipientList([
new RoyaltyRecipient(ZeroAddress, voucherInitValues.royaltyPercentage, DEFAULT_ROYALTY_RECIPIENT),
...royaltyRecipientList.royaltyRecipients,
]);

expect(returnedRoyaltyRecipientList).to.deep.equal(
expectedRoyaltyRecipientList,
"Royalty recipient mismatch"
);
});

Expand Down
49 changes: 49 additions & 0 deletions test/protocol/SequentialCommitHandlerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,55 @@ describe("IBosonSequentialCommitHandler", function () {
.sequentialCommitToOffer(buyer2.address, tokenId, priceDiscovery, { value: price2 })
).to.revertedWithCustomError(bosonErrors, RevertReasons.VOUCHER_NOT_RECEIVED);
});

context("Buyer rejects the voucher", async function () {
it("Buyer contract does not implement the receiver", async function () {
const [buyerContract] = await deployMockTokens(["Foreign20"]);

// Sequential commit to offer, expect revert
await expect(
sequentialCommitHandler
.connect(rando)
.sequentialCommitToOffer(await buyerContract.getAddress(), tokenId, priceDiscovery, {
value: price2,
})
).to.revertedWith(RevertReasons.ERC721_NON_RECEIVER);
});

it("Buyer contract reverts with custom error", async function () {
const buyerContractFactory = await getContractFactory("BuyerContract");
const buyerContract = await buyerContractFactory.deploy();
await buyerContract.waitForDeployment();

await buyerContract.setFailType(1); // Type 1 = revert with own error

// Sequential commit to offer, expect revert
await expect(
sequentialCommitHandler
.connect(rando)
.sequentialCommitToOffer(await buyerContract.getAddress(), tokenId, priceDiscovery, {
value: price2,
})
).to.revertedWith(RevertReasons.BUYER_CONTRACT_REVERT);
});

it("Buyer contract returns the wrong selector", async function () {
const buyerContractFactory = await getContractFactory("BuyerContract");
const buyerContract = await buyerContractFactory.deploy();
await buyerContract.waitForDeployment();

await buyerContract.setFailType(2); // Type 2 = wrong selector

// Sequential commit to offer, expect revert
await expect(
sequentialCommitHandler
.connect(rando)
.sequentialCommitToOffer(await buyerContract.getAddress(), tokenId, priceDiscovery, {
value: price2,
})
).to.revertedWith(RevertReasons.ERC721_NON_RECEIVER);
});
});
});
});

Expand Down

0 comments on commit 2920848

Please sign in to comment.