General questions regarding interfaces & standard contracts

I have a few general questions regarding interfaces and standard contracts (for example, IERC721 & ERC721) i was hoping someone may be able to answer…

1.)Is it correct to say… if i import ERC721.sol into my contract then all the ERC721.sol functions are accessible by users even if i don’t list all the functions in the contract? Meaning… i don’t necessarily have to list/write out each function in my contract but users can still use all of them (ERC721 functions) just as long as i import it into my contract?

2.)By contrast, if i use/import an interface like IERC721 into my contract than I “MUST” display/list “ALL” the functions in my contract? I can’t just use a few of them, i have to use ALL of them right?

3.)Why would one choose to use the interface (IERC721) when designing his contract, versus the actual standard contract (ERC721)? Is it just because its easier for other users to interact with it? Is that the main benefit?

1 Like

ok so your nearly right here so let me just explain a few things. firstly when u say use and import these mean two different things. lets start off with ERC721. this is the actual implementation contract. if you write some contract lets call it MyContract and import ERC721, then you wont have access to all of the ERC721 functions. in order to do so you need to inherit this contract. so you would declare your contract as

contract MyContraract is ERC721 {
...
...

}

by inheriting ERC721, you the programmer immediately have access to all function, global (public) vars etc to use or call in your MyContract contract. so no you dont have to rewrite out all of the existing functions in the ERC721 contract.

the main reason you would want to inherit a contract in the first place is to extend the original contract. So in the case of ERC721 say you wanted to create your own NFT that has custom features/functions beyond whats provided by default in ERC721, then this is the approach to use whereby you create a contract (MyContract), inherit ERC721 to get access to all of the base functions and then whatever custom functions you want to add u implement in your contracrt. however do note that if you want to use one of the functions that already exists in the ERC721 contract then you can use the overide method to make the changes you want. in this case you will have to rewrite the function. but in most cases you wont find yourself ever overriding functions and it can be dangerous and indlude attack verctors if you dont know what your doing.

now onto the second scenario and interfaces. Interfaces are best used when you dont want to extend some original contract but rather when you want to interact with some contracts functions. lfor example lets say your writing a contract and you want to be able to use the Uniswap contract to preform a swap. in this scenario we dont need to extend the uniswap contract, but rather we just want to have access to it so we can call the swap function. in this case there is no need to inherit the whole contract. Why not? well for a few reasons. firstly inheriting it and not using all of the functions will make your contract bigger and cost more to deploy, secondly theres lodes of redundant code that will not be used. so this is where interfaces come in. interfaces let you pick and choose excatly what functions you want to execute from some given contract as long as you know the function headers (etc its name and what args it takes, its visibiltty etc) and also the contracts deployment address. so keeping with the uniswap exapmle if you only wanted to access the swap function then you would write your interface like

interface IUniswap {
    swap(arg1, arg2) ......{

}
}

then to access this function in your contract somewhere you would write

function myFunction(arg1, arg2) public {
    IERC20(uniswapDeploymentAddress).swap(arg1, arg2)
 }

so this is what interfaces are for. so to awnsr your other assumption. no you have it the wrong way around. when writing and interface you dont need to define all functions in the implementation contract. you can have as many or as few of them as you need.

just to finish off i will give one more example from a apost ive written here a while ago on interfaces so it will be more clear.

lets imagine we have a contract which is deployed on mainnet and it has 100s or thousands of lines of code. It is a lot of effor to copy this file into ur project just so we can import it to access its functions. Also it will most likely be the case that we only want to access one or two functions of the contract. This is where interfaces come in.

If we know the address of the contract and the names of the functions we want to implement in our contract then we can declare an interface which allows us to access those functions without any mess in a much more concise way. Consider the example below

pragma solidity 0.8.0;

contract Test {

  function addNums(uint num1, num2) public returns (uint) {

    uint result = num1 + num2;

    return result;
  }

  function subtractNums(uint num1, num2) public returns (uint) {

    uint result = num1 - num2;

    return result;
  }
}

contract CallTest {


  function useAddSum(uint num1, uint Num2) {

    Test(addressOfTestContract).addNums(num1, num2)
  }

  function useSubtractSum(uint num1, uint Num2) {

    Test(addressOfTestContract).subtractNums(num1, num2)
  }
}

