Getting Started

In this quickstart guide, you will be deploying a Glacis-powered smart contract that sends a string message across chains.

To begin working with Glacis smart contracts, you can work with a starter client smart contract. You can easily access this on Remix.

Open in Remix

Please ensure that you understand how to use and deploy with Remix before starting this guide.

Glacis Client

The GlacisClient smart contract provides a full interface for your cross-chain smart contract to communicate with the GlacisRouter. You can send messages via _route and its other derivative functions, and receive messages by overriding the _receiveMessage function.

In this example, you can send and receive a string across chains:

contract GlacisClientTextSample is GlacisClientOwnable {
    string public currentMessage;

    constructor(
        address glacisRouter_,
        address owner_
    ) GlacisClientOwnable(glacisRouter_, 1, owner_) {}

    function sendMessage(
        address to,
        uint256 chainId,
        string memory message,
        uint8[] memory gmps,
        uint256[] memory fees
    ) external payable returns (bytes32) {
        return
            _route(
                chainId,
                to,
                abi.encode(message),
                gmps,
                fees,
                msg.sender,
                false,
                msg.value
            );
    }

    function _receiveMessage(
        uint8[] calldata, // fromGmpId,
        uint256, // fromChainId,
        address, // fromAddress,
        bytes memory payload
    ) internal override {
        (currentMessage) = abi.decode(payload, (string));
    }
}

To explain what's happening, let's go step by step.

The constructor includes construction of an ownable version of the GlacisClient:

GlacisClientOwnable(glacisRouter_, 1, owner_)

It requires an instance of the GlacisRouter smart contract so that it can send and receive messages. 1 refers to quorum (a redundancy feature). Finally, the contract is owned by the designated user, which will be relevant for initialization later.

    function sendMessage(
        address to,
        uint256 chainId,
        string memory message,
        uint8[] memory gmps,
        uint256[] memory fees
    ) external payable returns (bytes32) {
        return
            _route(
                chainId,                // destination chain ID
                to,                     // destination address
                abi.encode(message),    // payload
                gmps,                   // gmps
                fees,                   // fees
                msg(sender),            // refundAddress
                msg.value               // payment
            );
    }

To send a message across chains, this smart contract includes a sendMessage function, which is arbitrarily named. Within it is a call to the _route function, which is what sends the message to the GlacisRouter smart contract.

Note that there are a couple of inputs within the sendMessage function. First is the address of the contract that the message is being sent to, known as your destination address (to). The chainId is the Glacis Chain ID that the message is being sent to, which is typically the Ethereum chain ID. Then the message string that's being sent. The gmps and fees arrays should be the same length, and are important if you plan on sending a redundant message (the same message being sent through multiple GMPs).

The _route function includes much of these inputs as well as other configurations about the message, which you can learn more about on the GlacisClient page.

    function _receiveMessage(
        uint8[] calldata, // fromGmpId,
        uint256, // fromChainId,
        address, // fromAddress,
        bytes memory payload
    ) internal override {
        (value) = abi.decode(payload, (string));
    }

The receive message function can only be triggered by the GlacisRouter when it receives a cross-chain message. This function will inject information about the origins of the message, but in this case the only information desired is the message itself. Note that all payloads are encoded into bytes and received as bytes, but they can be easily encoded and decoded through abi.encode and abi.decode.

GlacisClient Initialization

You can add this entire smart contract to your project and deploy it to Moonbase Alpha or Fantom Testnet with the following information in the constructor:

GlacisClientTextSample(0x71917f371210cDA0540bC6770C349AdcedF7FB36, INSERT_YOUR_WALLET_ADDRESS_HERE)

Initializing it with the 0x71917f371210cDA0540bC6770C349AdcedF7FB36 address works because a GlacisRouter has been pre-deployed to this address on Moonbase Alpha.

After deployment, access control must be initialized. Since this example is using the ownable version of GlacisClient, we can use the addAllowedRoute function.

addAllowedRoute(GlacisCommons.GlacisRoute {
    fromChainId,    // 0 means any chain
    fromAddress,    // 0x00 means any address
    fromGmpId       // 0 means any GMP
})

You can call it with addAllowedRoute([0,0x0000000000000000000000000000000000000000,0]) to allow messages from all smart contracts. Typically you would only allow smart contracts from a small subset of chains, but this is for demonstration purposes.

NOTE: THE WILDCARD (0) IS DUE TO CHANGE IN FUTURE VERSIONS OF GLACIS

Try deploying and initializing an instance of this smart contract on both Moonbase Alpha and Fantom Testnet! This way you can send a message from one chain to the other.

You can properly deploy on Fantom Testnet with this constructor, which uses Fantom Testnet's GlacisRouter:

GlacisClientTextSample(0xe537e0F3e2b0AC8e9C73E940FDDc1Fd53E1F6EB3, INSERT_YOUR_WALLET_ADDRESS_HERE)

Don't forget to call addAllowedRoute on Fantom Testnet as well!

By the end of this process, you should have two addresses: MOONBASE_ALPHA_INSTANCE_ADDRESS and FANTOM_TESTNET_INSTANCE_ADDRESS.

Sending a Message

You can now send a cross-chain message by interacting with your deployed instance's sendMessage function. Try sending one from Moonbase Alpha to Fantom Testnet with the following call:

    function sendMessage(FANTOM_TESTNET_INSTANCE_ADDRESS, 1287, "Hello World", [1], [800000000000000000]) 
  • Set to as the FANTOM_TESTNET_INSTANCE_ADDRESS

  • Set chainId to Fantom Testnet's chain ID (4002)

  • Set whatever you like, such as Hello World, as the message

  • Use [1] as the gmps to indicate Axelar

  • Set [800000000000000000] for the fees. Note that you will have to send this message with 800000000000000000 wei as well to pay for cross-chain gas. This value is artificially high to ensure that the transaction executes

Invoking this call would send a message from the origin chain, through a GMP, and back to the same chain. A more complete example would have you send a message between chains, but that would require deployment to multiple chains.

In this case, since the GMP ID array has only 1 set, it is only Axelar. You would be able to see a cross-chain message on Axelarscan.

If you sent the message, congratulations! You have sent your first message with Glacis. You can also try with GMPs of [2] or [3] to try it out with LayerZero or Wormhole.

The next step would be to get associated with some of the more complex ideas of Glacis and start deploying smart contracts on multiple chains:

Last updated