Solidity Learnings: Config Contract

blake west
Warbler Labs
Published in
4 min readApr 26, 2021

Today I want to discuss a core smart contract development pattern that basically any dApp will find useful: The “Config Contract Pattern”. I’ve never seen anyone discuss it in a blog post, and yet I couldn’t imagine writing Goldfinch without it. It wasn’t obvious from the beginning that we should do it, but now it is.

1 minute Summary

  • Config Contracts are a way to separate data and logic in your smart contracts.
  • Instead of storing various addresses or magic numbers (ie. fees or thresholds) on a contract directly, you store it in the Config once, and all lookups go through it. This means all contracts within your protocol can use the same values and are always up to date.
  • It makes the code on your logic contracts much cleaner
  • It’s great for allowing some very limited changes via governance (ie. protocol fee %) while maintaining the rest of the contract as immutable and/or not having to grant any admin rights on the logic contract itself.
  • It comes at the cost of extra gas: one additional SLOAD call per lookup.
  • See our implementation here, and example usages here and here.

The Problem

When we were developing V1 of Goldfinch and our contracts needed to “know” certain things, we started out with code that looked something like this.

Here our CreditDesk is creating a CreditLine, so it needs to talk with the CreditLineFactory. Of course, to do this, it needs to know the address of the CreditLineFactory. So naturally, we store the creditLineFactoryAddress on the CreditDesk itself. This is simple and it works. Great.

Oh, but ya know what, we may need to change the CreditLineFactory if we upgrade our CreditLines, so let’s add a setter for that. And actually, as part of our guarded launch, we should limit how much a credit line can be for. Let’s add that in. And we’ll want to change that max number as we grow, so I guess let’s add a storage slot as well as a setter method for it. Ah, and one more thing, only the owner should be able to change these. And the owner should be able to change too when we hand control over to the community. Ok! Now our contract looks like this…

Are you starting to see a pattern? We’ve barely gotten started, and we already have 3 variables we need to store, all of which might change, and all of which require setters, yet none of which affect the underlying logic. And what if we have another contract that needs to know some of this same info (eg. protocolOwner or address of CreditLineFactory)? What if we add more contracts within our protocol that our CreditDesk needs to know about? This could pretty quickly turn into a lot of boiler plate and needless complexity. We can do better!

The Solution

In “traditional” web development, there are multiple solutions to this problem. Namely, config files, or databases. They let you spin up many stateless apps which can all get info from the same persistent storage. We can use that same idea here.

Enter the “Config Contract”. The idea is simple: any values that either live across many contracts within our protocol, or which are liable to change, but don’t affect underlying logic, can be stored in the ConfigContract, separate from all other contracts. A simple version looks like this:

Great, now let’s refactor our CreditDesk, to take advantage of the ConfigContract

Making It Even Cleaner

Alright! Looking better! We were able to delete 2 storage slots, and a couple setter methods. You might be thinking, however, that retrieving our addresses via numbers (ie. config.addresses(1)) is opaque. That's fair, but it can be easily solved with a library. So let's do that real quick. Here's a simple version (sorry, Github doesn’t know how to do syntax highlighting on solidity libraries yet):

Now we refactor our CreditDesk again to use this…

Boom! So clean! And if we ever need to add more things like this, we only add it in the config, and the CreditDesk will automatically have access. Plus any other future contracts will be able to use the same information very easily.

Seeing It In Action

So to show how much easier changes become, let’s add a new method to let borrowers actually drawdown cash. This means we’re going to need access to our Pool contract, which is where all the money will actually sit. Before we would have had to add a poolAddress storage slot, and a new setter method. But now, with the config contract, we can just do:

Recap

  • Config Contracts are a way to separate data and logic in your smart contracts.
  • Instead of storing various addresses or magic numbers (ie. fees or thresholds) on a contract directly, you store it in the Config once, and all lookups go through it. This means all contracts within your protocol can use the same values and are always up to date.
  • It makes the code on your logic contracts much cleaner
  • It’s great for allowing some very limited changes via governance (ie. protocol fee %) while maintaining the rest of the contract as immutable and/or not having to grant any admin rights on the logic contract itself.
  • It comes at the cost of extra gas: one additional SLOAD call per lookup (because you have to look up the config address in addition to the information itself). At worst this is 2100 gas after the Berlin upgrade, which may be relevant depending on your context. If you’re confident your config address will never change, then you can cut the extra cost out entirely by storing your config address with the `IMMUTABLE` keyword. That is left as an exercise for the reader.
  • We use this pattern for virtually every contract at Goldfinch
  • See our implementation here, and example usages here and here.

If you find this interesting, and you’re pumped about DeFi and Goldfinch’s mission, we’re hiring! Or come say hi in our Discord!

--

--

blake west
Warbler Labs

Cofounder, CTO @goldfinch_fi. Formerly: Senior Engineer @Coinbase, 1st hire @HintHealth, Musician. Also ML enthusiast