Skip to content

Commit b50742d

Browse files
Flat fee for TokenERC1155 platform-fee setup (#346)
* flat fee component on TokenERC1155 platform-fee setup * comments --------- Co-authored-by: Joaquim Verges <[email protected]>
1 parent 3e40bab commit b50742d

File tree

2 files changed

+202
-3
lines changed

2 files changed

+202
-3
lines changed

contracts/token/TokenERC1155.sol

+55-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,13 @@ contract TokenERC1155 is
6565
using StringsUpgradeable for uint256;
6666

6767
bytes32 private constant MODULE_TYPE = bytes32("TokenERC1155");
68-
uint256 private constant VERSION = 1;
68+
uint256 private constant VERSION = 2;
69+
70+
/// @dev Fee type variants: percentage fee and flat fee
71+
enum PlatformFeeType {
72+
Bps,
73+
Flat
74+
}
6975

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

116+
/// @dev The flat amount collected by the contract as fees on primary sales.
117+
uint256 private flatPlatformFee;
118+
119+
/// @dev Fee type variants: percentage fee and flat fee
120+
PlatformFeeType private platformFeeType;
121+
110122
/// @dev Contract level metadata.
111123
string public contractURI;
112124

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

139+
/// @dev Emitted when flat fee on primary sales is updated.
140+
event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee);
141+
142+
/// @dev Emitted when platform fee type is updated.
143+
event PlatformFeeTypeUpdated(PlatformFeeType feeType);
144+
127145
constructor() initializer {}
128146

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

179+
// Fee type Bps by default
180+
platformFeeType = PlatformFeeType.Bps;
181+
161182
_owner = _defaultAdmin;
162183
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
163184
_setupRole(MINTER_ROLE, _defaultAdmin);
@@ -304,6 +325,24 @@ contract TokenERC1155 is
304325
emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
305326
}
306327

328+
/// @dev Lets a module admin set a flat fee on primary sales.
329+
function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee)
330+
external
331+
onlyRole(DEFAULT_ADMIN_ROLE)
332+
{
333+
flatPlatformFee = _flatFee;
334+
platformFeeRecipient = _platformFeeRecipient;
335+
336+
emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee);
337+
}
338+
339+
/// @dev Lets a module admin set a flat fee on primary sales.
340+
function setPlatformFeeType(PlatformFeeType _feeType) external onlyRole(DEFAULT_ADMIN_ROLE) {
341+
platformFeeType = _feeType;
342+
343+
emit PlatformFeeTypeUpdated(_feeType);
344+
}
345+
307346
/// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
308347
function setOwner(address _newOwner) external onlyRole(DEFAULT_ADMIN_ROLE) {
309348
require(hasRole(DEFAULT_ADMIN_ROLE, _newOwner), "new owner not module admin.");
@@ -325,7 +364,17 @@ contract TokenERC1155 is
325364
return (platformFeeRecipient, uint16(platformFeeBps));
326365
}
327366

328-
/// @dev Returns the platform fee bps and recipient.
367+
/// @dev Returns the flat platform fee and recipient.
368+
function getFlatPlatformFeeInfo() external view returns (address, uint256) {
369+
return (platformFeeRecipient, flatPlatformFee);
370+
}
371+
372+
/// @dev Returns the platform fee type.
373+
function getPlatformFeeType() external view returns (PlatformFeeType) {
374+
return platformFeeType;
375+
}
376+
377+
/// @dev Returns default royalty info.
329378
function getDefaultRoyaltyInfo() external view returns (address, uint16) {
330379
return (royaltyRecipient, uint16(royaltyBps));
331380
}
@@ -408,7 +457,10 @@ contract TokenERC1155 is
408457
}
409458

410459
uint256 totalPrice = _req.pricePerToken * _req.quantity;
411-
uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
460+
uint256 platformFees = platformFeeType == PlatformFeeType.Flat
461+
? flatPlatformFee
462+
: ((totalPrice * platformFeeBps) / MAX_BPS);
463+
require(totalPrice >= platformFees, "price less than platform fee");
412464

