Switch branches/tags
Find file Copy path
018c211 Oct 26, 2018
1 contributor

Users who have contributed to this file

182 lines (159 sloc) 6.55 KB


CAP: 0006
Title: Add ManageBuyOffer Operation
Author: Jonathan Jove
Status: Draft
Created: 2018-10-24
Protocol version: TBD

Simple Summary

We introduce the ManageBuyOffer operation with functionality similar to the ManageOffer operation except that the amount is specified in terms of the buying asset instead of the selling asset.


The ManageOffer operation specifies the maximum amount of the selling asset that should be sold by the offer. It is not, however, possible to express the maximum amount of the buying asset that should be bought by the offer. These constraints are not equivalent because at the time of offer submission it is not known at what price the offer will execute. We propose to add a new operation called ManageBuyOffer which specifies the maximum amount of the buying asset that should be bought by the offer. The behavior is otherwise analogous to the extant ManageOffer operation.


Many financial institutions have an obligation to faithfully execute customer orders. Customer orders to sell a certain quantity of an asset in exchange for the maximum quantity of a different asset are already easily expressed in terms of the ManageOffer operation. In contrast, customer orders to buy a certain quantity of an asset in exchange for the minimum quantity of a different asset are not expressible in terms of the ManageOffer operation. We introduce the ManageBuyOffer operation to facilitate the execution of the latter kind of order.


ManageBuyOfferOp specification:

struct ManageBuyOfferOp
    Asset selling;
    Asset buying;
    int64 buyAmount; // amount being bought. if set to 0, delete the offer
    Price price;     // price of thing being sold in terms of what you are buying

    // 0=create a new offer, otherwise edit an existing offer
    uint64 offerID;

/******* ManageBuyOffer Result ********/

enum ManageBuyOfferResultCode
    // codes considered as "success" for the operation

    // codes considered as "failure" for the operation
    MANAGE_BUY_OFFER_MALFORMED = -1,     // generated offer would be invalid
    MANAGE_BUY_OFFER_SELL_NO_TRUST = -2, // no trust line for what we're selling
    MANAGE_BUY_OFFER_BUY_NO_TRUST = -3,  // no trust line for what we're buying
    MANAGE_BUY_OFFER_SELL_NOT_AUTHORIZED = -4, // not authorized to sell
    MANAGE_BUY_OFFER_BUY_NOT_AUTHORIZED = -5,  // not authorized to buy
    MANAGE_BUY_OFFER_LINE_FULL = -6,      // can't receive more of what it's buying
    MANAGE_BUY_OFFER_UNDERFUNDED = -7,    // doesn't hold what it's trying to sell
    MANAGE_BUY_OFFER_CROSS_SELF = -8,     // would cross an offer from the same user
    MANAGE_BUY_OFFER_SELL_NO_ISSUER = -9, // no issuer for what we're selling
    MANAGE_BUY_OFFER_BUY_NO_ISSUER = -10, // no issuer for what we're buying

    // update errors
    MANAGE_BUY_OFFER_NOT_FOUND = -11, // offerID does not match an existing offer

    MANAGE_BUY_OFFER_LOW_RESERVE = -12 // not enough funds to create a new Offer

union ManageBuyOfferResult switch (ManageBuyOfferResultCode code)
    ManageOfferSuccessResult success;

Updated Operation specification:

enum OperationType
    PAYMENT = 1,
    SET_OPTIONS = 5,
    ALLOW_TRUST = 7,
    INFLATION = 9,
    MANAGE_DATA = 10,

struct Operation
    // sourceAccount is the account used to run the operation
    // if not set, the runtime defaults to "sourceAccount" specified at
    // the transaction level
    AccountID* sourceAccount;

    union switch (OperationType type)
        CreateAccountOp createAccountOp;
    case PAYMENT:
        PaymentOp paymentOp;
    case PATH_PAYMENT:
        PathPaymentOp pathPaymentOp;
    case MANAGE_OFFER:
        ManageOfferOp manageOfferOp;
        CreatePassiveOfferOp createPassiveOfferOp;
    case SET_OPTIONS:
        SetOptionsOp setOptionsOp;
    case CHANGE_TRUST:
        ChangeTrustOp changeTrustOp;
    case ALLOW_TRUST:
        AllowTrustOp allowTrustOp;
        AccountID destination;
    case INFLATION:
    case MANAGE_DATA:
        ManageDataOp manageDataOp;
        BumpSequenceOp bumpSequenceOp;
        ManageBuyOfferOp manageBuyOfferOp;


Adding ManageBuyOffer will not require modifying what data is contained in the ledger. This can be understood by considering offer execution as two distinct processes. The first process begins when an offer is submitted. If this offer matches against an existing offer, then those offers must execute at the price of the existing offer. This repeats until either the submitted offer has executed entirely or the submitted offer does not match against any existing offer. During this first process, limits on the buying amount are not equivalent to limits on the selling amount since the execution price is variable.

The second process begins with adding to the offer book the remainder of the offer submitted at the start of the first process, if that offer was not already executed entirely. Any subsequent execution of this offer will occur at the price of this offer, unless the offer is modified (in which case the first process begins anew). Therefore, a limit on the buying amount is equivalent to a limit on the selling amount during the second process.

At this point, it is clear what the semantics of the ManageBuyOffer operation should be. During the first process, which is a subset of the apply-phase of the operation, the total amount that can be executed is limited by the buyAmount specified in the ManageBuyOfferOp. At the start of the second process, the remaining buyAmount is converted into a sell amount and stored in the ledger, analogous to what would be done with the remaining amount at the end of ManageOfferOp.

Backwards Compatibility

This proposal is fully backward compatible.

Test Cases

Some test cases that must be considered include:

  • If ManageBuyOfferOp and ManageOfferOp have the same max send and max receive after crossing offers, then the same offer is added to the ledger
  • ManageBuyOfferOp properly accounts for liabilities


No implementation yet.