|
| 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 | +- [Summary](#summary) |
| 10 | + |
| 11 | +## Introduction |
| 12 | + |
| 13 | +Solidity support two types of libraries ([see the documentation](https://docs.soliditylang.org/en/v0.8.19/contracts.html#libraries)): |
| 14 | + |
| 15 | +- If all the functions are internal, the library is compiled into bytecode and added into the contracts that use it. |
| 16 | +- If there are some external functions, the library should be deployed into some address. Finally, the bytecode calling the library should be linked. |
| 17 | + |
| 18 | +The following is only needed if your codebase uses libraries that need to be linked. |
| 19 | + |
| 20 | +## Example code |
| 21 | + |
| 22 | +For this tutorial, we will use [the metacoin example](https://github.com/truffle-box/metacoin-box). Let's start compiling it: |
| 23 | + |
| 24 | +``` |
| 25 | +$ git clone https://github.com/truffle-box/metacoin-box |
| 26 | +$ cd metacoin-box |
| 27 | +$ npm i |
| 28 | +``` |
| 29 | + |
| 30 | +## Deploying libraries |
| 31 | + |
| 32 | +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: |
| 33 | + |
| 34 | +```yaml |
| 35 | +deployContracts: [["0x1f", "ConvertLib"]] |
| 36 | +``` |
| 37 | +
|
| 38 | +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. |
| 39 | +
|
| 40 | +## Linking libraries |
| 41 | +
|
| 42 | +Before a contract can use a deployed library, its bytecode requires to be linked (e.g set the address that points to the deployed library contract). Normally, a compilation framework (e.g. truffle) will take care of this. 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 to pass to `crytic-compile` from Echidna: |
| 43 | + |
| 44 | +```yaml |
| 45 | +cryticArgs: ["--compile-libraries=(ConvertLib,0x1f)"] |
| 46 | +``` |
| 47 | + |
| 48 | +Going back to the example, if we have both config options in a single config file (`echidna.yaml`), we can run the metacoin contract |
| 49 | +in `exploration` mode: |
| 50 | + |
| 51 | +``` |
| 52 | +$ echidna . --test-mode exploration --corpus-dir corpus --contract MetaCoin --config echidna.yaml |
| 53 | +``` |
| 54 | + |
| 55 | +We can use the coverage report to verify that function using the library (`getBalanceInEth`) is not reverting: |
| 56 | + |
| 57 | +``` |
| 58 | + 28 | * | function getBalanceInEth(address addr) public view returns(uint){ |
| 59 | + 29 | * | return ConvertLib.convert(getBalance(addr),2); |
| 60 | + 30 | | } |
| 61 | +``` |
| 62 | + |
| 63 | +However, the code of the library itself will not have their coverage displayed correctly: |
| 64 | + |
| 65 | +``` |
| 66 | + 6 | | library ConvertLib{ |
| 67 | + 7 | | function convert(uint amount, uint conversionRate) public pure returns (uint convertedAmount) |
| 68 | + 8 | | { |
| 69 | + 9 | | return amount * conversionRate; |
| 70 | +10 | | } |
| 71 | +11 | | } |
| 72 | +``` |
| 73 | + |
| 74 | +This is caused by the usage of `delegatecall` to execute contract code and [unfortunately we do not have a workaround for it right now](https://github.com/crytic/echidna/issues/1042). |
| 75 | + |
| 76 | +## Summary |
| 77 | + |
| 78 | +Working with libraries in Echidna is supported. It involves to deploy the library to a particular address using `deployContracts` and then asking `crytic-compile` to link the bytecode with the same address using `--compile-libraries` command line. |
0 commit comments