Skip to content

Commit

Permalink
allow changes to referral fee percentage on the implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
spengrah committed Sep 11, 2024
1 parent f219dd5 commit c2b8288
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 13 deletions.
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);
}
}

0 comments on commit c2b8288

Please sign in to comment.