413465
if (_req.currency == CurrencyTransferLib.NATIVE_TOKEN) {
414466
require(msg.value == totalPrice, "must send total price.");

src/test/token/TokenERC1155.t.sol

+147
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,115 @@ contract TokenERC1155Test is BaseTest {
629629
Unit tests: platform fee
630630
//////////////////////////////////////////////////////////////*/
631631

632+
function test_state_PlatformFee_Flat_ERC20() public {
633+
vm.warp(1000);
634+
uint256 flatPlatformFee = 10;
635+
636+
vm.startPrank(deployerSigner);
637+
tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee);
638+
tokenContract.setPlatformFeeType(TokenERC1155.PlatformFeeType.Flat);
639+
vm.stopPrank();
640+
641+
// update mintrequest data
642+
_mintrequest.pricePerToken = 1;
643+
_mintrequest.currency = address(erc20);
644+
_signature = signMintRequest(_mintrequest, privateKey);
645+
646+
// approve erc20 tokens to tokenContract
647+
vm.prank(recipient);
648+
erc20.approve(address(tokenContract), _mintrequest.pricePerToken * _mintrequest.quantity);
649+
650+
// initial balances and state
651+
uint256 nextTokenId = tokenContract.nextTokenIdToMint();
652+
uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId);
653+
654+
uint256 erc20BalanceOfSeller = erc20.balanceOf(address(saleRecipient));
655+
uint256 erc20BalanceOfRecipient = erc20.balanceOf(address(recipient));
656+
657+
// mint with signature
658+
vm.prank(recipient);
659+
tokenContract.mintWithSignature(_mintrequest, _signature);
660+
661+
// check state after minting
662+
assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1);
663+
assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri));
664+
assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity);
665+
666+
// check erc20 balances after minting
667+
assertEq(
668+
erc20.balanceOf(recipient),
669+
erc20BalanceOfRecipient - (_mintrequest.pricePerToken * _mintrequest.quantity)
670+
);
671+
assertEq(
672+
erc20.balanceOf(address(saleRecipient)),
673+
erc20BalanceOfSeller + (_mintrequest.pricePerToken * _mintrequest.quantity) - flatPlatformFee
674+
);
675+
}
676+
677+
function test_state_PlatformFee_NativeToken() public {
678+
vm.warp(1000);
679+
uint256 flatPlatformFee = 10;
680+
681+
vm.startPrank(deployerSigner);
682+
tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee);
683+
tokenContract.setPlatformFeeType(TokenERC1155.PlatformFeeType.Flat);
684+
vm.stopPrank();
685+
686+
// update mintrequest data
687+
_mintrequest.pricePerToken = 1;
688+
_mintrequest.currency = address(NATIVE_TOKEN);
689+
_signature = signMintRequest(_mintrequest, privateKey);
690+
691+
// initial balances and state
692+
uint256 nextTokenId = tokenContract.nextTokenIdToMint();
693+
uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId);
694+
695+
uint256 etherBalanceOfSeller = address(saleRecipient).balance;
696+
uint256 etherBalanceOfRecipient = address(recipient).balance;
697+
698+
// mint with signature
699+
vm.prank(recipient);
700+
tokenContract.mintWithSignature{ value: _mintrequest.pricePerToken * _mintrequest.quantity }(
701+
_mintrequest,
702+
_signature
703+
);
704+
705+
// check state after minting
706+
assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1);
707+
assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri));
708+
assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity);
709+
710+
// check balances after minting
711+
assertEq(
712+
address(recipient).balance,
713+
etherBalanceOfRecipient - (_mintrequest.pricePerToken * _mintrequest.quantity)
714+
);
715+
assertEq(
716+
address(saleRecipient).balance,
717+
etherBalanceOfSeller + (_mintrequest.pricePerToken * _mintrequest.quantity) - flatPlatformFee
718+
);
719+
}
720+
721+
function test_revert_PlatformFeeGreaterThanPrice() public {
722+
vm.warp(1000);
723+
uint256 flatPlatformFee = 1 ether;
724+
725+
vm.startPrank(deployerSigner);
726+
tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee);
727+
tokenContract.setPlatformFeeType(TokenERC1155.PlatformFeeType.Flat);
728+
vm.stopPrank();
729+
730+
// update mintrequest data
731+
_mintrequest.pricePerToken = 1;
732+
_mintrequest.currency = address(erc20);
733+
_signature = signMintRequest(_mintrequest, privateKey);
734+
735+
// mint with signature
736+
vm.prank(recipient);
737+
vm.expectRevert("price less than platform fee");
738+
tokenContract.mintWithSignature(_mintrequest, _signature);
739+
}
740+
632741
function test_state_setPlatformFeeInfo() public {
633742
address _platformFeeRecipient = address(0x123);
634743
uint256 _platformFeeBps = 1000;
@@ -641,6 +750,33 @@ contract TokenERC1155Test is BaseTest {
641750
assertEq(_platformFeeBps, bps);
642751
}
643752

753+
function test_state_setFlatPlatformFee() public {
754+
address _platformFeeRecipient = address(0x123);
755+
uint256 _flatFee = 1000;
756+
757+
vm.prank(deployerSigner);
758+
tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee);
759+
760+
(address recipient_, uint256 fee) = tokenContract.getFlatPlatformFeeInfo();
761+
assertEq(_platformFeeRecipient, recipient_);
762+
assertEq(_flatFee, fee);
763+
}
764+
765+
function test_state_setPlatformFeeType() public {
766+
address _platformFeeRecipient = address(0x123);
767+
uint256 _flatFee = 1000;
768+
TokenERC1155.PlatformFeeType _feeType = TokenERC1155.PlatformFeeType.Flat;
769+
770+
vm.prank(deployerSigner);
771+
tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee);
772+
773+
vm.prank(deployerSigner);
774+
tokenContract.setPlatformFeeType(_feeType);
775+
776+
TokenERC1155.PlatformFeeType updatedFeeType = tokenContract.getPlatformFeeType();
777+
assertTrue(updatedFeeType == _feeType);
778+
}
779+
644780
function test_revert_setPlatformFeeInfo_ExceedsMaxBps() public {
645781
address _platformFeeRecipient = address(0x123);
646782
uint256 _platformFeeBps = 10001;
@@ -663,6 +799,17 @@ contract TokenERC1155Test is BaseTest {
663799
);
664800
vm.prank(address(0x1));
665801
tokenContract.setPlatformFeeInfo(address(1), 1000);
802+
803+
vm.expectRevert(
804+
abi.encodePacked(
805+
"AccessControl: account ",
806+
TWStrings.toHexString(uint160(address(0x1)), 20),
807+
" is missing role ",
808+
TWStrings.toHexString(uint256(role), 32)
809+
)
810+
);
811+
vm.prank(address(0x1));
812+
tokenContract.setFlatPlatformFeeInfo(address(1), 1000);
666813
}
667814

668815
function test_event_platformFeeInfo() public {

0 commit comments

Comments
 (0)