Skip to content

Flat fee for TokenERC1155 platform-fee setup #346

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

Merged
merged 3 commits into from
Feb 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
58 changes: 55 additions & 3 deletions contracts/token/TokenERC1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ contract TokenERC1155 is
using StringsUpgradeable for uint256;

bytes32 private constant MODULE_TYPE = bytes32("TokenERC1155");
uint256 private constant VERSION = 1;
uint256 private constant VERSION = 2;

/// @dev Fee type variants: percentage fee and flat fee
enum PlatformFeeType {
Bps,
Flat
}

// Token name
string public name;
Expand Down Expand Up @@ -107,6 +113,12 @@ contract TokenERC1155 is
/// @dev The % of primary sales collected by the contract as fees.
uint128 private platformFeeBps;

/// @dev The flat amount collected by the contract as fees on primary sales.
uint256 private flatPlatformFee;

/// @dev Fee type variants: percentage fee and flat fee
PlatformFeeType private platformFeeType;

/// @dev Contract level metadata.
string public contractURI;

Expand All @@ -124,6 +136,12 @@ contract TokenERC1155 is
/// @dev Token ID => royalty recipient and bps for token
mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;

/// @dev Emitted when flat fee on primary sales is updated.
event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee);

/// @dev Emitted when platform fee type is updated.
event PlatformFeeTypeUpdated(PlatformFeeType feeType);

constructor() initializer {}

/// @dev Initiliazes the contract, like a constructor.
Expand Down Expand Up @@ -158,6 +176,9 @@ contract TokenERC1155 is
require(_platformFeeBps <= MAX_BPS, "exceeds MAX_BPS");
platformFeeBps = _platformFeeBps;

// Fee type Bps by default
platformFeeType = PlatformFeeType.Bps;

_owner = _defaultAdmin;
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
_setupRole(MINTER_ROLE, _defaultAdmin);
Expand Down Expand Up @@ -304,6 +325,24 @@ contract TokenERC1155 is
emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
}

/// @dev Lets a module admin set a flat fee on primary sales.
function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee)
external
onlyRole(DEFAULT_ADMIN_ROLE)
{
flatPlatformFee = _flatFee;
platformFeeRecipient = _platformFeeRecipient;

emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee);
}

/// @dev Lets a module admin set a flat fee on primary sales.
function setPlatformFeeType(PlatformFeeType _feeType) external onlyRole(DEFAULT_ADMIN_ROLE) {
platformFeeType = _feeType;

emit PlatformFeeTypeUpdated(_feeType);
}

/// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
function setOwner(address _newOwner) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(hasRole(DEFAULT_ADMIN_ROLE, _newOwner), "new owner not module admin.");
Expand All @@ -325,7 +364,17 @@ contract TokenERC1155 is
return (platformFeeRecipient, uint16(platformFeeBps));
}

/// @dev Returns the platform fee bps and recipient.
/// @dev Returns the flat platform fee and recipient.
function getFlatPlatformFeeInfo() external view returns (address, uint256) {
return (platformFeeRecipient, flatPlatformFee);
}

/// @dev Returns the platform fee type.
function getPlatformFeeType() external view returns (PlatformFeeType) {
return platformFeeType;
}

/// @dev Returns default royalty info.
function getDefaultRoyaltyInfo() external view returns (address, uint16) {
return (royaltyRecipient, uint16(royaltyBps));
}
Expand Down Expand Up @@ -408,7 +457,10 @@ contract TokenERC1155 is
}

uint256 totalPrice = _req.pricePerToken * _req.quantity;
uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
uint256 platformFees = platformFeeType == PlatformFeeType.Flat
? flatPlatformFee
: ((totalPrice * platformFeeBps) / MAX_BPS);
require(totalPrice >= platformFees, "price less than platform fee");

if (_req.currency == CurrencyTransferLib.NATIVE_TOKEN) {
require(msg.value == totalPrice, "must send total price.");
Expand Down
147 changes: 147 additions & 0 deletions src/test/token/TokenERC1155.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,115 @@ contract TokenERC1155Test is BaseTest {
Unit tests: platform fee
//////////////////////////////////////////////////////////////*/

function test_state_PlatformFee_Flat_ERC20() public {
vm.warp(1000);
uint256 flatPlatformFee = 10;

vm.startPrank(deployerSigner);
tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee);
tokenContract.setPlatformFeeType(TokenERC1155.PlatformFeeType.Flat);
vm.stopPrank();

// update mintrequest data
_mintrequest.pricePerToken = 1;
_mintrequest.currency = address(erc20);
_signature = signMintRequest(_mintrequest, privateKey);

// approve erc20 tokens to tokenContract
vm.prank(recipient);
erc20.approve(address(tokenContract), _mintrequest.pricePerToken * _mintrequest.quantity);

// initial balances and state
uint256 nextTokenId = tokenContract.nextTokenIdToMint();
uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId);

uint256 erc20BalanceOfSeller = erc20.balanceOf(address(saleRecipient));
uint256 erc20BalanceOfRecipient = erc20.balanceOf(address(recipient));

// mint with signature
vm.prank(recipient);
tokenContract.mintWithSignature(_mintrequest, _signature);

// check state after minting
assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1);
assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri));
assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity);

