Offer lifecycle
The regulator-mandated targeted offer and counter-offer negotiation flow in AsseteraECS.
An offer is a bilateral, targeted negotiation: a maker proposes specific terms to a specific taker, who may accept, counter, or walk away. Unlike an order (open to any taker), an offer is aimed at one counterparty and supports back-and-forth counter-proposals before either side commits funds.
This targeted offer / counter-offer flow is a regulatory requirement, not an optional convenience. As a MiFID venue, Assetera must support directed, negotiable quotes between a named maker and a named taker. It is a first-class part of AsseteraECS, not sugar over the order book.
State machine
The on-chain OfferStatus enum is: None, Open, Countered, Accepted, Settled, Cancelled,
ForceCancelled, Expired. Accepted retains its numeric position for storage and ABI compatibility but
is never persisted: acceptance settles atomically and writes Settled. Each offer tracks proposedBy, the
party whose terms are currently on the table and whose tokens are escrowed for the current round.
Making an offer
makeOffer(taker, makerToken, makerAmount, takerToken, takerAmount, expireTs, att) opens an offer
targeted at taker, requiring a KYC attestation for the MakeOffer action. It snapshots the fee
terms onto the offer (fixed at makeOffer, never renegotiated) and emits OfferMade. The maker
cannot target themselves. It returns the new offer id.
Countering
replaceOffer(offerId, newMakerAmount, newTakerAmount, expireTs, att) is the negotiation step:
either party (maker or taker) can put fresh terms on the table while the offer is Open or
Countered, requiring a KYC attestation for the ReplaceOffer action. It flips proposedBy to
the caller (the new proposer's side becomes the escrowed side) and emits OfferReplaced. Fee terms
do not change across counters; they stay fixed from makeOffer. Counters can go back and forth
any number of times.
Accepting
acceptOffer(offerId, att) is called by the non-proposing party (you can't accept your own
proposal), requiring a KYC attestation for the AcceptOffer action. It brings in the accepting side,
settles both legs atomically, writes Settled, and emits OfferAccepted followed by OfferSettled in the
same transaction.
Settling
There is no public settleOffer step in the current ABI. Settlement happens inside acceptOffer, so there
is no accepted-but-unsettled period for an operator or client to advance. Both fees are denominated in the
configured settlement currency, and the asset leg moves gross. Use the deployment ABI for the exact
OfferSettled event fields.
Cancelling and expiry
Either party can cancel while the offer is Open or Countered via cancelOffer(offerId, att)
(KYC-gated on the CancelOffer action), which returns the currently-escrowed proposer's side and
emits OfferCancelled. Offers past their expireTs are swept by the permissionless, batched
sweepExpiredOffers(ids[]), which returns escrow to the current proposedBy and emits one
OfferExpired per swept id.
The admin escape hatch cancelOfferForUser(offerId, makerRecipient, takerRecipient) works while the offer
is Open or Countered, returns the current proposer's escrow to the applicable recipient, and emits
OfferForceCancelled.
AcceptOffer, ReplaceOffer, and CancelOffer attestations are action- and offer-bound. A public
SettleOffer function is not present; that enum value is retained only for ordinal compatibility.
For the full event field lists, see Events. For how attestations are produced, see Attestations.