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
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ By doing near mainnet testing, developers can quickly check sender authenticatio
| CCIP | ✅ | ✅ |
| a.DI | ✅ | |
| Arbitrum (native) | ✅ | |
| Relay | ✅ | |
| CCTP V2 | ✅ | |
| Circle Gateway | ✅ | |
## Getting Started

### Installation
Expand Down Expand Up @@ -114,6 +117,28 @@ adiHelper.helpMultiBridge(AdiHelper.MultiBridgeArgs({

**AMB endpoint addresses**: read each deployed adapter's configured AMB endpoint via its public getter (`HL_MAIL_BOX()`, `LZ_ENDPOINT()`, `getRouter()`) and pass that to `MultiBridgeArgs`. Do NOT hardcode canonical AMB addresses — deployments may use custom AMB infrastructure (different validator sets / ISMs / etc.).

Circle Gateway (unified USDC balance — there is NO source-chain message to relay; the helper plays Circle's attestation signer):

```solidity
CircleGatewayHelper gw = new CircleGatewayHelper(0); // 0 = default test signer key; persistent across forks

// optional source-side realism: fund + deposit into GatewayWallet on the source fork
gw.helpDeposit(ETH_FORK_ID, ETH_USDC, depositor, 1000e6);

// describe the transfer (Gateway domains: Ethereum 0, Avalanche 1, OP 2, Arbitrum 3, Base 6, Polygon 7, ...)
CircleGatewayHelper.TransferSpec memory spec =
gw.buildSpec(0, 3, ETH_USDC, ARB_USDC, depositor, recipient, destinationCaller, 1000e6, hookData);

// mint on the destination fork through the REAL GatewayMinter (pranked as destinationCaller when set)
CircleGatewayHelper.Attested memory a = gw.help(ARB_FORK_ID, spec);
// or hand the signed payload to a destination adapter that calls gatewayMint itself
gw.helpMintViaAdapter(ARB_FORK_ID, address(adapter), spec);
// or just attest and drive gatewayMint yourself (optionally with an explicit maxBlockHeight)
a = gw.helpAttest(ARB_FORK_ID, spec);
```

`helpSet` / `helpMintViaAdapterSet` / `helpAttestSet` use the `AttestationSet` wire format (several specs, one atomic mint; a set of one stays a set). `a.transferSpecHashes` are the minter's replay keys (`isTransferSpecHashUsed`). Attestations are EIP-191 (`personal_sign` over `keccak256(payload)`), valid through 1000 blocks from the destination fork's block by default; the minter's own checks (signer, expiry, domain, caller, token, replay, denylist, pause) all run for real. A reverting destination call restores your previously selected fork before re-raising.

To display estimations, run the `npm install` and `npm run compile` commands from the [utils/scripts directory](./utils/scripts) before running your tests. Then run tests with the `--ffi` flag and `ENABLE_ESTIMATES` env variable set to `true.`

**Gas estimation** is the gas costs required in native tokens to pay for the message delivery.
Expand Down
383 changes: 383 additions & 0 deletions src/circle-gateway/CircleGatewayHelper.sol

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/circle-gateway/interfaces/IERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transfer(address to, uint256 amount) external returns (bool);
}
8 changes: 8 additions & 0 deletions src/circle-gateway/interfaces/IGatewayAttestationReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice a destination contract that consumes a Gateway attestation itself (e.g. Superform's
/// CircleGatewayAdapter): it calls GatewayMinter.gatewayMint and acts on the TransferSpec's hookData
interface IGatewayAttestationReceiver {
function receiveAndExecute(bytes calldata attestationPayload, bytes calldata signature) external;
}
12 changes: 12 additions & 0 deletions src/circle-gateway/interfaces/IGatewayMinter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice the GatewayMinter surface the helper and its tests use
/// @dev proxy at 0x2222222d7164433c4C09B0b0D809a9b52C04C205 on every Gateway chain
interface IGatewayMinter {
function gatewayMint(bytes calldata attestationPayload, bytes calldata signature) external;
function owner() external view returns (address);
function isAttestationSigner(address signer) external view returns (bool);
function addAttestationSigner(address signer) external;
function isTransferSpecHashUsed(bytes32 transferSpecHash) external view returns (bool);
}
9 changes: 9 additions & 0 deletions src/circle-gateway/interfaces/IGatewayWallet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice the GatewayWallet surface the helper and its tests use
/// @dev proxy at 0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE on every Gateway chain
interface IGatewayWallet {
function deposit(address token, uint256 value) external;
function availableBalance(address token, address depositor) external view returns (uint256);
}
Loading
Loading