For the complete documentation index, see llms.txt. This page is also available as Markdown.

Run a secondary trade

Post, accept and finalize a secondary offer on a cyberCORP's DealManager under an exemption pathway

A cyberCORP's DealManager settles secondary trades of its Ledger Entry Tokens (LETs) through an offer flow: post an offer, accept it, finalize the settlement. Each acceptance creates a settlement agreement in the CyberAgreementRegistry (a bytes32 id), signed by both sides, and the DealManager holds the escrow itself. To settle in scrip instead, see Scripify and settle a secondary trade.

Offers have two sides (OfferSide.SELL, OfferSide.BUY) and support partial fills. Every settlement runs under one securities-law exemption pathway (ExemptionPathway: RULE_144, SECTION_4A7, SECTION_4A1HALF, RULE_144A, REGULATION_S), either pinned by the offer or elected by the buyer at acceptance. The structs are in ISecondaryTradeStorage.sol.

0. Set the trading policy once

Before any offer can settle, an owner or admin sets the company's trading policy on the DealManager:

// enable a pathway and set its exemption-specific conditions
dealManager.setPathwayThresholdConditions(ExemptionPathway.RULE_144, conds, true);
dealManager.setSpvThresholdConditions(fundConds);    // apply to every offer
dealManager.setClosingConditions(closingConds);      // checked at finalize only
dealManager.setMinTradeThreshold(minUnits, minConsideration);
dealManager.setSettlementWindow(window);             // seconds from acceptance
dealManager.setDefaultIntegrator(integrator);        // optional fee-split partner

A pathway that is not enabled can be neither pinned nor elected.

The company does not set the platform fee on a settlement. It comes from the DealManagerFactory's secondary rate (getSecondaryFeeRatio(), which MetaLeX can override per DealManager), set separately from the primary rate used for deals and rounds. A whitelisted integrator's share comes out of that fee. DealManager has the fee details.

1. Post the offer

  • A SELL offer requires the caller to be the LET's registered owner and the LET not to be void (CertificateVoided). The offered units are reserved on the LET, and reserved units cannot be scripified or reassigned.

  • A BUY offer pulls the full consideration into DealManager custody up front and must pin an exemption pathway.

  • HostingMode.ADMINISTERED delivers the buyer's lot to adminMultisig, so it needs a nonzero address there (MissingAdminMultisig otherwise), on a buy offer at posting and on a sell offer at acceptance.

  • offerorAgreementSig is stored with the offer and attached to each settlement agreement through signContractWithEscrow. Neither the DealManager nor the registry verifies it. The offeror's commitment rests on its own postOffer transaction (or its relayer authorization), and the stored signature records what it signed.

2. Accept all or part of the offer

Any counterparty can accept the whole offer or part of it:

Acceptance creates the settlement agreement in the registry, signed by both sides, and funds the escrow (on a sell offer the buyer pays here). It also re-runs the SPV and elected-pathway conditions against the actual buyer. A failed condition reverts the whole acceptance, and so does a seller LET voided since the offer was posted.

The registry verifies acceptorAgreementSig, so the acceptor signs SignatureData (with signer = the acceptor) for the settlement agreement's id before the agreement exists. On a registry with the current SignatureData type, that id is

The settlement expiry is not part of the id. n counts every earlier acceptance, including voided ones, so a competing acceptance mined first changes the id and invalidates the signature. The id formulas are in CyberAgreementRegistry.

Each lot is priced from the offer's running total: offer.consideration × (offer.unitsAccepted + params.units) / offer.units − offer.paymentAccepted, where params.units is this fill's size and offer.units the offer's total (integer division rounds down; a negative result counts as zero). The lot that exhausts the offer therefore pays whatever remains, and rounding does not accumulate across fills. A priced lot that rounds to zero reverts ZeroConsiderationFill. The minimum-trade threshold applies to a partial lot and to the remainder it leaves. The lot that exhausts the offer is exempt, so raising the threshold never strands an existing tail.

3. Finalize the settlement

Anyone can finalize after acceptance, within the settlement window:

Finalization re-checks the pathway, threshold and closing conditions, because eligibility must hold at settlement as well as at acceptance. It then pays the seller net of the secondary fee (any integrator share comes out of that fee), releases the unit reservation, and executes the ownership change through IssuanceManager.secondaryTransfer, which reduces the seller's LET and mints the buyer's LET with the seller's endorsement attached.

Cancel an offer or void a settlement

  • cancelOffer(offerId) lets the offeror cancel a live offer. It releases only the uncommitted units and consideration, and settlements already in flight resolve on their own.

  • voidSecondaryTradeAgreement(agreementId, signer, signature) records one party's void request. The settlement voids once both parties request it, or once it has expired.

  • voidExpiredSecondaryTradeAgreement(agreementId, signer, signature) unwinds a settlement past its expiry.

  • syncVoidedSecondaryTradeAgreement(agreementId) syncs a settlement that was voided directly in the registry.

Let a relayer submit for the user

postOffer, cancelOffer and acceptOffer each have a relayed overload (…, address forAddr, uint256 nonce, bytes sig), where sig is forAddr's EIP-712 authorization over the call parameters and nonce, so end users can trade without paying gas. voidSecondaryTradeAgreement's relayed overload takes (agreementId, signer, voidSignature, nonce, authSig): the registry void signature plus a separate relayer-authorization signature, with signer as the authorized party.

Negotiate a bespoke deal instead

The DealManager's generic deal flow runs alongside the offer flow, for primary issuances and negotiated bilateral deals: proposeDeal(...) or proposeAndSignDeal(...) (both return (bytes32 agreementId, uint256[] certIds)), then signDealAndPay(signer, agreementId, signature, partyValues, fillUnallocated, name, secret), then finalizeDeal(agreementId). voidExpiredDeal, revokeDeal and signToVoid unwind a deal.

The DealManager holds payment and LETs in escrow for deals too, so neither flow has a separate escrow contract to call; LeXscroWLite documents the escrow logic.

Function-level detail is in DealManager and CyberAgreementRegistry. The conditions in the trading policy are covered in Gate state transitions with conditions.

Last updated

Was this helpful?