# Post a Smart Offer

In this tutorial, you will learn how to post a [smart offer](/dev/protocol/technical-references/makers/maker-contract.md) managed by your own [maker contract](/dev/quick-links/glossary.md#maker-contract) that simply stores [inbound](/dev/quick-links/glossary.md#inbound) and [outbound](/dev/quick-links/glossary.md#outbound) tokens on its balance.

### Prerequisites[​](https://old.docs.mangrove.exchange/developers/strat-lib/getting-started/smart-offer#prerequisites) <a href="#prerequisites" id="prerequisites"></a>

* The tutorial assumes knowledge of solidity development.
* Follow [preparation](/dev/strat-lib/getting-started/set-up-your-local-environment.md) to create a new `tutorial` folder.
* Open your favorite solidity editor inside that folder.
* It is assumed that the `ADMIN_ADDRESS` has enough native tokens to complete the steps. (If you're using the foundry setup in [Set Up Your Local Environment](/dev/strat-lib/getting-started/set-up-your-local-environment.md) then the `ADMIN_ADDRESS` is set to one of the Foundry test addresses with some native tokens.)

### Simple maker contract (offer logic)[​](https://old.docs.mangrove.exchange/developers/strat-lib/getting-started/smart-offer#simple-maker-contract-offer-logic) <a href="#simple-maker-contract-offer-logic" id="simple-maker-contract-offer-logic"></a>

We want to create a new contract `OfferMakerTutorial` to implement an [offer logic](/dev/protocol/technical-references/makers/maker-contract.md) and utilize the `Direct` contract in our strat-library for this purpose. `Direct` provides a safety harness to make it easier to correctly interact with Mangrove, you can read more about it [here](/dev/strat-lib/background/building-blocks/direct.md).

Start by creating a new `OfferMakerTutorial.sol` file in the `src` folder, and add the following pieces:

#### Imports[​](https://old.docs.mangrove.exchange/developers/strat-lib/getting-started/smart-offer#imports) <a href="#imports" id="imports"></a>

Add the imports we are going to need, along with a standard solidity preamble.

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

// Import the types we will be using below
import {Direct} from "@mgv-strats/src/strategies/offer_maker/abstract/Direct.sol";
import {ILiquidityProvider} from "@mgv-strats/src/strategies/interfaces/ILiquidityProvider.sol";
import {IMangrove} from "@mgv/src/IMangrove.sol";
import {MgvLib, OLKey} from "@mgv/src/core/MgvLib.sol";
import {Tick} from "@mgv/lib/core/TickLib.sol";
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/508bc8ace7f1d2ab54397611875306dc1ec31754/src/toy_strategies/offer_maker/tutorial/OfferMakerTutorial.sol#L1-L9)

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

Next, add the contract and the code for the constructor.

We will skip some details here, which you can read more about later; [routers](/dev/quick-links/glossary.md#router), [gas requirements](/dev/quick-links/glossary.md#gasreq), and [deployment scripts](/dev/strat-lib/guides/deploying-your-contract.md).<br>

> Note: we also implement the `ILiquidityProvider` interface which makes the contract compatible with what the [SDK](/dev/interacting-with-js/getting-started.md) expects.

OfferMakerTutorial.sol - Contract and constructor

```
/// @title An example offer maker used in tutorials
contract OfferMakerTutorial is Direct, ILiquidityProvider {
  ///@notice Constructor
  ///@param mgv The core Mangrove contract
  ///@param deployer The address of the deployer
  constructor(IMangrove mgv, address deployer)
    // Pass on the reference to the core mangrove contract
    Direct(
      mgv,
      // Do not use a router - i.e., transfer tokens directly to and from the maker's reserve
      noRouter()
    )
  {}
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/508bc8ace7f1d2ab54397611875306dc1ec31754/src/toy_strategies/offer_maker/tutorial/OfferMakerTutorial.sol#L13-L26)

#### Add offer management functions[​](https://old.docs.mangrove.exchange/developers/strat-lib/getting-started/smart-offer#add-offer-management-functions) <a href="#add-offer-management-functions" id="add-offer-management-functions"></a>

The abstract contract `Direct` has internal functions that allows one to manage offers: `_newOffer` for posting offers, `_updateOffer` for updating existing offers and `_retractOffer` for unpublishing offers from Mangrove. We need to expose these functions in a restricted manner, so that only the administrator of the contract can manage offers. We expose them through functions matching the [`ILiquidityProvider`](/dev/strat-lib/technical-references/api-preferences/strats/src/strategies/interfaces/iliquidityprovider.md) interface.

> See [OfferArgs](/dev/strat-lib/technical-references/api-preferences/strats/src/strategies/interfaces/iofferlogic.md) for an explanation of the parameters for posting an offer.

> Also see [provision](/dev/quick-links/glossary.md#provision), [gasreq](/dev/quick-links/glossary.md#gasreq), and [offer list](/dev/quick-links/glossary.md#offer-list).

Add the below code to your contract.

OfferMakerTutorial.sol - Offer management functions

```
///@inheritdoc ILiquidityProvider
function newOffer(OLKey memory olKey, Tick tick, uint gives, uint gasreq)
  public
  payable /* the function is payable to allow us to provision an offer*/
  onlyAdmin /* only the admin of this contract is allowed to post offers using this contract*/
  returns (uint offerId)
{
  (offerId,) = _newOffer(
    OfferArgs({
      olKey: olKey,
      tick: tick,
      gives: gives,
      gasreq: gasreq,
      gasprice: 0,
      fund: msg.value, // WEIs in that are used to provision the offer.
      noRevert: false // we want to revert on error
    })
  );
}

///@inheritdoc ILiquidityProvider
function updateOffer(OLKey memory olKey, Tick tick, uint gives, uint offerId, uint gasreq)
  public
  payable
  override
  adminOrCaller(address(MGV))
{
  _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)
{
  return _retractOffer(olKey, offerId, deprovision);
}
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/508bc8ace7f1d2ab54397611875306dc1ec31754/src/toy_strategies/offer_maker/tutorial/OfferMakerTutorial.sol#L29-L69)

#### Emit in Posthook[​](https://old.docs.mangrove.exchange/developers/strat-lib/getting-started/smart-offer#emit-in-posthook) <a href="#emit-in-posthook" id="emit-in-posthook"></a>

When using our new contract, we can inspect traces and addresses but illustrative purposes, let's insert the following to emit an event in the [posthook](/dev/quick-links/glossary.md#makerposthook) when the offer is successfully taken.

OfferMakerTutorial.sol - Emit in Posthook

```

  ///@notice Event emitted when the offer is taken successfully.
  ///@param someData is a dummy parameter.
  event OfferTakenSuccessfully(uint someData);

  ///@notice Post-hook that is invoked when the offer is taken successfully.
  ///@inheritdoc Direct
  function __posthookSuccess__(MgvLib.SingleOrder calldata, bytes32) internal virtual override returns (bytes32) {
    emit OfferTakenSuccessfully(42);
    return 0;
  }
}
```

[See entire file on GitHub](https://github.com/mangrovedao/mangrove-strats/blob/508bc8ace7f1d2ab54397611875306dc1ec31754/src/toy_strategies/offer_maker/tutorial/OfferMakerTutorial.sol#L72-L83)

There are more hooks to enable the Mangrovian abilities of [last look](/dev/quick-links/glossary.md#last-look) and more advanced [reactive liquidity](/dev/quick-links/glossary.md#reactive-liquidity).

## Local test

The contract is now complete - you can see the full example by following the links to github.

But before deploying it on-chain, we should test it!

### Compilation

First, compile the contract:

```bash
forge build
```

### Start local node

Before proceeding, import the environment variables made as part of the preparation

```bash
source .env
```

Start Foundry's local node `anvil` to test things locally before broadcasting to the real chain, with `$RPC_URL` coming from `.env` and pointing, for instance, to the Polygon network.

```bash
anvil --fork-url $RPC_URL
```

### Create contract on chain

Start another terminal and import environment variables again

```bash
source .env
```

Now, create the `OfferMakerTutorial` contract on the `anvil` node with your private key by pointing to its local `rpc-url`, and supplying the parameters for Mangrove core contract (get it from Addresses for the network you have forked).\
You can also add it to your `.env` file.

```bash
export MANGROVE=<contract address> # 0xabcd.... 
```

```bash
forge create "OfferMakerTutorial" --private-key "$PRIVATE_KEY" --rpc-url $LOCAL_URL --constructor-args "$MANGROVE" "$ADMIN_ADDRESS"
```

> Note: you might need to add the `--legacy` argument.

\
The output should look like:

```bash
[⠒] Compiling...
No files changed, compilation skipped
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployed to: <contract address>
Transaction hash: 0xcfbe7503081ba9a749ee30fac8c40bfe19e0a5da665578ed00f40ce72694ca06
```

Take a note of the `Deployed to` address and save it in a variable:

```bash
export OFFER_MAKER=<contract address> # 0xabcd..., the address of the newly deployed contract
```

### Activate the contract

In this tutorial, we will use the WBTC/DAI market.\
Make sure to set variables with the tokens address into your `.env` file.

> Note: the example token addresses are for the Polygon Mumbai testnet.

```bash
export WBTC=0x2Fa2e7a6dEB7bb51B625336DBe1dA23511914a8A
export DAI=0xc8c0Cf9436F4862a8F60Ce680Ca5a9f0f99b5ded
```

We have to let Mangrove pull the outbound token from our new contract - we can use the `activate` function for that.

```bash
cast send --rpc-url $LOCAL_URL "$OFFER_MAKER" "activate(address)" "$WBTC" --private-key "$PRIVATE_KEY"
```

### Post an offer

Now that the contract is ready, we can use it to post an offer - note that we have to [provision](https://old.docs.mangrove.exchange/developers/terms/provision) the offer, and we do that by sending some native tokens to `newOffer`.\
In our example, we are offering 1 WBTC (gives) at tick 50 (tick 50 means the price ratio is `1.0001^50`).

**Note :**\
Later, if you'd like to take your own offer with a market order for testing purpose, it would be handy to have your offer at the very top of the book (i.e. with the best price possible). To do this, you could post your offer with the smallest tick (`-887272`), or use the [`MIN_TICK`](https://github.com/mangrovedao/mangrove-core/blob/2ae172805fd8b309c30b2dc877dba66245abbb3e/lib/core/Constants.sol#L52) constant in your test contract.

```bash
cast send --rpc-url $LOCAL_URL "$OFFER_MAKER" "newOffer((address, address, uint), int, uint, uint)(uint)" "($WBTC,$DAI,1)" 50 100000000 0  --private-key "$PRIVATE_KEY" --value 0.01ether
```

Instead of trying to parse the logs, we can make a note of the `transactionHash` at the end of the output and use local execution to see the `offerId` returned by `newOffer`.

```bash
cast run <transactionHash>
```

Which would output the following tail:

```bash
...
    └─ ← 0x0000000000000000000000000000000000000000000000000000000000000002     


Transaction successfully executed.
Gas used: 200843
```

`0x0000000000000000000000000000000000000000000000000000000000000002` is the offer id.

### Locking liquidity

If the offer was now taken, it will fail to deliver the promised liquidity. It promises up to 1 WBTC, but the contract has no WBTC to deliver. We can fix this by sending some WBTC to the contract:

```bash
cast send --rpc-url $LOCAL_URL "$WBTC" "transfer(address,uint)" "$OFFER_MAKER" 100000000 --private-key "$PRIVATE_KEY"
```

If you do not have the liquidity in your wallet, check [Getting tokens](#getting-tokens) below, and come back to this step afterwards.

**Note :**\
One of the big benefits of Mangrove is that **liquidity does not have to be locked in** - we will have a look at that in the Unlocking Liquidity guide.

#### Getting tokens

If the admin (acting as a maker) does not have required WBTC tokens then the smart offer will fail when taken.

> Note: this true in this particular case where we need to lock liquidity in our contract - that's how we designed it. Using a %%router|router%%, you can unlock your funds, and your offer **could still be posted** - your smart offer can source liquidity elsewhere on-chain.

If you don't have any WBTC, you can get some by using the following commands (taken from [foundry documentation](https://book.getfoundry.sh/tutorials/forking-mainnet-with-cast-anvil)), or the corresponding faucet. Just look for a token holder with large amounts of WBTC - you can check the list on Polygonscan. Also, remember to add the chosen address under `$LUCKY_USER` in your `.env` file.

```bash
# Display the amount of WBTC in the admin wallet 
cast call $WBTC "balanceOf(address)(uint256)" $ADMIN_ADDRESS

# Impersonate the LUCKY_USER before making a transfer of 1 WBTC to our admin wallet
cast rpc anvil_impersonateAccount $LUCKY_USER
cast send $WBTC --unlocked --from $LUCKY_USER "transfer(address,uint256)(bool)" $ADMIN_ADDRESS 100000000

# Verify that the transfer was successful
cast call $WBTC "balanceOf(address)(uint256)" $ADMIN_ADDRESS
```

### Update an offer

In a similar fashion, we can make use of the `updateOffer` function inside our contract. We will need the offer ID from earlier - let's add it in a variable:

```bash
export OFFER_ID_HEX=<0xabcd...> # the hexadecimal offer ID captured when posting the offer
export OFFER_ID=$(($OFFER_ID_HEX)) # the decimal ID of the offer captured above
```

Now, we can update our offer, for example by changing the amount of WBTC we give to 0.1:

```bash
cast send --rpc-url $LOCAL_URL "$OFFER_MAKER" "updateOffer((address, address, uint), int, uint, uint, uint)(uint)" "($WBTC,$DAI,1)" 50 10000000 "$OFFER_ID" 0 --private-key "$PRIVATE_KEY" --value 0.01ether
```

**Note :**\
To update an offer, here's something to keep in mind:

1. To change the volume offered => change `gives` (example above)
2. To change the price of the offer => change `tick`
3. To change both volume and price => change `gives` and `tick`

### Retract an offer

We can also remove our offer from the book, using `retractOffer`. Note that we don't need to provide a provision in this case, since we are pulling the offer off the market. We will actually get back our provision with that configuration.

```bash
cast send --rpc-url $LOCAL_URL "$OFFER_MAKER" "retractOffer((address, address, uint), uint, bool)(uint)" "($WBTC,$DAI,1)" "$OFFER_ID" true --private-key "$PRIVATE_KEY"
```

### Next steps

* You could publish the contract on mainnet by stopping Anvil and replacing the `--rpc-url $LOCAL_URL` in the above `create`, `activate`, and `approve` commands with `--rpc-url $RPC_URL` - and finally, the `newOffer` with sensible prices.
* To get a view of the order book, the Mangrove UI can be used, or you can use the SDK.
* To get a better understanding of how tokens flow between taker, maker, Mangrove, and maker contracts like `OfferMakerTutorial`, see Mangrove Offer.
* You can also add more features (such as reneging trades or unlocking/reactive liquidity) to your smart offer by looking at the next sections of this doc!
* At some point, you will need to measure the gas requirements of your smart offers - this page will give you pointers on how to do this.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mangrove.exchange/dev/strat-lib/getting-started/post-a-smart-offer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
