GM Points
On-chain points system for GM Lending and Looping activity
The GM Points contract stores tracks users that lend and borrow USDC via the Lending Pool and attributes points for their activity. Users also get points boosts for the number of referrals they acquire. The contract inherits from Solmate's Auth and OpenZeppelin's Pausable contracts.
constructor
At deployment, the constructor sets the deployer (msg.sender
) as the owner (referred to hereafter as Admin). It also converts the _poolContract
address into a LendingPool
contract instance and converts the usdcTokenAddress
into an ERC20 token instance.
Admin Functions
pause
Pauses functions with the whenNotPaused
modifier. Only the Admin can call.
unpause
Unpauses functions with the whenNotPaused
modifier. Only the Admin can call.
setPointsParameters
Sets point accrual ending time, points earned per dollar for both lending and borrowing, point boost per referral, and max referral boost allowed. This can only be called by the Admin.
Parameters:
Name | Type | Description |
---|---|---|
| uint256 | The time the Points Program ends |
| uint256 | Points per USDC dollar lent per day |
| uint256 | Points per USDC dollar borrowed per day |
| uint256 | The point boost per referral |
| uint256 | The maximum point boost a user can obtain for referrals. |
addReferrer
Sets an address as the caller's referrer. The referrer address cannot be the zero address, and the referral address also cannot be its own referrer. For the referral address, the call sets userRefs[msg.sender].hasReferrer
to true (this locks in the referrer, which can only be set once), stores the address in userRefs[msg.sender].referrer
. Then, the referral's address is added to the referrals
array in the referrer's userRefs
struct.
If the referral address calls this function again with a referrer set, it will revert.
Upon success, the call emits a ReferrerAdded
event with the caller's address and the new referrer's address as parameters.
Parameters:
Name | Type | Description |
---|---|---|
| address | The proposed referrer address of the caller, e.g. the referral. |
updatePoints
This public wrapper only allows the pool contract to call and update users' points. This enables claimPoints()
to call _updatePoints()
internally. See _updatePoints()
for logic description.
Parameters:
Name | Type | Description |
---|---|---|
| address | The user to calculate points for |
| PoolActivity | The pool action that called |
| uint256 | The amount of USDC tokens in the latest transaction |
Returns:
Name | Type | Description |
---|---|---|
| uint256 | The lending points earned since |
| uint256 | The borrowing points earned since |
getUserPoints
Retrieves a user's stored points, based off the lastUpdateTime
, which is the last time the user transacted with the pool. Thus, this does not return floating points (not-yet-stored points).
Parameters:
Name | Type | Description |
---|---|---|
| address | The user to query |
Returns:
Name | Type | Description |
---|---|---|
| uint256 | The time the Points Program ends |
| uint256 | Points per USDC dollar lent per day |
| uint256 | Points per USDC dollar borrowed per day |
| uint256 | The point boost per referral |
| uint256 | The maximum point boost a user can obtain for referrals. |
getTotalProgramStats
Retrieves program-wide number of users and points.
Returns:
Name | Type | Description |
---|---|---|
| uint256 | The total number of users that have points |
| uint256 | The total points earned by all users lending USDC, including referral boosts. |
| uint256 | The total points earned by all users borrowing USDC, including referral boosts |
getUserReferrals
Retrieves an array of a given user's referrals.
Parameters:
Name | Type | Description |
---|---|---|
| address | The user to query |
Returns:
Name | Type | Description |
---|---|---|
| address[] memory | The referrer's address array of referrals. |
calculateCurrentBoost
Calculates and returns a user's current points boost up to a predefined maximum. If there are no eligible referrals, then points boost defaults to PRECISION, which is 10_000
. Looping through the user's referrals array, the USDC balance in the Lending Pool is retrieved, and numEligibleRefs
increases by one with each referral that has over the MIN_DEPOSIT_AMOUNT
in the Lending Pool.
The totalBoost
is then calculated by multiplying the referralBoost
by numEligibleRefs
and adding that to PRECISION
. If the resulting number is above maxReferralBoost
, the total Boost is set to the max boost allowed and returned.
A user's boost can fluctuate and is freshly calculated at each updatePoints()
and claimPoints()
call. Note: this function only calculates the user's potential boost. It does not take into account the user's own point eligibility (e.g. min deposit check). That is determined in calculateFloatingPoints()
.
Parameters:
Name | Type | Description |
---|---|---|
| address | The user to calculate the boost for |
calculateFloatingPoints
Calculates the not-yet-stored "floating" points for a given user. Users can call this function at any time. As an example, a user can enter their address, 0 for a "deposit" transaction, 0 for the _amount
, and can enter their current boost retrieved from calculateCurrentBoost()
.
First, the days since the lastUpdateTime
are calculated. If it is past the pointsEndTime
, lastUpdateTime
becomes pointsEndTime
. The call then checks if the user's previously lending balance was above the MIN_DEPOSIT_AMOUNT
to be eligible for points. If not, then the function returns (0, 0)
.
When called in the updatePoints()
call chain, balances are retrieved after successful pool balance operations, so the user's USDC pool balance change that began the call is "undone" before calculating the floating points, since the new points should be earned based on the previously existing balance, if any. If the action was a deposit or withdraw, the USDC lending points are based on the user's pre-deposit/withdraw balance. Since no changes were made to the borrow balance, the currentBorrowBalance
is used to calculate borrow points. If the action was a borrow or repay, the USDC borrowing points are based on the user's pre-borrow/repay balance. Since no changes were made to the lend balance, the currentLendBalance
is used to calculate lending points.
Floating Points = (amount [borrowed/lent] * Points Per Day factor * days * total referral boost) / PRECISION.
Parameters:
Name | Type | Description |
---|---|---|
| address | The time the Points Program ends |
| uint8 | The uint8 representation of a specific PoolActivity Enum element |
| uint256 | The amount of USDC tokens in the latest transaction |
| uint256 | The maximum point boost a user can obtain for referrals |
Returns:
Name | Type | Description |
---|---|---|
| uint256 | Points for lending USDC that have been earned but not yet stored |
| uint256 | Points for borrowing USDC that have been earned but not yet stored |
claimPoints
Claims all floating points for a user. Since points live on-chain, and there is no automatic way to claim them for users, this function is primarily for calculating users' final floating points, adding them to existing points, and storing them after the Points Program ends. If block.timestamp
is past pointsEndTime
, the function will revert. If the user has no earned points to claim, the function will revert.
The remaining floating points are calculated via an internal call to _updatePoints()
. The user's totalEarnedPts
are then stored in a temporary variable and zeroed out in the user's userPoints
struct to combat reentrancy. The points are then finally stored in the claimedPoints
member of the struct. A Claim
event is emitted with the address of the caller and the totalEarnedPts
as parameters.
Returns:
Name | Type | Description |
---|---|---|
| uint256 | The total amount of points a user has earned over the entire program. |
_updatePoints
All the main functions and calculations are tied together here. Using the update time, the current boost, and the floating points, user point structs are updated with the latest calculated points.
If userPoints[_user].lastUpdateTime
is zero, then the address is a brand new user and programStats.totalNumUsers
increments by one. Then the lastUpdateTime
is set to the block.timestamp
and (0, 0)
is returned.
The user's total points boost is obtained from calculateCurrentBoost()
and is passed into calculateFloatingPoints()
, which returns the floatingLendPoints
and floatingBorrowPoints
. The user's lastUpdateTime
is updated to either block.timestamp
or pointsEndTime
, whichever time is earlier. The function then updates the following struct members with either floatingLendPoints
, floatingBorrowPoints
, or the sum of both: userPoints[_user].lendingUSDCPoints
, programStats.totalLendingPoints
, userPoints[_user].borrowingUSDCPoints
, programStats.totalBorrowingPoints
, and userPoints[_user].totalEarnedPoints
.
The function then returns the floating points, albeit with different names: newLendPoints
and newBorrowPoints
.
Parameters:
Name | Type | Description |
---|---|---|
| address | The user that will potentially accrue points |
| PoolActivity | The pool action that called |
| uint256 | The amount of USDC tokens in the latest transaction |
Returns:
Name | Type | Description |
---|---|---|
| uint256 | The lending points earned since |
| uint256 | The borrowing points earned since |
_updateTime
If after pointsEndTime
, then this function returns the pointsEndTime
. If not, it returns block.timestamp
.
Returns:
Name | Type | Description |
---|---|---|
unnamed | uint256 |
|
Last updated