mirror of
https://github.com/logos-messaging/waku-rlnv1-contract.git
synced 2026-01-08 01:03:08 +00:00
feat: membership
This commit is contained in:
parent
3d44ca94b9
commit
4a26f8ff0e
8
contracts/IPriceCalculator.sol
Normal file
8
contracts/IPriceCalculator.sol
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.15;
|
||||
|
||||
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);
|
||||
}
|
||||
29
contracts/LinearPriceCalculator.sol
Normal file
29
contracts/LinearPriceCalculator.sol
Normal file
@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.15;
|
||||
|
||||
import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";
|
||||
import {IPriceCalculator} from "./IPriceCalculator.sol";
|
||||
|
||||
/// @title Linear Price Calculator to determine the price to acquire a membership
|
||||
contract LinearPriceCalculator is IPriceCalculator, Ownable {
|
||||
address private token;
|
||||
uint private pricePerMessage;
|
||||
|
||||
constructor(address _token, uint16 _price) Ownable() {
|
||||
token = _token;
|
||||
pricePerMessage = _price;
|
||||
}
|
||||
|
||||
/// Set accepted token and price per message
|
||||
/// @param _token The token accepted by the membership management for RLN
|
||||
/// @param _price Price per message per epoch
|
||||
function setTokenAndPrice(address _token, uint _price) external onlyOwner {
|
||||
token = _token;
|
||||
pricePerMessage = _price;
|
||||
}
|
||||
|
||||
function calculate(uint _rateLimit) external view returns (address, uint) {
|
||||
return (token, _rateLimit * pricePerMessage);
|
||||
}
|
||||
|
||||
}
|
||||
64
contracts/Membership.sol
Normal file
64
contracts/Membership.sol
Normal file
@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.15;
|
||||
|
||||
import {IPriceCalculator} from "./IPriceCalculator.sol";
|
||||
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
|
||||
import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import "openzeppelin-contracts/contracts/utils/Context.sol";
|
||||
|
||||
error IncorrectAmount();
|
||||
error OnlyTokensAccepted();
|
||||
error TokenMismatch();
|
||||
|
||||
error InvalidRateLimit();
|
||||
error ExceedMaxRateLimitPerEpoch();
|
||||
|
||||
contract Membership {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
IPriceCalculator public priceCalculator;
|
||||
|
||||
uint public maxTotalRateLimitPerEpoch;
|
||||
uint16 public maxRateLimitPerMembership;
|
||||
uint16 public minRateLimitPerMembership;
|
||||
|
||||
uint public totalRateLimitPerEpoch;
|
||||
|
||||
function __Membership_init(
|
||||
address _priceCalculator,
|
||||
uint _maxTotalRateLimitPerEpoch,
|
||||
uint16 _maxRateLimitPerMembership,
|
||||
uint16 _minRateLimitPerMembership
|
||||
) internal {
|
||||
priceCalculator = IPriceCalculator(_priceCalculator);
|
||||
maxTotalRateLimitPerEpoch = _maxTotalRateLimitPerEpoch;
|
||||
maxRateLimitPerMembership = _maxRateLimitPerMembership;
|
||||
minRateLimitPerMembership = _minRateLimitPerMembership;
|
||||
}
|
||||
|
||||
function transferMembershipFees(address _from, uint _rateLimit) internal {
|
||||
(address token, uint price) = priceCalculator.calculate(_rateLimit);
|
||||
if (token == address(0)) {
|
||||
if (msg.value != price) revert IncorrectAmount();
|
||||
} else {
|
||||
if (msg.value != 0) revert OnlyTokensAccepted();
|
||||
IERC20(token).safeTransferFrom(_from, address(this), price);
|
||||
}
|
||||
}
|
||||
|
||||
function acquireRateLimit(uint256[] memory commitments, uint _rateLimit) internal {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
pragma solidity 0.8.15;
|
||||
|
||||
import {WakuRln} from "./WakuRln.sol";
|
||||
import {Membership} from "./Membership.sol";
|
||||
import {IPoseidonHasher} from "rln-contract/PoseidonHasher.sol";
|
||||
import {UUPSUpgradeable} from "openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol";
|
||||
import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
|
||||
@ -12,7 +13,7 @@ error NoStorageContractAvailable();
|
||||
error IncompatibleStorage();
|
||||
error IncompatibleStorageIndex();
|
||||
|
||||
contract WakuRlnRegistry is OwnableUpgradeable, UUPSUpgradeable {
|
||||
contract WakuRlnRegistry is OwnableUpgradeable, UUPSUpgradeable, Membership {
|
||||
uint16 public nextStorageIndex;
|
||||
mapping(uint16 => address) public storages;
|
||||
|
||||
@ -27,8 +28,14 @@ contract WakuRlnRegistry is OwnableUpgradeable, UUPSUpgradeable {
|
||||
_;
|
||||
}
|
||||
|
||||
function initialize(address _poseidonHasher) external initializer {
|
||||
modifier onlyValidStorageIndex(uint16 storageIndex) {
|
||||
if (storageIndex >= nextStorageIndex) revert NoStorageContractAvailable();
|
||||
_;
|
||||
}
|
||||
|
||||
function initialize(address _poseidonHasher, address _priceCalculator) external initializer {
|
||||
poseidonHasher = IPoseidonHasher(_poseidonHasher);
|
||||
__Membership_init(_priceCalculator);
|
||||
__Ownable_init();
|
||||
}
|
||||
|
||||
@ -54,6 +61,7 @@ contract WakuRlnRegistry is OwnableUpgradeable, UUPSUpgradeable {
|
||||
}
|
||||
|
||||
function register(uint256[] calldata commitments) external onlyUsableStorage {
|
||||
// TODO: modify function to receive rate limit
|
||||
// iteratively check if the storage contract is full, and increment the usingStorageIndex if it is
|
||||
while (true) {
|
||||
try WakuRln(storages[usingStorageIndex]).register(commitments) {
|
||||
@ -72,16 +80,24 @@ contract WakuRlnRegistry is OwnableUpgradeable, UUPSUpgradeable {
|
||||
}
|
||||
}
|
||||
|
||||
function register(uint16 storageIndex, uint256[] calldata commitments) external {
|
||||
if (storageIndex >= nextStorageIndex) revert NoStorageContractAvailable();
|
||||
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);
|
||||
WakuRln(storages[storageIndex]).register(commitments);
|
||||
}
|
||||
|
||||
function register(uint16 storageIndex, uint256 commitment) external {
|
||||
if (storageIndex >= nextStorageIndex) revert NoStorageContractAvailable();
|
||||
function register(uint16 storageIndex, uint256 commitment) external payable onlyValidStorageIndex(storageIndex) {
|
||||
// optimize the gas used below
|
||||
uint256[] memory commitments = new uint256[](1);
|
||||
commitments[0] = commitment;
|
||||
|
||||
// TODO: modify function to receive the number of messages
|
||||
uint _rateLimit = 4;
|
||||
acquireRateLimit(commitments, _rateLimit);
|
||||
transferMembershipFees(_msgSender(), _rateLimit);
|
||||
|
||||
WakuRln(storages[storageIndex]).register(commitments);
|
||||
}
|
||||
|
||||
|
||||
16
deploy/002_deploy_price_calculator.ts
Normal file
16
deploy/002_deploy_price_calculator.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { HardhatRuntimeEnvironment } from "hardhat/types";
|
||||
import { DeployFunction } from "hardhat-deploy/types";
|
||||
|
||||
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
|
||||
const { deployments, getUnnamedAccounts } = hre;
|
||||
const { deploy } = deployments;
|
||||
|
||||
const [deployer] = await getUnnamedAccounts();
|
||||
|
||||
await deploy("WakuSimplePriceCalculator", {
|
||||
from: deployer,
|
||||
log: true,
|
||||
});
|
||||
};
|
||||
export default func;
|
||||
func.tags = ["WakuSimplePriceCalculator"];
|
||||
@ -10,15 +10,24 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
|
||||
const poseidonHasherAddress = (await deployments.get("PoseidonHasher"))
|
||||
.address;
|
||||
|
||||
const priceCalculatorAddress = (
|
||||
await deployments.get("WakuSimplePriceCalculator")
|
||||
).address;
|
||||
|
||||
const implRes = await deploy("WakuRlnRegistry_Implementation", {
|
||||
contract: "WakuRlnRegistry",
|
||||
from: deployer,
|
||||
log: true,
|
||||
});
|
||||
|
||||
let initializeAbi = ["function initialize(address _poseidonHasher)"];
|
||||
let initializeAbi = [
|
||||
"function initialize(address _poseidonHasher, address _priceCalculator)",
|
||||
];
|
||||
let iface = new hre.ethers.utils.Interface(initializeAbi);
|
||||
const data = iface.encodeFunctionData("initialize", [poseidonHasherAddress]);
|
||||
const data = iface.encodeFunctionData("initialize", [
|
||||
poseidonHasherAddress,
|
||||
priceCalculatorAddress,
|
||||
]);
|
||||
|
||||
await deploy("WakuRlnRegistry_Proxy", {
|
||||
contract: "ERC1967Proxy",
|
||||
@ -30,4 +39,4 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
|
||||
|
||||
export default func;
|
||||
func.tags = ["WakuRlnRegistry"];
|
||||
func.dependencies = ["PoseidonHasher"];
|
||||
func.dependencies = ["PoseidonHasher", "WakuSimplePriceCalculator"];
|
||||
@ -2,6 +2,8 @@
|
||||
pragma solidity ^0.8.15;
|
||||
|
||||
import "../contracts/WakuRlnRegistry.sol";
|
||||
import "../contracts/IPriceCalculator.sol";
|
||||
import "../contracts/LinearPriceCalculator.sol";
|
||||
import {PoseidonHasher} from "rln-contract/PoseidonHasher.sol";
|
||||
import {DuplicateIdCommitment, FullTree} from "rln-contract/RlnBase.sol";
|
||||
import {ERC1967Proxy} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
||||
@ -15,11 +17,14 @@ contract WakuRlnRegistryTest is Test {
|
||||
|
||||
WakuRlnRegistry public wakuRlnRegistry;
|
||||
PoseidonHasher public poseidonHasher;
|
||||
IPriceCalculator public priceCalculator;
|
||||
|
||||
function setUp() public {
|
||||
LinearPriceCalculator p = new LinearPriceCalculator();
|
||||
priceCalculator = IPriceCalculator(address(p));
|
||||
poseidonHasher = new PoseidonHasher();
|
||||
address implementation = address(new WakuRlnRegistry());
|
||||
bytes memory data = abi.encodeCall(WakuRlnRegistry.initialize, address(poseidonHasher));
|
||||
bytes memory data = abi.encodeCall(WakuRlnRegistry.initialize, (address(poseidonHasher), address(priceCalculator)));
|
||||
address proxy = address(new ERC1967Proxy(implementation, data));
|
||||
wakuRlnRegistry = WakuRlnRegistry(proxy);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user