|
| 1 | +# Working with external libraries |
| 2 | + |
| 3 | +**Table of contents:** |
| 4 | + |
| 5 | +- [Introduction](#introduction) |
| 6 | +- [Example code](#example-code) |
| 7 | +- [Deploying libraries](#deploying-libraries) |
| 8 | +- [Linking libraries](#linking-libraries) |
| 9 | + |
| 10 | +## Introduction |
| 11 | + |
| 12 | +One important feature used Solidity introduced a brand new concept of smart contract library. While this feature is not extremely popular, a good amount of contracts use them to structure code and reduce the amount of bytecode deployed. |
| 13 | +Before continue, it is very recommended to review [the official Solidity documentation](https://docs.soliditylang.org/en/v0.8.19/contracts.html#libraries) to make sure |
| 14 | +you understand how libraries are created and deployed. When a user creates a library, Solidity will do one of these two options: |
| 15 | + |
| 16 | +* If all the functions are internal, the library is compiled into bytecode and added into the contracts that use it. |
| 17 | +* If there are some external functions, the library should be deployed into some address. Finally, the bytecode calling the library should be linked. |
| 18 | + |
| 19 | +If your libraries only contain internal functions, then Echidna will work correctly and you don't need to do anything extra to start your testing (you can skip the rest of the tutorial). |
| 20 | +However, if you need to use libraries that needed to be deployed (and the bytecode needs to be linked), then you will need this tutorial. |
| 21 | + |
| 22 | +## Example code |
| 23 | + |
| 24 | +For this tutorial, we will use [the metacoin example](https://github.com/truffle-box/metacoin-box). Let's start compiling it: |
| 25 | + |
| 26 | +``` |
| 27 | +$ git clone https://github.com/truffle-box/metacoin-box |
| 28 | +$ cd metacoin-box |
| 29 | +$ npm i |
| 30 | +``` |
| 31 | + |
| 32 | +## Deploying libraries |
| 33 | + |
| 34 | +Libraries are contracts that need to be deployed first. Fortunately, Echidna allows us to do that easily, using the `deployContracts` option. In the metacoin example, we can use: |
| 35 | + |
| 36 | +```yaml |
| 37 | +deployContracts: [ |
| 38 | + ["0x1f", "ConvertLib"], |
| 39 | +] |
| 40 | +``` |
| 41 | + |
| 42 | +The address where the library should be deployed is arbitrary, but it should be the same as the one in the used during the linking process. |
| 43 | + |
| 44 | +## Linking libraries |
| 45 | + |
| 46 | +Before a contract can use a deployed library, its bytecode requires to be linked. This procedure requries to replace a particular string place holder |
| 47 | +in the bytecode by the address of the deployed library. Normally, either solc or the compilation framework (e.g. truffle) will take care of this. |
| 48 | +However, in our case, we will use `crytic-compile`, since it is easier to handle all cases from different frameworks just adding one new argument |
| 49 | +to pass to `crytic-compile` from Echidna: |
| 50 | + |
| 51 | +```yaml |
| 52 | +cryticArgs: [ |
| 53 | + "--compile-libraries=(ConvertLib,0x1f)", |
| 54 | +] |
| 55 | +``` |
| 56 | + |
| 57 | +Going back to the example, if we have both config options in a single config file (`echidna.yaml), we can run the metacoin contract |
| 58 | +in `exploration` mode: |
| 59 | + |
| 60 | +``` |
| 61 | +$ echidna . --test-mode exploration --corpus-dir corpus --contract MetaCoin --config echidna.yaml |
| 62 | +``` |
| 63 | + |
| 64 | +We can use the coverage report to verify that function using the library (`getBalanceInEth`) is not reverting: |
| 65 | + |
| 66 | +``` |
| 67 | + 28 | * | function getBalanceInEth(address addr) public view returns(uint){ |
| 68 | + 29 | * | return ConvertLib.convert(getBalance(addr),2); |
| 69 | + 30 | | } |
| 70 | +``` |
| 71 | + |
| 72 | +However, the code of library itself will not have their coverage displayed correctly: |
| 73 | + |
| 74 | +``` |
| 75 | + 6 | | library ConvertLib{ |
| 76 | + 7 | | function convert(uint amount, uint conversionRate) public pure returns (uint convertedAmount) |
| 77 | + 8 | | { |
| 78 | + 9 | | return amount * conversionRate; |
| 79 | +10 | | } |
| 80 | +11 | | } |
| 81 | +``` |
| 82 | + |
| 83 | +This is caused by the usage of `delegatecall` to execute contract code and unfortunately we do not have a workaround for it right now. |
0 commit comments