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

Allow changes to referral fee percentage on the implementation #2

Merged
merged 1 commit into from
Sep 12, 2024
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: 48 additions & 10 deletions src/PublicLockV14Eligibility.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ contract PublicLockV14Eligibility is HatsEligibilityModule, ILockKeyPurchaseHook
/// @dev Thrown when the hat minting fails
error HatMintFailed();

/// @dev Thrown when a non-referrer calls a function only authorized to the referrer
error NotReferrer();

/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/

/// @notice Emitted when the referrer fee percentage is set in the implementation contract
event ImplementationReferrerFeePercentageSet(uint256 referrerFeePercentage);

/*//////////////////////////////////////////////////////////////
DATA MODELS
//////////////////////////////////////////////////////////////*/
Expand All @@ -51,9 +61,6 @@ contract PublicLockV14Eligibility is HatsEligibilityModule, ILockKeyPurchaseHook
/// @notice The address to split key purchase fees to, set as a referrer on the lock
address public immutable REFERRER;

/// @notice The percentage of key purchase fees that go to the referrer, in basis points (10000 = 100%)
uint256 public immutable REFERRER_FEE_PERCENTAGE;

/// @notice The Unlock Protocol factory contract
/// @dev Used only for the implementation contract; for clones/instances, use {unlock}
IUnlock public unlock_;
Expand All @@ -63,6 +70,10 @@ contract PublicLockV14Eligibility is HatsEligibilityModule, ILockKeyPurchaseHook
return PublicLockV14Eligibility(IMPLEMENTATION()).unlock_();
}

/// @notice The referrer fee percentage for this module instance and associated lock. It is set to the value of the
/// {implementationReferrerFeePercentage} during {setUp}, and cannot be updated.
uint256 public referrerFeePercentage;

/**
* This contract is a clone with immutable args, which means that it is deployed with a set of
* immutable storage variables (ie constants). Accessing these constants is cheaper than accessing
Expand All @@ -88,6 +99,10 @@ contract PublicLockV14Eligibility is HatsEligibilityModule, ILockKeyPurchaseHook
MUTABLE STATE
//////////////////////////////////////////////////////////////*/

/// @notice The referrer fee percentage that will be used for subsequent instances of this module.
/// Will be 0 for all instances; use {referrerFeePercentage} for the fee percentage for a given instance.
uint256 public implementationReferrerFeePercentage;

/// @notice The Unlock Protocol lock contract that is created along with this module and coupled to the hat
IPublicLock public lock;

Expand All @@ -98,18 +113,18 @@ contract PublicLockV14Eligibility is HatsEligibilityModule, ILockKeyPurchaseHook
/// @notice Deploy the implementation contract and set its version
/// @param _version The version of the implementation contract
/// @param _referrer The referrer address, which will receive a portion of the fees
/// @param _referrerFeePercentage The percentage of fees to go to the referrer, in basis points (10000 = 100%)
/// @param __referrerFeePercentage The percentage of fees to go to the referrer, in basis points (10000 = 100%)
/// @dev This is only used to deploy the implementation contract, and should not be used to deploy clones
constructor(string memory _version, IUnlock _unlock, address _referrer, uint256 _referrerFeePercentage)
constructor(string memory _version, IUnlock _unlock, address _referrer, uint256 __referrerFeePercentage)
HatsModule(_version)
{
unlock_ = _unlock;
REFERRER = _referrer;
REFERRER_FEE_PERCENTAGE = _referrerFeePercentage;
implementationReferrerFeePercentage = __referrerFeePercentage;
}

/*//////////////////////////////////////////////////////////////
INITIALIZOR
INITIALIZER
//////////////////////////////////////////////////////////////*/

/// @inheritdoc HatsModule
Expand Down Expand Up @@ -142,8 +157,12 @@ contract PublicLockV14Eligibility is HatsEligibilityModule, ILockKeyPurchaseHook
_onKeyGrantHook: address(0)
});

// set referrer fee
lock.setReferrerFee(REFERRER, REFERRER_FEE_PERCENTAGE);
// set referrer fee percentage in this instance
uint256 fee = PublicLockV14Eligibility(IMPLEMENTATION()).implementationReferrerFeePercentage();
referrerFeePercentage = fee;

// set referrer and their fee percentage in the lock
lock.setReferrerFee(REFERRER, fee);

