app.go, using simapp as the reference. It assumes an ibc-go application that already has the transfer module wired. Wire the middleware for IBC v1, IBC v2, or both.
The middleware only understands ICS-20 transfer packets, so it must wrap the transfer application. It cannot sit on a non-transfer route.
Before you begin
Confirm the application already has the middleware’s dependencies:- The transfer keeper and, for IBC v2, the transfer v2 module.
- The IBC keeper, including its
ChannelKeeper,ClientKeeper, and for v2 itsChannelKeeperV2. - The bank keeper.
- A governance authority address, the only account allowed to change limits.
Wire rate limiting for IBC v1
These changes give a complete v1 integration. They add the keeper, register the middleware at the top of the transfer stack, and add the module to the manager.- The last keeper argument is the governance authority. The keeper stores it and enforces it on every message. A chain that uses the standard governance module passes
authtypes.NewModuleAddress(govtypes.ModuleName).String(). - The module runs a begin-block hook and has genesis state, so it must appear in the begin-blocker order and the init and export genesis orderings.
- If the chain already wires other transfer middleware, add rate limiting to the same stack with another
Nextcall rather than creating a new stack. See the packet forward example below.
With packet forward middleware
If the chain runs packet forward middleware, place rate limiting above it in the same stack, so limits apply to the final inbound transfer before it is forwarded:Wire rate limiting for IBC v2
These changes give a complete v2 integration. IBC v2 uses a separate router, and the middleware wraps the transfer v2 module directly.The keeper, store key, module registration, and ordering are shared across versions. If the chain already wired v1 rate limiting above, those parts are done. Only the v2 router wiring is new, so skip to the step that builds
transferModuleV2.app.IBCKeeper.ChannelKeeperV2. Both are required, and the constructor panics if either is nil.
The v2 middleware enforces the same limits as v1. It converts each v2 payload to the internal packet form and calls the same keeper logic, so a denom’s limit applies identically regardless of which IBC version carried the transfer.
What can go wrong
- The application panics on start. The store key is likely unregistered, or a nil dependency reached the v2 constructor.
- Limits never trigger. Confirm the middleware is on the route the transfers use, and that the module is in the begin-blocker order so windows advance.
Next steps
- Configure rate limits through governance.
- Review how limits are evaluated in the rate limiting overview.