diff --git a/src/ripple/app/tx/impl/InvariantCheck.cpp b/src/ripple/app/tx/impl/InvariantCheck.cpp index b05f7a1d4..01fe2cc75 100644 --- a/src/ripple/app/tx/impl/InvariantCheck.cpp +++ b/src/ripple/app/tx/impl/InvariantCheck.cpp @@ -110,8 +110,8 @@ XRPNotCreated::visitEntry( drops_ -= (*before)[sfAmount].xrp().drops(); break; case ltOPTION_OFFER: - if (isXRP((*before)[sfAmount])) - drops_ -= (*before)[sfAmount].xrp().drops(); + if (isXRP((*before)[sfLockedBalance])) + drops_ -= (*before)[sfLockedBalance].xrp().drops(); break; default: break; @@ -136,8 +136,8 @@ XRPNotCreated::visitEntry( drops_ += (*after)[sfAmount].xrp().drops(); break; case ltOPTION_OFFER: - if (!isDelete && isXRP((*after)[sfAmount])) - drops_ += (*after)[sfAmount].xrp().drops(); + if (!isDelete && isXRP((*after)[sfLockedBalance])) + drops_ += (*after)[sfLockedBalance].xrp().drops(); break; default: break; diff --git a/src/ripple/app/tx/impl/OptionCreate.cpp b/src/ripple/app/tx/impl/OptionCreate.cpp index ba8ec7059..47f759829 100644 --- a/src/ripple/app/tx/impl/OptionCreate.cpp +++ b/src/ripple/app/tx/impl/OptionCreate.cpp @@ -49,8 +49,8 @@ OptionCreate::preflight(PreflightContext const& ctx) } auto const flags = ctx.tx.getFlags(); - std::optional const swapID = ctx.tx[~sfInvoiceID]; - std::uint32_t const quantity = ctx.tx.getFieldU32(sfQualityIn); + std::optional const swapID = ctx.tx[~sfSwapID]; + std::uint32_t const quantity = ctx.tx.getFieldU32(sfQuantity); bool const isClose = (flags & tfPosition) != 0; @@ -124,7 +124,7 @@ sealOption( bool const _isSell = (flags & tfAction) != 0; // Skip Sealed Options - if (sleItem->getFieldU32(sfQualityOut) == 0) + if (sleItem->getFieldU32(sfToSeal) == 0) { continue; } @@ -132,7 +132,7 @@ sealOption( // looking for oposite option position. if (_isPut == isPut && _isSell != isSell) { - uint256 const sealID = sleItem->getFieldH256(sfInvoiceID); + uint256 const sealID = sleItem->getFieldH256(sfSwapID); if (sealID.isNonZero()) { continue; @@ -159,11 +159,11 @@ OptionCreate::doApply() beast::Journal const& j = ctx_.journal; AccountID const srcAccID = ctx_.tx.getAccountID(sfAccount); - uint256 const optionID = ctx_.tx.getFieldH256(sfOfferID); + uint256 const optionID = ctx_.tx.getFieldH256(sfOptionID); auto const flags = ctx_.tx.getFlags(); STAmount const premium = ctx_.tx.getFieldAmount(sfAmount); - std::uint32_t const quantity = ctx_.tx.getFieldU32(sfQualityIn); - std::optional const swapID = ctx_.tx[~sfInvoiceID]; + std::uint32_t const quantity = ctx_.tx.getFieldU32(sfQuantity); + std::optional const swapID = ctx_.tx[~sfSwapID]; STAmount const totalPremium = STAmount(premium.issue(), (premium.mantissa() * quantity)); @@ -176,7 +176,7 @@ OptionCreate::doApply() if (!sleOptionAcc) return tecINTERNAL; - STAmount const strikePrice = sleOptionAcc->getFieldAmount(sfAmount); + STAmount const strikePrice = sleOptionAcc->getFieldAmount(sfStrikePrice); std::uint32_t const expiration = sleOptionAcc->getFieldU32(sfExpiration); STAmount const quantityShares = STAmount(strikePrice.issue(), isXRP(premium) ? quantity * 1000000 : quantity); @@ -184,6 +184,11 @@ OptionCreate::doApply() bool const isSell = (flags & tfAction) != 0; bool const isClose = (flags & tfPosition) != 0; + // std::cout << "OptionCreate.getIssuer(): " << strikePrice.getIssuer() << "\n"; + // std::cout << "OptionCreate.getCurrency(): " << strikePrice.getCurrency() << "\n"; + // std::cout << "OptionCreate.mantissa(): " << strikePrice.mantissa() << "\n"; + // std::cout << "OptionCreate.expiration: " << expiration << "\n"; + auto optionBookDirKeylet = keylet::optionBook( strikePrice.getIssuer(), strikePrice.getCurrency(), @@ -202,9 +207,9 @@ OptionCreate::doApply() JLOG(j.warn()) << "Updating Sealed Offer: sealID"; auto sealedKeylet = ripple::keylet::unchecked(*sealID); auto sealedOption = sb.peek(sealedKeylet); - sealedOption->setFieldH256(sfInvoiceID, optionOfferKeylet.key); - uint32_t currSealed = sealedOption->getFieldU32(sfQualityOut); - sealedOption->setFieldU32(sfQualityOut, currSealed - quantity); + sealedOption->setFieldH256(sfSwapID, optionOfferKeylet.key); + uint32_t currSealed = sealedOption->getFieldU32(sfToSeal); + sealedOption->setFieldU32(sfToSeal, currSealed - quantity); oppAccID = sealedOption->getAccountID(sfOwner); sb.update(sealedOption); } @@ -248,23 +253,22 @@ OptionCreate::doApply() optionOffer->setFlag(flags); optionOffer->setAccountID(sfOwner, srcAccID); optionOffer->setFieldU64(sfOwnerNode, *page); - optionOffer->setFieldH256(sfOfferID, optionID); - optionOffer->setFieldU32(sfQualityIn, quantity); - optionOffer->setFieldU32(sfQualityOut, quantity); - optionOffer->setFieldAmount(sfTakerPays, STAmount(0)); // Premium - optionOffer->setFieldAmount(sfAmount, STAmount(0)); // Locked + optionOffer->setFieldH256(sfOptionID, optionID); + optionOffer->setFieldU32(sfQuantity, quantity); + optionOffer->setFieldU32(sfToSeal, quantity); + optionOffer->setFieldAmount(sfAmount, premium); // Premium + optionOffer->setFieldAmount(sfLockedBalance, STAmount(0)); // Locked if (sealID) { JLOG(j.warn()) << "Updating Option Offer: sealID"; - optionOffer->setFieldH256(sfInvoiceID, *sealID); - optionOffer->setFieldU32(sfQualityOut, 0); + optionOffer->setFieldH256(sfSwapID, *sealID); + optionOffer->setFieldU32(sfToSeal, 0); } if (isSell) { JLOG(j.warn()) << "Updating Option Offer: isSell"; - // Update the locked balance and premium - optionOffer->setFieldAmount(sfTakerPays, premium); // Premium - optionOffer->setFieldAmount(sfAmount, quantityShares); // Locked + // Update the locked balance + optionOffer->setFieldAmount(sfLockedBalance, quantityShares); // Locked } Option const option{ @@ -305,7 +309,7 @@ OptionCreate::doApply() { JLOG(j.warn()) << "Updating Option Offer: isClose"; auto optionOffer = sb.peek(optionOfferKeylet); - uint256 const sealID = optionOffer->getFieldH256(sfInvoiceID); + uint256 const sealID = optionOffer->getFieldH256(sfSwapID); if (!sealID) { JLOG(j.warn()) @@ -321,15 +325,15 @@ OptionCreate::doApply() JLOG(j.warn()) << "OptionCreate: Swap Option does not exist."; return tecINTERNAL; } - swapOption->setFieldH256(sfInvoiceID, optionOfferKeylet.key); + swapOption->setFieldH256(sfSwapID, optionOfferKeylet.key); sb.update(swapOption); // Update New Option - optionOffer->setFieldH256(sfInvoiceID, *swapID); + optionOffer->setFieldH256(sfSwapID, *swapID); sb.update(optionOffer); // Erase Swap Sealed Option - uint256 const swapSealedID = swapOption->getFieldH256(sfInvoiceID); + uint256 const swapSealedID = swapOption->getFieldH256(sfSwapID); if (!swapSealedID) { JLOG(j.warn()) << "OptionCreate: Swap Option is not sealed."; diff --git a/src/ripple/app/tx/impl/OptionExecute.cpp b/src/ripple/app/tx/impl/OptionExecute.cpp index 53b1c6a99..6ac438de0 100644 --- a/src/ripple/app/tx/impl/OptionExecute.cpp +++ b/src/ripple/app/tx/impl/OptionExecute.cpp @@ -60,8 +60,8 @@ OptionExecute::doApply() beast::Journal const& j = ctx_.journal; AccountID const srcAccID = ctx_.tx.getAccountID(sfAccount); - uint256 const optionID = ctx_.tx.getFieldH256(sfOfferID); - uint256 const offerID = ctx_.tx.getFieldH256(sfInvoiceID); + uint256 const optionID = ctx_.tx.getFieldH256(sfOptionID); + uint256 const offerID = ctx_.tx.getFieldH256(sfSwapID); auto const flags = ctx_.tx.getFlags(); auto sleSrcAcc = sb.peek(keylet::account(srcAccID)); @@ -74,7 +74,7 @@ OptionExecute::doApply() return tecNO_TARGET; AccountID const ownrAccID = sleOptionOffer->getAccountID(sfOwner); - auto const sealedID = sleOptionOffer->getFieldH256(sfInvoiceID); + auto const sealedID = sleOptionOffer->getFieldH256(sfSwapID); auto oppOfferKeylet = ripple::keylet::unchecked(sealedID); auto sleSealedOffer = sb.peek(oppOfferKeylet); if (!sleSealedOffer) @@ -93,9 +93,9 @@ OptionExecute::doApply() if (!sleOption) return tecINTERNAL; - STAmount const strikePrice = sleOption->getFieldAmount(sfAmount); - STAmount const quantityShares = sleSealedOffer->getFieldAmount(sfAmount); - std::uint32_t const quantity = sleOptionOffer->getFieldU32(sfQualityIn); + STAmount const strikePrice = sleOption->getFieldAmount(sfStrikePrice); + STAmount const quantityShares = sleSealedOffer->getFieldAmount(sfLockedBalance); + std::uint32_t const quantity = sleOptionOffer->getFieldU32(sfQuantity); STAmount const totalValue = STAmount(strikePrice.issue(), (strikePrice.mantissa() * quantity)); if (flags & tfOptionExpire) diff --git a/src/ripple/app/tx/impl/OptionList.cpp b/src/ripple/app/tx/impl/OptionList.cpp index 013fa9943..ef8a99f4b 100644 --- a/src/ripple/app/tx/impl/OptionList.cpp +++ b/src/ripple/app/tx/impl/OptionList.cpp @@ -62,15 +62,15 @@ OptionList::doApply() AccountID const srcAccID = ctx_.tx.getAccountID(sfAccount); std::uint32_t const expiration = ctx_.tx.getFieldU32(sfExpiration); - STAmount const amount = ctx_.tx.getFieldAmount(sfAmount); + STAmount const strikePrice = ctx_.tx.getFieldAmount(sfStrikePrice); auto sleSrcAcc = sb.peek(keylet::account(srcAccID)); if (!sleSrcAcc) return terNO_ACCOUNT; - auto const optionKeylet = keylet::option(amount.getIssuer(), expiration); + auto const optionKeylet = keylet::option(strikePrice.getIssuer(), expiration); auto const sleOption = std::make_shared(optionKeylet); - (*sleOption)[sfAmount] = amount; + (*sleOption)[sfStrikePrice] = strikePrice; (*sleOption)[sfExpiration] = expiration; // apply diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index 1f9d15368..d0e22eaab 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -410,6 +410,8 @@ extern SF_UINT32 const sfRewardLgrLast; extern SF_UINT32 const sfFirstNFTokenSequence; extern SF_UINT32 const sfImportSequence; extern SF_UINT32 const sfXahauActivationLgrSeq; +extern SF_UINT32 const sfQuantity; +extern SF_UINT32 const sfToSeal; // 64-bit integers (common) extern SF_UINT64 const sfIndexNext; @@ -483,6 +485,8 @@ extern SF_UINT256 const sfURITokenID; extern SF_UINT256 const sfGovernanceFlags; extern SF_UINT256 const sfGovernanceMarks; extern SF_UINT256 const sfEmittedTxnID; +extern SF_UINT256 const sfOptionID; +extern SF_UINT256 const sfSwapID; // currency amount (common) extern SF_AMOUNT const sfAmount; @@ -496,6 +500,7 @@ extern SF_AMOUNT const sfFee; extern SF_AMOUNT const sfSendMax; extern SF_AMOUNT const sfDeliverMin; extern SF_AMOUNT const sfLockedBalance; +extern SF_AMOUNT const sfStrikePrice; // currency amount (uncommon) extern SF_AMOUNT const sfMinimumOffer; diff --git a/src/ripple/protocol/impl/LedgerFormats.cpp b/src/ripple/protocol/impl/LedgerFormats.cpp index 64b2eed9c..19b580ea2 100644 --- a/src/ripple/protocol/impl/LedgerFormats.cpp +++ b/src/ripple/protocol/impl/LedgerFormats.cpp @@ -368,7 +368,7 @@ LedgerFormats::LedgerFormats() add(jss::Option, ltOPTION, { - {sfAmount, soeREQUIRED}, + {sfStrikePrice, soeREQUIRED}, {sfExpiration, soeREQUIRED}, {sfPreviousTxnID, soeREQUIRED}, {sfPreviousTxnLgrSeq, soeREQUIRED} @@ -380,14 +380,14 @@ LedgerFormats::LedgerFormats() { {sfOwner, soeREQUIRED}, {sfOwnerNode, soeREQUIRED}, - {sfOfferID, soeREQUIRED}, // OptionID - {sfAmount, soeREQUIRED}, // Locked Amount - {sfTakerPays, soeREQUIRED}, // Premium - {sfQualityIn, soeREQUIRED}, // Quantity - {sfQualityOut, soeREQUIRED}, // To Seal + {sfOptionID, soeREQUIRED}, // OptionID + {sfLockedBalance, soeREQUIRED}, // Locked Amount + {sfAmount, soeREQUIRED}, // Premium + {sfQuantity, soeREQUIRED}, // Quantity + {sfToSeal, soeREQUIRED}, // To Seal {sfBookDirectory, soeREQUIRED}, {sfBookNode, soeREQUIRED}, - {sfInvoiceID, soeOPTIONAL}, // MatchID + {sfSwapID, soeOPTIONAL}, // MatchID {sfCheckID, soeOPTIONAL}, // SwapID {sfPreviousTxnID, soeREQUIRED}, {sfPreviousTxnLgrSeq, soeREQUIRED} diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index a72208607..bc4fc9540 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -157,6 +157,8 @@ CONSTRUCT_TYPED_SFIELD(sfLockCount, "LockCount", UINT32, CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50); +CONSTRUCT_TYPED_SFIELD(sfToSeal, "ToSeal", UINT32, 94); +CONSTRUCT_TYPED_SFIELD(sfQuantity, "Quantity", UINT32, 95); CONSTRUCT_TYPED_SFIELD(sfXahauActivationLgrSeq, "XahauActivationLgrSeq",UINT32, 96); CONSTRUCT_TYPED_SFIELD(sfImportSequence, "ImportSequence", UINT32, 97); CONSTRUCT_TYPED_SFIELD(sfRewardTime, "RewardTime", UINT32, 98); @@ -236,6 +238,8 @@ CONSTRUCT_TYPED_SFIELD(sfURITokenID, "URITokenID", UINT256, CONSTRUCT_TYPED_SFIELD(sfGovernanceFlags, "GovernanceFlags", UINT256, 99); CONSTRUCT_TYPED_SFIELD(sfGovernanceMarks, "GovernanceMarks", UINT256, 98); CONSTRUCT_TYPED_SFIELD(sfEmittedTxnID, "EmittedTxnID", UINT256, 97); +CONSTRUCT_TYPED_SFIELD(sfOptionID, "OptionID", UINT256, 96); +CONSTRUCT_TYPED_SFIELD(sfSwapID, "SwapID", UINT256, 95); // currency amount (common) CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1); @@ -261,6 +265,7 @@ CONSTRUCT_TYPED_SFIELD(sfLockedBalance, "LockedBalance", AMOUNT, CONSTRUCT_TYPED_SFIELD(sfBaseFeeDrops, "BaseFeeDrops", AMOUNT, 22); CONSTRUCT_TYPED_SFIELD(sfReserveBaseDrops, "ReserveBaseDrops", AMOUNT, 23); CONSTRUCT_TYPED_SFIELD(sfReserveIncrementDrops, "ReserveIncrementDrops", AMOUNT, 24); +CONSTRUCT_TYPED_SFIELD(sfStrikePrice, "StrikePrice", AMOUNT, 25); // variable length (common) CONSTRUCT_TYPED_SFIELD(sfPublicKey, "PublicKey", VL, 1); diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index b60b558f6..5e552c9a5 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -460,7 +460,7 @@ TxFormats::TxFormats() add(jss::OptionList, ttOPTION_LIST, { - {sfAmount, soeREQUIRED}, + {sfStrikePrice, soeREQUIRED}, {sfExpiration, soeREQUIRED}, {sfTicketSequence, soeOPTIONAL}, }, @@ -469,10 +469,10 @@ TxFormats::TxFormats() add(jss::OptionCreate, ttOPTION_CREATE, { - {sfOfferID, soeREQUIRED}, // Option ID + {sfOptionID, soeREQUIRED}, // Option ID {sfAmount, soeREQUIRED}, // Premium - {sfQualityIn, soeREQUIRED}, // Quantity - {sfInvoiceID, soeOPTIONAL}, // Swap ID + {sfQuantity, soeREQUIRED}, // Quantity + {sfSwapID, soeOPTIONAL}, // Swap ID {sfTicketSequence, soeOPTIONAL}, }, commonFields); @@ -480,8 +480,8 @@ TxFormats::TxFormats() add(jss::OptionExecute, ttOPTION_EXECUTE, { - {sfOfferID, soeREQUIRED}, - {sfInvoiceID, soeOPTIONAL}, + {sfOptionID, soeREQUIRED}, + {sfSwapID, soeOPTIONAL}, {sfTicketSequence, soeOPTIONAL}, }, commonFields); diff --git a/src/test/app/Option_test.cpp b/src/test/app/Option_test.cpp index 97f65989c..6d05e5a8e 100644 --- a/src/test/app/Option_test.cpp +++ b/src/test/app/Option_test.cpp @@ -58,8 +58,8 @@ struct Option_test : public beast::unit_test::suite std::uint32_t const& seq) { auto const sle = env.le(keylet::optionOffer(account, seq)); - if (sle->isFieldPresent(sfAmount)) - return (*sle)[sfAmount]; + if (sle->isFieldPresent(sfLockedBalance)) + return (*sle)[sfLockedBalance]; return STAmount(0); } @@ -67,13 +67,13 @@ struct Option_test : public beast::unit_test::suite optionlist( jtx::Account const& account, std::uint32_t expiration, - STAmount const& amount) + STAmount const& strikePrice) { using namespace jtx; Json::Value jv; jv[jss::TransactionType] = jss::OptionList; jv[jss::Account] = account.human(); - jv[jss::Amount] = amount.getJson(JsonOptions::none); + jv[sfStrikePrice.jsonName] = strikePrice.getJson(JsonOptions::none); jv[sfExpiration.jsonName] = expiration; return jv; } @@ -83,15 +83,15 @@ struct Option_test : public beast::unit_test::suite jtx::Account const& account, uint256 const& optionId, uint32_t const& quantity, - STAmount const& amount) + STAmount const& premium) { using namespace jtx; Json::Value jv; jv[jss::TransactionType] = jss::OptionCreate; jv[jss::Account] = account.human(); - jv[sfOfferID.jsonName] = to_string(optionId); - jv[jss::Amount] = amount.getJson(JsonOptions::none); - jv[sfQualityIn.jsonName] = quantity; + jv[sfOptionID.jsonName] = to_string(optionId); + jv[jss::Amount] = premium.getJson(JsonOptions::none); + jv[sfQuantity.jsonName] = quantity; return jv; } @@ -105,8 +105,8 @@ struct Option_test : public beast::unit_test::suite Json::Value jv; jv[jss::TransactionType] = jss::OptionExecute; jv[jss::Account] = account.human(); - jv[sfOfferID.jsonName] = to_string(optionId); - jv[sfInvoiceID.jsonName] = to_string(offerId); + jv[sfOptionID.jsonName] = to_string(optionId); + jv[sfSwapID.jsonName] = to_string(offerId); ; return jv; } @@ -125,6 +125,33 @@ struct Option_test : public beast::unit_test::suite return keylet::optionOffer(account, sequence).key; } + static auto + getOptionList( + jtx::Env& env, + AccountID const& issuer) + { + Json::Value jvbp; + jvbp[jss::ledger_index] = "current"; + jvbp[jss::account] = to_string(issuer); + jvbp[jss::type] = "option"; + return env.rpc("json", "account_objects", to_string(jvbp))[jss::result]; + } + + static auto + getOptionBookOffers( + jtx::Env& env, + STAmount const& strike_price, + std::uint32_t expiration) + { + Json::Value jvbp; + jvbp[jss::ledger_index] = "current"; + jvbp[jss::strike_price][jss::currency] = to_string(strike_price.issue().currency); + jvbp[jss::strike_price][jss::issuer] = to_string(strike_price.issue().account); + jvbp[jss::strike_price][jss::value] = to_string(strike_price.mantissa()); + jvbp[jss::expiration] = to_string(expiration); + return env.rpc("json", "option_book_offers", to_string(jvbp))[jss::result]; + } + void testBookBuy(FeatureBitset features) { @@ -364,7 +391,7 @@ struct Option_test : public beast::unit_test::suite // network::makeNetworkConfig(21337), // features, // nullptr, - // beast::severities::kWarning}; + // beast::severities::kTrace}; auto const feeDrops = env.current()->fees().base; @@ -408,6 +435,9 @@ struct Option_test : public beast::unit_test::suite BEAST_EXPECT( lockedValue(env, writer, seq) == XRP(quantity)); + auto jrr = getOptionBookOffers(env, XRP(strikePrice), expiration); + std::cout << "RESULT: " << jrr << "\n"; + preWriter = env.balance(writer); preBuyer = env.balance(buyer); @@ -428,6 +458,9 @@ struct Option_test : public beast::unit_test::suite preWriter = env.balance(writer); preBuyer = env.balance(buyer); + auto jrr1 = getOptionBookOffers(env, XRP(strikePrice), expiration); + std::cout << "RESULT1: " << jrr1 << "\n"; + // Execute Option env(optionexecute(buyer, optionId, offerId), ter(tesSUCCESS)); env.close(); @@ -438,6 +471,11 @@ struct Option_test : public beast::unit_test::suite BEAST_EXPECT( env.balance(writer) == preWriter + XRP(quantity * strikePrice)); + + auto jrrList = getOptionList(env, zeroAcct); + std::cout << "RESULT LIST: " << jrrList << "\n"; + auto jrr2 = getOptionBookOffers(env, XRP(strikePrice), expiration); + std::cout << "RESULT2: " << jrr2 << "\n"; } public: