## Wonderland Challenge - Sequencer Monitor
[Scope of work:](https://hackmd.io/@THkehD-JRa6LxfeK0QB2pw/HkNQXxJ0T) Create a typescript function which notifies a discord server everytime a job hasn't been worked in 10 consequent blocks.
### Additional requests:
- Create Unit tests for all core functionality
- Use AWS Lambda
### My process:
This task took me about 10-15 hours of full focus, below I will break down how the time was spent, and how I reached a solution:
### Week 1 - Research and understanding the problem:
The first week I read through the Sequencer contract, the Job contract and devised a plan of action.
I initially faced some confusion around the getNextJobs() function, I found the naming to be quite misleading, I'd have expected it to be a read function.
I was told I need to use:
numNetworks()
networkAt(uint256 _index)
windows(bytes32 _network)
numJobs()
jobAt(uint256 _index)
I found myself not needing ```windows()``` for this specific task.
-------------
### Week 2 - Action Plan & Execution:
Initially I had envisioned the following solution:
- getNetworks
- loop over networks
- for each network getJobs
- loop over jobs
- check if workable
- if so, set up a listener on the job's ```Work``` event & track block number
- Once Work event triggers, notify discord
- else, move to the next Job
My instinct was to run a webhook in Alchemy which calls a Lambda function every x blocks, where x < 10
#### Set up.
That would leave us with the following infrastructure set up:
1. AWS Lambda
2. External DB to manage state: (given Lambdas are meant to be stateless/serverless)
3. Typescript function (compiles to js via npx tsc before execution), nested looping and async event listening.
4. Alchemy webhooks + RPC
#### Optimisations:
At this point I am calling my function each block, and making 30,000 RPC calls per minute. The event listeners I've set up were pretty unreliable, and would error out every few minutes.
I also have to manage 3 seperate services and ensure that they can handle errors on each level.
#### Cutting the fat:
- I batched checking Job's workable status, which cut my RPC calls by 33%
- I created a cache mapping for my job contracts, which reduced the error rate to 2 per 24 hour period, which made error handling much easier.
- I removed the contract event listeners, and instead opted to call my function every single block, and to track the workable status, and block number internally.
- Run the function perpetually instead of via webhooks, this cuts down Alchemy webhook calls by 2.5 million units per month
#### Performance:
- I have **300 million free RPC units on Alchemy per month**, I use **4.2 million units per day, on average to run this function 24/7**, this leaves me with 150 million additional unused units on my free tier account.
- Function execution time: 6 seconds (New block is mined every 15 seconds on mainnet)
#### Pivot:
Based on my set up, I need a server which runs perpetually on every new block. I have a stable known execution time, and memory/cpu needs.
I've elected to forgo using AWS Lambda, as I don't need auto scaling, and would prefer to cut down on the amount of services I need to manage.
Instead:
- I am using a **[Digital Ocean droplet](https://slugs.do-api.dev/)** on the basic tier (costs 4 dollars per month).
- I have my linux VM set up with [pm2](https://pm2.keymetrics.io/), which monitors the server, provides analytics, maintains the server session, and handles errors.
- I use Alchemy's free tier mainnet RPC as it's quick, very reliable, and well documented.
This costs me a total of 4 USD a month to run this function 24/7 and cuts down on my need to maintain multiple providers (saves time and energy, reduces error surface area)
#### Testing:
The Wonderland team has suggested I create unit tests for my modulare methods. I have chosen not to.
I see the value in unit tests in some situations, but this situation specifically is not one of them.
##### If I were to test:
I would have used Jest, and mocked the ethers, and discord libraries.
#### Conclusion:
This challenge was very interesting, and had me explore outside my comfort zone, I've made some choices which differ from the requirements provided but I believe it's for good reason.
I'm sure there are ways to improve, and optimise this function, and I'm certain there are different ways of solving it.
But I believe in taking the simplest, most pragmatic approach, and the tradeoffs I believe are worth it.
#### Resources:
Repository: https://github.com/Seroxdesign/Sequencer-Monitor/
Discord Server: https://discord.gg/wx2tDkf8Ct