Features
Cancel Offer
The lifecycle of an offer finishes with its cancellation. In this section, we will cover the process of canceling an offer.
Monochain Cancelling Process
In a monochain scenario, offer cancellation is a straightforward procedure. Essentially, only two actions are required:
- Unlocking source token: The source tokens, that seller have freezed while creating the offer, must be refunded.
- Delete the offer: The offer must be deleted from the offers mapping.
Cancellation authority
Only the seller who created the offer has the authority to cancel it.
In order to cancel the offer, seller has to invoke method cancelOffer
.
Cancel offer interface
function cancelOffer(
bytes32 _offerId,
MessagingFee calldata _fee,
bytes calldata _extraSendOptions
) external payable returns (MessagingReceipt memory msgReceipt);
About the parameters of this method:
- offerId: The ID of the offer to be canceled
- fee: The Layer Zero fee, that would be zero in the monochain offer case
- extraSendOptions: Options required for Layer Zero cross-chain transaction. In monochain case would be empty.
Upon calling the cancelOffer
method, the OTC market will:
- Validate data: Verify whether the offer exists, if the cancellation is being initiated from the source chain, and if the signer has the authority to cancel the offer.
- Unlock the assets: The source token from Escrow will be transferred to seller account
- Delete the offer: Offer will be deleted from the offers mapping.
Cross-chain Cancelling Process
Cross-Chain Offer Flow: Cancel offer
In the current version of the Bakstag Protocol, the cancellation process starts in the source chain OTC market. To ensure that the offer has been successfully canceled on the destination chain, the cancellation process is designed in type of ABA cross-chain transaction. Here is the reason why ABA type transaction is required for the cancellation.
Cross-chain offer cancellation is split into 2 cross-chain messages:
- Cancel Offer Order: Message from the offer source chain to destination chain, ordering destination chain OTC to delete the offer.
- Offer Canceled: Message from the destination chain OTC to source chain informing that the offer is successfully deleted in destination chain OTC and now can be deleted on the source chain OTC.
Since the cancellation of a cross-chain offer involves two transactions, the quote
method must be called twice — once on the destination chain and once on the source chain.
1. Quote on the destination chain
Firstly, quoteCancelOffer
should be invoked on the destination chain OTC, in order to quote sending the Offer Canceled message.
Quote cancel offer interface
function quoteCancelOffer(bytes32 _offerId) external returns (MessagingFee memory fee);
The quote is a view (read) function, so calling it incurs no cost.
The only required parameter is offerId — ID of the offer to be canceled.
The quoteCancelOffer
returns the returnFee
— Layer Zero fee, required to send the Offer Canceled message. returnFee
will be useful in the following step.
2. Building Extra Send Options
ExtraSendOptions
are required in order to invoke the quoteCancelOfferOrder
method on the source chain OTC market.
Building extra send options
import { OptionsBuilder } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/libs/OptionsBuilder.sol";
extraSendOptions = OptionsBuilder.newOptions().addExecutorLzReceiveOption(0, uint128(returnFee.nativeFee));
To build extraSendOptions
the returnFee
form the previous step is required.
2. Quote on the source chain
After computing the extraSendOptions
, the quoteCancelOfferOrder
can be invoked.
Quote cancel offer order interface
function quoteCancelOfferOrder(
bytes32 _srcSellerAddress,
bytes32 _offerId,
bytes calldata _extraSendOptions,
bool _payInLzToken
) external returns (MessagingFee memory fee);
The required parameters are:
- srcSellerAddress: address of the signer of future
cancelOffer
transaction. - offerId: ID of the offer to be canceled
- extraSendOptions: The built earlier
extraSendOptions
. - payInLzToken: Decide whether to cover LayerZero fee in native or ZRO token.
The quoteCancelOfferOrder
is a view (read) function, so calling it incurs no cost.
quoteCancelOfferOrder
returns the fee
required for the main cancelOffer
method.
3. Cancel
On source chain OTC market the method cancelOffer
should be invoked. The interface of this method can be seen here.
The fee
from the source chain quote is required in this context, as well as extraSendOptions
parameter built earlier.
While canceling the cross-chain offer, the OTC market will:
- Validate data: The OTC market will verify whether the offer exists, if the cancellation is being initiated from the source chain, and if the signer has the authority to cancel the offer.
- Send Cancel Offer Order message Send the
CancelOfferOrder
cross-chain message to the destination chain OTC market.
Once the Cancel Offer Order
message is delivered to the destination chain OTC market, it will:
- Cancel the offer: Delete offer from the offers mapping.
- Emit Event: Log
OfferCanceled
event notifying offchain workers. - Send Offer Canceled message: Send the
OfferCanceled
cross-chain message to the source chain OTC market.
When the Offer Canceled
message is delivered, the OTC market will:
- Cancel the offer: Delete offer from the offers mapping.
- Emit Event: Log
OfferCanceled
event notifying offchain workers.
4. Problematic
Offer is Accepted and Canaled simultaneously
As previously mentioned, the Bakstag Protocol currently maintains a duplicate storage of offers. Therefore, to cancel an offer, it must be deleted in both the source and destination chains OTC markets. Likewise the cancelOffer
request should come from source chain OTC market
The simplest way to cancel the offer is:
- Cancel on source chain
- Cancel on destination chain
But this implementation has a problem:
Let’s consider a scenario where an offer source chain is A
and destination chain is B
. The offer is being canceled on chain A
while simultaneously being accepted on chain B
. Here’s what would happen:
A
OTC market: Offer is canceledB
OTC market: Offer is accepted
A
OTC market: Offer is deleted from the mapping and the assets are refunded to the sellerB
OTC market: Offer is updated, buyer's destination token transferred to destination seller address and treasury
A
OTC market:OfferCanceled
cross-chain message send to destination chain OTC market.B
OTC market:OfferAccepted
cross-chain message send to source chain OTC market.
A
OTC market:OfferAccepted
message received, but offer no longer exist on this OTC marked, so source token can not be transferred to source buyer addressB
OTC market: Offer Canceled message received, the offer deleted from mapping.
The example above illustrates a simple cross-chain transaction is insufficient for canceling an offer.
Now, let’s consider the same scenario, but using the Cancel Offer implementation currently employed in the Bakstag Protocol:
A
OTC market: Offer is canceledB
OTC market: Offer is accepted
A
OTC market:CancelOfferOrder
cross-chain message is sent to destination chain OTC market.B
OTC market: Offer is updated, destination token transferred from the buyer account to destination seller address and treasury.OfferAccepted
cross-chain message send to source chain OTC market.
A
OTC market:OfferAccepted
message received, source token transferred to source buyer addressB
OTC market:CancelOfferOrder
message received, offer deleted from the mapping,OfferCanceled
cross-chain message sent back to source chain OTC market.
A
OTC market:OfferCanceled
message received the offer deleted from the mapping, source token transferred to source seller address
Not enough gas for sending OfferCanceled
message
The ABA-type transaction introduces another potential issue. The seller specifies the amount of gas available for executing the transaction on the destination chain by providing the extraSendOptions
parameter. What happens if the seller specifies an insufficient amount of gas to process the CancelOfferOrder
message on the destination OTC market?
The answer is straightforward — nothing critically wrong would occur. If there isn't enough gas to process the CancelOfferOrder
and send the OfferCanceled
message back to the source chain OTC market, the transaction would revert on destination chian. As a result, the offer would not be deleted from either the source chain or the destination chain OTC markets.