DEV Community

Cover image for Interacting with external smart contracts in Solidity
Matheus Costa
Matheus Costa

Posted on

Interacting with external smart contracts in Solidity

You probably started writing smart contracts but stumbled upon this question: how can I interact with other smart contracts?

In this example I'll be using the CryptoKitties smart contract, so let's dive into it.

Step 1: Defining the contract interface

In solidity we need to define an interface to interact with external smart contracts, for that we need to know what functions we'll be using and its structures.

CryptoKitties smart contract has a function called getKitty, which you pass the kitty id as an argument and it returns the kitty data.

function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
) {
    Kitty storage kit = kitties[_id];

    isGestating = (kit.siringWithId != 0);
    isReady = (kit.cooldownEndBlock <= block.number);
    cooldownIndex = uint256(kit.cooldownIndex);
    nextActionAt = uint256(kit.cooldownEndBlock);
    siringWithId = uint256(kit.siringWithId);
    birthTime = uint256(kit.birthTime);
    matronId = uint256(kit.matronId);
    sireId = uint256(kit.sireId);
    generation = uint256(kit.generation);
    genes = kit.genes;
}
Enter fullscreen mode Exit fullscreen mode

So to use this function in our smart contract we need to define an interface, which is basically a contract copying this function but without its body (the curly braces), so the contract knows what it receives and what it returns.

interface ICryptoKitties {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Using the interface to import the CryptoKitties smart contract

For this step, we'll be importing their smart contract using our interface.

pragma solidity ^0.8.4;

interface ICryptoKitties {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ExampleContract {
  address kittiesAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  ICryptoKitties kittyContract = ICryptoKitties(kittiesAddress);
}

Enter fullscreen mode Exit fullscreen mode

You can also have a function to set the contract address, so if CryptoKitties change their contract you don't lose yours.

Step 3: Using the functions defined in the interface

Now we can use the functions defined in the interface in our smart contract, but remember you can only use public/external functions. As in solidity a function can return multiple values, we need to use parentheses to get the variables we need, using commas to skip unwanted variables in the same order as the function definition.

pragma solidity ^0.8.4;

interface ICryptoKitties {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ExampleContract {
  address kittiesAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  ICryptoKitties kittyContract = ICryptoKitties(kittiesAddress);


function getKittyGenes(uint _kittyId) external view returns (
uint kittyGenes
) {
  (,,,,,,,,,kittyGenes) = kittyContract.getKitty(_kittyId);

  return kittyGenes;
  }
}
Enter fullscreen mode Exit fullscreen mode

Similar way

We can also import the entire contract and inherit its properties and functions. Let's take OpenZeppelin Ownable smart contract as example, using its onlyOwner modifier to allow only the contract owner to use this function.

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/access/Ownable.sol";

interface ICryptoKitties {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ExampleContract is Ownable {
  address kittiesAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  ICryptoKitties kittyContract = ICryptoKitties(kittiesAddress);


function getKittyGenes(uint _kittyId) external view onlyOwner returns (
uint kittyGenes
) {
  (,,,,,,,,,kittyGenes) = kittyContract.getKitty(_kittyId);

  return kittyGenes;
  }
}
Enter fullscreen mode Exit fullscreen mode

We are also able to modify any of the functions inherited in our smart contract, this opens up a lot of possibilities.

Conclusion

As you can see it's pretty easy to interact with external contracts, I hope you find this tutorial useful and reach me out if you have any questions!

Discussion (1)

Collapse
_lehmoura profile image
lele

super cool topic mathew