How to Call One Contract from Another in Solidity

ยท

Smart contracts often need to interact with other deployed contracts on the blockchain. This guide explores two primary methods for achieving this in Solidity, complete with practical examples and ERC20 token use cases.

Method 1: Interface-Based Contract Calls (Recommended)

How It Works

When a client (e.g., JavaScript) interacts with a smart contract, it requires the contract's ABI. Similarly, in Solidity, you can call another deployed contract by declaring its interface.

Example: Basic Contract Interaction

Deployed Contract (Contract 1):

pragma solidity ^0.4.24;
contract Deployed {
    uint public a = 1;
    function setA(uint _a) public returns (uint) {
        a = _a;
        return a;
    }
}

After deploying this contract, note its address (e.g., 0x123...).

Calling Contract (Contract 2):

pragma solidity ^0.4.24;
interface Deployed {
    function setA(uint) public returns (uint);
    function a() public pure returns (uint);
}

contract Existing {
    Deployed dc;
    
    function Existing(address t) public {
        dc = Deployed(t);
    }
    
    function getA() public view returns (uint result) {
        return dc.a();
    }
    
    function setA(uint _val) public returns (uint result) {
        dc.setA(_val);
        return _val;
    }
}

Key Steps:

  1. Declare an interface matching the deployed contract's functions.
  2. Initialize the interface with the deployed contract's address.
  3. Call functions through the interface.

๐Ÿ‘‰ Learn more about Solidity interfaces

ERC20 Token Example

To interact with ERC20 tokens like LOOM:

pragma solidity ^0.4.24;
interface Tokenloom {
    function transfer(address to, uint256 value) external returns (bool);
}

contract StandardToken {
    function transferLoomToken(uint256 amount) public {
        Tokenloom token = Tokenloom(0x090652...);
        token.transfer(0xafe288..., amount);
    }
}

Method 2: Low-Level Calls (call/delegatecall)

For contracts without ABIs or when you need more control:

pragma solidity ^0.4.18;
contract ExistingWithoutABI {
    address dc;
    
    function ExistingWithoutABI(address t) public {
        dc = t;
    }
    
    function setASignature(uint val) public returns(bool success) {
        require(dc.call(bytes4(keccak256("setA(uint256)")),val));
        return true;
    }
}

FAQ Section

Q1: Why use interfaces instead of direct calls?
Interfaces provide type safety and clearer code structure, reducing errors in function signatures.

Q2: Can one interface call multiple contracts?
Yes! Consolidate all needed functions into a single interface to interact with multiple contracts.

Q3: When should I use call/delegatecall?
Use these for dynamic calls or when the target contract's ABI is unavailable. Be cautious with security implications.

Q4: How do I handle return values with call?
For complex returns, you'll need to manually decode the data using abi.decode().

๐Ÿ‘‰ Explore advanced contract interactions

Best Practices

  1. Security: Always verify external contract addresses.
  2. Gas Efficiency: Interface calls are generally more gas-efficient than low-level calls.
  3. Error Handling: Implement robust error handling, especially with low-level calls.

Remember, proper contract interaction design is crucial for building composable DeFi applications and complex blockchain ecosystems.