Solidity Integration of Multiple Contracts and a Library

I’m trying to understand the correct way to Deploy and then Import multiple Smart-Contracts and a Library when creating one big project.

As an example, the project will have the following Library and three Smart-Contracts.

We begin with:

The Library

pragma solidity >=0.4.0 <0.8.0;

library PizzaLib {

    struct Pizza {
       uint256 pieSize;
       string ingredients;
    }

    function mix(Pizza calldata pizza, uint256 pSize, string theIngredients) external pure returns (Pizza memory) {
        Pizza memory newPizza = pizza;
        newPizza.pieSize = pSize;
        newPizza.ingredients = theIngredients;

        return newPizza;
    }
}

This library will be used by all three upcoming smart-contracts (and perhaps more contracts to come) so I’m therefore going to deploy it independently. That way it’s sitting in it’s own address on the Blockchain and any contract can access it (which I believe is how this should work – but if I’m wrong even about this much, please let me know.)

Next is:
Contract # 1

// Note that I'm putting an "import" statement here for the Library I just created, 
// but do I actually need this statement? Or will my migration/deployment 
// file - below - take care of linking the Library to this Contract 
// anyway? (That's question #1)
// 
// For now I'll put the import statement in:

import "PizzaLib.sol"; 

using PizzaLib for PizzaLib.Pizza;

contract MakePizza {
   // -Has functions to make various Types of Pizza
   // etc.
}

Now we move on to Contract # 2

This contract will also need to use the ‘library’, so question # 1 applies here as well (do I import or not?)
But here I have an additional question – question #2 – which is: this contract needs to not only access the ‘library’, but also contract # 1 (MakePizza), which was deployed independently, meaning it too resides in its own unique address on the Blockchain.
So, how do I instantiate MakePizza here, in the following DeliverPizza contract?

I’m thinking that at this point I need to NOT use import statements and NOT use the is syntax, as follows:

import "MakePizza.sol";

contract DeliverPizza is MakePizza {
 . . . 
}

Instead, I should just create the contract as follows – omitting both the import and is MakePizza statements:

contract DeliverPizza  {
    // This contract only handles the Deliveries of Pizzas. So it:
    // 1. Defines a “Customer” Struct (customer’s name, phone number, etc.)
    // 2. Defines a “CustomerAddress” Struct (street address, city, zip-code, etc.) 
    // 3. Has “delivery” type functions
}

Up next is Contract # 3
This guy needs to reference/instantiate everything we’ve done up until now: the PizzaLib library and the MakePizza and DeliverPizza contracts:

Contract HandleTheMoney   {
    // Defines all things having to do with funds:
    // 1. Acceptable Payment methods
    // 2. Defines various Fees
    // 3. Declares the Permissions re who’s allowed to withdraw funds from the contract, etc.
}

Question here again is: how do I instantiate / reference those other contracts in this contract?

The last contract is Contract # 4
Same thing. It needs to reference everything we spoke of up until now.
Usually I would use the is syntax, as follows….

contract JoesPizzaShop is MakePizza, DeliverPizza, HandleTheMoney {
    // The “master” contract, bringing everything together
}

…but I don’t think that’s the way to go what with all the contracts being deployed independently, so it’d just be:

contract JoesPizzaShop {
    // Need to instantiate all the other contracts here...
}

FINALLY, we have the migration file – which I’m pretty sure I’ve got right:

module.exports = async function(deployer, network, accounts) {
  await deployer.deploy(PizzaLib, { overwrite: false });

  await deployer.link(PizzaLib, [MakePizza, DeliverPizza, HandleTheMoney, JoesPizzaShop]);
  
  await deployer.deploy(MakePizza);
  await deployer.deploy(DeliverPizza);
  await deployer.deploy(HandleTheMoney);
  await deployer.deploy(JoesPizzaShop);
}

Here’s what I’m doing there:

  1. Deploying the PizzaLib first. Then,
  2. Linking it to all my contracts – which are listed in an array. And finally,
  3. Deploying each and every one of those contracts, one at a time.

So to recap my questions:

  1. Do I use import or not?
  2. Do I need to put the using PizzaLib for PizzaLib.Pizza; in every contract?
  3. Do I need the is statement in all my contract declaration statements or not?
  4. How do I reference / instantiate the different contracts in each-other so I can call methods from one in another?

Answer

I think the main question here is how do you want to structure your code:

  • your master contract to inherit all functionality and manage everything from a single contract’s state
  • have separate contracts and states (i.e you could make pizza without using the master contract)

If you want to use different levels of complexity (inheriting), then:

  • use is when you want to inherit
  • there is no need to deploy all contracts
  • import and link the library to those contracts that use it
  • only deploy the library and the last contract

If you want to have separate contracts that can also be used individually:

  • don’t use is
  • deploy library and link it to well the contracts that use it
  • import the library in all the contracts that use it
  • you can find out how to reference the contracts’ functions from another contract here

For more info on how to deal with libraries: https://blockheroes.dev/import-external-contracts-libraries/

Attribution
Source : Link , Question Author : Mark55 , Answer Author : Ioana Roceanu

Leave a Comment