Offer lifecycle
The regulator-mandated targeted offer and counter-offer negotiation flow in AsseteraExchange.
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 the exchange, not sugar over the order book.
State machine
The on-chain OfferStatus enum is: None, Open, Countered, Accepted, Settled, Cancelled,
ForceCancelled, Expired. 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 escrows the accepting side's
tokens (so at this point both sides are escrowed), moves the offer to Accepted, and emits
OfferAccepted with the agreed terms.
Settling
settleOffer(offerId, makerAtt, takerAtt) is an operator action (OPERATOR_ROLE) that finalizes an
accepted offer, swapping the escrowed tokens between the two parties and moving the offer to
Settled. It requires a fresh KYC attestation from both parties for the SettleOffer action. It
emits OfferSettled; note the received amounts in that event are already net of fee.
Roadmap: as with order settlement, on-chain operator settlement is being parked for the
production baseline. settleOffer is present on the current testnet contract but is not part of
the intended production standard flow.
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 in any
non-terminal state (including Accepted, where both legs are escrowed) and emits
OfferForceCancelled.
The CancelOffer and SettleOffer actions are deliberately distinct from the order-side Cancel
and Settle actions: an attestation signed for one can never be replayed against the other.
For the full event field lists, see Events. For how attestations are produced, see Attestations.