// check erc20 balances after minting
assertEq(
erc20.balanceOf(recipient),
erc20BalanceOfRecipient - (_mintrequest.pricePerToken * _mintrequest.quantity)
);
assertEq(
erc20.balanceOf(address(saleRecipient)),
erc20BalanceOfSeller + (_mintrequest.pricePerToken * _mintrequest.quantity) - flatPlatformFee
);
}

function test_state_PlatformFee_NativeToken() public {
vm.warp(1000);
uint256 flatPlatformFee = 10;

vm.startPrank(deployerSigner);
tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee);
tokenContract.setPlatformFeeType(TokenERC1155.PlatformFeeType.Flat);
vm.stopPrank();

// update mintrequest data
_mintrequest.pricePerToken = 1;
_mintrequest.currency = address(NATIVE_TOKEN);
_signature = signMintRequest(_mintrequest, privateKey);

// initial balances and state
uint256 nextTokenId = tokenContract.nextTokenIdToMint();
uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId);

uint256 etherBalanceOfSeller = address(saleRecipient).balance;
uint256 etherBalanceOfRecipient = address(recipient).balance;

// mint with signature
vm.prank(recipient);
tokenContract.mintWithSignature{ value: _mintrequest.pricePerToken * _mintrequest.quantity }(
_mintrequest,
_signature
);

// check state after minting
assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1);
assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri));
assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity);

// check balances after minting
assertEq(
address(recipient).balance,
etherBalanceOfRecipient - (_mintrequest.pricePerToken * _mintrequest.quantity)
);
assertEq(
address(saleRecipient).balance,
etherBalanceOfSeller + (_mintrequest.pricePerToken * _mintrequest.quantity) - flatPlatformFee
);
}

function test_revert_PlatformFeeGreaterThanPrice() public {
vm.warp(1000);
uint256 flatPlatformFee = 1 ether;

vm.startPrank(deployerSigner);
tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee);
tokenContract.setPlatformFeeType(TokenERC1155.PlatformFeeType.Flat);
vm.stopPrank();

// update mintrequest data
_mintrequest.pricePerToken = 1;
_mintrequest.currency = address(erc20);
_signature = signMintRequest(_mintrequest, privateKey);

// mint with signature
vm.prank(recipient);
vm.expectRevert("price less than platform fee");
tokenContract.mintWithSignature(_mintrequest, _signature);
}

function test_state_setPlatformFeeInfo() public {
address _platformFeeRecipient = address(0x123);
uint256 _platformFeeBps = 1000;
Expand All @@ -641,6 +750,33 @@ contract TokenERC1155Test is BaseTest {
assertEq(_platformFeeBps, bps);
}

function test_state_setFlatPlatformFee() public {
address _platformFeeRecipient = address(0x123);
uint256 _flatFee = 1000;

vm.prank(deployerSigner);
tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee);

(address recipient_, uint256 fee) = tokenContract.getFlatPlatformFeeInfo();
assertEq(_platformFeeRecipient, recipient_);
assertEq(_flatFee, fee);
}

function test_state_setPlatformFeeType() public {
address _platformFeeRecipient = address(0x123);
uint256 _flatFee = 1000;
TokenERC1155.PlatformFeeType _feeType = TokenERC1155.PlatformFeeType.Flat;

vm.prank(deployerSigner);
tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee);

vm.prank(deployerSigner);
tokenContract.setPlatformFeeType(_feeType);

TokenERC1155.PlatformFeeType updatedFeeType = tokenContract.getPlatformFeeType();
assertTrue(updatedFeeType == _feeType);
}

function test_revert_setPlatformFeeInfo_ExceedsMaxBps() public {
address _platformFeeRecipient = address(0x123);
uint256 _platformFeeBps = 10001;
Expand All @@ -663,6 +799,17 @@ contract TokenERC1155Test is BaseTest {
);
vm.prank(address(0x1));
tokenContract.setPlatformFeeInfo(address(1), 1000);

vm.expectRevert(
abi.encodePacked(
"AccessControl: account ",
TWStrings.toHexString(uint160(address(0x1)), 20),
" is missing role ",
TWStrings.toHexString(uint256(role), 32)
)
);
vm.prank(address(0x1));
tokenContract.setFlatPlatformFeeInfo(address(1), 1000);
}

function test_event_platformFeeInfo() public {
Expand Down