Smart-contract

Smart-contract plays an important role in RLN protocol. Having it may confuse you, as you can think that RLN protocol should be used on-chain. However, the registry and slashing are the only mechanisms that happen on-chain. The actual usage of RLN after a user registers, is off-chain, and if they break the rate-limit they will be slashed and removed from the registry on-chain.

As it was said smart-contract only plays a registry role in the protocol. Though it's not necessary to use on-chain smart-contract registry - it's a good practice, because the best fit for RLN is non-consensus anonymous environments, and the only thing that should be truly/verifiably consistent is a user set, that's why to store it we need a registry, preferably on a blockchain.

You can find a Foundry project for the RLN registry in our GitHub. It was audited internally by PSE.

The project contains:

  • RLN.sol - main contract; functionality for registration and withdrawal (or slashing);
  • Verifier.sol & IVerifier.sol - generated contracts for zkSNARK (Groth16) verification used for withdrawal (or slashing).

We'll focus on main components of the registry contract and we'll skip verifier, cause it was generated by SnarkJS.


Registry contract

Constructor

Constructor gets all the necessary parameters for initialization of the contract. These parameters are immutable, so it's not possible to change it after the initialization.

It takes following parameters:

  • minimalDeposit: minimal membership deposit, cost of 1 message;
  • maximalRate: maximal rate - maximum amount of messages one can send;
  • depth: depth of the merkle tree;
  • feePercentage: fee percentage;
  • feeReceiver: address of the fee receiver;
  • freezePeriod: amount of blocks for withdrawal time-lock;
  • _token: address of the ERC20 contract;
  • _verifier: address of the Groth16 Verifier.

Fees play important role in the economical side of the protocol. They prevent self-slashing attack vector, by making it economically inefficient; and also fees prevent money laundering schemes.

Registration

Function register is used for registration. It receives \(identityCommitment\) as the argument as well as stake amount. Then it calculate messageLimit, based on the stake amount: \[messageLimit = amount / MINIMAL\_DEPOSIT\].

Then it stores the \(identityCommitment\) in the users' set, and emits an event. Client nodes that need to listen to the event, cause based on that they calculate \(rateCommitment\) value. It's important part, because by doing that we don't need to calculate Poseidon hash on-chain, that's not cheap.

It's also important to understand that we may not want to have different rate-limits for different users. We can enforce that by setting the hard value of stake amount.

Withdraw | Slashing

There are two terms: withdraw and slash used in the RLN protocol. Let's define them:

  • withdraw - when the account that withdraws an \(identityCommitment\) (and stake) is the same that registered it;
  • slash - when the account that withdraws an \(identityCommitment\) (and stake) is different to the one that registered it.

It's important to separate these terms, because in withdraw method, users don't have to pay fees. The reason is if it's the same account that's used for withdrawal, then it's clearly not a money laundering. That's not the case with slashing, and that's why users pay fee for slashing.

Self slashing is not a problem, cause users will still lose some amount of their stake, so it's economically inefficient.

But there was a problem with self withdrawal with front-run. It's when users spam and quickly remove/withdraw themselves before others can do it. The problem is solved with time-lock withdrawal: when users initialize the withdraw, their funds being locked to freezePeriod amount of blocks, giving others an opportunity to slash (slashing happens immediately).

Withdraw function emits an event, and it's important for client nodes to listen to it, as they should remove the removed \(identityCommitment\) from their membership Merkle tree, because if they don't - removed user will be able to spam.


Important notes

You may have a question - who's fees receiver. In general that's the application-level question, because the creator of RLN app may want to receive them or donate.

We recommend to burn the fees.

But it's important to understand - to burn (send to zero address or call the burn function in the contract of token) centralized ERC20 stablecoin tokens such as USDT or USDC is the synonymous with transferring money to these companies. What we recommend is to create another contract that'll be fee receiver. The tokens will be locked on the contract, and it'll only contain one function - burn. Anyone can call this burn function, and it'll swap locked tokens to the ETH on any DEX, pay for the caller gas and burn swapped ETH.