// add lock manager role to the configured address
lock.addLockManager(lockConfig.lockManager);
Expand Down Expand Up @@ -180,7 +199,7 @@ contract PublicLockV14Eligibility is HatsEligibilityModule, ILockKeyPurchaseHook
bytes calldata /* data */
) external view returns (uint256 minKeyPrice) {
// Check if referrer fee is correct. Fail minting if incorrect.
if (lock.referrerFees(REFERRER) != REFERRER_FEE_PERCENTAGE) {
if (lock.referrerFees(REFERRER) != referrerFeePercentage) {
revert InvalidReferrerFee();
}

Expand Down Expand Up @@ -246,6 +265,25 @@ contract PublicLockV14Eligibility is HatsEligibilityModule, ILockKeyPurchaseHook
revert NotTransferable();
}

/*//////////////////////////////////////////////////////////////
ADMIN FUNCTIONS
//////////////////////////////////////////////////////////////*/

/**
* @notice Sets the referrer fee percentage on the implementation contract. This value will be used for subsequent
* instances of this module. It will not change the referrer fee percentage for existing instances.
* @dev This function can only be called by the referrer.
* @param _referrerFeePercentage The new referrer fee percentage
*/
function setImplementationReferrerFeePercentage(uint256 _referrerFeePercentage) external {
// caller must be the referrer
if (msg.sender != REFERRER) revert NotReferrer();

implementationReferrerFeePercentage = _referrerFeePercentage;

emit ImplementationReferrerFeePercentageSet(_referrerFeePercentage);
}

/*//////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
Expand Down
50 changes: 47 additions & 3 deletions test/PublicLockEligibility.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,16 @@ contract Deployment is WithInstanceTest {
assertEq(instance.hatId(), targetHat);
}

function test_referrerFeePercentage() public view {
assertEq(instance.REFERRER_FEE_PERCENTAGE(), referrerFeePercentage);
function test_referrerFeePercentage_instance() public view {
assertEq(instance.referrerFeePercentage(), referrerFeePercentage, "wrong instance value");

assertEq(instance.implementationReferrerFeePercentage(), 0, "wrong implementation value");
}

function test_referrerFeePercentage_implementation() public view {
assertEq(implementation.implementationReferrerFeePercentage(), referrerFeePercentage);

assertEq(implementation.referrerFeePercentage(), 0);
}

function test_referrer() public view {
Expand Down Expand Up @@ -425,7 +433,6 @@ contract GetWearerStatus is WithInstanceTest {
uint256 key = _purchaseSingleKey(lock, wearer);

// the wearer should be eligible

(bool eligible, bool standing) = instance.getWearerStatus(wearer, targetHat);
assertTrue(eligible);
assertTrue(standing);
Expand Down Expand Up @@ -526,3 +533,40 @@ contract MaxNumberOfKeys is WithInstanceTest {
assertEq(instance.maxNumberOfKeys(), newMaxNumberOfKeys);
}
}

contract SetImplementationReferrerFeePercentage is WithInstanceTest {
uint256 public oldFee;
uint256 public newFee;

function setUp() public override {
super.setUp();

oldFee = referrerFeePercentage;
newFee = oldFee * 3;
}

function test_referrerCanSet() public {
// set the referrer fee percentage
vm.expectEmit(true, true, true, true);
emit PublicLockV14Eligibility.ImplementationReferrerFeePercentageSet(newFee);
vm.prank(referrer);
implementation.setImplementationReferrerFeePercentage(newFee);

// referrer fee percentage for existing instance should not change
assertEq(instance.referrerFeePercentage(), oldFee);

// new instance should have the new referrer fee percentage
deployInstance.prepare(false, address(implementation), targetHat, saltNonce + 1, lockConfig);
PublicLockV14Eligibility newInstance = deployInstance.run();
assertEq(newInstance.referrerFeePercentage(), newFee);
}

function test_revert_nonReferrerCannotSet() public {
// try to set the referrer fee percentage, expecting a revert
vm.expectRevert(PublicLockV14Eligibility.NotReferrer.selector);
vm.prank(makeAddr("non-referrer"));
implementation.setImplementationReferrerFeePercentage(newFee);

assertEq(implementation.implementationReferrerFeePercentage(), oldFee);
}
}
Loading