Giter Site home page Giter Site logo

Comments (7)

fleupold avatar fleupold commented on September 15, 2024 1

One thing to keep in mind is that changing a storage value from 0 to non-zero costs 20k gas whereas overriding a non-zero storage value with another value costs only 5k gas (https://ethereum.github.io/yellowpaper/paper.pdf page 25).

Therefore, if we want to allow individual nonce invalidation, I think packing 256 bits into one word is crucial even if we only match a single order per batch.

from gp-v2-contracts.

josojo avatar josojo commented on September 15, 2024

Good catch!

Initially, I was running

for (uint256 i = 0; i < orders.length; i++) {
            require(
                nonces[orders[i].owner] < orders[i].nonce,
                "nonce already used"
            );
            nonces[orders[i].owner] = orders[i].nonce;
        }

that would not have these issues. But then, this is also far from optimal, as we some times want to include orders from the same user with different nonces, but we don't want to submit them in the order of the nonces.

Not sure, what to do here. Maybe we have to require users to have order nonces and limitprices aligned.

from gp-v2-contracts.

fedgiac avatar fedgiac commented on September 15, 2024

Another issue I noticed while working on this: If the users creates two orders with nonce 1 and 2 respectively but only the order with nonce 2 is executed, the order with nonce 1 is invalidated.

from gp-v2-contracts.

fedgiac avatar fedgiac commented on September 15, 2024

The solution I propose is to transform this:

    mapping(address => uint8) public nonces;

into this [0]:

    mapping(address => mapping(uint8 => bool)) public invalidatedOrders;

Every user-created order is linked to a nonce, like now.
When an order with nonce i is settled, the contract checks that invalidatedOrders[user][i] == false, else it fails. Then invalidatedOrders[user][i] is set to true.

The cost for the solver is 1 memory write per order, similar to the current mechanism.
This has the following benefits:

  • easy to cancel any orders trustlessly (solves #20).
  • easy to perform checks on whether any order is valid.
  • the solution settlment cost of this solution is the same as the current one (assuming one order per user; on the other hand, the current implementation is broken for more than one order per user).
  • assuming the user trusts the transaction creation service, the user never needs to recreate orders because previous orders were invalidated.
  • there is the option of "soft invalidating" an unexecuted order by having the user create a different order with the same nonce. If the replacement order is ever executed, then the previous order is also safely cancelled.

The only disadvantage I can think of when compared to the current solution is that the cost of cancelling orders for a user is linear with the number of orders.

[0] I wrote uint8 and bool for clarity, but for gas efficiency we'd probably be better off with uint256 for both. If we think it's common for users to have multiple orders matched in the same batch, we could pack many consecutive bools in the same storage slot so that the price of settling up to 256 orders for the same users is at most 2 storage writes, compared to the naïve solution with 256 storage writes. Not sure this is a common enough occurrence to warrant the extra complexity.

from gp-v2-contracts.

nlordell avatar nlordell commented on September 15, 2024

The current idea is to have a mapping from order hash to executed amount. Something like:

    /// @dev Bitmap to invalid the orders
    mapping(bytes32 => uint256) public executedAmounts;

Computing the key:

bytes32 orderKey = keccak(abi.encode(order.digest, order.validTo, order.owner));

Verifying the order has not been used:

uint256 previouslyExecutedAmount = executedAmounts[orderKey];
if (order.partiallyFillable) {
    uint256 orderTotalAmount = order.kind == OrderKind.SELL ? order.sellAmount : order.buyAmount;
    require(previouslyExecutedAmount.add(executedAmount) <= orderTotalAmount);
} else {
    require(previouslyExecutedAmount == 0);
}

And then do something like this to signal the order has been used:

executedAmounts[orderKey] = executedAmount;

Or, when doing a gas refund:

executedAmounts[orderKey] = 0;

from gp-v2-contracts.

nlordell avatar nlordell commented on September 15, 2024

@fleupold also suggested using the nonce to compute the order key instead of the order digest. See the original discussion here #142 (comment)

from gp-v2-contracts.

nlordell avatar nlordell commented on September 15, 2024

Closed by #213

from gp-v2-contracts.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.