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
);
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
Was this helpful?