I’ve pushed a change to the GIP adding the callhooks feature I mentioned above.
I can’t edit the opening post so I’ll put the diff below and you can check out the full updated proposal in HackMD.
I’ve also pushed it to radicle, the Graph seed still doesn’t seem to work on the web UI so I also pushed it to pine.radicle.garden.
diff --git a/gips/0031-arbitrum-grt-bridge.md b/gips/0031-arbitrum-grt-bridge.md
index 1333dc0..ed6d096 100644
--- a/gips/0031-arbitrum-grt-bridge.md
+++ b/gips/0031-arbitrum-grt-bridge.md
@@ -56,6 +56,7 @@ The tokens are held in escrow by a separate BridgeEscrow contract, that can be r
- The bridge can burn tokens on L2, which triggers releasing the same amount of tokens escrowed on L1. This requires a second transaction, calling the Arbitrum Outbox after the dispute period, to finalize the transfer.
- Pausing the protocol through the Controller will not pause the bridge - pausing the bridge is done directly by calling setPause on the gateway contract on each layer.
- The bridge shall be integrated with Arbitrum’s L1GatewayRouter (assuming the Arbitrum team agrees after the Council signals intent).
+- When sending tokens from L1, the bridge shall allow a whitelisted set of addresses to send arbitrary calldata to L2 together with tokens; this callhook transaction must be executed when the tokens are minted in L2 by calling the address of the recipient for the tokens with calldata provided by the L1 sender.
## Support for Arbitrum’s Gateway Router
@@ -71,7 +72,7 @@ The main components of the bridge will be a contract in L1 (L1GraphTokenGateway)
Implements the [ITokenGateway](https://github.com/OffchainLabs/arbitrum/blob/master/packages/arb-bridge-peripherals/contracts/tokenbridge/libraries/gateway/ITokenGateway.sol) interface:
-- `outboundTransfer`: initiates a transfer to L2. Tokens will be put in escrow by being transferred to BridgeEscrow. The transfer of tokens must be approved by the sender before calling this.
+- `outboundTransfer`: initiates a transfer to L2. Tokens will be put in escrow by being transferred to BridgeEscrow. The transfer of tokens must be approved by the sender before calling this. Only addresses that are whitelisted can include arbitrary calldata in the `data` parameter.
- `finalizeInboundTransfer`: to be called through `Outbox.executeTransaction` when a transfer from L2 has completed the dispute period. Will release the tokens from escrow and send to the specified address. Callable only from the L2GraphTokenGateway via the Arbitrum Bridge.
- `calculateL2TokenAdress`: returns the address for L2GraphToken (set by governance).
@@ -93,7 +94,7 @@ This contract's main role is to hold GRT and allow the gateway (or in the future
Also implements the [ITokenGateway](https://github.com/OffchainLabs/arbitrum/blob/master/packages/arb-bridge-peripherals/contracts/tokenbridge/libraries/gateway/ITokenGateway.sol) interface, but from the L2 side:
- `outboundTransfer`: initiates a transfer to L1. Tokens will be burnt on L2. The transfer of tokens must be approved by the sender before calling this.
-- `finalizeInboundTransfer`: Callable only from the L1GraphTokenGateway’s L2 alias. Mints tokens on L2 and transfers them to the specified address.
+- `finalizeInboundTransfer`: Callable only from the L1GraphTokenGateway’s L2 alias. Mints tokens on L2 and transfers them to the specified address. If the sender is whitelisted, any additional calldata sent in the `data` parameter will trigger a call to the destination address passing this calldata.
- `calculateL2TokenAdress`: returns the address for L2GraphToken.
`outboundTransfer` and `finalizeInboundTransfer` are pausable through a `paused` property, that is set by:
@@ -182,6 +183,12 @@ Considering these risks, there are three safety features that we could add to th
- Pausability: the same addresses that are currently able to pause Graph protocol contracts should also be able to pause the bridge contracts in case of an issue; this should give the team time to investigate and fix any ongoing breaches without any more assets being lost.
- Escape hatch: In the event of a catastrophic L2 failure (i.e. the Arbitrum L2 completely stops working/existing), we'd like governance to have a way to help users recover the tokens locked in the escrow. The exact failure mode and state of the network at the time would affect how the community decides to react to it, but the proposed implementation does provide a viable escape hatch: governance could provide a Merkle tree based snapshot of the last valid L2 state, and use the escrow's approveAll function to use a contract that checks a user's proof of inclusion to release the funds.
+## Support for whitelisted callhooks
+
+Arbitrum's implementation of the custom gateway used to include support for an `escrowAndCall` behavior, where the tokens sender could include extra calldata together with the tokens transfer. This was removed in the initial release. In our case, however, there are several parts of the potential L2 architecture that could benefit from sending data together with the tokens. In particular, our indexing rewards calculation will likely rely on a supply of tokens for rewards being sent, together with a value for snapshotted token supply that is used in the rewards calculation. Other L1/L2 migration helpers might require sending tokens together with additional information like subgraph signal or indexer delegation.
+
+For these reasons, we propose including the callhook functionality in the bridge, but only for a whitelisted set of addresses. This way, governance can enable callhooks for the trusted relevant contracts (e.g. the RewardsManager or GNS) while not allowing any untrusted calls. If the Arbitrum team release a similar feature in the future (as they are currently working on something like this), we can have a separate GIP to upgrade our implementation to be compatible with theirs.
+
## Repository structure
The bridge can either be set up in its own repository, or integrated into the [graphprotocol/contracts](https://github.com/graphprotocol/contracts) repo. For now, we propose integrating it into the same repository, as there will be some shared code in parts of the gateway contracts (e.g. for the pausable logic). It might also be more convenient when we start adding more protocol-specific behavior.