Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-linear Block-based Orders #269

Merged
merged 24 commits into from
Sep 19, 2024
Merged

Non-linear Block-based Orders #269

merged 24 commits into from
Sep 19, 2024

Conversation

codyborn
Copy link
Collaborator

@codyborn codyborn commented Aug 23, 2024

  • New reactor, order type, and decay library to support non-linear block-based decay
  • Util lib
  • Util tests
  • NonLinearDecay Tests
  • Reactor tests

Some notable differences from Dutchv2 Orders:

  1. Instead of an uint256 endAmount, input and output use a NonLinearDecay curve struct, which defines the relative points on the decay curve. The points are relative so that the cosigner only needs to override the startAmount and the user’s signed curve can remain unchanged.
  2. We use uint16 to store the relative block numbers. With 16 bits, we can define curves with points far into the future (11 days on Arbitrum and 1.5 years on Mainnet). If we were to use 8 bits, we could only define curves with 64 seconds into the future on Arbitrum.
  3. NonLinearDutchInput now contains a maxAmount property to hold the max point on the input curve. Used for constructing the InputToken object for Permit2.

Copy link
Collaborator

@marktoda marktoda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Some thoughts

src/base/ReactorStructs.sol Outdated Show resolved Hide resolved
src/lib/ExclusivityLib.sol Outdated Show resolved Hide resolved
src/lib/ExclusivityLib.sol Outdated Show resolved Hide resolved
src/lib/Util.sol Outdated Show resolved Hide resolved
src/lib/NonLinearDutchDecayLib.sol Outdated Show resolved Hide resolved
src/lib/Util.sol Outdated Show resolved Hide resolved
src/lib/NonLinearDutchDecayLib.sol Outdated Show resolved Hide resolved
src/lib/NonLinearDutchOrderLib.sol Outdated Show resolved Hide resolved
// The tokens that must be received to satisfy the order
NonLinearDutchOutput[] baseOutputs;
// signed over by the cosigner
CosignerData cosignerData;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since these are purely relative, the cosigner now has full power to set the start amount to whatever they want? some thoughts here:

  • this is a lot more power than cosigner has had in the past. Maybe we want to consider setting a min/max value?
  • potentially cosigned start/end amounts could be so high/low to cause under/overflow after adjustment - probably want to make sure we cover that case

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cosigner can only update the startAmount (not the relative amounts) and only if it's better than what the swapper signed (see here). The curve and base amounts are set by the swapper. Because of this, I don't think the cosigner has more trust than it did in the past. LMK if I'm missing something.

If the cosigner is malicious and attempts to cause overflow, the tx will revert (as it's currently implemented). Underflow isn't possible since we're subtracting an int256 from a uint256. In the case of overflow we could either:

  1. Do nothing and have the swapper deal with the failed tx (eg. use another cosigner).
  2. Detect the overflow in the reactor and fallback to using the user provided input. This will be more gas intensive since we'd need to loop through the curve and check each point.

Given this could only occur with a malicious cosigner and the worst case is that the tx reverts, I think it's safe to just let the tx revert.

}
uint256 blockDelta = block.number - decayStartBlock;
// iterate through the points and locate the current segment
for (uint256 i = 0; i < curve.relativeAmount.length; i++) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also because the max number of points on the curve is known and strictly increasing, we can optimize this via a binary search once we know if blockDelta is greater than or equal to the mid value in the curve

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've implemented this in this commit however, I think it actually costs more gas for the length of points we're dealing with.

Linear Search

image

Binary Search

image

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performed more extensive gas testing comparing linear with binary searching. Binary search only starts showing real benefits when we have 14 or more curve points. Opting for linear search since we will likely have less than 5 curve points defined and it's less bug prone.

image

src/reactors/NonLinearDutchOrderReactor.sol Outdated Show resolved Hide resolved
src/reactors/NonLinearDutchOrderReactor.sol Outdated Show resolved Hide resolved
@marktoda
Copy link
Collaborator

seems you need a forge fmt

Copy link
Collaborator

@marktoda marktoda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any tests for the reactor as a whole?

src/lib/V3DutchDecayLib.sol Outdated Show resolved Hide resolved
src/lib/V3DutchDecayLib.sol Outdated Show resolved Hide resolved
src/lib/V3DutchDecayLib.sol Outdated Show resolved Hide resolved
src/lib/V3DutchDecayLib.sol Outdated Show resolved Hide resolved
src/lib/V3DutchDecayLib.sol Outdated Show resolved Hide resolved
src/reactors/V3DutchOrderReactor.sol Outdated Show resolved Hide resolved
src/types/Uint16Array.sol Show resolved Hide resolved
src/lib/NonLinearDutchDecayLib.sol Outdated Show resolved Hide resolved
src/lib/ExclusivityLib.sol Show resolved Hide resolved
src/lib/MathExt.sol Outdated Show resolved Hide resolved
src/lib/NonlinearDutchDecayLib.sol Show resolved Hide resolved
src/lib/NonlinearDutchDecayLib.sol Outdated Show resolved Hide resolved
test/lib/NonLinearDutchDecayLib.t.sol Show resolved Hide resolved
test/lib/NonLinearDutchDecayLib.t.sol Outdated Show resolved Hide resolved
test/reactors/V3DutchOrderReactor.t.sol Show resolved Hide resolved
test/reactors/V3DutchOrderReactor.t.sol Outdated Show resolved Hide resolved
test/reactors/V3DutchOrderReactor.t.sol Show resolved Hide resolved
@codyborn codyborn mentioned this pull request Sep 11, 2024
* Gas adjustment feature and tests

* Update 712 structs

* Final tests

* Address PR feedback

* Natspec

* PR Feedback

* edge case fix and gas improvements
marktoda
marktoda previously approved these changes Sep 18, 2024
Copy link
Collaborator

@marktoda marktoda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! Few ideas

src/lib/MathExt.sol Outdated Show resolved Hide resolved
src/lib/MathExt.sol Show resolved Hide resolved
Uint16Array relativeBlocks = fromUnderlying(curve.relativeBlocks);
uint16 blockDelta = uint16(block.number - decayStartBlock);
int256 curveDelta;
// Special case for when we need to use the decayStartBlock (0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment could use a little more color, taking me some effort to figure out what's happening here

So decayStartBlock is already past, but the first block delta hasn't been hit yet? So you're anchoring the left end on block 0? I'm not sure I see the reason even for linear decay there, since it's just going to be sooo close to startAmount + relativeAmounts[0] right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edit: ohh it's blockDelta of 0, not block 0 right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is one case where maybe named parameters for linearDecay would help

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible to just contain this case within locateCurvePosition?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just refactored it.. lmk what you thnk!

@codyborn codyborn dismissed zhongeric’s stale review September 19, 2024 20:59

Addressed review comments. Eric is out this week so can't respond.

@codyborn codyborn merged commit e4bd92a into main Sep 19, 2024
3 checks passed
@codyborn codyborn deleted the nonlinear_decay branch September 19, 2024 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants