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
type is a gateway,
that stores the remote contract address and expose methods constructing the communication helpers.
These methods are:
Remote::querier
- returns theBoundQuerier<_, Contract>
,Remote::executor
- returns theExecutorBuilder<(EmptyExecutorBuilderState, Contract)>
.
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:
use cw_storey::containers::Item;
pub struct Contract<'a> {
remote: Item<Remote<'a, ExternalContract>>,
}
we can also store a Remote
to some contract implementing an interface:
use cw_storey::containers::Item;
pub struct Contract<'a, CounterT> {
remote: Item<
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
is a
wrapper over the
QuerierWrapper
.
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
is
used to construct the
WasmMsg
.
It’s generic over two states limiting it’s functionality depending on the stage it’s in:
EmptyExecutorBuilderState
- Setting funds for the transaction,ReadyExecutorBuilderState
-Building
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();