First of all, inheritance does not link contracts together. It produces single contract that has logic from both, base and inherited contracts. For actual linking, there are several common ways:
First way: hardcoded addresses
contract A {
...
}
contract B {
A a = A (/* address of deployed contract A here */);
}
This way is the cheapest in terms of gas, but does not allow using the same bytecode of contract B
in different networks, say in devnet, testnet, and mainnet.
Second way: constructor parameter
contract A {
...
}
contract B {
A a;
constructor (A _a) public {
a = _a;
}
}
Third way: deploy in constructor
This way bi-directional link may easily be established.
contract A {
B b;
constructor () public {
b = B (msg.sender);
}
}
contract B {
A a;
constructor () public {
a = new A ();
}
}
Fourth way: service registry
This way allows deploying smart contracts in any order and even modifying link after deployment.
contract A {
ServiceRegistry serviceRegistry;
constructor (ServiceRegistry _serviceRegistry) public {
serviceRegistry = _serviceRegistry;
serviceRegistry.register ("A", address (this));
}
function getB () internal view returns (B) {
return B (serviceRegistry.get ("B"));
}
}
contract B {
ServiceRegistry serviceRegistry;
constructor (ServiceRegistry _serviceRegistry) public {
serviceRegistry = _serviceRegistry;
serviceRegistry.register ("B", address (this));
}
function getA () internal view returns (A) {
return A (serviceRegistry.get ("A"));
}
}
etc.