# Peggy rate-limit design ## 1. High level design The module is meant as a safety control in the event of a bug, attack, or economic failure of an external zone. It prevents massive inflows or outflows of erc20 or Injective's tokens to/from Injective in a short time frame. - The implementation for rate-limit has already existed. However, the existing design is lacking as it doesn't take into account of Peggy. We should come up with a new design to work with both Peggy and IBC, ideally, re-use some of the design from existing rate-limit module. - In the existing rate-limit module, the module keeps track of throughput of IBC channels via IBC middleware so that it can stop transfering in/out on any channel whose throughput exceeds its configured limit. We can use the same model in our module but with some modification to adapt for Peggy. Thus, the module has a universal state which is the throughput data of tokens coming from IBC channels and Peggy, BUT the logic to fetch the throughput data and to restrict transfering is different for IBC token and Peggy token. The net inflow and outflow is used (rather than the total inflow/outflow) to prevent DOS attacks where someone repeatedly sends the same token back and forth across the same channel, causing the rate limit to be reached. Lets say about example above, some one send 5 tokens back and forth 2 times can reach the rate-limit if we using total in/out flow. But if we net, actually its zero. ## 2. Detailed Design Each rate limit is defined by the following three components: - TokenId: Defines the Denom and Eth Token Contract. - Quota: Defines the rate limit time duration (Duration) and the max threshold for in/out flows (`MaxPercentRecv` and `MaxPercentSend`). - Flow: Stores the current Inflow, Outflow and TokenSupply. Each time a quota expires, the inflow and outflow get reset to 0 and the TokenSupply gets recalculated by query total supply. Throughout the rate-limit epoch, the inflow and outflow each increase monotonically. The net flow is used when determining if a transfer would exceed the quota. * For sending from Injective to Ethereum. Exeed quota if `(OutFlow - InFlow + tx.Amount) / TokenSupply > MaxPercentSend` * For sending from Ethereum to Injective. Exeed quota if `(InFlow - OutFlow + tx.Amount) / TokenSupply > MaxPercentRecv` For peggy's rate-limit implementation, we focus on 2 flows: * Transfer from Ethereum to Injective (Claim) * Transfer from Injective to Ethereum (Withdrawn) ### a. Send from Ethereum to Injective ![sendToInj.drawio (3)](https://hackmd.io/_uploads/S1ttHi9DT.png) ERC-20 tokens are transferred from Ethereum to Injective through the following mechanism: 1. **Depositing ERC-20 tokens on the Peggy Contract:** A user initiates a transfer of ERC-20 tokens from Ethereum to Injective by calling the `SendToCosmos` function on the Peggy contract which deposits tokens on the Peggy contract and emits a `SendToCosmosEvent`. The deposited tokens will remain locked until withdrawn at some undetermined point in the future. This event contains the amount and type of tokens, as well as a destination address on the Injective Chain to receive the funds. 2. **Confirming the deposit:** Each peggo orchestrator witnesses the `SendToCosmosEvent` and sends a `MsgDepositClaim` which contains the deposit information to the Peggy module (inflow) 3. **Check rate-limit**: Check whether `msg.Amount` has exceeded the threshold of inflow rate-limit or not. - If not: the deposit succeeds and will then be processed, after that increase InFlow. - If the asset is Ethereum originated, the tokens are minted and transferred to the intended recipient's address on the Injective Chain. - If the asset is Cosmos originated, the coins are unlocked and transferred to the intended recipient's address on the Injective Chain. - Else: the transfer was rejected. - If `msg.Amount` greater than or equal to inflow rate-limit thresh hold => add to `OutgoingPool` => refund to user (TBD for fee resend (from user or protocol)) - If `msg.Amount` smaller than inflow rate-limit thresh hold => handle it in the next rate-limit epoch (this tx will be prioritied) ### b. Send from Injective to Ethereum ![sendToEth.drawio (1)](https://hackmd.io/_uploads/SknZaRzD6.png) 1. Request Withdrawal from Injective: A user can initiate the transfer of assets from the Injective Chain to Ethereum by sending a MsgSendToEth transaction to the peggy module. * Check `msg.Amount` could exeed the rate-limit of out flow threshold. * If not, increase OutFlow. * Else we reject the tx. * Check Denom types then add tx to Outgoing pool. 2. After passing rate-limit and tx was added to OutGoingPool. Flow will be same as before. ## Example Walk-Through Using the example above, let's say we created a 24 hour rate limit on `peggy0xdAC17F958D2ee523a2206206994597C13D831ec7` (`usdt`), with a 10% send and receive threshold. 1. At the start of the ratelimit epoch, the supply will be queried, to determine the total supply of token. Let's say the total supply was 100. 2. If someone transferred `8usdt` from `Ethereum -> Injective`, the `Inflow` would increment by 8. 3. If someone tried to transfer another `8usdt` from `Ethereum -> Injective`, it would exceed the quota since `(8+8)/100 = 16%` (which is greater than 10%), and with the amount smaller than inflow rate-limit threshold (8 < 10), the transfer tx will be added to the store to resend in the next rate-limit epoch. 4. If someone tried to transfer `12peggy0xdAC17F958D2ee523a2206206994597C13D831ec7` from `Injective -> Ethereum`, the `Outflow` would increment by 12. Notice, even though 12 is greater than 10% the total channel value, the _net_ outflow is only `4peggy0xdAC17F958D2ee523a2206206994597C13D831ec7` (since it's offset by the `8usdt` `Inflow`). As a result, this transaction would succeed. 5. Now if the person in (3) attempted to retry their transfer of`8usdt` from `Ethereum -> Injective`, the `Inflow` would increment by 8 and the transaction would succeed (leaving a net inflow of 4). 6. Finally, at the end of the 24 hours, the `Inflow` and `Outflow` would get reset to 0 and the `TokenSupply` would be re-calculated. In this example, the new denom value would be 104 (since more `usdt` was sent to Injective, and thus more `peggy0xdAC17F958D2ee523a2206206994597C13D831ec7` was minted). 7. Get pending transfer txs in the previous rate-limit epoch to resend (8usdt) from `Ethreum -> Injective`, the `Inflow` would increment by 8 and the transaction would succeed. 8. Continue process transfer txs in this rate-limit epoch. ![Screenshot 2023-12-28 at 21.23.51](https://hackmd.io/_uploads/rJxMSZoD6.png) ### There are some cases we need to note: - Cause we handle rate-limit txs by peggy module on Injective side.There will be different things for users create tx from Ethereum & Injective when the rate-limit threshold is reached : * On Injective side, we will reject tx immediately. * On Ethereum side, we have to wait tx to be submited to peggy module then reject and refund to user. This lead to it take another fees for refunding. (TBD) Should user pay this fees and we need to think about how to limit tx from Eth side when rate-limit is reached ?