Overview
Inter-contract communication is a cornerstone of decentralized application development, enabling a powerful principle known as composability. This allows developers to build small, specialized smart contracts that can be combined like building blocks to create more sophisticated systems. In this tutorial, we'll explore how Synergeia smart contracts can call functions on other contracts, handle data, and maintain atomicity.
Core Concepts
- Calling External Contracts: A smart contract can call a public function on another smart contract if it knows the target contract's address and the function signature. This is a fundamental mechanism for creating interactions between different parts of a decentralized application.
- Handling Return Values: External calls can return data. Your contract needs to be able to handle these return values, whether they indicate success, failure, or contain specific information.
- State Changes and Atomicity: All state changes within a single transaction, including those across multiple contracts, are atomic. This means that if any part of the transaction fails (for example, an external call reverts), all state changes made up to that point are rolled back. This ensures the integrity of the blockchain state.
Example: A User Profile and Registry
Let's consider a simple example with two contracts: a `UserProfile` contract that manages a user's data, and a `Registry` contract that keeps a list of all registered users. When a new user profile is created, the `UserProfile` contract will call the `Registry` contract to add the new user to the list.
// Note: This is a conceptual Rust example for illustrative purposes.
// Registry Contract: Stores a list of registered users
struct Registry {
registered_users: Vec,
}
impl Registry {
// Public function to add a user to the registry
pub fn add_user(&mut self, user: UserAddress) {
self.registered_users.push(user);
}
}
// UserProfile Contract: Manages user profiles
struct UserProfile {
registry_address: ContractAddress,
user_data: HashMap,
}
impl UserProfile {
// Creates a new user profile and calls the Registry contract
pub fn create_profile(&mut self, user: UserAddress, data: String) {
self.user_data.insert(user.clone(), data);
// Call the 'add_user' function on the Registry contract
// The runtime would handle the cross-contract call mechanism
runtime::call(
self.registry_address,
"add_user",
(user,)
);
}
}