EVM Message Lifecycle

1. Transfer

A client calls on transfer to initiate an NTT transfer. The client must specify at minimum, the amount of the transfer, the recipient chain, and the recipient address on the recipient chain. transfer also supports a flag to specify whether the NttManager should queue rate-limited transfers or revert. Clients can also include additional instructions to forward along to the Transceiver on the source chain. Depending on mode set in the initial configuration of the NttManager contract, transfers are either "locked" or "burned". Once the transfer has been forwarded to the Transceiver, the NttManager emits the TransferSent event.

Events

/// @notice Emitted when a message is sent from the nttManager.
/// @dev Topic0
///      0x9716fe52fe4e02cf924ae28f19f5748ef59877c6496041b986fbad3dae6a8ecf
/// @param recipient The recipient of the message.
/// @param amount The amount transferred.
/// @param fee The amount of ether sent along with the tx to cover the delivery fee.
/// @param recipientChain The chain ID of the recipient.
/// @param msgSequence The unique sequence ID of the message.
event TransferSent(
    bytes32 recipient, uint256 amount, uint256 fee, uint16 recipientChain, uint64 msgSequence
);

2. Rate Limit

A transfer can be rate-limited both on the source and destination chains. If a transfer is rate-limited on the source chain and the shouldQueue flag is enabled, it is added to an outbound queue. The transfer can be released after the configured _rateLimitDuration has expired via the completeOutboundQueuedTransfer method. The OutboundTransferQueued and OutboundTransferRateLimited events are emitted.

If the client attempts to release the transfer from the queue before the expiry of the rateLimitDuration, the contract reverts with a OutboundQueuedTransferStillQueued error.

Similarly, transfers that are rate-limited on the destination chain are added to an inbound queue. These transfers can be released from the queue via the completeInboundQueuedTransfer method. The InboundTransferQueued event is emitted.

If the client attempts to release the transfer from the queue before the expiry of the rateLimitDuration, the contract reverts with a InboundQueuedTransferStillQueued error.

To disable the rate-limiter, set _rateLimitDuration to 0 and enable the _skipRateLimiting field in the NttManager constructor. Configuring this incorrectly will throw an error. If the rate-limiter is disabled, the inbound and outbound rate-limits can be set to 0.

Events

/// @notice Emitted whenn an outbound transfer is queued.
/// @dev Topic0
///      0x69add1952a6a6b9cb86f04d05f0cb605cbb469a50ae916139d34495a9991481f.
/// @param queueSequence The location of the transfer in the queue.
event OutboundTransferQueued(uint64 queueSequence);
/// @notice Emitted when an outbound transfer is rate limited.
/// @dev Topic0
///      0x754d657d1363ee47d967b415652b739bfe96d5729ccf2f26625dcdbc147db68b.
/// @param sender The initial sender of the transfer.
/// @param amount The amount to be transferred.
/// @param currentCapacity The capacity left for transfers within the 24-hour window.
event OutboundTransferRateLimited(
    address indexed sender, uint64 sequence, uint256 amount, uint256 currentCapacity
);
/// @notice Emitted when an inbound transfer is queued
/// @dev Topic0
///      0x7f63c9251d82a933210c2b6d0b0f116252c3c116788120e64e8e8215df6f3162.
/// @param digest The digest of the message.
event InboundTransferQueued(bytes32 digest);

3. Send

Once the NttManager forwards the message to the Transceiver, the message is transmitted via the sendMessage method. The method signature is enforced by the Transceiver but transceivers are free to determine their own implementation for transmitting messages. (e.g. a message routed through the Wormhole Transceiver can be sent via standard relaying, a specialized or custom relayer, or manually published via the core bridge).

Once the message has been transmitted, the contract emits the SendTransceiverMessage event.

Events

/// @notice Emitted when a message is sent from the transceiver.
/// @dev Topic0
///      0x53b3e029c5ead7bffc739118953883859d30b1aaa086e0dca4d0a1c99cd9c3f5.
/// @param recipientChain The chain ID of the recipient.
/// @param message The message.
event SendTransceiverMessage(
    uint16 recipientChain, TransceiverStructs.TransceiverMessage message
);
  1. Receive

