Maker contract

Overview

A maker contract is a smart contract bound to an offer on Mangrove. When a market order matches that offer, Mangrove calls the maker contract to execute its logic.

Maker contracts define two callbacks:

  1. Trade Execution — via makerExecute

  2. Trade Posthook — via makerPosthook

Trade Execution


function makerExecute(MgvLib.SingleOrder calldata order)
    internal 
    returns (uint gasused, bytes32 makerData);

The logic associated with an offer must be implemented in the makerExecute callback function. (See data structures for SingleOrder type).

Example

import {IERC20, IMaker, SingleOrder} from "@mgv/src/core/MgvLib.sol";


contract MyOffer is IMaker {
    // IMangrove mgv = IMangrove(payable(<address of Mangrove>));
    // Mangrove contract
    IMangrove mgv = IMangrove(payable(mgv));
    address reserve; // token reserve for inbound tokens
    
    // an example of offer execution that simply verifies that `this` contract has enough outbound tokens to satisfy the market order.
    function makerExecute(MgvLib.SingleOrder calldata order) external returns (bytes32 makerData) {
        // revert below (in case of insufficient funds) to signal mangrove we renege on trade
        // reverting as soon as early to minimize bounty
        require(
            IERC20(order.offer.gives()).balanceOf(address(this)) >= order.offer.wants(),
            "MyOffer/NotEnoughFunds");
    
        // do not perform any state changing call if caller is not Mangrove!
        require(msg.sender == mgv, "MyOffer/OnlyMangroveCanCallMe");
        // `order.gives` has been transfered by Mangrove to `this` balance
        // sending incoming tokens to reserve
        IERC20(order.offer.wants()).transfer(reserve, order.offer.gives());
        // this string will be passed to `makerPosthook`
        return "MyOffer/tradeSuccess";
    }
}

Inputs

  • order — struct containing order execution data:

    • olKey — identifies the offer list

      • outbound_tkn — token the offer sends

      • inbound_tkn — token the offer receives

      • tickSpacing — spacing between valid ticks

    • offerId — Id of the matched offer

    • offer — information about the matched offer

    • takerWants — amount of outbound tokens the taker wants to receive

    • takerGives — amount of inbound tokens the taker gives

    • offerDetail — optional detail about the offer (only populated when needed)

    • global — snapshot of Mangrove’s global configuration.

    • local — snapshot of local (market-level) configuration, cleaned of tick tree info.

The protocol guarantees that takerGives / takerWants matches the offer price within precision limits.

Outputs

  • gasused — is the gas consumed by the execution.

  • makerData — arbitrary data returned by the maker. Passed directly to makerPosthook.

Important Notes

Security

  • Always require msg.sender == address(Mangrove).

  • The prev/next pointers from an offer are removed before sending it to the maker. This ensures that the maker has no information about the state of the book when it gets called.

Failing Early

  • To renege on a trade, revert with a string reason (castable to bytes32).

  • Example: revert("MyOffer/NotEnoughFunds");

  • Failing later (e.g., after partial execution) increases gas cost and bounty loss.

Gas Efficiency

  • The taker’s bounty is proportional to gas consumed during failure.

  • Check conditions early to minimize gas usage on revert.

Reentrancy Protection

  • Mangrove prevents reentrancy during makerExecute.

Locked Offer List

  • During makerExecute, the offer list is temporarily locked. Offers cannot be added, removed, or updated at this stage. Use makerPosthook to modify offers after execution.

Trade Posthook

After makerExecute returns, Mangrove calls the maker’s posthook to handle updates or clean-up.

function makerPosthook(
    MgvLib.SingleOrder calldata order,
    MgvLib.OrderResult calldata result
  ) external;

This function is optional but useful for reposting offers, updating states, or tracking results.

Example

import {IERC20, IMaker, SingleOrder, OrderResult, MgvStructs} from "@mgv/src/core/MgvLib.sol";

abstract contract MakerContract is IMaker {
    // context 
    // IMangrove mgv = IMangrove(payable(<address of Mangrove>));
    // Mangrove contract
    IMangrove mgv = IMangrove(payable(mgv));
    
    // Example of post-hook
    // if market order was a success, try to repost residual offer at the same price
    function makerPosthook(MgvLib.SingleOrder calldata order, MgvLib.OrderResult calldata result) external {
        require (msg.sender == mgv, "posthook/invalid_caller");
        if (result.mgvData == "mgv/tradeSuccess") {
            // retrieving offer data
            // the following call to updateOfferByTick will revert if:
            //    * `this` MakerContract doesn't have enough provision on Mangrove for the offer
            //    * the residual/(GASREQ+offer_gasbase) is below Mangrove's minimal density
            //    * NB : a reverting posthook does not revert the offer execution
            // update the offer with the "ByTick" version
            mgv.updateOfferByTick(
                order.olkey, // same offer list
                order.offer.tick, // same tick
                order.offer.gives() - TickLib.inboundFromOutbound(order.offer.ticktick, order.takerWants()), // what the offer was giving, minus what the taker took (wants)
                order.offerDetail.gasreq(), // keep offer's current gasreq 
                order.offerDetail.gasprice(), // keep offer's current gasprice
                order.offerId // ID of the offer to be updated 
            );
        }
    }
}

Inputs

  • oder (SingleOrder)

    • is the same as in makerExecute.

  • result (OrderResult) contains:

    • makerData message returned or revert reason from makerExecute.

    • mgvData —Internal Mangrove status code — indicates trade result (e.g. "mgv/tradeSuccess", "mgv/offerFail").

Outputs

None.

Important Notes

Security

  • Always ensure: require(msg.sender == address(Mangrove), "posthook/invalid_caller");

Gas Handling

  • The posthook runs with the remaining gas from the offer’s gasreq, after subtracting the gas used by makerExecute.

Reposting Offers

  • The offer list is unlocked during makerPosthook. You can:

    • Repost the same offer with a new price or volume.

    • Add new offers.

    • Remove or update existing ones.

Reverting

  • Reverting during makerPosthook does not cancel the trade. Settlement from makerExecute remains valid.

Last updated