-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add talent protocol discount validator (#97)
* init * First pass impl * Natspec * Add tests * Add zero-address checks * Address PR comments * Fix typo * Add clarity to possible revert case.
- Loading branch information
1 parent
edbe86f
commit 4e64f80
Showing
5 changed files
with
155 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
//SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.23; | ||
|
||
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; | ||
import {Ownable} from "solady/auth/Ownable.sol"; | ||
|
||
import {IDiscountValidator} from "src/L2/interface/IDiscountValidator.sol"; | ||
|
||
/// @title Discount Validator for: Talent Protocol Builder Score | ||
/// | ||
/// @notice Enables discounts for users who have minted their Talent Protocol Builder Score . | ||
/// Discounts are granted based on the claimer having some score higher than this contract's `threshold`. | ||
/// | ||
/// @author Coinbase (https://github.com/base-org/usernames) | ||
contract TalentProtocolDiscountValidator is IDiscountValidator, Ownable { | ||
/// @notice Thrown when setting a critical address to the zero-address. | ||
error NoZeroAddress(); | ||
|
||
/// @notice Emitted when the threshold is updated by the `owner`. | ||
event ThresholdUpdated(uint256 newThreshold); | ||
|
||
/// @notice Interface with the Talent Protocol `PassportBuilderScore` contract. | ||
TalentProtocol immutable talentProtocol; | ||
|
||
/// @notice The score threshold for qualifying for a discount. | ||
uint256 public threshold; | ||
|
||
/// @notice constructor | ||
/// | ||
/// @param owner_ The `Ownable` contract `owner`. | ||
/// @param talentProtocol_ The address of the Talent Protocol PassportBuilderScore contract. | ||
/// @param threshold_ The score required to qualify for a discount. | ||
constructor(address owner_, address talentProtocol_, uint256 threshold_) { | ||
if (owner_ == address(0)) revert NoZeroAddress(); | ||
if (talentProtocol_ == address(0)) revert NoZeroAddress(); | ||
talentProtocol = TalentProtocol(talentProtocol_); | ||
threshold = threshold_; | ||
_initializeOwner(owner_); | ||
} | ||
|
||
/// @notice Allows the `owner` to set the qualifying threshold. | ||
/// | ||
/// @param threshold_ The new discount-qualifying threshold. | ||
function setThreshold(uint256 threshold_) external onlyOwner { | ||
threshold = threshold_; | ||
emit ThresholdUpdated(threshold_); | ||
} | ||
|
||
/// @notice Required implementation for compatibility with IDiscountValidator. | ||
/// | ||
/// @dev No additional data is required for validating this discount. | ||
/// NOTE: The call to `getScoreByAddress` can revert. This case should be handled gracefully by integrators. | ||
/// | ||
/// @param claimer the discount claimer's address. | ||
/// | ||
/// @return `true` if the validation data provided is determined to be valid for the specified claimer, else `false`. | ||
function isValidDiscountRegistration(address claimer, bytes calldata) external view returns (bool) { | ||
return (talentProtocol.getScoreByAddress(claimer) >= threshold); | ||
} | ||
} | ||
|
||
/// @notice Lightweight interface for the PassportBuilderScore.sol contract. | ||
/// https://basescan.org/address/0xBBFeDA7c4d8d9Df752542b03CdD715F790B32D0B#readContract | ||
interface TalentProtocol { | ||
/// @notice Gets the score of a given address. | ||
/// | ||
/// @param wallet The address to get the score for. | ||
/// | ||
/// @return The score of the given address. | ||
function getScoreByAddress(address wallet) external view returns (uint256); | ||
} |
24 changes: 24 additions & 0 deletions
24
test/discounts/TalenProtocolDiscountValidator/IsValidDiscountRegistration.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
//SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.23; | ||
|
||
import {TalentProtocolDiscountValidatorBase} from "./TalentProtocolDiscountValidatorBase.t.sol"; | ||
|
||
contract IsValidDiscountRegistration is TalentProtocolDiscountValidatorBase { | ||
function test_returnsTrue_whenTheScoreMeetsTheThreshold() public { | ||
talent.setScore(threshold); | ||
bool ret = validator.isValidDiscountRegistration(userA, ""); | ||
assertTrue(ret); | ||
} | ||
|
||
function test_returnsTrue_whenTheScoreExceedsTheThreshold() public { | ||
talent.setScore(threshold + 1); | ||
bool ret = validator.isValidDiscountRegistration(userA, ""); | ||
assertTrue(ret); | ||
} | ||
|
||
function test_returnsFalse_whenTheScoreIsBelowTheThreshold() public { | ||
talent.setScore(threshold - 1); | ||
bool ret = validator.isValidDiscountRegistration(userA, ""); | ||
assertFalse(ret); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
test/discounts/TalenProtocolDiscountValidator/SetThreshold.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.23; | ||
|
||
import {Ownable} from "solady/auth/Ownable.sol"; | ||
import {TalentProtocolDiscountValidatorBase} from "./TalentProtocolDiscountValidatorBase.t.sol"; | ||
|
||
contract SetThreshold is TalentProtocolDiscountValidatorBase { | ||
function test_reverts_whenCalledByNonOwner() public { | ||
vm.prank(userA); | ||
vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); | ||
validator.setThreshold(0); | ||
} | ||
|
||
function test_allowsOwnerToSetThreshold() public { | ||
vm.prank(owner); | ||
validator.setThreshold(1); | ||
assertEq(1, validator.threshold()); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
test/discounts/TalenProtocolDiscountValidator/TalentProtocolDiscountValidatorBase.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
//SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.23; | ||
|
||
import {Test} from "forge-std/Test.sol"; | ||
import {TalentProtocolDiscountValidator} from "src/L2/discounts/TalentProtocolDiscountValidator.sol"; | ||
import {MockBuilderScorePassport} from "test/mocks/MockBuilderScorePassport.sol"; | ||
|
||
contract TalentProtocolDiscountValidatorBase is Test { | ||
MockBuilderScorePassport talent; | ||
TalentProtocolDiscountValidator validator; | ||
address owner = makeAddr("owner"); | ||
address userA = makeAddr("userA"); | ||
address userB = makeAddr("userB"); | ||
|
||
uint256 threshold = 50; | ||
|
||
function setUp() public { | ||
talent = new MockBuilderScorePassport(threshold); | ||
validator = new TalentProtocolDiscountValidator(owner, address(talent), threshold); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.23; | ||
|
||
import {TalentProtocol} from "src/L2/discounts/TalentProtocolDiscountValidator.sol"; | ||
|
||
contract MockBuilderScorePassport is TalentProtocol { | ||
uint256 score; | ||
|
||
constructor(uint256 score_) { | ||
score = score_; | ||
} | ||
|
||
function getScoreByAddress(address) external view returns (uint256) { | ||
return score; | ||
} | ||
|
||
function setScore(uint256 score_) external { | ||
score = score_; | ||
} | ||
} |