The imports themselves do nothing to the compiled contract size and may even include code that is not included in the output bytecode. They merely make the code visible to the compiler so it can refer to the code, if needed.
If a factory contract A is to deploy an instance contract B, then the bytecode of the instance contract B is a hard coded into the factory contract A. That is, A "knows" how to deploy a B.
contract B {}
contract A {}
B b;
function createNewB() public {
b = new B();
}
}
You can organise your source code for re-use and this makes no difference to the compiler.
import "./B.sol";
contract A {}
B b;
function createNewB() public {
b = new B();
}
}
You can have another client contract C that needs to send/receive messages to B. There are two ways to do that. One is expensive, the other is inexpensive.
contract B {
function foo() public {
// do stuff
}
}
contract C {
B b;
function doBFoo() {
b.foo();
}
}
In this case, all of B was compiled into C. This was necessary because we cast a variable b
as type B
which is a contract. Therefore, we got the works.
The alternative is to include only the surface area of B - the function signatures that C will talk to, but not the instructions inside the functions. Interface contracts are sets of undefined functions.
contract BInterface {
function foo() public;
}
contract B is BInterface {
function foo() public {
// do stuff
}
}
contract C {
BInterface b;
function doBFoo() public {
b.foo();
}
}
In this case, C will include the much more compact BInterface which describes the function signatures but not the instructions inside each function.
In case it isn't clear, in each case (except b = new B()
) you need a contract address. This is often done in a constructor.
For example, if we say:
B b;
We are saying there is a B
called b
. B
is defined with contract B
so B
is a contract with a known interface, but where is it? b
isn't much good unless it also has an address. Something like
function C(address bAddress) public {
b = B(bAddress);
}
It will also work as
function C(address bAddress) public {
b = BInterface(bAddress);
}
If this contract only wants to communicate with instances of B
then the interface is the only needed information.
Interfaces are a solution to chains of imported code and they solve for circular dependencies. For example, if A needs to talk to B and B needs to talk to A.
contract AInterface{}
contract BInterface{}
contract A is AInterface {
BInterface b;
}
contract B is BInterface {
AInterface a;
}
contract FactoryInterface {}
contract Factory is FactoryInterface {
A a;
B b;
}
Hope it helps.