Skip to main content
Fixed wrong datatype issue on `createB` function in line `bList.push(...)`
Source Link
import "./B.sol";

contract A {

  address[] public bList;
  event LogNewB(address creator, address contractB);

  function createB() public returns(address contractB) {
    B b = new B();
    emit LogNewB(msg.sender, b);
    bList.push(address(b));
    return address(b);
  }
import "./B.sol";

contract A {

  address[] public bList;
  event LogNewB(address creator, address contractB);

  function createB() public returns(address contractB) {
    B b = new B();
    emit LogNewB(msg.sender, b);
    bList.push(b);
    return address(b);
  }
import "./B.sol";

contract A {

  address[] public bList;
  event LogNewB(address creator, address contractB);

  function createB() public returns(address contractB) {
    B b = new B();
    emit LogNewB(msg.sender, b);
    bList.push(address(b));
    return address(b);
  }
Source Link
Rob Hitchens
  • 55.6k
  • 11
  • 91
  • 146

You have approximately answered your own question. Let's walk through the details and sort out the concerns and syntax.

The requirements for reading a contract are

  1. Knowledge of the ABI. That can be derived by compiling source code or just given.
  2. Knowledge of the contract address.

Contract design can assist with the second requirement.

You wouldn't do:

contract A { import "./B.sol"; ...

But, you might do

import "./B.sol";

contract A { ...

And, you could have a function to deploy a new B:

function createB() public returns(address contractB) {
  B b = new B(); // cast b as B, defined by "contract B" in the imported file
  return address(b);
}

Since that is a state-changing function, software clients will only get a transactionHash and not address(b) - that response is only available to other contracts. It can be very useful and is generally recommended to also emit an event for an important state-change such as this. Events are observable by a Web3 watcher.

import "./B.sol";

contract A {

  event LogNewB(address creator, address contractB);

  function createB() public returns(address contractB) {
    B b = new B();
    emit LogNewB(msg.sender, b);
    return address(b);
  }

What if the observer isn't watching? It can be useful to make this discoverable by inspecting the contract. A can keep track of the B instances it deploys:

address[] public bList;
...
bList.push(b);

Altogether ...

import "./B.sol";

contract A {

  address[] public bList;
  event LogNewB(address creator, address contractB);

  function createB() public returns(address contractB) {
    B b = new B();
    emit LogNewB(msg.sender, b);
    bList.push(b);
    return address(b);
  }

Since bList is public, you get a "free" getter that will return the address at a row, roughly equivalent to:

function bList(uint row) public view returns(address) {
  return bList[row];
}

Now there are three ways for a Web3 client to construct a list of all the B contracts that were deployed by A. They can listen to event logs, resolve mined transaction hashes and inspect the log arguments, or inspect the bList. Together with knowledge of ABI for B, the client can:

var instanceB = web3.eth.contract(abi).at(address);

As Julien pointed out, Web3 is indifferent about the method that was used to deploy the contract.

Hope it helps.

p.s. It may be helpful to add a quick function to reveal the length of the bList, otherwise software clients will not know where the end of the array is. Something like:

function getBCount() public view returns(uint) { return bList.length; }