Contract-to-Contract Interaction
Contract-to-Contract Interaction in GenLayer allows developers to create more complex and modular Intelligent Contracts by enabling communication between them. This feature is crucial for building scalable and composable decentralized applications, allowing contracts to read from and write to other contracts.
Please note that this feature is in progress and will be subject to changes in the near future.
Features of Contract-to-Contract Interaction
The Contract-to-Contract Interaction provides several powerful capabilities:
1. Cross-Contract Reads
Contracts can read data from other contracts, allowing for the sharing of state and information across different parts of your application.
2. Cross-Contract Writes
Contracts can initiate state changes in other contracts, enabling complex multi-step operations that span multiple contracts.
3. Modular Design
By separating concerns into different contracts, developers can create more maintainable and upgradable systems.
4. Composability
Contracts can be designed to work together, creating more powerful and flexible applications by combining different functionalities.
How to Use Contract-to-Contract Interaction in Your Contracts
As GenLayer calldata is dynamically typed users can send whatever they want to a contract, or use statically typed interface to facilitate type checking and autocompletion
Exact parameters of .view()
and .write()
are subject to change
Dynamically typed approach
address = Address("0x03FB09251eC05ee9Ca36c98644070B89111D4b3F")
result = gl.ContractAt(address).view().method_name(1, '234')
gl.ContractAt(address).write(gas=10**5).method_name(1, '234')
# ^ write methods do not return anything!
Statically typed approach
@gl.contract_interface
class GenLayerContractIface:
class View:
def method_name(self, a: int, b: str): ...
class Write:
pass
### in your contract method ###
address = Address("0x03FB09251eC05ee9Ca36c98644070B89111D4b3F")
result = GenLayerContractIface(address).view().method_name(1, '234')
Methods for Contract-to-Contract Interaction
1. Reading from Another Contract
To read data from another contract, create an instance of the Contract
class and call its methods:
token_contract = gl.ContractAt(self.token_contract_address)
balance = token_contract.view().get_balance_of(account_address)
2. Writing to Another Contract
To initiate a state change in another contract, call a method that modifies its state:
token_contract = gl.ContractAt(self.token_contract_address)
success = token_contract.emit(gas=100000).transfer(to_address, amount)
3. Handling Multiple Contract Interactions
For more complex scenarios involving multiple contracts:
@gl.contract
class MultiContractInteraction:
token_contract: Address
storage_contract: Address
def __init__(self, token_contract: str, storage_contract: str):
self.token_contract = Address(token_contract)
self.storage_contract = Address(storage_contract)
@gl.public.write
def complex_operation(self, account: str, amount: int, data: str) -> bool:
token = gl.ContractAt(self.token_contract)
storage = gl.ContractAt(self.storage_contract)
# Read from token contract
balance = token.view().get_balance_of(account)
if balance >= amount:
# Write to token contract
token.emit(gas=100000).transfer(self.address, amount)
# Write to storage contract
storage.emit(gas=100000).store_data(account, data)
return True
return False
Interacting with Ghost Contracts
Eth contracts have statically typed calldata format, which means that only statically typed approach with interfaces is applicable
This is not supported in the Studio right now
@gl.ghost_contract
class GhostContractIface:
class View:
def method_name(self, param: str, /) -> tuple[u32, str]: ...
class Write:
def bar(self, param: str, /): ...
### in your contract method ###
address = Address("0x03FB09251eC05ee9Ca36c98644070B89111D4b3F")
i, s = GhostContractIface(address).view().method_name('234')
Best Practices for Contract-to-Contract Interaction
- Security: Always validate inputs and check permissions before allowing cross-contract interactions.
- Error Handling: Implement proper error handling for cases where called contracts might revert or throw exceptions.
- Upgradability: Consider using upgradable patterns if you need to change contract interactions in the future.
- Testing: Thoroughly test all contract interactions, including edge cases and potential vulnerabilities.