Skip to content

Commit

Permalink
feat: store membership details
Browse files Browse the repository at this point in the history
  • Loading branch information
richard-ramos committed Aug 27, 2024
1 parent e800707 commit 9d2518d
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 41 deletions.
2 changes: 1 addition & 1 deletion contracts/IPriceCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
195 changes: 159 additions & 36 deletions contracts/Membership.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 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, uint _rateLimit) internal {
(address token, uint price) = priceCalculator.calculate(_rateLimit);
if (token == address(0)) {
if (msg.value != price) revert IncorrectAmount();
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 _isExpired(uint256 expirationDate) internal view returns (bool) {
return expirationDate + gracePeriod > block.timestamp;
}

function oldestMembership() public TODO {
return memberships[oldest];
function isExpired(uint256 membershipMapIdx) public view returns (bool) {
return _isExpired(memberships[membershipMapIdx].expirationDate);
}

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 popOldestMembership() public returns (uint256) {
TODO data;
require(newest > oldest);
data = memberships[oldest];
delete memberships[oldest];
oldest += 1;
return data;
function deleteOldestMembership() internal {
require(newestMembership > oldestMembership);
delete memberships[oldestMembership];
oldestMembership += 1;
}

function length() public view returns (uint256) {
return newest - oldest;
function getMembershipLength() public view returns (uint256) {
return newestMembership - oldestMembership;
}
}
6 changes: 2 additions & 4 deletions contracts/WakuRlnRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}
Expand Down

0 comments on commit 9d2518d

Please sign in to comment.