Starter kit for cross-domain apps (xApps).
Connext's xcall
is a single interface that can be used to send assets and arbitrary calldata from one chain to another.
In general, there are three types of information that can be bridged between chains.
- Asset transfers
- Unauthenticated calls
- Authenticated calls
This starter repo contains example contracts that demonstrate how these can be construted with xcall
.
At a high level, this is the call flow between contracts:
The SimpleBridge
transfers tokens from a user on the origin chain to a specified address on the destination domain.
Since no calldata is involved, no target contract is needed.
Note that when sending tokens, the user will first have to call approve
on the ERC20 to set a spending allowance for the SimpleBridge
contract.
Tokens will move from the user's wallet => SimpleBridge => Connext => recipient.
- SimpleBridge.sol
- Send funds across chains
The DestinationGreeter
contract on the destination chain has an updateGreeting
function that changes a stored greeting
variable. The SourceGreeter
contract on the origin chain uses xcall
to send encoded calldata for updateGreeting
.
To demonstrate a combination of an asset transfer and an arbitrary call in a single xcall
, the updateGreeting
function will require a payment to update the greeting. For this example, the contract will be okay with any amount greater than 0.
updateGreeting
is implemented as an unauthenticated call (there are no checks to determine who is calling the function). And so, this flow is essentially the same as the simple bridge except encoded calldata is also included in the xcall
.
- SourceGreeter.sol
- DestinationGreeter.sol
- Deposit funds into a liquidity pool on the destination chain
- Execute a token swap on the destination chain
- Connect DEX liquidity across chains in a single seamless transaction
- Zap into a vault from any chain
The DestinationGreeterAuthenticated
contract sets some permissioning constraints. It only allows greeting
to be updated from SourceGreeterAuthenticated
. In order to enforce this, the contract checks that the caller is the original sender from the origin domain.
- SourceGreeterAuthenticated.sol
- DestinationGreeterAuthenticated.sol
- Hold a governance vote on Origin Chain and execute the outcome of it on the Destination Chain (and other DAO operations)
- Lock-and-mint or burn-and-mint token bridging
- Critical protocol operations such as replicating/syncing global constants (e.g. PCV) across chains
- Bringing UniV3 TWAPs to every chain without introducing oracles
- Chain-agnostic veToken governance
- Metaverse-to-metaverse interoperability
An example of a nested xcall
, when another xcall
is executed in the destination receiver contract.
- Ping.sol
- Pong.sol
- Implement JS-style "callbacks" to respond asynchronously between chains
This project uses Foundry for testing, deploying, and interacting with contracts. Fully compatible hardhat support will be added in the near future.
- See the official Foundry installation instructions.
- Also, download make if you don't already have it.
- Get some testnet tokens! The simplest method is to go to the testnet Bridge UI and mint yourself some TEST tokens. You can also call the
mint()
function directly in the TEST token contract.
src
├─ contract-examples
| └─ simple-bridge
│ └─ SimpleBridge.sol
| └─ greeter
│ └─ SourceGreeter.sol
│ └─ DestinationGreeter.sol
| └─ greeter-authenticated
│ └─ SourceGreeterAuthenticated.sol
│ └─ DestinationGreeterAuthenticated.sol
| └─ ping-pong
│ └─ Ping.sol
│ └─ Pong.sol
| └─ test
│ └─ ...
├─ sdk-examples
│ └─ node-examples
make install
yarn
foundryup
Copy the .env.example
into .env
and fill in all the placeholders under the GENERAL
section. Initial values are provided for Goerli as origin and Optimism-Goerli as destination.
There are some starter test cases in the src/tests
directory for each of the examples.
make test-unit-simple-bridge
make test-unit-destination-greeter
make test-unit-destination-greeter-auth
make test-unit-ping
make test-unit-pong
This uses forge's --forked
mode. Make sure you have GOERLI_RPC_URL
defined in your .env
file as these tests currently fork Goerli.
make test-forked-simple-bridge
make test-forked-source-greeter
make test-forked-source-greeter-auth
Deploy contracts in this repository using the RPC provider of your choice (make sure all the variables under GENERAL
are set in .env
).
```bash
make deploy-simple-bridge
```
```bash
make deploy-source-greeter
```
```bash
make deploy-destination-greeter
```
```h
make deploy-source-greeter-auth
```
Use the origin domain and address of `SourceGreeterAuthenticated` contract address as values for `ORIGIN_DOMAIN` and `SOURCE_CONTRACT` in `.env` before deploying `DestinationGreeterAuthenticated`.
```bash
make deploy-destination-greeter-auth
```
```bash
make deploy-ping
```
```bash
make deploy-pong
```
It's much easier to read contract values after they're verified! We use another forge command to do this.
For example, to verify DestinationGreeter.sol
:
forge verify-contract --chain 1735356532 <CONTRACT_ADDRESS> src/contract-examples/greeter/DestinationGreeter.sol:DestinationGreeter <ETHERSCAN_KEY>
The core set of Connext contracts have already been deployed to testnet. For the most up-to-date contracts, please reference the Connext deployments.
-
Execute
transfer
on SimpleBridgeAfter deploying SimpleBridge, set the
SIMPLE_BRIDGE
andRECIPIENT
variables in.env
and run:With Forge:
make transfer
With Hardhat:
yarn hardhat transfer
-
Execute
updateGreeting
on SourceGreeterAfter deploying SourceGreeter and DestinationGreeter, set the
SOURCE_GREETER
,DESTINATION_GREETER
,DESTINATION_TOKEN
, andNEW_GREETING
variables in.env
and run:With Forge:
make update-greeting
With Hardhat:
yarn hardhat update-greeting
-
Execute
updateGreeting
on SourceGreeterAuthenticatedAfter deploying SourceGreeterAuthenticated and DestinationGreeterAuthenticated, set the
SOURCE_GREETER_AUTHENTICATED
andDESTINATION_GREETER_AUTHENTICATED
variables in.env
and run:With Forge:
make update-greeting-auth
With Hardhat:
yarn hardhat update-greeting-auth
-
Execute
startPingPong
on PingAfter deploying Ping and Pong, set the
PING
andPONG
variables in.env
and run:With Forge:
make start-ping-pong
With Hardhat:
yarn hardhat start-ping-pong
You can just check your wallet balance in the Simple Bridge example to see if the funds arrived at the destination address. To check calldata results, you can read the updated variables on the target contract on Etherscan or use tools like Foundry's cast
command.
There is a simple NodeJS example of using the SDK in /src/sdk-examples/
. This example demonstrates how to configure the SDK, construct the various params (like estimating relayer fee), and call xcall
.
The script fires off a cross-chain transfer that sends funds from your wallet on the source domain to the same address on the destination domain.
For a more detailed step-by-step, check out the SDK Guide.
-
Make sure dependencies are installed.
yarn install
-
Get some testnet tokens! The simplest method is to go to the testnet Bridge UI and mint yourself some TEST tokens. You can also call the
mint()
function directly in the TEST token contract. -
Make sure you set your private key in
.env
PRIVATE_KEY = <PRIVATE_KEY>
(Optionanal) The example uses sane defaults for a Goerli -> Optimism-Goerli transfer but feel free to change these (especially RPCs as they currently use public defaults):
ORIGIN_RPC_URL GOERLI_RPC_URL OPTIMISM_GOERLI_RPC_URL ORIGIN_TOKEN AMOUNT SLIPPAGE
yarn xtransfer