Refactor core/ for multi-callee support#511
Conversation
…t + other adjustments
core/src/auctions.ts
Outdated
| let marketDataRecords = await getMarketData(network, auction.collateralSymbol, collateralToCoverDebt); | ||
| const exchangeFees = await getDefaultMarketFee(network); | ||
| const exchangeFeePerUnitDAI = exchangeFees.exchangeFeeDAI.dividedBy(collateralToCoverDebt); | ||
| for (let marketData of Object.values(marketDataRecords)) { |
There was a problem hiding this comment.
Currently, you put the whole loop into the try/catch here. So in case any market fails (which is expected, see comment below catch) the auction will be returned without any market data. Please make so that each market data is checked separately
Nit: you might want to move the whole loop into a separate function to simplify try/catching it.
There was a problem hiding this comment.
We use the loop to populate marketData after getting them. If there is a place where the code would fail/throw an error, it's when calling getMarketData(). Looking at the code again, I still don't understand why we should push the try/catch inside the loop; we would be basically trying to catch assignments. Or maybe I don't understand what you mean concretely.
There was a problem hiding this comment.
We normally should not care if functions can or can not throw errors, we should assume they always can. My main point is that if one of the markets fail (which we expect to happen), the full "list" of markets should still be sent to the frontend:
- Single
marketDatacan't be null or undefined - The
maketDataRecordsitself can't be null or undefined
There was a problem hiding this comment.
So the question then boils down to: why do you need try catch above the whole loop then if you catch market problems on the level deeper? As it will return incorrect state, where marketDataRecords === undefined.
The proposed solution is to wrap the parts into try/catch at the place where you need to replace them with other "error"-like objects
There was a problem hiding this comment.
The whole thing is still in the try/catch, right?
core/src/calleeFunctions/index.ts
Outdated
| try { | ||
| marketUnitPrice = await allCalleeFunctions[marketData.callee].getMarketPrice( | ||
| network, | ||
| collateral, | ||
| marketId, | ||
| amount | ||
| ); | ||
| } catch { | ||
| marketUnitPrice = new BigNumber(NaN); | ||
| } |
There was a problem hiding this comment.
Why do you need to try/catch it here? (if your function can still throw)
In other words, if you want to return market data records in any case, you need to move Unsupported collateral symbol "${collateralSymbol}" to be thrown on the market level?
I also suggest to add new optional key marketData.errorMessage which will contain those errors (because previously it was cached on the frontend level and we were able to display this error)
There was a problem hiding this comment.
We throw above to make sure that there is at least one valid callee name (i.e. contained in allCalleeFunctions), because otherwise we can't call getMarketPrice(). On the other hand, we try/catch below because the getMarketPrice() method of each callee can be different (encoding routes vs. fetching token reserves...) and is prone to error, so if we encounter one, we return NaN.
There was a problem hiding this comment.
I also updated the code to continue the loop in the case of an invalid callee name (latest commit).
There was a problem hiding this comment.
I still don't see any errorMessage inside marketData
|
Although the loop in |
| let marketUnitPrice: BigNumber; | ||
| try { | ||
| marketUnitPrice = await allCalleeFunctions[marketData.callee].getMarketPrice( | ||
| network, | ||
| collateral, | ||
| marketId, | ||
| amount | ||
| ); | ||
| } catch (error: any) { | ||
| const errorMessage = error; | ||
| marketUnitPrice = new BigNumber(NaN); | ||
| return { | ||
| ...marketData, | ||
| marketUnitPrice, | ||
| errorMessage, | ||
| }; | ||
| } | ||
| return { | ||
| ...marketData, | ||
| marketUnitPrice: marketUnitPrice ? marketUnitPrice : new BigNumber(NaN), | ||
| }; |
There was a problem hiding this comment.
Why do you need to try catch it here if you already catching it in the parent function getMarketData (within the loop)?
| marketData = { | ||
| errorMessage: error, | ||
| marketUnitPrice: new BigNumber(NaN), | ||
| route: [], | ||
| }; |
There was a problem hiding this comment.
Why does this suddenly returns a route?
core/src/auctions.ts
Outdated
| let marketDataRecords = await getMarketData(network, auction.collateralSymbol, collateralToCoverDebt); | ||
| const exchangeFees = await getDefaultMarketFee(network); | ||
| const exchangeFeePerUnitDAI = exchangeFees.exchangeFeeDAI.dividedBy(collateralToCoverDebt); | ||
| for (let marketData of Object.values(marketDataRecords)) { |
There was a problem hiding this comment.
The whole thing is still in the try/catch, right?
| marketData = { | ||
| ...marketData, | ||
| marketUnitPrice: marketData.marketUnitPrice.plus(exchangeFeePerUnitDAI), | ||
| marketUnitPriceToUnitPriceRatio: auction.approximateUnitPrice | ||
| .minus(marketData.marketUnitPrice) | ||
| .dividedBy(marketData.marketUnitPrice), | ||
| ...exchangeFees, | ||
| transactionGrossProfit: calculateTransactionGrossProfit( | ||
| marketData.marketUnitPrice, | ||
| collateralToCoverDebt, | ||
| auction.approximateUnitPrice | ||
| ), | ||
| transactionGrossProfitDate: calculateTransactionGrossProfitDate( | ||
| auction, | ||
| marketData.marketUnitPrice, | ||
| currentDate | ||
| ), | ||
| }; |
There was a problem hiding this comment.
The idea of the last suggestion was to move this whole block with calculations into the getMarketDataById. The reason is: it's extremely confusing that everything is a marketData and there is no types to enforce proper keys. You call makertData collateral.exchanges[marketId] in one place, { errorMessage: error, marketUnitPrice: new BigNumber(NaN), route: [], } in another, then here you call marketData something else, but those are all different thing and we have typescript to enforce them
There was a problem hiding this comment.
Tested on hardhat fork looking at an active ETH-A auction
Running the bot resulted in the following errors:
- When testing on current block: Not able to participate in any auction (similar to the problem seen when using the UI below) -> needs to be addressed
ℹ collateral keeper: auction "ETH-A:836" is not tradable 19:00:25
Testing the frontend:
- Currently unable to swap any collateral into profit as the button to access transaction page is disabled (see below)
LukSteib
left a comment
There was a problem hiding this comment.
Tested via hardhat fork, simulation ETH-A auction
- The bot throws an error when trying to execute the swap transaction (see terminal output below). While authorisation steps work as expected there seems to be a problem with callee contract determination
- Similar problem on frontend (see console output below)
terminal output for bot execution:
ℹ collateral keeper: auction "ETH-A:836" net profit is 6154 DAI after transaction fees, moving on to the execution 12:16:04
ℹ keeper: wallet "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" has not been authorized yet. Attempting authorization now...
ℹ Transaction is preparing to be mined... 12:16:04
ℹ Transaction is pending to be mined... 12:16:05
ℹ Transaction was mined into block "15939548" 12:16:05
ℹ keeper: wallet "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" successfully authorized via "0x76f9eed11c31cd9fcfe3226af1dd21613f9f2fbed4f724b211cdfe55bc3ee9c3" transaction
ℹ collateral keeper: auction "ETH-A:836" net profit is 6154 DAI after transaction fees, moving on to the execution 12:16:05
ℹ keeper: wallet "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" has already been authorized 12:16:05
ℹ keeper: collateral "ETH-A" has not been authorized on wallet "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" yet. Attempting authorization now...
ℹ Transaction is preparing to be mined... 12:16:05
ℹ Transaction is pending to be mined... 12:16:05
ℹ Transaction was mined into block "15939549" 12:16:05
ℹ keeper: collateral "ETH-A" successfully authorized on wallet "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" via "0x6c4ee203e3f0d4810f93a9ea76c37d6af77f0b254f5cc3133bd64c3932ed9706" transaction
ℹ collateral keeper: auction "ETH-A:836" net profit is 6154 DAI after transaction fees, moving on to the execution 12:16:05
ℹ keeper: wallet "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" has already been authorized 12:16:05
ℹ keeper: collateral "ETH-A" has already been authorized on wallet "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" 12:16:05
ℹ collateral keeper: auction "ETH-A:836": attempting swap execution 12:16:05
ERROR collateral keeper: unexpected error: No callee address found for the "ETH-A" collateral on "custom" networkconsole output with stack trace:
Bidding error Error: No callee address found for the "ETH-A" collateral on "custom" network
at getCalleeAddressByCollateralType (CALLEES.ts?96e2:45:1)
at _callee15$ (auctions.ts?450c:334:1)
at tryCatch (router-sdk.cjs.development.js?ce8d:423:1)
at Generator.invoke [as _invoke] (router-sdk.cjs.development.js?ce8d:654:1)
at Generator.eval [as next] (router-sdk.cjs.development.js?ce8d:479:1)
at asyncGeneratorStep (asyncToGenerator.js?1da1:3:1)
at _next (asyncToGenerator.js?1da1:25:1)
at eval (asyncToGenerator.js?1da1:32:1)
at new Promise (<anonymous>)
at eval (asyncToGenerator.js?1da1:21:1)
|
Next round of functional review (again based on hardhat fork with
console error on swap execution: |
|
I removed the dummy
The above bidding error, however, will resolve after |
The main/staging should never be in a broken state. So if we want to merge a refactoring PR, current functionality should stay intact. You can achieve that by temporary passing the suggest market id in the modified store action. |
| try { | ||
| marketData = await getMarketDataById(network, collateral, marketId, amount); | ||
| } catch (error: any) { | ||
| marketData = { | ||
| errorMessage: error?.message, | ||
| marketUnitPrice: new BigNumber(NaN), | ||
| route: [], // dummy value: MarketData requires either a route or tokens | ||
| }; | ||
| } |
There was a problem hiding this comment.
If you remember, we want to show the route on the frontend. In case there is no "marketData", the route will be empty and there will be nothing to display.
If you move try/catch down the getMarketDataById, it can return correct route or token based on the collateral config, not just a dummy empty value.
Please fix this in the next frontend PR
There was a problem hiding this comment.
getMarketDataById can throw an error if the callee is invalid. That's why I'm catching it here. So, what do you mean exactly by moving try/catch down?
There was a problem hiding this comment.
Good question. I guess linked throw condition shouldn't even be possible (eg via static typing), or should be validated during tests as it's strictly depends on the static config, not on the external conditions.


Closes #497 .
Checklist:
#)