Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion PAYLOAD_ID_ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# Payload ID Architecture - Unified Design

## Overview

Unified payload ID structure for all three payload types: Write, Trigger, and Message.

## Payload ID Structure

### Bit Layout (256 bits total)

```
[Origin: 64 bits][Verification: 64 bits][Pointer: 64 bits][Reserved: 64 bits]
```

Each component breakdown:

- **Origin (64 bits)**: `chainSlug (32 bits) | switchboardId/watcherId (32 bits)`
- **Verification (64 bits)**: `chainSlug (32 bits) | switchboardId/watcherId (32 bits)`
- **Pointer (64 bits)**: Counter value
Expand All @@ -19,6 +22,7 @@ Each component breakdown:
## Payload Type Specifications

### 1. Write Payloads (EVMX → On-chain)

- **Origin**: `evmxChainSlug (32) | watcherId (32)`
- Generated by: Watcher (on EVMX)
- Verified by: Watcher offchain (links source)
Expand All @@ -31,6 +35,7 @@ Each component breakdown:
**Where Created**: `Watcher.sol` → `getCurrentPayloadId()`

### 2. Trigger Payloads (On-chain → EVMX)

- **Origin**: `srcChainSlug (32) | srcSwitchboardId (32)`
- Generated by: FastSwitchboard
- Verified by: Watcher offchain (verifies source)
Expand All @@ -43,6 +48,7 @@ Each component breakdown:
**Where Created**: `FastSwitchboard.sol` → `processPayload()`

### 3. Message Payloads (Plug → Plug)

- **Origin**: `srcChainSlug (32) | srcSwitchboardId (32)`
- Generated by: MessageSwitchboard
- Verified by: Destination switchboard (checks source)
Expand All @@ -57,18 +63,21 @@ Each component breakdown:
## Decoding and Verification

### Socket Verification (Destination)

1. Decode `payloadId` using `getVerificationInfo(payloadId)`
2. Extract `verificationChainSlug` and `verificationSwitchboardId`
3. Verify against local config:
- `verificationChainSlug == local chainSlug`
- `verificationSwitchboardId == local switchboardId`

### Source Verification (Off-chain Watcher)

1. Decode `payloadId` using `getOriginInfo(payloadId)`
2. Extract `originChainSlug` and `originId`
3. Verify source configuration matches expected values

### Payload Type Detection

- Check if `originChainSlug` or `verificationChainSlug` matches `evmxChainSlug`
- If `originChainSlug == evmxChainSlug`: **Write Payload**
- If `verificationChainSlug == evmxChainSlug`: **Trigger Payload**
Expand All @@ -79,33 +88,39 @@ Each component breakdown:
### IdUtils.sol Functions

#### Encoding

- `createPayloadId(originChainSlug, originId, verificationChainSlug, verificationId, pointer)`
- Creates new payload ID with all components

#### Decoding

- `decodePayloadId(payloadId)` - Full decode
- `getVerificationInfo(payloadId)` - Gets verification components (for Socket routing)
- `getOriginInfo(payloadId)` - Gets origin components (for source verification)

### Required Updates

1. **Watcher.sol**

- Update `getCurrentPayloadId()` to use new format
- Use `evmxSlug` as origin chain slug
- Use hardcoded `watcherId = 1` for now
- Get `dstSwitchboardId` from `switchboards` mapping

2. **FastSwitchboard.sol**

- Add state variables: `evmxChainSlug`, `watcherId` (with onlyOwner setters)
- Implement `processPayload()` to create payload ID
- Add counter: `uint64 public triggerPayloadCounter`
- Use: `origin = (chainSlug, switchboardId)`, `verification = (evmxChainSlug, watcherId)`

3. **MessageSwitchboard.sol**

- Update `_createDigestAndPayloadId()` to use new format
- Use: `origin = (chainSlug, switchboardId)`, `verification = (dstChainSlug, dstSwitchboardId)`

4. **Socket.sol**

- Update `execute()` to decode payload ID and verify verification components
- Remove old `createPayloadId` usage
- Use `getVerificationInfo()` to extract routing info
Expand All @@ -116,16 +131,19 @@ Each component breakdown:
## Security Considerations

### Verification Flow

1. **Destination (Socket)**: Verifies verification component matches local config
2. **Source (Watcher offchain)**: Verifies origin component matches expected source
3. **Pointer verification**: Skipped for now (to be added later)

### Counter Management

- Each switchboard maintains its own counter
- Prevents cross-switchboard collisions
- Counters are monotonic (never decrease)

### ID Uniqueness

- Guaranteed by switchboard-specific counters
- Origin + Verification provide additional context
- Reserved bits allow future expansion without breaking changes
Expand All @@ -141,4 +159,3 @@ Each component breakdown:
- Add pointer verification mechanism
- Use reserved bits for additional metadata (payload version, flags, etc.)
- Support multiple watchers (remove hardcoded watcherId = 1)

13 changes: 6 additions & 7 deletions contracts/evmx/base/AppGatewayBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "../helpers/AddressResolverUtil.sol";
import "../interfaces/IAppGateway.sol";
import "../interfaces/IForwarder.sol";
import "../interfaces/IPromise.sol";
import "../interfaces/IERC20.sol";
import "../interfaces/IGasAccountToken.sol";

import {InvalidPromise, AsyncModifierNotSet} from "../../utils/common/Errors.sol";
import {FAST, READ, WRITE, SCHEDULE} from "../../utils/common/Constants.sol";
Expand Down Expand Up @@ -111,8 +111,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway {
return bytes32(0);
}

onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_])
.getOnChainAddress();
onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress();
}

////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -160,22 +159,22 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway {
uint256 nonce,
bytes memory signature
) = abi.decode(feesApprovalData_, (address, uint256, uint256, uint256, bytes));
IERC20(address(feesManager__())).permit(spender, value, deadline, nonce, signature);
gasAccountToken__().permit(spender, value, deadline, nonce, signature);
}

/// @notice Withdraws fee tokens
/// @param chainSlug_ The chain slug
/// @param token_ The token address
/// @param amount_ The amount
/// @param receiver_ The receiver address
function _withdrawCredits(
function _withdrawToChain(
uint32 chainSlug_,
address token_,
uint256 amount_,
address receiver_
) internal {
IERC20(address(feesManager__())).approve(address(feesManager__()), type(uint256).max);
feesManager__().withdrawCredits(
gasAccountToken__().approve(address(gasAccountManager__()), type(uint256).max);
gasAccountManager__().withdrawToChain(
chainSlug_,
token_,
amount_,
Expand Down
Loading