# Creating a Direct contract

**Work in progress**

This page is currently being updated - thank you for your understanding.

**Info :**

This section will go through two implementations of a [maker contract](https://docs.mangrove.exchange/dev/protocol/technical-references/makers/maker-contract), which inherits from the **Direct** contract. If you don't know what the Direct contract is, we recommend reading through both the documentation on [MangroveOffer](https://docs.mangrove.exchange/dev/strat-lib/technical-references/api-preferences/strats/src/strategies/mangroveoffer) and on [Direct](https://docs.mangrove.exchange/dev/strat-lib/background/building-blocks/direct) before continuing.

### A simple `Direct` implementation - the `OfferMaker`[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#a-simple-direct-implementation---the-offermaker) <a href="#a-simple-direct-implementation---the-offermaker" id="a-simple-direct-implementation---the-offermaker"></a>

Below, we start by going through a fairly simple implementation of the abstract [Direct](https://docs.mangrove.exchange/dev/strat-lib/background/building-blocks/direct) contract.

Recall that `Direct` is an abstract implementation of `MangroveOffer`, which is itself a partial implementation of [`IOfferLogic`](https://docs.mangrove.exchange/dev/strat-lib/technical-references/api-preferences/strats/src/strategies/interfaces/iofferlogic) - the basic interface for maker contracts built with the Strat Library.

#### Constructor[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#constructor) <a href="#constructor" id="constructor"></a>

The Direct constructor looks like this:

Direct contract's constructor

```
constructor(IMangrove mgv, AbstractRouter router_, address reserveId) MangroveOffer(mgv) {
  if (router_ != NO_ROUTER) {
    setRouter(router_);
  }
  address reserveId_ = reserveId == address(0) ? address(this) : reserveId;
  RESERVE_ID = reserveId_;
  emit SetReserveId(reserveId_);
}
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/strategies/offer_maker/abstract/Direct.sol#L28-L35)

Details:

* `mgv` (the address of the Mangrove contract) is provided to MangroveOffer.
* The specific arguments of the Direct's constructor are:
  * the [`router_`](https://docs.mangrove.exchange/dev/quick-links/glossary#router)'s address, and
  * its [`reserveId`](https://docs.mangrove.exchange/dev/quick-links/glossary#reserve-identifier).

**Note :**&#x20;

Passing `address(0)` as `reserveId` is interpreted by Direct as requiring `reserveId` to be the contract's address.

The `router_` argument can be either the address of a deployed [router](https://docs.mangrove.exchange/dev/quick-links/glossary#router), or the zero address cast to an `AbstractRouter` type, when you wish to build a `Direct` contract that will do its own liquidity routing. (In the latter case, for clarity, you may also use the public constant [`NO_ROUTER`](https://docs.mangrove.exchange/dev/technical-references/api-preferences/strats/src/strategies/mangroveoffer#no_router) provided by `MangroveOffer`.)

We will allow users of `OfferMaker` to supply a [router](https://docs.mangrove.exchange/dev/quick-links/glossary#router), and use the following constructor for our contract:

Preamble and constructor

```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {ILiquidityProvider} from "@mgv-strats/src/strategies/interfaces/ILiquidityProvider.sol";
import {OLKey} from "@mgv/src/core/MgvLib.sol";
import {Tick, TickLib} from "@mgv/lib/core/TickLib.sol";
import {Direct} from "@mgv-strats/src/strategies/offer_maker/abstract/Direct.sol";
import {IMangrove} from "@mgv/src/IMangrove.sol";
import {AbstractRouter} from "@mgv-strats/src/strategies/routers/abstract/AbstractRouter.sol";
import {ITesterContract} from "@mgv-strats/src/toy_strategies/interfaces/ITesterContract.sol";
import {IERC20} from "@mgv/lib/IERC20.sol";

contract OfferMaker is ILiquidityProvider, ITesterContract, Direct {
  // router_ needs to bind to this contract
  // since one cannot assume `this` is admin of router, one cannot do this here in general
  constructor(IMangrove mgv, AbstractRouter router_, address deployer, address owner) Direct(mgv, router_, owner) {
    // stores total gas requirement of this strat (depends on router gas requirements)
    // if contract is deployed with static address, then one must set admin to something else than msg.sender
    if (deployer != msg.sender) {
      setAdmin(deployer);
    }
  }
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/toy_strategies/offer_maker/OfferMaker.sol#L1-L22)

[`gasreq`](https://docs.mangrove.exchange/dev/quick-links/glossary#gasreq)

We use 30K for default [`gasreq`](https://docs.mangrove.exchange/dev/quick-links/glossary#gasreq) of our strat. This does not leave room for any advanced [offer logic](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-logic), so we'll stick here with a very simple one where liquidity is stored on this contract. See [how to evaluate `gasreq`](https://docs.mangrove.exchange/dev/strat-lib/guides/determining-gas-requirements) for more information.

#### Simple offer management[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#simple-offer-management) <a href="#simple-offer-management" id="simple-offer-management"></a>

With this constructor in place we almost have a deployable maker contract. `Direct` already provides the implementation of a default [offer logic](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-logic) as well as internal functions to post, update and retract offers posted by our contract.

However, `Direct` does not expose any function able to [create new offers](https://docs.mangrove.exchange/dev/protocol/technical-references/makers#posting-a-new-offer) on Mangrove, since the [`_newOffer`](https://docs.mangrove.exchange/dev/technical-references/api-preferences/strats/src/strategies/offer_maker/abstract/direct#newoffer) function of Direct is internal. The requirement in our constructor to implement `ILiquidityProvider` imposes on us to have a public `newOffer` function. Using `ILiquidityProvider` ensures our contract is compatible with the [Mangrove SDK](https://docs.mangrove.exchange/dev/interacting-with-js), which expects the `ILiquidityProvider` ABI.

Our implementation of `newOffer` is simply to expose the internal `_newOffer` provided by Direct making sure the function is admin restricted (`Direct` provides the appropriate modifier `onlyAdmin`):

Offer management functions

```
///@inheritdoc ILiquidityProvider
function newOffer(OLKey memory olKey, Tick tick, uint gives, uint gasreq)
  public
  payable
  override
  onlyAdmin
  returns (uint offerId)
{
  (offerId,) = _newOffer(
    OfferArgs({olKey: olKey, tick: tick, gives: gives, gasreq: gasreq, gasprice: 0, fund: msg.value, noRevert: false})
  );
}

///@inheritdoc ILiquidityProvider
function updateOffer(OLKey memory olKey, Tick tick, uint gives, uint offerId, uint gasreq)
  public
  payable
  override
  onlyAdmin
{
  _updateOffer(
    OfferArgs({olKey: olKey, tick: tick, gives: gives, gasreq: gasreq, gasprice: 0, fund: msg.value, noRevert: false}),
    offerId
  );
}

///@inheritdoc ILiquidityProvider
function retractOffer(OLKey memory olKey, uint offerId, bool deprovision)
  public
  adminOrCaller(address(MGV))
  returns (uint freeWei)
{
  freeWei = _retractOffer(olKey, offerId, deprovision);
  if (freeWei > 0) {
    require(MGV.withdraw(freeWei), "Direct/withdrawFail");
    // sending native tokens to `msg.sender` prevents reentrancy issues
    // (the context call of `retractOffer` could be coming from `makerExecute` and a different recipient of transfer than `msg.sender` could use this call to make offer fail)
    (bool noRevert,) = admin().call{value: freeWei}("");
    require(noRevert, "mgvOffer/weiTransferFail");
  }
}
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/toy_strategies/offer_maker/OfferMaker.sol#L24-L64)

FIXME: Describe the new functions in `OfferMaker`: `newOfferByVolume` and `updateOfferByVolume`

Our maker contract is now complete and ready to be [tested](https://docs.mangrove.exchange/dev/strat-lib/guides/testing-a-maker-contract) and [deployed](https://docs.mangrove.exchange/dev/strat-lib/guides/deploying-your-contract).

Redeeming funds

We do not provide any method to redeem inbound or outbound tokens from the contract. However, `MangroveOffer` provides an admin-only `approve` function, that allows contract's admin to retrieve any token, following a call sequence of the form:

```
makerContract.approve(token, address(this), amount);
token.transferFrom(address(makerContract), address(this), amount);
```

### Advanced Direct offer: Liquidity Amplification with `Amplifier`[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#advanced-direct-offer-liquidity-amplification-with-amplifier) <a href="#advanced-direct-offer-liquidity-amplification-with-amplifier" id="advanced-direct-offer-liquidity-amplification-with-amplifier"></a>

With a simple implementation of `Direct` under our belt, let us proceed show how we can tweak our maker contract to do something more interesting that posting plain offers on Mangrove.

Suppose we have a certain amount `N` of some `BASE` token and we wish to put it for sale on two markets at the same time. To simplify assume that `BASE` is some volatile asset like ETH and we wish to sell it for any of two (equivalent-ish) stables `STABLE1` and `STABLE2` (e.g. DAI and USDC).

Of course, if we offer `N` tokens *both* on the (`BASE`, `STABLE1`) and the (`BASE`, `STABLE2`) [offer lists](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-list), one of our offers will fail if both are taken.

We have a design choice here. Either we

1. let the second offer fail and compensate the taker with our offer's [bounty](https://docs.mangrove.exchange/dev/quick-links/glossary#bounty), or,
2. incorporate in our offer logic that we wish to [retract](https://old.docs.mangrove.exchange/developers/protocol/technical-references/reactive-offer/#retracting-an-offer) the second offer when the first one is taken.

Let's follow the second design principle as it allows us to illustrate how to use the [hooks](https://docs.mangrove.exchange/dev/quick-links/glossary#hook) provided by `Direct` to update offer prices or to retract offers.

#### Constructor[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#constructor-1) <a href="#constructor-1" id="constructor-1"></a>

We modify the simple constructor of `OfferMaker` to take into account the additional gas requirements of `Amplifier`'s logic: To retract (or update) the second offer each time an offer is taken. We also choose to specialize instances of our maker contract to a particular choice of `BASE`, `STABLE1` and `STABLE2` tokens - requiring these to be given as arguments when construing the contract.

In the constructor below, we also show how to instantiate and setup a simple [router](https://docs.mangrove.exchange/dev/quick-links/glossary#router) in order to use the deployer's account as fund reserve.

Amplifier - Preamble and constructor

```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "@mgv-strats/src/strategies/offer_maker/abstract/Direct.sol";
import "@mgv-strats/src/strategies/routers/SimpleRouter.sol";
import {MgvLib, Offer, OfferDetail} from "@mgv/src/core/MgvLib.sol";
import {TickLib, Tick} from "@mgv/lib/core/TickLib.sol";

contract Amplifier is Direct {
  IERC20 public immutable BASE;
  IERC20 public immutable STABLE1;
  IERC20 public immutable STABLE2;
  uint public immutable TICK_SPACING1;
  uint public immutable TICK_SPACING2;

  ///mapping(IERC20 => mapping(IERC20 => uint)) // base -> stable -> offerid

  uint offerId1; // id of the offer on stable 1
  uint offerId2; // id of the offer on stable 2

  //           MangroveOffer <-- makerExecute
  //                  /\
  //                 / \
  //        Forwarder  Direct <-- offer management (our entry point)
  //    OfferForwarder  OfferMaker <-- new offer posting

  constructor(
    IMangrove mgv,
    IERC20 base,
    IERC20 stable1,
    IERC20 stable2,
    uint tickSpacing1,
    uint tickSpacing2,
    address admin
  ) Direct(mgv, NO_ROUTER, admin) {
    // SimpleRouter takes promised liquidity from admin's address (wallet)
    STABLE1 = stable1;
    STABLE2 = stable2;
    TICK_SPACING1 = tickSpacing1;
    TICK_SPACING2 = tickSpacing2;
    BASE = base;
    AbstractRouter router_ = new SimpleRouter();
    setRouter(router_);
    // adding `this` to the allowed makers of `router_` to pull/push liquidity
    // Note: `admin` needs to approve `this.router()` for base token transfer
    router_.bind(address(this));
    router_.setAdmin(admin);
    setAdmin(admin);
  }
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/toy_strategies/offer_maker/Amplifier.sol#L1-L49)

Note that as we manually construct and configure `router_` and set it as the router of `Amplifier`, we initially send the constant `NO_ROUTER` as argument to the `Direct` constructor.

As in the example above, we need to create a way for the maker contract to post an offer. For this example, we will not try to comply to the `ILiquidityProvider` interface - and therefore this contract will no longer be fully usable with the SDK. We will use a custom way of posting our two offers in the same transaction.

#### Publishing amplified liquidity[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#publishing-amplified-liquidity) <a href="#publishing-amplified-liquidity" id="publishing-amplified-liquidity"></a>

We already know some of the parameters we need to implement posting new offers, since we gave them in the constructor: We know the [inbound](https://docs.mangrove.exchange/dev/quick-links/glossary#inbound) and the [outbound](https://docs.mangrove.exchange/dev/quick-links/glossary#outbound) tokens of both offers. Also, we do not want the [offer owner](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-owner) to have to specify new offer's [`gasprice`](https://docs.mangrove.exchange/dev/quick-links/glossary#gasprice) and [`gasreq`](https://docs.mangrove.exchange/dev/quick-links/glossary#gasreq) so we just use default values.

If we specify a `gasprice` of zero when posting the offer, Mangrove will use [its own gas price](https://docs.mangrove.exchange/dev/protocol/technical-references/governance-parameters/global-variables#gas-price-and-oracle). For `gasreq`, we can use the public getter `offerGasreq()`, which returns the default gas requirement for the contract plus the gas required for the [router](https://docs.mangrove.exchange/dev/quick-links/glossary#router).

This leaves us having to provide the amount that the offer should [`give`](https://docs.mangrove.exchange/dev/quick-links/glossary#gives) in `BASE` token, and the amount of `STABLE1` and `STABLE2`, which the offer [wants](https://docs.mangrove.exchange/dev/quick-links/glossary#wants) - `wants1` and `wants2`. We also need to specify the TODO:%pivot ids|pivot-id% for insertion of the two offers (`pivot1` and `pivot2`) in the relevant [offer lists](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-list). As for `OfferMaker`, we only want the admin of the contract to able to post offers, so we use the modifier `onlyAdmin` again.

Amplifier - Publishing amplified offers

```
function newAmplifiedOffers(
  // this function posts two asks
  uint gives,
  uint wants1,
  uint wants2,
  uint gasreq
) external payable onlyAdmin returns (uint, uint) {
  // there is a cost of being paternalistic here, we read MGV storage
  // an offer can be in 4 states:
  // - not on mangrove (never has been)
  // - on an offer list (isLive)
  // - not on an offer list (!isLive) (and can be deprovisioned or not)
  // MGV.retractOffer(..., deprovision:bool)
  // deprovisioning an offer (via MGV.retractOffer) credits maker balance on Mangrove (no native token transfer)
  // if maker wishes to retrieve native tokens it should call MGV.withdraw (and have a positive balance)
  require(
    !MGV.offers(OLKey(address(BASE), address(STABLE1), TICK_SPACING1), offerId1).isLive(),
    "Amplifier/offer1AlreadyActive"
  );
  require(
    !MGV.offers(OLKey(address(BASE), address(STABLE2), TICK_SPACING2), offerId2).isLive(),
    "Amplifier/offer2AlreadyActive"
  );
  // FIXME the above requirements are not enough because offerId might be live on another base, stable market

  Tick tick = TickLib.tickFromVolumes(wants1, gives);

  (offerId1,) = _newOffer(
    OfferArgs({
      olKey: OLKey(address(BASE), address(STABLE1), TICK_SPACING1),
      tick: tick,
      gives: gives,
      gasreq: gasreq,
      gasprice: 0,
      fund: msg.value,
      noRevert: false
    })
  );
  // no need to fund this second call for provision
  // since the above call should be enough
  tick = TickLib.tickFromVolumes(wants2, gives);

  (offerId2,) = _newOffer(
    OfferArgs({
      olKey: OLKey(address(BASE), address(STABLE2), TICK_SPACING2),
      tick: tick,
      gives: gives,
      gasreq: gasreq,
      gasprice: 0,
      fund: 0,
      noRevert: false
    })
  );

  return (offerId1, offerId2);
}
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/toy_strategies/offer_maker/Amplifier.sol#L60-L115)

In the implementation of `newAmplifiedOffers` notice the calls to the offer data getter `MGV.offers(address, address, uint)`: This returns a packed data structure `offer` whose fields `f` can be unpacked by doing `offer.f()` (see the documentation for the [offer data structure](https://docs.mangrove.exchange/dev/protocol/technical-references/the-order-book/offer-views)).

**Possible gas optimization**&#x20;

If both our amplified offers were once live on Mangrove, but are no longer (either after a retract or because one of them was consumed by a taker), it is more gas efficient to [update the offers](https://docs.mangrove.exchange/dev/protocol/technical-references/makers#updating-an-offer) to reinstate them on the [offer list](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-list), rather than creating new ones as we do in the above code.

#### Updating an under-collateralized offer on the fly[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#updating-an-under-collateralized-offer-on-the-fly) <a href="#updating-an-under-collateralized-offer-on-the-fly" id="updating-an-under-collateralized-offer-on-the-fly"></a>

With `newAmplifiedOffers` implemented, we can now post new offers. We hope that one of these offers will be taken at some point. When this happens, as per the specification we decided upon [above](#advanced-direct-offer-liquidity-amplification-with-amplifier), we wish to retract the other offer, which is now un(der)-collateralized, in order to save some [provision](https://docs.mangrove.exchange/dev/quick-links/glossary#provision). To do this we override the `posthookSuccess` [hook](https://docs.mangrove.exchange/dev/quick-links/glossary#hook).

The signature and first line of our custom [hook](https://docs.mangrove.exchange/dev/quick-links/glossary#hook) looks like this:

Amplifier - Reposting the residual

```
function __posthookSuccess__(MgvLib.SingleOrder calldata order, bytes32 makerData)
  internal
  override
  returns (bytes32)
{
  // reposts residual if any (conservative hook)
  bytes32 repost_status = super.__posthookSuccess__(order, makerData);
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/toy_strategies/offer_maker/Amplifier.sol#L120-L126)

Notice that we call `super`'s implementation of the hook. This ultimately ends up attempting to repost the offer residual (cf. the documentation of [Post trade hooks for MangroveOffer](https://docs.mangrove.exchange/dev/background/building-blocks/mangroveoffer#post-trade-hooks) and the reference for [Customizing `makerPosthook`](https://docs.mangrove.exchange/dev/technical-references/principal-hooks#customizing-makerposthook)). The return value captured in `repost_status` tells us whether the offer had a residual (in case of a [maker partial fill](https://docs.mangrove.exchange/dev/quick-links/glossary#maker-partial-fill)).

**Default reposting policy**

Direct offers that are partially filled are automatically reposted during posthook, adapting [wants](https://docs.mangrove.exchange/dev/quick-links/glossary#wants) to remaining [gives](https://docs.mangrove.exchange/dev/quick-links/glossary#gives) in order to maintain offer's original price. Direct's posthook returns the constants `REPOST_SUCCESS` in case the offer's residual was reposted, or `COMPLETE_FILL` if the offer was entirely consumed by taker (these constants are defined in `MangroveOffer`). If the offer fails to repost, the hook returns Mangrove's reason.

**Implementing case 1: An offer was reposted with a residual**[**​**](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#implementing-case-1-an-offer-was-reposted-with-a-residual)

We continue our implementation of the `__posthookSuccess__` hook by handling case 1:

Amplifier - Reposting case 1: An offer was reposted with a residual

```
(OLKey memory altOlKey, uint alt_offerId) = IERC20(order.olKey.inbound_tkn) == STABLE1
  ? (OLKey(order.olKey.outbound_tkn, address(STABLE2), TICK_SPACING2), offerId2)
  : (OLKey(order.olKey.outbound_tkn, address(STABLE1), TICK_SPACING1), offerId1);
if (repost_status == REPOST_SUCCESS) {
  (uint new_alt_gives,) = __residualValues__(order); // in base units
  Offer alt_offer = MGV.offers(altOlKey, alt_offerId);
  OfferDetail alt_detail = MGV.offerDetails(altOlKey, alt_offerId);

  uint old_alt_wants = alt_offer.wants();
  // old_alt_gives is also old_gives
  uint old_alt_gives = order.offer.gives();
  // we want new_alt_wants == (old_alt_wants:96 * new_alt_gives:96)/old_alt_gives:96
  // so no overflow to be expected :)
  uint new_alt_wants;
  unchecked {
    new_alt_wants = (old_alt_wants * new_alt_gives) / old_alt_gives;
  }

  // the call below might throw
  bytes32 reason = _updateOffer(
    OfferArgs({
      olKey: altOlKey,
      gives: new_alt_gives,
      tick: TickLib.tickFromVolumes(new_alt_wants, new_alt_gives),
      gasreq: alt_detail.gasreq(),
      gasprice: 0,
      fund: 0,
      noRevert: true
    }),
    alt_offerId
  );
  if (reason != REPOST_SUCCESS) {
    return "posthook/altOfferRepostFail";
  } else {
    return "posthook/bothOfferReposted";
  }
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/toy_strategies/offer_maker/Amplifier.sol#L132-L167)

Notice the use of the hook [`__residualGives__`](https://docs.mangrove.exchange/dev/technical-references/api-preferences/strats/src/strategies/mangroveoffer#residualvalues) in the code snippet above. For the offer currently being executed, it returns the [give](https://docs.mangrove.exchange/dev/quick-links/glossary#gives) at that offer when it is reposted. By default, this is calculated by subtracting what the taker took during [`makerExecute`](https://docs.mangrove.exchange/dev/quick-links/glossary#makerexecute) from what the offer originally gave.

Also notice that we go through a slightly more complex calculation to compute the updated wants for the *other* offer: We cannot use [`__residualWants__`](https://docs.mangrove.exchange/dev/technical-references/api-preferences/strats/src/strategies/mangroveoffer#residualvalues) to deduce the amount of tokens the *other* offer should [want](https://docs.mangrove.exchange/dev/quick-links/glossary#wants), because we cannot assume both `STABLE1` and `STABLE2` have the same decimals. (For this example, we only assume that they have the same value with respect to `BASE`.) We could zero-pad or truncate, but it is more elegant to compute the new [wants](https://docs.mangrove.exchange/dev/quick-links/glossary#wants) based on the new [gives](https://docs.mangrove.exchange/dev/quick-links/glossary#gives) - we set the constraint that we wish to preserve the TODO:%entailed price|offer-entailed-price%.

#### Retracting the uncollateralized offer on the fly[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#retracting-the-uncollateralized-offer-on-the-fly) <a href="#retracting-the-uncollateralized-offer-on-the-fly" id="retracting-the-uncollateralized-offer-on-the-fly"></a>

During the execution of the [offer logic](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-logic) it may occur that the taken offer does not repost itself on the [offer list](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-list). This may happen for the following reasons:

* the offer was completely filled
* the offer is [partially filled](https://docs.mangrove.exchange/dev/quick-links/glossary#maker-partial-fill) but its residual is below the offer list's [density](https://docs.mangrove.exchange/dev/quick-links/glossary#density)
* the offer no longer has enough [provision](https://docs.mangrove.exchange/dev/quick-links/glossary#provision). This last case may occur if one is reposting an offer that has failed (because a part of the [provision](https://docs.mangrove.exchange/dev/quick-links/glossary#provision) was turned into a [bounty](https://docs.mangrove.exchange/dev/quick-links/glossary#bounty)), or because Mangrove's [gasprice](https://docs.mangrove.exchange/dev/quick-links/glossary#gasprice) is now above the offer's gasprice. (This may happen, if Mangrove updated its [own gasprice](https://docs.mangrove.exchange/dev/protocol/technical-references/governance-parameters) after the offer was last posted.)

In all of these cases we wish to retract the other offer from the book.

**Implementing case 2: An offer was not reposted, and we need to retract the other offer**[**​**](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#implementing-case-2-an-offer-was-not-reposted-and-we-need-to-retract-the-other-offer)

We continue our hook by handling case 2 from our [breakdown above](#updating-an-under-collateralized-offer-on-the-fly).

Amplifier - Reposting case 2: Offer was not reposted

```
} else {
  // repost failed or offer was entirely taken
  retractOffer({olKey: altOlKey, offerId: alt_offerId, deprovision: false});
  return "posthook/bothRetracted";
}
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/toy_strategies/offer_maker/Amplifier.sol#L168-L172)

**Refunding offer automatically**

There is an alternative to retracting both offers in case the taken offer failed to repost itself for lack of provision: We might replenish the maker contract's balance on Mangrove. However, we advise against refunding provisions automatically within the [offer logic](https://docs.mangrove.exchange/dev/quick-links/glossary#offer-logic) itself:

Suppose that you instrumented your offer logic to do this. Now, if an attacker found a reproducible way of making your offer fail, they could loop that attack for as long as you repost a reprovision for your offer. This could ultimately draining your native token balance!

#### Managing offer failure[​](https://old.docs.mangrove.exchange/developers/strat-lib/guides/DirectHowTo#managing-offer-failure) <a href="#managing-offer-failure" id="managing-offer-failure"></a>

When writing posthooks, we need to consider all possible outcomes. The first outcome we have handled above assumed that the offer was successful. However, it might also be that the offer failed when it was taken. In this setup, this may happen, for instance, because we opted for using a router that brings liquidity from deployer's account. Nothing prevents this account from being empty when the market order actually arrives.

If this happens, this means that the offer that was unsuccessfully taken is no longer live on Mangrove and that some [bounty](https://docs.mangrove.exchange/dev/quick-links/glossary#bounty) has been sent to the taker. However, in this case, we **know** that the *other* offer will also fail if taken. For this reason, in case if a trade fails, rather than waiting for the other offer to fail by itself, we can save some [provision](https://docs.mangrove.exchange/dev/quick-links/glossary#provision) and override [`posthookFallback`](https://docs.mangrove.exchange/dev/technical-references/principal-hooks#posthook-after-trade-failure) to retract the other offer:

Amplifier - Managing offer failure

```
function __posthookFallback__(MgvLib.SingleOrder calldata order, MgvLib.OrderResult calldata)
  internal
  override
  returns (bytes32)
{
  // if we reach this code, trade has failed for lack of base token
  (IERC20 alt_stable, uint tickSpacing, uint alt_offerId) = IERC20(order.olKey.inbound_tkn) == STABLE1
    ? (STABLE2, TICK_SPACING2, offerId2)
    : (STABLE1, TICK_SPACING1, offerId1);
  retractOffer({
    olKey: OLKey(order.olKey.outbound_tkn, address(alt_stable), tickSpacing),
    offerId: alt_offerId,
    deprovision: false
  });
  return "posthook/bothFailing";
}
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/a265abeb96a053e386d346c7c9e431878382749c/src/toy_strategies/offer_maker/Amplifier.sol#L203-L218)

```solidity
```
