Skip to main content

A multi-chain, multi-producer, multi-consumer app


Clone the examples repo and look at the app before starting!

The contracts in this app consist of a channel over which two different sender and receiver contracts communicate. The senders each hold a reference to the channel, and call channel.send(num) to send a number, num, over the channel. The channel holds references to both receivers, and its send function stores num to both receivers. Here's a picture of what's going on, where S1 and S2 are both senders, and R1 and R2 are both receivers:

S1 ───> Channel ───> R1
S2 ___↗ `───> R2

The contracts in the four files---Sender1.sol, Sender2.sol, Channel.sol, Receiver1.sol, and Receiver2.sol---run on different chains. The Cubist config file currently specifies Avalanche for Sender1 and Recevier1, an Avalanche subnet for the Channel, Ethereum for Sender2, and Polygon for Receiver2. You can change these chains by updating your Cubist config.

Even though the contracts run on different chains, they look "normal"---Cubist automatically generates the cross-chain code that lets the contracts interact. For example, the Channel contract holds references to two different cross-chain receivers, but it looks like standard Solidity code:

import './Receiver1.sol';
import './Receiver2.sol';

contract Channel { // deployed on an Avalanche subnet
uint256 number;
R1 r1; // receiver deployed on Avalanche
R2 r2; // receiver deployed on Polygon

constructor (R1 addr1, R2 addr2) {
r1 = addr1;
r2 = addr2;

function send(uint256 num) public {; // cross-chain call; // cross-chain call
number = num;

The Sender contracts look similarly simple; here's the code for Sender1.sol:

import './Channel.sol';

contract S1 { // deployed on Avalanche
Channel channel; // deployed on an Avalanche subnet

constructor (Channel addr) {
channel = addr;

function send(uint256 num) public {
channel.send(num); // cross-chain call

Given the source code and target chains for each contract, Cubist analyzes the contracts and automatically produces their cross-chain interaction code. This code consists of generated "shim" contracts that should not be modified by the user; Cubist handles shim generation and deployment (almost completely) behind the scenes, automatically. Cubist also provides a relayer service that works in concert with the generated shims in order to send function calls from one chain to another.


The next Cubist release will support bridge providers beyond the Cubist Trusted Relayer---e.g., Axelar or Layer Zero. You can swap bridge providers just as you swap chains, with a single line in the configuration file. Contract us at [email protected] or on discord if you're interested in bridge provider support!