Once a message has been emitted by a Transceiver on the source chain, an off-chain process (for example, a relayer) will forward the message to the corresponding Transceiver on the recipient chain. The relayer interacts with the Transceiver via an entrypoint for receiving messages. For example, the relayer will call the receiveWormholeMessage method on the WormholeTransceiver contract to execute the message. The ReceiveRelayedMessage event is emitted during this process.

This method should also forward the message to the NttManager on the destination chain. Note that the the Transceiver interface does not declare a signature for this method because receiving messages is specific to each Transceiver, and a one-size-fits-all solution would be overly restrictive.

The NttManager contract allows an M of N threshold for Transceiver attestations to determine whether a message can be safely executed. For example, if the threshold requirement is 1, the message will be executed after a single Transceiver delivers a valid attestation. If the threshold requirement is 2, the message will only be executed after two Transceivers deliver valid attestations. When a message is attested to by a Transceiver, the contract emits the MessageAttestedTo event.

NTT implements replay protection, so if a given Transceiver attempts to deliver a message attestation twice, the contract reverts with TransceiverAlreadyAttestedToMessage error. NTT also implements replay protection against re-executing messages. This check also acts as reentrancy protection as well.

If a message had already been executed, the contract ends execution early and emits the MessageAlreadyExecuted event instead of reverting via an error. This mitigates the possibility of race conditions from transceivers attempting to deliver the same message when the threshold is less than the total number of available of Transceivers (i.e. threshold < totalTransceivers) and notifies the client (off-chain process) so they don't attempt redundant message delivery.

Events

/// @notice Emitted when a relayed message is received.
/// @dev Topic0
///      0xf557dbbb087662f52c815f6c7ee350628a37a51eae9608ff840d996b65f87475
/// @param digest The digest of the message.
/// @param emitterChainId The chain ID of the emitter.
/// @param emitterAddress The address of the emitter.
event ReceivedRelayedMessage(bytes32 digest, uint16 emitterChainId, bytes32 emitterAddress);
/// @notice Emitted when a message is received.
/// @dev Topic0
///     0xf6fc529540981400dc64edf649eb5e2e0eb5812a27f8c81bac2c1d317e71a5f0.
/// @param digest The digest of the message.
/// @param emitterChainId The chain ID of the emitter.
/// @param emitterAddress The address of the emitter.
/// @param sequence The sequence of the message.
event ReceivedMessage(
    bytes32 digest, uint16 emitterChainId, bytes32 emitterAddress, uint64 sequence
);
/// @notice Emitted when a message has already been executed to
///         notify client of against retries.
/// @dev Topic0
///      0x4069dff8c9df7e38d2867c0910bd96fd61787695e5380281148c04932d02bef2.
/// @param sourceNttManager The address of the source nttManager.
/// @param msgHash The keccak-256 hash of the message.
event MessageAlreadyExecuted(bytes32 indexed sourceNttManager, bytes32 indexed msgHash);

6. Mint or Unlock

Once a transfer has been successfully verified, the tokens can be minted (if the mode is "burning") or unlocked (if the mode is "locking") to the recipient on the destination chain. Note that the source token decimals are bounded betweeen 0 and TRIMMED_DECIMALS as enforced in the wire format. The transfer amount is untrimmed (scaled-up) if the destination chain token decimals exceed TRIMMED_DECIMALS. Once the approriate number of tokens have been minted or unlocked to the recipient, the TransferRedeemed event is emitted.

Events

/// @notice Emitted when a transfer has been redeemed
///         (either minted or unlocked on the recipient chain).
/// @dev Topic0
///      0x504e6efe18ab9eed10dc6501a417f5b12a2f7f2b1593aed9b89f9bce3cf29a91.
/// @param digest The digest of the message.
event TransferRedeemed(bytes32 indexed digest);

Last updated