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-examplesmake install
yarn
foundryupCopy 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-pongThis 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-authDeploy 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 transferon SimpleBridgeAfter deploying SimpleBridge, set the SIMPLE_BRIDGEandRECIPIENTvariables in.envand run:With Forge: make transferWith Hardhat: yarn hardhat transfer 
- 
Execute updateGreetingon SourceGreeterAfter deploying SourceGreeter and DestinationGreeter, set the SOURCE_GREETER,DESTINATION_GREETER,DESTINATION_TOKEN, andNEW_GREETINGvariables in.envand run:With Forge: make update-greetingWith Hardhat: yarn hardhat update-greeting 
- 
Execute updateGreetingon SourceGreeterAuthenticatedAfter deploying SourceGreeterAuthenticated and DestinationGreeterAuthenticated, set the SOURCE_GREETER_AUTHENTICATEDandDESTINATION_GREETER_AUTHENTICATEDvariables in.envand run:With Forge: make update-greeting-authWith Hardhat: yarn hardhat update-greeting-auth 
- 
Execute startPingPongon PingAfter deploying Ping and Pong, set the PINGandPONGvariables in.envand run:With Forge: make start-ping-pongWith 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 .envPRIVATE_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
