feat: Just in time polling of funds#116
Draft
anxolin wants to merge 6 commits into
Draft
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
Contributor
Author
|
I have read the CLA Document and I hereby sign the CLA |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
ComposableCowPoller, a small contract that funds a conditional order just in time, right before each part settles, instead of locking up the full amount when the order is created.I don't expect this PR to be merged as it is, my goal is to create a PoC and test the results of the exploration done on ways to implement JIT polling of funds (see private notes).
This is a follow up on cowdao-grants/cow-shed#53 and anxolin/cow-sdk-scripts#12 (slack)
Why
For order types that run over long periods (e.g. TWAP, specially when using it for DCA), pre-funding the entire thing up front is capital-inefficient and sometimes impossible: the funds may only arrive shortly before each part needs to trade.
This contract lets capital stay in the user's own account (an EOA or treasury) and pulls only what the current part needs, only when it needs it.
How it works
A funder registers a schedule for a given order, pointing at:
funder),salt, andstaticInput.The schedule is stored under an appData-independent key
id = scheduleId(funder, handler, owner, salt).Anyone can then call
topUp(id)(designed to run as a CoW pre-hook). The contract re-derives the order'sctxon-chain from the stored params and pulls exactly the current part'ssellAmountfrom the funder into the owner. It is deliberately generic: it works for any conditional order, with no order-type-specific logic of its own.Design note: making
topUpembeddable in the order's own appDataThe natural identifier for a schedule is the order's
ctx(ComposableCoW.hash(params)). But the goal is to triggertopUpfrom a pre-hook in the order's own appData, so each discrete part funds itself just-in-time — and that creates a cycle:ctxcontains the order'sappDatahash → but the hook (which references the schedule) lives inside thatappData. Any order-derived identifier (ctx,staticInput, or the order digest) has the same problem, since they all transitively containappData. keccak256 has no findable fixed point, so atopUp(ctx)hook can never be embedded in the appData it commits to.The fix is to key the schedule by data that is independent of
appData—funder,handler,owner,salt— and to storesaltso the poller can reconstructctxon-chain insidetopUp. The hook calldatatopUp(id)then no longer depends onappData, breaking the cycle. Every discrete part inherits the parent order'sappData, so each part self-funds via its own pre-hook.topUpis permissionless on purpose — safety comes from constraining what the call can do, not who makes it:ComposableCoW(cancelling it disables the poller).ctxis re-derived on-chain from the stored params, never taken from the caller.getTradeableOrder— never from the caller. Outside an active window that call reverts, so nothing can be pulled when no part is tradeable.idis namespaced byfunder, so nobody can register, overwrite, or squat another funder's schedule.Only the funder can register or revoke a schedule, so nobody can point a pull at someone else's funds.
How to review
src/types/ComposableCowPoller.solwhich is the main contract. You might want to review my private notes on the exploration I did for the options to arrive to this proposal.ctx-keyed version, before the appData-independentidchange described above. The new shape needs a redeploy.Test plan
forge test --match-contract ComposableCowPollerTest -vv