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:
Remote::querier
(opens in a new tab) - returns theBoundQuerier<_, Contract>
(opens in a new tab),Remote::executor
(opens in a new tab) - returns theExecutorBuilder<(EmptyExecutorBuilderState, Contract)>
(opens in a new tab).
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 representation 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:
EmptyExecutorBuilderState
(opens in a new tab) - Setting funds for the transaction,ReadyExecutorBuilderState
(opens in a new tab) -Building
(opens in a new tab) theWasmMsg
.
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();