In this example the contract whos functions we want to access is the Test contract. And we do so by calling them in our contract that we are writing, namely the callTest contract. In order to do this we need to specify the contract name and thhen call the function we want. This might not seem so bad here because the Test contract is very small. But imagine this contract was thousands of lines of code and we only wanted to call one or two functions. Well copying the entire contract into ours would wake things very cluttered and uneadable.

This is where we use the interface. Using the interface we can access the same contracts functins but we dont need to copy over the whole thing, we only need to use the declare the headers of the funtions we want to intercat with. Look how the example changes when we use an inerface suprisingly its not much

pragma solidity 0.8.0;

contract ITest {

  function addNums(uint num1, num2) external returns (uint);
  function subtractNums(uint num1, num2) external returns (uint);
}

contract CallTest {


  function useAddSum(uint num1, uint Num2) {

    ITest(addressOfTestContract).addNums(num1, num2)
  }

  function useSubtractSum(uint num1, uint Num2) {

    ITest(addressOfTestContract).subtractNums(num1, num2)
  }
}

even in this example you can see that we save so much space by using the interface as we dont need to declare the function bodies and we can still access the functions by interfaceing into the contract with ITest(addressOfTest).functionName(arg1, arg2)

This becomes really useful when the contract your interfacing into has dozens of functions and you only need access to one or two. This way you can just write an interface and include the headers of the two functions you need and leave everything else out.

So thats interfaces in a nutshell I hope anyone who reads this finds it useful. Any questions let me know

Note that when writing an interface you should always have the functions visibilities as external. This is because we are always calling the functions in an interface contract from another contract outside and enver from withtin the interface itself. This is just something to keep in mind. Its not too imprortant to remeber as soldidty will throw an error but its good to know none the less

1 Like

This was a great explanation. Thank you! Lots of things i was iffy about much more clear now. I do have one more question if you get to it… When you inherit a contract , for example you say… MyContract is ERC721… don’t you need to import it too? I thought when you inherit a contract you also need to import it?

Are there particular instances when you would want to “import” a contract but not necessarily inherit it? Meaning, i would write the import statement at the top of my contract but not necessarily inherit that contract. What are the use cases for importing only (not inheriting) ?

1 Like

yeah you will always need to import it. thats a given for any contract

@mgrane5 I have one more question i am hoping you can assist with. It’s regarding this get Kitty assignment…

One thing that really confuses me here is we are inheriting an interface. I thought interfaces are better for communicating with external contracts. Wouldn’t it be better to simply inherit the actual ERC721 contract instead of IERC721? If we did that wouldn’t it be a more appropriate way to extend functionality?

When i did inherit IERC721 in this exercise by importing the contract and declaring KittyContract is IERC721. I ran into some issues when compiling, most notably i had several “missing implemenation” errors. This was because there was a number of functions like safeTransfer that i did not implement in my contract. But this goes against what we discussed yesterday… i thought with interfaces you can pick and choose which functions to interact with it?

Sorry for all the questions. Just ended up very confused with regard to inheritance and interfaces for quite some time now, As soon as i think i understand it, i run into issues. Thanks for the help so far and any additional help you can provide here.

1 Like

Ahhh i figured out one of the problems here… the reason it said “missing implementation” is because i had ALL of those functions in the IERC721 contract. I guess if you only want to use say 3 functions in MyContract than you must make sure only those 3 functions are in IERC721. If you have addtional functions in IERC721 that are not implemented than it will throw. That makes sense.

hmm it is is possible to inherit interfaces but i agree with you its not a pattern i would follow. you are correct interfaces are best used to interface with functions from external contracts. if you want to have access to the functions in another contract inheritance is the way but i would not recommend inheriting interfaces, rather just import them and directly call them once u know the contract address.

yes interfaces you can pick and choose what functions u implelent. this os why its sometime sbetter to use interfaces when u only want one or two functions from some contract. you can then just define these in your custom intrface rather than importing and inheriting the implenetation contract which will cost more to deploy.

however for the sake of this course t best be able to repoduce the instructors code i would follow exsctly what they’re doing if your not feeling comfortable to change thing sand follow with your own approach. however the latter could be a good learning excercise none th eless.