Communication

Communication helpers

Sylvia defines few types to ease the communication between contracts. By design, they are very limited in their functionality and serve as an infrastructure. All of the message constructing methods are meant to be provided via traits.

To gate these methods to be only accessible for the specific contracts, all of these types are generic over a contract type.

💡

The goal of these helpers is to automate the message construction, making the code more readable and reducing risk of an error.

Remote

The Remote (opens in a new tab) type is a gateway, that stores the remote contract address and expose methods constructing the communication helpers.

These methods are:

💡

We recommend to store the Remote type as the contract state and access the BoundQuerier and ExecutorBuilder via above methods.

The Remote can be stored as a represantation of a specific contract:

pub struct Contract<'a> {
    remote: Item<Remote<'a, ExternalContract>>,
}

we can also store a Remote to some contract implementing an interface:

pub struct Contract<'a> {
    remote: Remote<
        'a,
        dyn SomeInterface<
            Error = StdError,
            ExecC = ExampleMsg,
            QueryC = ExampleQuery,
            CounterT = CounterT,
        >,
    >,
}
💡

Note that we have to use the dynamic dispatch here and also provide values for every associated type defined in the interface.

The initialization of the Remote requires only the address of the external contract:

self.remote
    .save(ctx.deps.storage, &sylvia::types::Remote::new(remote_addr))?;

and in the usage you can access the BoundQuerier and the ExecutorBuilder.

let bound_querier = self
    .remote
    .load(ctx.deps.storage)?
    .querier(&ctx.deps.querier);
 
let executor_builder = self
    .remote
    .load(ctx.deps.storage)?
    .executor();

BoundQuerier

The BoundQuerier (opens in a new tab) is a wrapper over the QuerierWrapper (opens in a new tab).

We provide the querying functionality via traits. The contract and interface macros generate these traits for us. You can learn about that in this section. If you don't use Sylvia yet, you can create and implement those traits manually. Feel free to get inspiration from above example.

With the traits implemented, a contract with a query method some_query can be queried as follows:

let some_response = self
    .remote
    .load(ctx.deps.storage)?
    .querier(&ctx.deps.querier)
    .count()?;

ExecutorBuilder

The ExecutorBuilder (opens in a new tab) is used to construct the WasmMsg (opens in a new tab).

It's generic over two states limiting it's functionality depending on the stage it's in:

For the ExecutorBuilder generic over EmptyExecutorBuilderState, we also provide the message constructing functionality. The contract and interface macros generate a trait with methods for every execute message. You can learn about that in this section. If you don't use Sylvia yet, you can create and implement those traits manually. Feel free to get inspiration from above example.

With the traits implemented, we can create an execute method some_exec can be queried as follows:

let wasm_msg = self
    .remote
    .load(ctx.deps.storage)?
    .executor()
    .some_exec()?
    .build();