Clean up how we codegen Config - #60
Conversation
DZakh
commented
Jul 4, 2024
- Fixes comments from Move codegen from Config.res file #59
- A step closer to get rid of ChainMap
|
Here's the diff of the codegen output.🙌🧠🦜 |
| chainMap: networks->Js.Array2.map(n => { | ||
| (n.chain, n) | ||
| })->ChainMap.fromArray->Utils.unwrapResultExn, |
There was a problem hiding this comment.
This does, kind of, break the reasoning for the ChainMap abstraction.
It's supposed to be that you don't create one without defining all chains. And that way you can always look it up by chain and guarantee that a value exists at that key.
| let getConfigByChainId: int => configYaml = chainId => | ||
| RegisterHandlers.getChain( | ||
| Belt.Result.getExn(ChainMap.Chain.fromChainId(chainId)), | ||
| )->mapChainConfigToConfigYaml | ||
| let getConfigByChainId: int => configYaml = chainId => { | ||
| let config = Config.getConfig() | ||
| config.chainMap->ChainMap.get(ChainMap.Chain.fromChainId(chainId)->Belt.Result.getExn)->mapChainConfigToConfigYaml | ||
| } |
There was a problem hiding this comment.
FYI this was a file that Denham added to make it possible to see values that you defined in your config.yaml from within your handlers. Maybe we can think about if there's better ways during refactor to expose these values to the user.
There was a problem hiding this comment.
Yeah, I even found documentation for it. I actually wanted to suggest to replace it with a chainConfig object in a handlers among side event and context.
JonoPrest
left a comment
There was a problem hiding this comment.
@DZakh, can we discuss what you're planning to do with ChainMap?
Otherwise the properties I was looking for with it is:
- It should ensure that all chain values are defined.
- You should be able to look up by a "chain" key and guarantee the value exists
- It should be easy to do immutable changes
If these properties are blocking you or are suboptimal then keen to hear why and what you are thinking.
|
@JonoPrest A short answer is that I don't see value in I'll continue doing what I already started in another PR, but let's have a sync on Monday, to ensure that we are on the same page. |
The value in the abstraction is that it guarentees lookup by chain. There are a lot of places in the code where we lookup by chain. I'm happy to get rid of unneeded abstractions but this still seems useful to me. |
| chain: { | ||
| id: {{chain_config.network_config.id}}, | ||
| }, |
There was a problem hiding this comment.
Are there other fields that will exist on this chain record?
| chainId->ChainMap.Chain.fromChainId->Utils.unwrapResultExn | ||
| let chain = chainId->ChainMap.Chain.evm | ||
| if !(config.chainMap->ChainMap.has(chain)) { | ||
| Js.Exn.raiseError("No chain with id " ++ chain->ChainMap.Chain.toString ++ " found in config.yaml") |
There was a problem hiding this comment.
I started creating an error handling "framework" in ErrorHandling.res
It allows you to raise exceptions with logs etc and pass a logger with a context around. I know this is essentially a global function but maybe we can use that pattern or extend on it to make error logs/exceptions more uniform.
| let get: (t<'a>, Chain.t) => 'a = (self, chain) => | ||
| switch Map.get(self, chain) { | ||
| | Some(v) => v | ||
| | None => Js.Exn.raiseError("No chain with id " ++ chain->Chain.toString ++ " found in config.yaml") | ||
| } |
There was a problem hiding this comment.
Can we return result<'a> from this and make it a safe API?
|
@DZakh, I think it looks good but lets keep the ChainMap api safe and return result or option type form the get method. |
DZakh
left a comment
There was a problem hiding this comment.
It throws, but in the end the safety of using ChainMap should be the same as before:
-
It used to throw before, now the error message is more readable
-
There are two ways to create
Chain.t- With
ChainMap.Chain.makeUnsafe- used for initial config set up and mocks - With
Config.getChain- which validates that the provided chainId is a part of the defined config. If it's not, it'll throw with a nice error message. Before it returned a result with exception, but we always unwrapped the exception, so the behaviour is not changed
- With
-
ChainMap.makewhich allowed to safely create items for all possible cases now replaces with:ChainMap.fromArray- it doesn't have any validation but it used only for initial config creationChainMap.map- all the other places useChainMap.map, so it's guaranteed to have all chains from config
| let fromArray: array<(Chain.t, 'a)> => t<'a> = arr => { | ||
| arr->Map.fromArray(~id=module(Chain.ChainIdCmp)) | ||
| arr->Map.fromArray(~id=module(ChainIdCmp)) |
There was a problem hiding this comment.
Can we call this fromArrayUnsafe or something? This is where it's unsafe IMO
| let makeUnsafe = (~chainId) => { | ||
| id: chainId, | ||
| } | ||
|
|
||
| module ChainIdCmp = Belt.Id.MakeComparableU({ | ||
| type t = t | ||
| let cmp = (a, b) => Pervasives.compare(a->toChainId, b->toChainId) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Should we not just make the type directly the id? Until we need more fields?
There was a problem hiding this comment.
Makes sense, since it's opaque now
| let convertViemDecodedEvent: Viem.decodedEvent<'a> => Viem.decodedEvent< | ||
| Types.{{contract.name.capitalized}}.{{event.name.capitalized}}.eventArgs, | ||
| > = X.magic | ||
| > = Utils.magic |
There was a problem hiding this comment.
I actually regret having a module called Utils 🤣, but it's probably a good place to put this.
There was a problem hiding this comment.
Yeah, I thought it's better not to have two Utils modules :)
f518388 to
50cc6f8
Compare
JonoPrest
left a comment
There was a problem hiding this comment.
Cool, I think its looking good 👍🏼 looks like I'll have some conflicts in the other PRs but will sort that out.
Sorry 😔 |