Takers
Posting a New Offer
Most offers are posted by maker contracts, which source liquidity on demand when Mangrove calls them.
Smart offers: offers posted via maker contracts.
On-the-fly offers: offers posted directly by EOAs (Externally Owned Accounts).
There are two ways to post offers and specify an offer's price:
newOfferByTick- price defined by a ticknewOfferByVolume- price defined by the ratiowants/gives.
*ByVolume is a convenience wrapper around *ByTick. Both functions return the same output and behave identically.
function newOfferByTick(
OLKey memory olKey,
Tick tick,
uint gives,
uint gasreq,
uint gasprice
) external payable returns (uint offerId);
function newOfferByVolume(
OLKey memory olKey,
uint wants,
uint gives,
uint gasreq,
uint gasprice
) external payable returns (uint offerId);Both functions are payable. A non-zero msg.value will credit the maker's Mangrove balance before locking the offer's provision.
Inputs
olKey: identifies the offer listoutbound_tkn: token sent by the offerinbound_tkn: token received by the offertickSpacing: spacing between valid ticks (see Ticks, ratios, and prices)
gives: amount ofoutbound_tknpromised by the offer.Must be > 0 and fit in 127 bits.
Must meet offer list density and
gasreqconstraints.
gasreq: gas made available to the offer execution logic.Must be ≤
gasmaxand fit in 24 bitsShould cover
makerExecuteandmakerPosthookcalls
gasprice: gas price override in Mwei/gas used to compute the order provision (see offer bounties).Must fit in 26 bits.
newOfferByTick(olKey, tick, gives, gasreq, gasprice)
tick: the desired "price" tick.The actual tick is rounder up to the nearest valid tick satisfying
offerTick % tickSpacing == 0.
newOfferByVolume(olKey, wants, gives, gasreq, gasprice)
wants: the amount of inbound tokens requested by the offer.Must fit in 127 bits and be strictly positive.
The ratio
wants/gives(rounded down) specifies the desired "price" ratio and thus tick.
Outputs
offerIdthe id of the newly created offer.Note that offer ids are scoped to offer lists, so many offers can share the same id if they belong to different offer lists.
Provisioning
Each offer must be provisioned in native tokens. If an offer fails, a portion of its provision compensates the taker for gas spent.
Ensure your maker contract holds enough provision before posting an offer. You can fund it by sending native tokens to Mangrove; the balance is stored and reused automatically.
Offer Execution
If the offer’s account is a contract, it must implement the IMaker interface:
function makerExecute(MgvLib.SingleOrder calldata order) external;Otherwise, Mangrove will revert on execution.
The offer account must also:
Maintain a sufficient ERC20 allowance for
outbound_tkn(used viatransferFrom).Satisfy density requirements (tokens per gas spent).
Example
import {IMangrove} from "@mgv/src/IMangrove.sol";
import "@mgv/src/core/MgvLib.sol";
// context of the call
// IMangrove mgv = IMangrove(payable(<address of Mangrove>));
// Mangrove contract
IMangrove mgv = IMangrove(payable(mgv));
// OLKey olKey = OLKey(<address of outbound token>, <address of inbound token>, <tick spacing>);
// struct containing outbound_tkn, inbound_tkn and tickSpacing
OLKey memory olKey = OLKey(address(base), address(quote), 1);
// Tick tick = TickLib.tickFromRatio(mantissa,exponent);
// ratios are represented as a (mantissa,exponent) pair which represents the number `mantissa * 2**-exponent`
// calculates the tick from a desired 1.25 ratio (1.25 = 20 * 2^(-4))
Tick tick = TickLib.tickFromRatio(20, 4);
// creates an offer at tick
mgv.newOfferByTick(olKey, tick, 1 ether, 10_000, 0);
// creates an offer at ⌈wants/tick⌉
mgv.newOfferByVolume(olKey, 1 ether, 1 ether, 10_000, 0);Events
OfferWrite
Emitted when a new offer is written to the order book.
DebitWei
Emitted when the maker’s Mangrove balance is debited to cover offer provision.
CreditWei
Emitted when the maker’s Mangrove balance is credited (e.g., from msg.value).
Event definitions
// logging new offer's data
event OfferWrite(
bytes32 indexed olKeyHash,
address indexed maker, // account that created the offer, will be called upon execution
int tick,
uint gives,
uint gasprice, // gasprice that was used to compute the offer bounty
uint gasreq,
uint id // id of the new offer
);
// `maker` balance on Mangrove (who is `msg.sender`) is debited of `amount` WEIs to provision the offer
event DebitWei(address maker, uint amount);
// `maker` balance on Mangrove is credited of `amount` WEIs if `msg.value > 0`.
event CreditWei(address maker, uint amount); Gatekeeping and Errors
mgv/dead
Mangrove contract is terminated
mgv/inactive
Target market is inactive
mgv/offerIdOverflow
Max number of offers (2²⁴) reached
mgv/writeOffer/gasprice/tooBig
Gas price too large
mgv/writeOffer/gives/tooBig
Offer volume too large
mgv/writeOffer/gasreq/tooHigh
Gasreq above gasmax
mgv/writeOffer/gives/tooLow
Gives must be > 0
mgv/writeOffer/density/tooLow
Density too low
mgv/writeOffer/tick/outOfRange
Tick invalid
mgv/insufficientProvision
Maker provision insufficient
Updating an Offer
Offers can be updated using the same two price-specification methods:
updateOfferByTickupdateOfferByVolume
updateOfferByVolume converts the volumes into a tick internally. Both versions are payable and can top up the maker’s balance via msg.value.
function updateOfferByTick(
OLKey memory olKey,
Tick tick,
uint gives,
uint gasreq,
uint gasprice,
uint offerId
) external payable;
function updateOfferByVolume(
OLKey memory olKey,
uint wants,
uint gives,
uint gasreq,
uint gasprice,
uint offerId
) external payable;Inputs
offerId — ID of the offer to update
All other parameters are identical to
newOfferByTickandnewOfferByVolume(see above)
Output
None
Offer Updater
Only the creator of the offer (msg.sender) can update it.
Unauthorized calls revert with mgv/updateOffer/unauthorized.
Example
import {IMangrove} from "@mgv/src/IMangrove.sol";
import "@mgv/src/core/MgvLib.sol";
// continuing from the previous example for the creation of new offers
// context of the call:
// mgv: address of Mangrove's deployment typed as IMangrove
// olKey struct containing outbound_tkn, inbound_tkn and tickSpacing
// creates an offer at `tick` and store its ID in ofrId_1
uint ofrId_1 = mgv.newOfferByTick(olKey, tick, 1 ether, 10_000, 0);
// getting packed (outTkn, inbTkn, tickSpacing) offer list data
Offer offer_1 = mgv.offers(olKey, ofrId_1);
OfferDetail detail_1 = mgv.offerDetails(olKey, ofrId_1);
// update the offer with the "ByTick" version
mgv.updateOfferByTick(
olKey,
tick,
offer_1.gives() * 9 / 10, // decrease what offer gives by 10%
detail_1.gasreq(), // keep offer's current gasreq
detail_1.gasprice(), // keep offer's current gasprice
ofrId_1 // id of the offer to be updated
);
// retrieves the amount of wants from the tick and gives
uint wants = TickLib.inboundFromOutbound(tick, offer_1.gives());
// update the offer with the "ByVolume" version
mgv.updateOfferByVolume(
olKey,
wants, // keep what the offer wants
offer_1.gives() * 12 / 10, // increase what offer gives by 20%
detail_1.gasreq(), // keep offer's current gasreq
detail_1.gasprice(), // keep offer's current gasprice
ofrId_1 // id of the offer to be updated
);Events
OfferWrite
Emitted when an offer is updated.
DebitWei
Maker debited if provision needs to increase.
CreditWei
Maker credited if provision decreased.
event OfferWrite(
bytes32 indexed olKeyHash,
address indexed maker, // account that created the offer, will be called upon execution
int tick,
uint gives,
uint gasprice, // gasprice that was used to compute the offer bounty
uint gasreq,
uint id // id of the new offer
);
// if old offer bounty is insufficient to cover the update,
// `maker` is debited of `amount` WEIs to complement the bounty
event DebitWei(address maker, uint amount);
// if old offer bounty is greater than the actual bounty,
// `maker` is credited of the corresponding `amount`.
event CreditWei(address maker, uint amount);Reusing Offers
Executed or retracted offers are removed from the offer list but can be updated and reinserted. Updating existing offers is significantly cheaper than creating new ones.
Gatekeeping and Errors
mgv/dead
Mangrove is terminated
mgv/inactive
Offer list is inactive
mgv/writeOffer/gives/tooLow
Gives ≤ 0
mgv/writeOffer/gasreq/tooHigh
Gasreq above limit
mgv/writeOffer/density/tooLow
Density constraint not met
mgv/updateOffer/unauthorized
Caller not offer owner
mgv/insufficientProvision
Provision insufficient
Retracting an offer
An offer can be withdrawn from the order book using retractOffer .
function retractOffer(
OLKey memory olKey,
uint offerId,
bool deprovision
) external returns (uint provision);Inputs
olKey — identifies the offer list
offerId — ID of the offer to retract
deprovision — if true, releases the offer’s provision (in wei)
Output
provision — amount of native tokens released (in wei)
Example
import {IMangrove} from "@mgv/src/IMangrove.sol";
// continuing from the previous example for the creation of new offers
// context of the call:
// mgv: address of Mangrove's deployment typed as IMangrove
// olKey struct containing outbound_tkn, inbound_tkn and tickSpacing
// ofrId_1: offer identifier of the offer created in the examples for new offer creation
// retracting offer with ID = ofrId_1
mgv.retractOffer(
olKey,
ofrId_1, // id of the offer to be retracted
false // no deprovision of the offer, saves gas if one wishes to repost the offer later
);If you choose to deprovision, you can later withdraw the funds using:
mgv.withdraw(amount);Events
OfferRetract
Emitted when an offer is removed from the book.
Credit
Emitted when provision is released back to the maker.
// emitted on all successful retractions
event OfferRetract(
bytes32 indexed olKeyHash,
address indexed maker,
uint id, // the id of the offer that has been removed from the offer list
bool deprovision
);
// emitted if offer is deprovisioned
event Credit(
address maker, // account being credited
uint amount // amount (in wei) being credited to the account
); Authorization
Only the maker contract that created the offer may call retractOffer.
Unauthorized calls revert with mgv/retractOffer/unauthorized.
Last updated