Composability, part 1 — wire simple-token into my-token until it compiles.
Composability — how one contract can install and call into another. This is
the first of two chapters. Here you wire simple-token into my-token
until everything compiles. The next chapter
makes the cross-contract call actually run, then tests it.Putting it together: incrementer and receiver each install a simple-token holder, and increment() also transfers a token.
Make my-token’s incrementer and receiver each hold simple-tokens.
Whenever increment() fires, also transfer one token from incrementer to
receiver.Across the two chapters we’ll:
Externalize simple-token’s self_refs as contract refs / instances (this chapter)
Add simple-token as a dependency of my-token(this chapter)
Install simple-token holders inside the my-token components (this chapter)
Call their functions across contracts, then test it (next chapter)
Each phase below ends with a checkpoint. If a phase’s checkpoint doesn’t pass,
fix it before moving on — the later phases build directly on it.
The generic implementation lives at one entity ID; alpha and beta are
distinct instances (think USDC vs USDT) sharing the same ABI but handling
different tokens.
Checkpoint 1 — gen genie build simple-token/simple-token succeeds and
the generated instance::alpha / instance::beta paths exist.
Installing a third-party component currently requires a GvmContract. Store
it on root, accept it in deploy(), and hand it to components via
installation_request().
Checkpoint 3 — gen genie build my-token is green; each my-token component stores its
own simple-token holder. You haven’t called across contracts yet — that’s
the next chapter.
Full solution: my-token at the end of Phase 3
my-token/src/my_token_contracts.rs
use genie::contract;/// Implementation of the MyToken contract#[contract]pub mod my_token { /// Root component for MyToken pub mod root { use genie::{GvmContract, Result, deploy, installation_request, owner, view}; /// Root implementation for MyToken pub struct MyTokenRoot { value: u64, simple_token_contract: GvmContract, } impl MyTokenRoot { /// Handles installation requests for MyToken components. #[deploy] pub async fn deploy(simple_token_contract: GvmContract) -> Result<Self> { Ok(Self { value: 0, simple_token_contract }) } #[installation_request] pub async fn installation_request( &mut self, _component_type: MyTokenComponents, ) -> Result<GvmContract> { Ok(self.simple_token_contract) } /// Sets the stored value (owner-only activation) #[owner] pub fn set_value(&mut self, new_value: u64) -> Result<()> { self.value = new_value; Ok(()) } /// Gets the stored value (view function) #[view] pub fn get_value(&self) -> Result<u64> { Ok(self.value) } } } /// incrementer component for MyToken pub mod incrementer { use super::self_refs::Receiver; use genie::GvmContract; use genie::{GvmError, Result, install, owner, private}; use simple_token::simple_token_contract::instance::alpha::Holder; use simple_token::simple_token_contract::simple_token::SimpleTokenClient; /// Incrementer implementation pub struct CounterIncrementer { incrementer_simple_token_holder: Holder, } impl CounterIncrementer { /// Install handler for Incrementer #[install] pub async fn install(installation_return_value: GvmContract) -> Result<Self> { let incrementer_simple_token_holder = SimpleTokenClient::installer() .holder.install(installation_return_value).await? .try_into()?; Ok(Self { incrementer_simple_token_holder }) } #[owner] pub async fn increment(&mut self, to: Receiver, with_confirmation: bool) -> Result<()> { let receive_future = to.remote.receive(); if with_confirmation { if let Err(increment_result) = receive_future.await { //Perform an action that reflects the action failed + //Throw an error that accurately describes what happened return self.handle_increment_failure(increment_result); } else { //Action succeeded so you can update something }; } else { receive_future .spawn() .on_error(Self::callbacks().handle_increment_failure); } Ok(()) } #[owner] pub async fn increment_same_entity(&mut self, to: Receiver) -> Result<()> { if let Err(increment_result) = to.local.receive() { return self.handle_increment_failure(increment_result); } Ok(()) } #[private] pub fn handle_increment_failure(&mut self, delivery_error: GvmError) -> Result<()> { //There's nothing meaningful to fix so throw an error Err(delivery_error.into()) } } } /// Receiver component for MyToken pub mod receiver { use genie::GvmContract; use genie::{Result, install, private, view}; use simple_token::simple_token_contract::instance::alpha::Holder; use simple_token::simple_token_contract::simple_token::SimpleTokenClient; /// Receiver implementation pub struct CounterReceiver { counter: u128, receiver_simple_token_holder: Holder, } impl CounterReceiver { /// Install handler for CounterReceiver #[install] pub async fn install(simple_token_contract: GvmContract) -> Result<Self> { let receiver_simple_token_holder = SimpleTokenClient::installer() .holder.install(simple_token_contract).await? .try_into()?; Ok(Self { counter: 0, receiver_simple_token_holder }) } #[view] pub fn get_count(&self) -> Result<u128> { Ok(self.counter) } #[private] pub fn receive(&mut self) -> Result<()> { self.counter += 1; Ok(()) } } }}