How to import local contracts in Solidity
Published
Table of contents
If you're building a big application and your logic is split into different smat contracts, you'll probably need to import them to use their methods.
Notice that in this article I'm fousing on local imports and not on imports from libraries like OpenZeppelin
When working with local imports we need to take into account the order in which your contracts are deployed to the blockchain. In this example I'll be using Hardhat so my deploy script will be adapted for this framework but if you're using Truffle or other framework, your script will probably be very similar.
Updated to add that Truffle has the deploy.link method which is very useful in these cases.
Solidity import statement
The import statement in Solidity is pretty simple, just point to the source file of the contract you want to import.
// File: Contract_A.sol
pragma solidity ^0.8.0;
import './Contract_B.sol';
contract ContractA {
// contract code here...
}
Save imported contract address
In order to successfuly import another contract and use its methods we need to know the address where it's deployed. If we're deploying both contracts at the same time, we have to make sure they're deployed in the correct order.
In this example Contract_A
is importing Contract_B
so we'd need Contract_B
to be deployed first. Then in the Contract_A
constructor we need to receive the Contract_B
address and save it in a state variable (in the example below this variable is contractB_ref
).
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "./Contract_B.sol";
contract Contract_A {
// Type / visibility / variable name
Contract_B public contractB_ref;
// receive address during deployment script
constructor(Contract_B _addrContractB) {
contractB_ref = _addrContractB;
}
}
Deploy script to reference imported contracts
Now that we've defined a state variable to store the imported contract's address, we just need to prepare the deployment script to deploy our contracts in the correct order and pass the Contract_B deployment address to the Contract_A so it can be saved when the contructor function is invoked.
// File: deploy.js (by default Hardhat creates it as /scripts/sample-script.js)
const hre = require('hardhat');
async function main() {
// Hardhat always runs the compile task when running scripts with its command
// line interface.
//
// If this script is run directly using `node` you may want to call compile
// manually to make sure everything is compiled
// await hre.run('compile');
// We get the contract to deploy
const Contract_B = await hre.ethers.getContractFactory('Contract_B');
const contractb = await Contract_B.deploy();
await contractb.deployed();
console.log('ContractB deployed to:', contractb.address);
const Contract_A = await hre.ethers.getContractFactory('Contract_A');
// pass contractA constructor the address where contractB was just deployed
const contracta = await Contract_A.deploy(contractb.address);
console.log('ContractA deployed to:', contracta.address);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
TAGS