Intelligent Contracts
Advanced Features
Contract-to-Contract Interaction

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_interaction
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_interaction
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:

multi_contract_interaction
@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

  1. Security: Always validate inputs and check permissions before allowing cross-contract interactions.
  2. Error Handling: Implement proper error handling for cases where called contracts might revert or throw exceptions.
  3. Upgradability: Consider using upgradable patterns if you need to change contract interactions in the future.
  4. Testing: Thoroughly test all contract interactions, including edge cases and potential vulnerabilities.