mirror of
https://github.com/logos-messaging/waku-rlnv1-contract.git
synced 2026-01-06 00:03:08 +00:00
feat: store membership details
This commit is contained in:
parent
e800707d6c
commit
9d2518dc16
@ -5,4 +5,4 @@ interface IPriceCalculator {
|
||||
/// Returns the token and price to pay in `token` for some `_rateLimit`
|
||||
/// @param _rateLimit the rate limit the user wants to acquire
|
||||
function calculate(uint _rateLimit) external view returns (address, uint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,85 +12,208 @@ error TokenMismatch();
|
||||
|
||||
error InvalidRateLimit();
|
||||
error ExceedMaxRateLimitPerEpoch();
|
||||
error NotInGracePeriod(uint256 membershipMapIdx);
|
||||
error NotHolder(uint256 membershipMapIdx);
|
||||
|
||||
contract Membership {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
IPriceCalculator public priceCalculator;
|
||||
// TODO START - add owned setters to all these variables
|
||||
|
||||
uint public maxTotalRateLimitPerEpoch;
|
||||
IPriceCalculator public priceCalculator;
|
||||
uint256 public maxTotalRateLimitPerEpoch;
|
||||
uint16 public maxRateLimitPerMembership;
|
||||
uint16 public minRateLimitPerMembership;
|
||||
|
||||
enum MembershipStatus { Undefined, Active, GracePeriod, Expired, ErasedAwaitsWithdrawal, Erased }
|
||||
uint256 public expirationTerm;
|
||||
uint256 public gracePeriod;
|
||||
|
||||
// TODO END
|
||||
|
||||
enum MembershipStatus {
|
||||
NonExistent,
|
||||
Active,
|
||||
GracePeriod,
|
||||
Expired,
|
||||
ErasedAwaitsWithdrawal,
|
||||
Erased
|
||||
}
|
||||
|
||||
uint public totalRateLimitPerEpoch;
|
||||
|
||||
mapping(uint256 => MembershipDetails) public memberships;
|
||||
uint256 public oldestMembership = 1;
|
||||
uint256 public newestMembership = 1;
|
||||
|
||||
struct MembershipDetails {
|
||||
address holder;
|
||||
// TODO: should we store the commitment?
|
||||
uint256 expirationDate;
|
||||
address token;
|
||||
uint256 amount;
|
||||
}
|
||||
|
||||
event ExpiredMembership(uint256 membershipMapIndex, address holder); // TODO: should it contain the commitment?
|
||||
event MembershipExtended(
|
||||
uint256 membershipMapIndex,
|
||||
uint256 newExpirationDate
|
||||
); // TODO: should it contain the commitment?
|
||||
|
||||
function __Membership_init(
|
||||
address _priceCalculator,
|
||||
uint _maxTotalRateLimitPerEpoch,
|
||||
uint16 _maxRateLimitPerMembership,
|
||||
uint16 _minRateLimitPerMembership
|
||||
uint16 _minRateLimitPerMembership,
|
||||
uint _expirationTerm,
|
||||
uint _gracePeriod
|
||||
) internal {
|
||||
priceCalculator = IPriceCalculator(_priceCalculator);
|
||||
maxTotalRateLimitPerEpoch = _maxTotalRateLimitPerEpoch;
|
||||
maxRateLimitPerMembership = _maxRateLimitPerMembership;
|
||||
minRateLimitPerMembership = _minRateLimitPerMembership;
|
||||
expirationTerm = _expirationTerm;
|
||||
gracePeriod = _gracePeriod;
|
||||
}
|
||||
|
||||
function transferMembershipFees(address _from, uint _rateLimit) internal {
|
||||
(address token, uint price) = priceCalculator.calculate(_rateLimit);
|
||||
if (token == address(0)) {
|
||||
if (msg.value != price) revert IncorrectAmount();
|
||||
function registerMembership(
|
||||
address _sender,
|
||||
uint256[] memory commitments,
|
||||
uint _rateLimit
|
||||
) internal {
|
||||
// TODO: for each commitment?
|
||||
(address token, uint amount) = priceCalculator.calculate(_rateLimit);
|
||||
acquireRateLimit(_sender, commitments, _rateLimit, token, amount);
|
||||
transferMembershipFees(
|
||||
_sender,
|
||||
token,
|
||||
amount * _rateLimit * commitments.length
|
||||
);
|
||||
}
|
||||
|
||||
function transferMembershipFees(
|
||||
address _from,
|
||||
address _token,
|
||||
uint _amount
|
||||
) internal {
|
||||
if (_token == address(0)) {
|
||||
if (msg.value != _amount) revert IncorrectAmount();
|
||||
} else {
|
||||
if (msg.value != 0) revert OnlyTokensAccepted();
|
||||
IERC20(token).safeTransferFrom(_from, address(this), price);
|
||||
IERC20(_token).safeTransferFrom(_from, address(this), _amount);
|
||||
}
|
||||
}
|
||||
|
||||
function acquireRateLimit(uint256[] memory commitments, uint _rateLimit) internal {
|
||||
function acquireRateLimit(
|
||||
address _sender,
|
||||
uint256[] memory _commitments,
|
||||
uint256 _rateLimit,
|
||||
address _token,
|
||||
uint256 _amount
|
||||
) internal {
|
||||
// TODO: for each commitment?
|
||||
if (
|
||||
_rateLimit < minRateLimitPerMembership ||
|
||||
_rateLimit > maxRateLimitPerMembership
|
||||
) revert InvalidRateLimit();
|
||||
|
||||
uint newTotalRateLimitPerEpoch = totalRateLimitPerEpoch + _rateLimit;
|
||||
if (newTotalRateLimitPerEpoch > maxTotalRateLimitPerEpoch) revert ExceedMaxRateLimitPerEpoch();
|
||||
|
||||
// TODO: store _rateLimit
|
||||
// TODO:
|
||||
// Epoch length epoch 10 minutes
|
||||
// Membership expiration term T 180 days
|
||||
// Membership grace period G 30 days
|
||||
if (newTotalRateLimitPerEpoch > maxTotalRateLimitPerEpoch) {
|
||||
// Determine if there are any available spot in the membership map
|
||||
// by looking at the oldest membership. If it's expired, we can use it
|
||||
MembershipDetails storage oldestMembershipDetails = memberships[
|
||||
oldestMembership
|
||||
];
|
||||
|
||||
if (isExpired(oldestMembershipDetails.expirationDate)) {
|
||||
emit ExpiredMembership(
|
||||
oldestMembership,
|
||||
memberships[oldestMembership].holder
|
||||
);
|
||||
deleteOldestMembership();
|
||||
// TODO: move balance from expired to the current holder
|
||||
} else {
|
||||
revert ExceedMaxRateLimitPerEpoch();
|
||||
}
|
||||
}
|
||||
|
||||
newestMembership += 1;
|
||||
memberships[newestMembership] = MembershipDetails({
|
||||
holder: _sender,
|
||||
expirationDate: block.timestamp + expirationTerm,
|
||||
token: _token,
|
||||
amount: _amount
|
||||
});
|
||||
}
|
||||
|
||||
mapping (uint256 => TODO) memberships;
|
||||
|
||||
uint256 private oldest = 1;
|
||||
uint256 private newest = 1;
|
||||
function extendMembership(
|
||||
address _sender,
|
||||
uint256[] memory membershipMapIdx
|
||||
) public {
|
||||
for (uint256 i = 0; i < membershipMapIdx.length; i++) {
|
||||
uint256 currentMembershipMapIdx = membershipMapIdx[i];
|
||||
|
||||
// TODO - use a struct to store the membership details: commitment, date, status
|
||||
// TODO - keep track of balances, use msg.sender?
|
||||
MembershipDetails storage mdetails = memberships[
|
||||
currentMembershipMapIdx
|
||||
];
|
||||
|
||||
function pushMembership(TODO data) internal {
|
||||
newest += 1;
|
||||
memberships[newest] = data;
|
||||
if (!_isGracePeriod(mdetails.expirationDate))
|
||||
revert NotInGracePeriod(currentMembershipMapIdx);
|
||||
|
||||
if (_sender != mdetails.holder)
|
||||
revert NotHolder(currentMembershipMapIdx);
|
||||
|
||||
uint256 newExpirationDate = block.timestamp + expirationTerm;
|
||||
|
||||
// TODO: remove current membership
|
||||
// TODO: add membership at the end (since it will be the newest)
|
||||
|
||||
emit MembershipExtended(currentMembershipMapIdx, newExpirationDate);
|
||||
}
|
||||
}
|
||||
|
||||
function oldestMembership() public TODO {
|
||||
return memberships[oldest];
|
||||
function _isExpired(uint256 expirationDate) internal view returns (bool) {
|
||||
return expirationDate + gracePeriod > block.timestamp;
|
||||
}
|
||||
|
||||
function popOldestMembership() public returns (uint256) {
|
||||
TODO data;
|
||||
require(newest > oldest);
|
||||
data = memberships[oldest];
|
||||
delete memberships[oldest];
|
||||
oldest += 1;
|
||||
return data;
|
||||
function isExpired(uint256 membershipMapIdx) public view returns (bool) {
|
||||
return _isExpired(memberships[membershipMapIdx].expirationDate);
|
||||
}
|
||||
|
||||
function length() public view returns (uint256) {
|
||||
return newest - oldest;
|
||||
function _isGracePeriod(
|
||||
uint256 expirationDate
|
||||
) internal view returns (bool) {
|
||||
return
|
||||
block.timestamp >= expirationDate &&
|
||||
block.timestamp <= expirationDate + gracePeriod;
|
||||
}
|
||||
|
||||
function isGracePeriod(
|
||||
uint256 membershipMapIdx
|
||||
) public view returns (bool) {
|
||||
uint256 expirationDate = memberships[membershipMapIdx].expirationDate;
|
||||
return _isGracePeriod(expirationDate);
|
||||
}
|
||||
|
||||
function withdraw() public {}
|
||||
|
||||
// TODO - keep track of balances, use msg.sender
|
||||
|
||||
function getOldestMembership()
|
||||
public
|
||||
view
|
||||
returns (MembershipDetails memory)
|
||||
{
|
||||
return memberships[oldestMembership];
|
||||
}
|
||||
|
||||
function deleteOldestMembership() internal {
|
||||
require(newestMembership > oldestMembership);
|
||||
delete memberships[oldestMembership];
|
||||
oldestMembership += 1;
|
||||
}
|
||||
|
||||
function getMembershipLength() public view returns (uint256) {
|
||||
return newestMembership - oldestMembership;
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,8 +83,7 @@ contract WakuRlnRegistry is OwnableUpgradeable, UUPSUpgradeable, Membership {
|
||||
function register(uint16 storageIndex, uint256[] calldata commitments) external onlyValidStorageIndex(storageIndex) {
|
||||
// TODO: modify function to receive the ratelimit to buy
|
||||
uint _rateLimit = 4;
|
||||
acquireRateLimit(commitments, _rateLimit);
|
||||
transferMembershipFees(_msgSender(), _rateLimit * commitments.length);
|
||||
registerMembership(_msgSender(), commitments, _rateLimit);
|
||||
WakuRln(storages[storageIndex]).register(commitments);
|
||||
}
|
||||
|
||||
@ -95,8 +94,7 @@ contract WakuRlnRegistry is OwnableUpgradeable, UUPSUpgradeable, Membership {
|
||||
|
||||
// TODO: modify function to receive the number of messages
|
||||
uint _rateLimit = 4;
|
||||
acquireRateLimit(commitments, _rateLimit);
|
||||
transferMembershipFees(_msgSender(), _rateLimit);
|
||||
registerMembership(_msgSender(), commitments, _rateLimit);
|
||||
|
||||
WakuRln(storages[storageIndex]).register(commitments);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user