mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
options build
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -3,7 +3,7 @@
|
||||
"C_Cpp.clang_format_path": ".clang-format",
|
||||
"C_Cpp.clang_format_fallbackStyle": "{ ColumnLimit: 0 }",
|
||||
"[cpp]":{
|
||||
"editor.wordBasedSuggestions": false,
|
||||
"editor.wordBasedSuggestions": "off",
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.semanticHighlighting.enabled": true,
|
||||
"editor.tabSize": 4,
|
||||
|
||||
@@ -88,6 +88,7 @@ target_sources (xrpl_core PRIVATE
|
||||
src/ripple/protocol/impl/Issue.cpp
|
||||
src/ripple/protocol/impl/Keylet.cpp
|
||||
src/ripple/protocol/impl/LedgerFormats.cpp
|
||||
src/ripple/protocol/impl/Option.cpp
|
||||
src/ripple/protocol/impl/PublicKey.cpp
|
||||
src/ripple/protocol/impl/Quality.cpp
|
||||
src/ripple/protocol/impl/Rate2.cpp
|
||||
@@ -219,6 +220,7 @@ install (
|
||||
src/ripple/protocol/Keylet.h
|
||||
src/ripple/protocol/KnownFormats.h
|
||||
src/ripple/protocol/LedgerFormats.h
|
||||
src/ripple/protocol/Option.h
|
||||
src/ripple/protocol/Protocol.h
|
||||
src/ripple/protocol/PublicKey.h
|
||||
src/ripple/protocol/Quality.h
|
||||
@@ -446,6 +448,9 @@ target_sources (rippled PRIVATE
|
||||
src/ripple/app/tx/impl/NFTokenCreateOffer.cpp
|
||||
src/ripple/app/tx/impl/NFTokenMint.cpp
|
||||
src/ripple/app/tx/impl/OfferStream.cpp
|
||||
src/ripple/app/tx/impl/OptionCreate.cpp
|
||||
src/ripple/app/tx/impl/OptionExecute.cpp
|
||||
src/ripple/app/tx/impl/OptionList.cpp
|
||||
src/ripple/app/tx/impl/PayChan.cpp
|
||||
src/ripple/app/tx/impl/Payment.cpp
|
||||
src/ripple/app/tx/impl/SetAccount.cpp
|
||||
@@ -732,6 +737,7 @@ if (tests)
|
||||
src/test/app/NFTokenBurn_test.cpp
|
||||
src/test/app/NFTokenDir_test.cpp
|
||||
src/test/app/OfferStream_test.cpp
|
||||
src/test/app/Option_test.cpp
|
||||
src/test/app/Offer_test.cpp
|
||||
src/test/app/OversizeMeta_test.cpp
|
||||
src/test/app/Path_test.cpp
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#include <ripple/app/hook/applyHook.h>
|
||||
#include <ripple/app/ledger/OpenLedger.h>
|
||||
#include <ripple/app/ledger/TransactionMaster.h>
|
||||
#include <ripple/app/misc/HashRouter.h>
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/app/misc/Transaction.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/app/tx/impl/Import.h>
|
||||
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/Slice.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/st.h>
|
||||
#include <ripple/protocol/tokens.h>
|
||||
#include <boost/multiprecision/cpp_dec_float.hpp>
|
||||
#include <any>
|
||||
|
||||
@@ -85,9 +85,11 @@ OrderBookDB::update(std::shared_ptr<ReadView const> const& ledger)
|
||||
return;
|
||||
}
|
||||
|
||||
decltype(optionBooks_) optionBooks;
|
||||
decltype(allBooks_) allBooks;
|
||||
decltype(xrpBooks_) xrpBooks;
|
||||
|
||||
optionBooks.reserve(optionBooks_.size());
|
||||
allBooks.reserve(allBooks_.size());
|
||||
xrpBooks.reserve(xrpBooks_.size());
|
||||
|
||||
@@ -161,6 +163,14 @@ OrderBookDB::addOrderBook(Book const& book)
|
||||
xrpBooks_.insert(book.in);
|
||||
}
|
||||
|
||||
void
|
||||
OrderBookDB::addOptionOrderBook(Option const& option)
|
||||
{
|
||||
std::lock_guard sl(mLock);
|
||||
|
||||
optionBooks_.insert(option.issue);
|
||||
}
|
||||
|
||||
// return list of all orderbooks that want this issuerID and currencyID
|
||||
std::vector<Book>
|
||||
OrderBookDB::getBooksByTakerPays(Issue const& issue)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <ripple/app/ledger/AcceptedLedgerTx.h>
|
||||
#include <ripple/app/ledger/BookListeners.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/protocol/Option.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace ripple {
|
||||
@@ -40,6 +41,9 @@ public:
|
||||
void
|
||||
addOrderBook(Book const&);
|
||||
|
||||
void
|
||||
addOptionOrderBook(Option const&);
|
||||
|
||||
/** @return a list of all orderbooks that want this issuerID and currencyID.
|
||||
*/
|
||||
std::vector<Book>
|
||||
@@ -55,6 +59,7 @@ public:
|
||||
|
||||
BookListeners::pointer
|
||||
getBookListeners(Book const&);
|
||||
|
||||
BookListeners::pointer
|
||||
makeBookListeners(Book const&);
|
||||
|
||||
@@ -71,6 +76,9 @@ private:
|
||||
// Maps order books by "issue in" to "issue out":
|
||||
hardened_hash_map<Issue, hardened_hash_set<Issue>> allBooks_;
|
||||
|
||||
// does an order book to XRP exist
|
||||
hash_set<Issue> optionBooks_;
|
||||
|
||||
// does an order book to XRP exist
|
||||
hash_set<Issue> xrpBooks_;
|
||||
|
||||
|
||||
@@ -109,6 +109,10 @@ XRPNotCreated::visitEntry(
|
||||
if (isXRP((*before)[sfAmount]))
|
||||
drops_ -= (*before)[sfAmount].xrp().drops();
|
||||
break;
|
||||
case ltOPTION_OFFER:
|
||||
if (isXRP((*before)[sfAmount]))
|
||||
drops_ -= (*before)[sfAmount].xrp().drops();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -131,6 +135,10 @@ XRPNotCreated::visitEntry(
|
||||
if (!isDelete && isXRP((*after)[sfAmount]))
|
||||
drops_ += (*after)[sfAmount].xrp().drops();
|
||||
break;
|
||||
case ltOPTION_OFFER:
|
||||
if (!isDelete && isXRP((*after)[sfAmount]))
|
||||
drops_ += (*after)[sfAmount].xrp().drops();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -492,6 +500,8 @@ LedgerEntryTypesMatch::visitEntry(
|
||||
case ltURI_TOKEN:
|
||||
case ltIMPORT_VLSEQ:
|
||||
case ltUNL_REPORT:
|
||||
case ltOPTION:
|
||||
case ltOPTION_OFFER:
|
||||
break;
|
||||
default:
|
||||
invalidTypeAdded_ = true;
|
||||
|
||||
422
src/ripple/app/tx/impl/OptionCreate.cpp
Normal file
422
src/ripple/app/tx/impl/OptionCreate.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2024 XRPL-Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/OrderBookDB.h>
|
||||
#include <ripple/app/tx/impl/OptionCreate.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/ledger/ApplyView.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/Option.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
TxConsequences
|
||||
OptionCreate::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
return TxConsequences{ctx.tx, TxConsequences::normal};
|
||||
}
|
||||
|
||||
NotTEC
|
||||
OptionCreate::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (ctx.tx.getFlags() & tfOptionCreateMask)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "OptionCreate: Invalid flags set.";
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
auto const flags = ctx.tx.getFlags();
|
||||
std::optional<uint256> const swapID = ctx.tx[~sfInvoiceID];
|
||||
std::uint32_t const quantity = ctx.tx.getFieldU32(sfQualityIn);
|
||||
|
||||
bool const isClose = (flags & tfPosition) != 0;
|
||||
|
||||
if (isClose && !swapID)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "OptionCreate: Cannot close option without swap ID.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (quantity % 100)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "OptionCreate: Quantity must be a multple of 100.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint256>
|
||||
sealOption(
|
||||
Sandbox& sb,
|
||||
STAmount strikePrice,
|
||||
std::uint32_t expiration,
|
||||
bool isPut,
|
||||
bool isSell)
|
||||
{
|
||||
const uint256 uBookBaseOpp = getOptionBookBase(
|
||||
strikePrice.getIssuer(),
|
||||
strikePrice.getCurrency(),
|
||||
strikePrice.mantissa(),
|
||||
expiration);
|
||||
// std::cout << "BOOK BASE: " << uBookBaseOpp << "\n";
|
||||
const uint256 uBookEndOpp = getOptionQualityNext(uBookBaseOpp);
|
||||
// std::cout << "BOOK BASE END: " << uBookEndOpp << "\n";
|
||||
auto key = sb.succ(uBookBaseOpp, uBookEndOpp);
|
||||
if (!key)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sleOfferDir = sb.read(keylet::page(key.value()));
|
||||
uint256 offerIndex;
|
||||
unsigned int bookEntry;
|
||||
|
||||
if (!cdirFirst(sb, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// auto sleOffer = sb.read(keylet::unchecked(offerIndex));
|
||||
// STAmount premium = sleOffer->getFieldAmount(sfAmount);
|
||||
// auto const dir = to_string(sleOffer->getFieldH256(sfBookDirectory));
|
||||
// std::cout << "dir: " << dir << "\n";
|
||||
// auto const uTipIndex = sleOfferDir->key();
|
||||
// auto const optionQuality = getOptionQuality(uTipIndex);
|
||||
// std::cout << "optionQuality: " << optionQuality << "\n";
|
||||
// STAmount dirRate = STAmount(premium.issue(), optionQuality);
|
||||
// std::cout << "dirRate: " << dirRate << "\n";
|
||||
// BEAST_EXPECT(100 / rate == 110);
|
||||
|
||||
do
|
||||
{
|
||||
auto sleItem = sb.read(keylet::child(offerIndex));
|
||||
if (!sleItem)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const flags = sleItem->getFlags();
|
||||
bool const _isPut = (flags & tfType) != 0;
|
||||
bool const _isSell = (flags & tfAction) != 0;
|
||||
|
||||
// Skip Sealed Options
|
||||
if (sleItem->getFieldU32(sfQualityOut) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// looking for oposite option position.
|
||||
if (_isPut == isPut && _isSell != isSell)
|
||||
{
|
||||
uint256 const sealID = sleItem->getFieldH256(sfInvoiceID);
|
||||
if (sealID.isNonZero())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return offerIndex;
|
||||
}
|
||||
}
|
||||
} while (
|
||||
cdirNext(sb, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex));
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TER
|
||||
OptionCreate::doApply()
|
||||
{
|
||||
if (!view().rules().enabled(featureOptions))
|
||||
return temDISABLED;
|
||||
|
||||
Sandbox sb(&ctx_.view());
|
||||
|
||||
beast::Journal const& j = ctx_.journal;
|
||||
|
||||
AccountID const srcAccID = ctx_.tx.getAccountID(sfAccount);
|
||||
uint256 const optionID = ctx_.tx.getFieldH256(sfOfferID);
|
||||
auto const flags = ctx_.tx.getFlags();
|
||||
STAmount const premium = ctx_.tx.getFieldAmount(sfAmount);
|
||||
std::uint32_t const quantity = ctx_.tx.getFieldU32(sfQualityIn);
|
||||
std::optional<uint256> const swapID = ctx_.tx[~sfInvoiceID];
|
||||
|
||||
STAmount const totalPremium =
|
||||
STAmount(premium.issue(), (premium.mantissa() * quantity));
|
||||
|
||||
auto sleSrcAcc = sb.peek(keylet::account(srcAccID));
|
||||
if (!sleSrcAcc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto sleOptionAcc = sb.peek(keylet::unchecked(optionID));
|
||||
if (!sleOptionAcc)
|
||||
return tecINTERNAL;
|
||||
|
||||
STAmount const strikePrice = sleOptionAcc->getFieldAmount(sfAmount);
|
||||
std::uint32_t const expiration = sleOptionAcc->getFieldU32(sfExpiration);
|
||||
STAmount const quantityShares = STAmount(strikePrice.issue(), isXRP(premium) ? quantity * 1000000 : quantity);
|
||||
|
||||
bool const isPut = (flags & tfType) != 0;
|
||||
bool const isSell = (flags & tfAction) != 0;
|
||||
bool const isClose = (flags & tfPosition) != 0;
|
||||
|
||||
auto optionBookDirKeylet = keylet::optionBook(
|
||||
strikePrice.getIssuer(),
|
||||
strikePrice.getCurrency(),
|
||||
strikePrice.mantissa(),
|
||||
expiration);
|
||||
auto optionOfferKeylet =
|
||||
ripple::keylet::optionOffer(srcAccID, ctx_.tx.getSeqProxy().value());
|
||||
|
||||
std::optional<uint256> const sealID =
|
||||
sealOption(sb, strikePrice, expiration, isPut, isSell);
|
||||
|
||||
// Update Sealed Option
|
||||
AccountID oppAccID{AccountID{}};
|
||||
if (sealID)
|
||||
{
|
||||
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);
|
||||
oppAccID = sealedOption->getAccountID(sfOwner);
|
||||
sb.update(sealedOption);
|
||||
}
|
||||
|
||||
// Block No Market Buys
|
||||
if (!isSell && !sealID)
|
||||
{
|
||||
JLOG(j.warn()) << "OptionCreate: No open sell offers exist.";
|
||||
return tecNO_TARGET;
|
||||
}
|
||||
|
||||
// Insert New Option (3 Times [issuer, party, counter-party])
|
||||
if (!isClose)
|
||||
{
|
||||
JLOG(j.warn()) << "Creating Option Offer: !isClose";
|
||||
// Add Option to Issuer
|
||||
if (!isXRP(premium))
|
||||
{
|
||||
// unimplemented
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
// Add Option to Self
|
||||
std::uint32_t ownerCount{(*sleSrcAcc)[sfOwnerCount]};
|
||||
++ownerCount;
|
||||
XRPAmount const newReserve{sb.fees().accountReserve(ownerCount)};
|
||||
adjustOwnerCount(sb, sleSrcAcc, 1, j);
|
||||
|
||||
auto optionOffer = std::make_shared<SLE>(optionOfferKeylet);
|
||||
auto const page = sb.dirInsert(
|
||||
keylet::ownerDir(account_),
|
||||
optionOfferKeylet,
|
||||
describeOwnerDir(account_));
|
||||
if (!page)
|
||||
{
|
||||
std::cout << "NO PAGE"
|
||||
<< "\n";
|
||||
return tecDIR_FULL;
|
||||
}
|
||||
|
||||
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
|
||||
if (sealID)
|
||||
{
|
||||
JLOG(j.warn()) << "Updating Option Offer: sealID";
|
||||
optionOffer->setFieldH256(sfInvoiceID, *sealID);
|
||||
optionOffer->setFieldU32(sfQualityOut, 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
|
||||
}
|
||||
|
||||
Option const option{
|
||||
strikePrice.issue(), strikePrice.mantissa(), expiration};
|
||||
|
||||
// std::cout << "BOOK BASE: " << strikePrice.mantissa() << "\n";
|
||||
// std::cout << "PREMIUM: " << premium.mantissa() << "\n";
|
||||
|
||||
auto dir =
|
||||
keylet::optionQuality(optionBookDirKeylet, premium.mantissa());
|
||||
bool const bookExisted = static_cast<bool>(sb.peek(dir));
|
||||
auto const bookNode =
|
||||
sb.dirAppend(dir, optionOfferKeylet, [&](SLE::ref sle) {
|
||||
sle->setFieldH160(
|
||||
sfTakerPaysIssuer, strikePrice.issue().account);
|
||||
sle->setFieldH160(
|
||||
sfTakerPaysCurrency, strikePrice.issue().currency);
|
||||
sle->setFieldU64(sfBaseFee, strikePrice.mantissa());
|
||||
sle->setFieldU32(sfExpiration, expiration);
|
||||
sle->setFieldU64(sfExchangeRate, premium.mantissa());
|
||||
});
|
||||
|
||||
if (!bookNode)
|
||||
{
|
||||
JLOG(j_.debug()) << "final result: failed to add offer to book";
|
||||
return tecDIR_FULL;
|
||||
}
|
||||
|
||||
// if (!bookExisted)
|
||||
// ctx_.app.getOrderBookDB().addOptionOrderBook(option);
|
||||
|
||||
optionOffer->setFieldH256(sfBookDirectory, dir.key);
|
||||
optionOffer->setFieldU64(sfBookNode, *bookNode);
|
||||
sb.insert(optionOffer);
|
||||
}
|
||||
|
||||
if (isClose)
|
||||
{
|
||||
JLOG(j.warn()) << "Updating Option Offer: isClose";
|
||||
auto optionOffer = sb.peek(optionOfferKeylet);
|
||||
uint256 const sealID = optionOffer->getFieldH256(sfInvoiceID);
|
||||
if (!sealID)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "OptionCreate: Cannot close option that has not sealed.";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
// Update Swap Option
|
||||
auto swapKeylet = ripple::keylet::unchecked(*swapID);
|
||||
auto swapOption = sb.peek(swapKeylet);
|
||||
if (!swapOption)
|
||||
{
|
||||
JLOG(j.warn()) << "OptionCreate: Swap Option does not exist.";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
swapOption->setFieldH256(sfInvoiceID, optionOfferKeylet.key);
|
||||
sb.update(swapOption);
|
||||
|
||||
// Update New Option
|
||||
optionOffer->setFieldH256(sfInvoiceID, *swapID);
|
||||
sb.update(optionOffer);
|
||||
|
||||
// Erase Swap Sealed Option
|
||||
uint256 const swapSealedID = swapOption->getFieldH256(sfInvoiceID);
|
||||
if (!swapSealedID)
|
||||
{
|
||||
JLOG(j.warn()) << "OptionCreate: Swap Option is not sealed.";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
auto swapSealedKeylet = ripple::keylet::unchecked(swapSealedID);
|
||||
auto swapSealedOption = sb.peek(swapSealedKeylet);
|
||||
if (!swapSealedOption)
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "OptionCreate: Swap Sealed Option does not exist.";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
sb.erase(swapSealedOption);
|
||||
|
||||
// Erase New Sealed Option
|
||||
auto sealedKeylet = ripple::keylet::unchecked(sealID);
|
||||
auto sealedOption = sb.peek(sealedKeylet);
|
||||
sb.erase(sealedOption);
|
||||
}
|
||||
|
||||
// xfer the premium from the buyer to the seller
|
||||
if (!isSell && sealID)
|
||||
{
|
||||
JLOG(j.warn()) << "Updating Option Balances: !isSell && sealID";
|
||||
auto sleOppAcc = sb.peek(keylet::account(oppAccID));
|
||||
if (!sleOppAcc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
// Native
|
||||
if (isXRP(premium))
|
||||
{
|
||||
// ensure the account can cover the native
|
||||
if (mSourceBalance < totalPremium.xrp())
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
|
||||
// subtract the balance from the buyer
|
||||
{
|
||||
STAmount bal = mSourceBalance;
|
||||
bal -= totalPremium.xrp();
|
||||
if (bal < beast::zero || bal > mSourceBalance)
|
||||
return tecINTERNAL;
|
||||
sleSrcAcc->setFieldAmount(sfBalance, bal);
|
||||
}
|
||||
|
||||
// add the balance to the writer
|
||||
{
|
||||
STAmount bal = sleOppAcc->getFieldAmount(sfBalance);
|
||||
STAmount prior = bal;
|
||||
bal += totalPremium.xrp();
|
||||
if (bal < beast::zero || bal < prior)
|
||||
return tecINTERNAL;
|
||||
sleOppAcc->setFieldAmount(sfBalance, bal);
|
||||
}
|
||||
}
|
||||
|
||||
sb.update(sleOppAcc);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.warn()) << "Updating Option Balances: isSell";
|
||||
if (isXRP(quantityShares))
|
||||
{
|
||||
// subtract the balance from the writer
|
||||
if (mSourceBalance < quantityShares.xrp())
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
{
|
||||
STAmount bal = mSourceBalance;
|
||||
bal -= quantityShares.xrp();
|
||||
if (bal < beast::zero || bal > mSourceBalance)
|
||||
return tecINTERNAL;
|
||||
|
||||
sleSrcAcc->setFieldAmount(sfBalance, bal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply
|
||||
sb.update(sleSrcAcc);
|
||||
sb.apply(ctx_.rawView());
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
XRPAmount
|
||||
OptionCreate::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
return Transactor::calculateBaseFee(view, tx);
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
54
src/ripple/app/tx/impl/OptionCreate.h
Normal file
54
src/ripple/app/tx/impl/OptionCreate.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2024 XRPL-Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TX_OPTIONCREATE_H_INCLUDED
|
||||
#define RIPPLE_TX_OPTIONCREATE_H_INCLUDED
|
||||
|
||||
#include <ripple/app/tx/impl/Transactor.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class OptionCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit OptionCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
203
src/ripple/app/tx/impl/OptionExecute.cpp
Normal file
203
src/ripple/app/tx/impl/OptionExecute.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2024 XRPL-Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/tx/impl/OptionExecute.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/ledger/ApplyView.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
TxConsequences
|
||||
OptionExecute::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
return TxConsequences{ctx.tx, TxConsequences::normal};
|
||||
}
|
||||
|
||||
NotTEC
|
||||
OptionExecute::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (ctx.tx.getFlags() & tfOptionExpireMask)
|
||||
{
|
||||
// There are no flags (other than universal).
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set.";
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TER
|
||||
OptionExecute::doApply()
|
||||
{
|
||||
if (!view().rules().enabled(featureOptions))
|
||||
return temDISABLED;
|
||||
|
||||
Sandbox sb(&ctx_.view());
|
||||
|
||||
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);
|
||||
auto const flags = ctx_.tx.getFlags();
|
||||
|
||||
auto sleSrcAcc = sb.peek(keylet::account(srcAccID));
|
||||
if (!sleSrcAcc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto optionOfferKeylet = ripple::keylet::unchecked(offerID);
|
||||
auto sleOptionOffer = sb.peek(optionOfferKeylet);
|
||||
if (!sleOptionOffer)
|
||||
return tecNO_TARGET;
|
||||
|
||||
AccountID const ownrAccID = sleOptionOffer->getAccountID(sfOwner);
|
||||
auto const sealedID = sleOptionOffer->getFieldH256(sfInvoiceID);
|
||||
auto oppOfferKeylet = ripple::keylet::unchecked(sealedID);
|
||||
auto sleSealedOffer = sb.peek(oppOfferKeylet);
|
||||
if (!sleSealedOffer)
|
||||
return tecNO_TARGET;
|
||||
|
||||
AccountID const oppAccID = sleSealedOffer->getAccountID(sfOwner);
|
||||
auto sleOppAcc = sb.peek(keylet::account(oppAccID));
|
||||
if (!sleOppAcc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const optionFlags = sleOptionOffer->getFlags();
|
||||
bool const isPut = (optionFlags & tfType) != 0;
|
||||
bool const isSell = (optionFlags & tfAction) != 0;
|
||||
|
||||
auto sleOption = sb.peek(keylet::unchecked(optionID));
|
||||
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 totalValue = STAmount(strikePrice.issue(), (strikePrice.mantissa() * quantity));
|
||||
|
||||
if (flags & tfOptionExpire)
|
||||
{
|
||||
JLOG(j.warn()) << "OptionExecute: EXPIRE OPTION";
|
||||
sb.erase(sleSealedOffer);
|
||||
sb.erase(sleOptionOffer);
|
||||
sb.apply(ctx_.rawView());
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
switch (isPut)
|
||||
{
|
||||
case 0: {
|
||||
JLOG(j.warn()) << "OptionExecute: EXERCISE CALL";
|
||||
if (isXRP(quantityShares))
|
||||
{
|
||||
if (mSourceBalance < totalValue.xrp())
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
|
||||
STAmount hBalance = mSourceBalance;
|
||||
|
||||
// subtract the total value from the buyer
|
||||
{
|
||||
hBalance -= totalValue.xrp();
|
||||
if (hBalance < beast::zero || hBalance > mSourceBalance)
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
// add the total value to the writer
|
||||
{
|
||||
STAmount wBalance = sleOppAcc->getFieldAmount(sfBalance);
|
||||
STAmount prior = wBalance;
|
||||
wBalance += totalValue.xrp();
|
||||
if (wBalance < beast::zero || wBalance < prior)
|
||||
return tecINTERNAL;
|
||||
sleOppAcc->setFieldAmount(sfBalance, wBalance);
|
||||
}
|
||||
|
||||
// add the shares to the buyer
|
||||
{
|
||||
STAmount prior = hBalance;
|
||||
hBalance += quantityShares.xrp();
|
||||
if (hBalance < beast::zero || hBalance < prior)
|
||||
return tecINTERNAL;
|
||||
}
|
||||
sleSrcAcc->setFieldAmount(sfBalance, hBalance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
JLOG(j.warn()) << "OptionExecute: EXERCISE PUT";
|
||||
if (isXRP(quantityShares))
|
||||
{
|
||||
// add the total value to the holder
|
||||
{
|
||||
STAmount hBalance = mSourceBalance;
|
||||
hBalance += totalValue.xrp();
|
||||
if (hBalance < beast::zero || hBalance < mSourceBalance)
|
||||
return tecINTERNAL;
|
||||
sleSrcAcc->setFieldAmount(sfBalance, hBalance);
|
||||
}
|
||||
|
||||
STAmount wBalance = sleOppAcc->getFieldAmount(sfBalance);
|
||||
if (wBalance < totalValue.xrp())
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
// subtract the total value from the writer
|
||||
{
|
||||
STAmount prior = wBalance;
|
||||
wBalance -= totalValue.xrp();
|
||||
if (wBalance < beast::zero || wBalance > prior)
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
// add the shares to the writer
|
||||
{
|
||||
STAmount prior = wBalance;
|
||||
wBalance += quantityShares.xrp();
|
||||
if (wBalance < beast::zero || wBalance < prior)
|
||||
return tecINTERNAL;
|
||||
}
|
||||
sleOppAcc->setFieldAmount(sfBalance, wBalance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
// apply
|
||||
sb.update(sleOppAcc);
|
||||
sb.update(sleSrcAcc);
|
||||
sb.erase(sleSealedOffer);
|
||||
sb.erase(sleOptionOffer);
|
||||
sb.apply(ctx_.rawView());
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
XRPAmount
|
||||
OptionExecute::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
return Transactor::calculateBaseFee(view, tx);
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
54
src/ripple/app/tx/impl/OptionExecute.h
Normal file
54
src/ripple/app/tx/impl/OptionExecute.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2024 XRPL-Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TX_OPTIONEXECUTE_H_INCLUDED
|
||||
#define RIPPLE_TX_OPTIONEXECUTE_H_INCLUDED
|
||||
|
||||
#include <ripple/app/tx/impl/Transactor.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class OptionExecute : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit OptionExecute(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
88
src/ripple/app/tx/impl/OptionList.cpp
Normal file
88
src/ripple/app/tx/impl/OptionList.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2024 XRPL-Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/tx/impl/OptionList.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/ledger/ApplyView.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
TxConsequences
|
||||
OptionList::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
return TxConsequences{ctx.tx, TxConsequences::normal};
|
||||
}
|
||||
|
||||
NotTEC
|
||||
OptionList::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
JLOG(ctx.j.warn()) << (ctx.tx.getFlags()) << "\n";
|
||||
JLOG(ctx.j.warn()) << (ctx.tx.getFlags() & tfUniversalMask) << "\n";
|
||||
if (ctx.tx.getFlags() & tfUniversalMask)
|
||||
{
|
||||
// There are no flags (other than universal).
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set.";
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
TER
|
||||
OptionList::doApply()
|
||||
{
|
||||
if (!view().rules().enabled(featureOptions))
|
||||
return temDISABLED;
|
||||
|
||||
Sandbox sb(&ctx_.view());
|
||||
|
||||
beast::Journal const& j = ctx_.journal;
|
||||
|
||||
AccountID const srcAccID = ctx_.tx.getAccountID(sfAccount);
|
||||
std::uint32_t const expiration = ctx_.tx.getFieldU32(sfExpiration);
|
||||
STAmount const amount = ctx_.tx.getFieldAmount(sfAmount);
|
||||
|
||||
auto sleSrcAcc = sb.peek(keylet::account(srcAccID));
|
||||
if (!sleSrcAcc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const optionKeylet = keylet::option(amount.getIssuer(), expiration);
|
||||
auto const sleOption = std::make_shared<SLE>(optionKeylet);
|
||||
(*sleOption)[sfAmount] = amount;
|
||||
(*sleOption)[sfExpiration] = expiration;
|
||||
|
||||
// apply
|
||||
sb.insert(sleOption);
|
||||
sb.apply(ctx_.rawView());
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
XRPAmount
|
||||
OptionList::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
return Transactor::calculateBaseFee(view, tx);
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
54
src/ripple/app/tx/impl/OptionList.h
Normal file
54
src/ripple/app/tx/impl/OptionList.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2024 XRPL-Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TX_OPTIONLIST_H_INCLUDED
|
||||
#define RIPPLE_TX_OPTIONLIST_H_INCLUDED
|
||||
|
||||
#include <ripple/app/tx/impl/Transactor.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class OptionList : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit OptionList(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <ripple/app/tx/impl/CancelOffer.h>
|
||||
#include <ripple/app/tx/impl/CashCheck.h>
|
||||
#include <ripple/app/tx/impl/Change.h>
|
||||
#include <ripple/app/tx/impl/ClaimReward.h>
|
||||
#include <ripple/app/tx/impl/CreateCheck.h>
|
||||
#include <ripple/app/tx/impl/CreateOffer.h>
|
||||
#include <ripple/app/tx/impl/CreateTicket.h>
|
||||
@@ -37,8 +38,12 @@
|
||||
#include <ripple/app/tx/impl/NFTokenCancelOffer.h>
|
||||
#include <ripple/app/tx/impl/NFTokenCreateOffer.h>
|
||||
#include <ripple/app/tx/impl/NFTokenMint.h>
|
||||
#include <ripple/app/tx/impl/OptionCreate.h>
|
||||
#include <ripple/app/tx/impl/OptionExecute.h>
|
||||
#include <ripple/app/tx/impl/OptionList.h>
|
||||
#include <ripple/app/tx/impl/PayChan.h>
|
||||
#include <ripple/app/tx/impl/Payment.h>
|
||||
#include <ripple/app/tx/impl/Remit.h>
|
||||
#include <ripple/app/tx/impl/SetAccount.h>
|
||||
#include <ripple/app/tx/impl/SetHook.h>
|
||||
#include <ripple/app/tx/impl/SetRegularKey.h>
|
||||
@@ -122,6 +127,12 @@ invoke_preflight(PreflightContext const& ctx)
|
||||
return invoke_preflight_helper<EscrowFinish>(ctx);
|
||||
case ttESCROW_CANCEL:
|
||||
return invoke_preflight_helper<EscrowCancel>(ctx);
|
||||
case ttOPTION_CREATE:
|
||||
return invoke_preflight_helper<OptionCreate>(ctx);
|
||||
case ttOPTION_EXECUTE:
|
||||
return invoke_preflight_helper<OptionExecute>(ctx);
|
||||
case ttOPTION_LIST:
|
||||
return invoke_preflight_helper<OptionList>(ctx);
|
||||
case ttPAYCHAN_CLAIM:
|
||||
return invoke_preflight_helper<PayChanClaim>(ctx);
|
||||
case ttPAYCHAN_CREATE:
|
||||
@@ -243,6 +254,12 @@ invoke_preclaim(PreclaimContext const& ctx)
|
||||
return invoke_preclaim<EscrowFinish>(ctx);
|
||||
case ttESCROW_CANCEL:
|
||||
return invoke_preclaim<EscrowCancel>(ctx);
|
||||
case ttOPTION_CREATE:
|
||||
return invoke_preclaim<OptionCreate>(ctx);
|
||||
case ttOPTION_EXECUTE:
|
||||
return invoke_preclaim<OptionExecute>(ctx);
|
||||
case ttOPTION_LIST:
|
||||
return invoke_preclaim<OptionList>(ctx);
|
||||
case ttPAYCHAN_CLAIM:
|
||||
return invoke_preclaim<PayChanClaim>(ctx);
|
||||
case ttPAYCHAN_CREATE:
|
||||
@@ -326,6 +343,12 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
return EscrowFinish::calculateBaseFee(view, tx);
|
||||
case ttESCROW_CANCEL:
|
||||
return EscrowCancel::calculateBaseFee(view, tx);
|
||||
case ttOPTION_CREATE:
|
||||
return OptionCreate::calculateBaseFee(view, tx);
|
||||
case ttOPTION_EXECUTE:
|
||||
return OptionExecute::calculateBaseFee(view, tx);
|
||||
case ttOPTION_LIST:
|
||||
return OptionList::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_CLAIM:
|
||||
return PayChanClaim::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_CREATE:
|
||||
@@ -469,6 +492,18 @@ invoke_apply(ApplyContext& ctx)
|
||||
EscrowCancel p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttOPTION_CREATE: {
|
||||
OptionCreate p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttOPTION_EXECUTE: {
|
||||
OptionExecute p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttOPTION_LIST: {
|
||||
OptionList p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttPAYCHAN_CLAIM: {
|
||||
PayChanClaim p(ctx);
|
||||
return p();
|
||||
|
||||
@@ -30,7 +30,7 @@ mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
|
||||
{
|
||||
using namespace boost::multiprecision;
|
||||
|
||||
uint128_t result;
|
||||
boost::multiprecision::uint128_t result;
|
||||
result = multiply(result, value, mul);
|
||||
|
||||
result /= div;
|
||||
|
||||
@@ -145,111 +145,78 @@ private:
|
||||
};
|
||||
|
||||
// VFALCO TODO This should only be enabled for maps.
|
||||
class pair_value_compare
|
||||
: public beast::detail::empty_base_optimization<Compare>
|
||||
#ifdef _LIBCPP_VERSION
|
||||
,
|
||||
public std::binary_function<value_type, value_type, bool>
|
||||
#endif
|
||||
class pair_value_compare : public Compare
|
||||
{
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument = value_type;
|
||||
using second_argument = value_type;
|
||||
using result_type = bool;
|
||||
#endif
|
||||
|
||||
bool
|
||||
operator()(value_type const& lhs, value_type const& rhs) const
|
||||
{
|
||||
return this->member()(lhs.first, rhs.first);
|
||||
return Compare::operator()(lhs.first, rhs.first);
|
||||
}
|
||||
|
||||
pair_value_compare()
|
||||
{
|
||||
}
|
||||
|
||||
pair_value_compare(pair_value_compare const& other)
|
||||
: beast::detail::empty_base_optimization<Compare>(other)
|
||||
pair_value_compare(pair_value_compare const& other) : Compare(other)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend aged_ordered_container;
|
||||
|
||||
pair_value_compare(Compare const& compare)
|
||||
: beast::detail::empty_base_optimization<Compare>(compare)
|
||||
pair_value_compare(Compare const& compare) : Compare(compare)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Compares value_type against element, used in insert_check
|
||||
// VFALCO TODO hoist to remove template argument dependencies
|
||||
class KeyValueCompare
|
||||
: public beast::detail::empty_base_optimization<Compare>
|
||||
#ifdef _LIBCPP_VERSION
|
||||
,
|
||||
public std::binary_function<Key, element, bool>
|
||||
#endif
|
||||
class KeyValueCompare : public Compare
|
||||
{
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument = Key;
|
||||
using second_argument = element;
|
||||
using result_type = bool;
|
||||
#endif
|
||||
|
||||
KeyValueCompare() = default;
|
||||
|
||||
KeyValueCompare(Compare const& compare)
|
||||
: beast::detail::empty_base_optimization<Compare>(compare)
|
||||
KeyValueCompare(Compare const& compare) : Compare(compare)
|
||||
{
|
||||
}
|
||||
|
||||
// VFALCO NOTE WE might want only to enable these overloads
|
||||
// if Compare has is_transparent
|
||||
#if 0
|
||||
template <class K>
|
||||
bool operator() (K const& k, element const& e) const
|
||||
{
|
||||
return this->member() (k, extract (e.value));
|
||||
}
|
||||
|
||||
template <class K>
|
||||
bool operator() (element const& e, K const& k) const
|
||||
{
|
||||
return this->member() (extract (e.value), k);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
operator()(Key const& k, element const& e) const
|
||||
{
|
||||
return this->member()(k, extract(e.value));
|
||||
return Compare::operator()(k, extract(e.value));
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& e, Key const& k) const
|
||||
{
|
||||
return this->member()(extract(e.value), k);
|
||||
return Compare::operator()(extract(e.value), k);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& x, element const& y) const
|
||||
{
|
||||
return this->member()(extract(x.value), extract(y.value));
|
||||
return Compare::operator()(extract(x.value), extract(y.value));
|
||||
}
|
||||
|
||||
Compare&
|
||||
compare()
|
||||
{
|
||||
return beast::detail::empty_base_optimization<Compare>::member();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Compare const&
|
||||
compare() const
|
||||
{
|
||||
return beast::detail::empty_base_optimization<Compare>::member();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -148,115 +148,84 @@ private:
|
||||
};
|
||||
|
||||
// VFALCO TODO hoist to remove template argument dependencies
|
||||
class ValueHash : private beast::detail::empty_base_optimization<Hash>
|
||||
#ifdef _LIBCPP_VERSION
|
||||
,
|
||||
public std::unary_function<element, std::size_t>
|
||||
#endif
|
||||
class ValueHash : public Hash
|
||||
{
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using argument_type = element;
|
||||
using result_type = size_t;
|
||||
#endif
|
||||
|
||||
ValueHash()
|
||||
{
|
||||
}
|
||||
|
||||
ValueHash(Hash const& h)
|
||||
: beast::detail::empty_base_optimization<Hash>(h)
|
||||
ValueHash(Hash const& h) : Hash(h)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t
|
||||
operator()(element const& e) const
|
||||
{
|
||||
return this->member()(extract(e.value));
|
||||
return Hash::operator()(extract(e.value));
|
||||
}
|
||||
|
||||
Hash&
|
||||
hash_function()
|
||||
{
|
||||
return this->member();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Hash const&
|
||||
hash_function() const
|
||||
{
|
||||
return this->member();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Compares value_type against element, used in find/insert_check
|
||||
// VFALCO TODO hoist to remove template argument dependencies
|
||||
class KeyValueEqual
|
||||
: private beast::detail::empty_base_optimization<KeyEqual>
|
||||
#ifdef _LIBCPP_VERSION
|
||||
,
|
||||
public std::binary_function<Key, element, bool>
|
||||
#endif
|
||||
class KeyValueEqual : public KeyEqual
|
||||
{
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument_type = Key;
|
||||
using second_argument_type = element;
|
||||
using result_type = bool;
|
||||
#endif
|
||||
|
||||
KeyValueEqual()
|
||||
{
|
||||
}
|
||||
|
||||
KeyValueEqual(KeyEqual const& keyEqual)
|
||||
: beast::detail::empty_base_optimization<KeyEqual>(keyEqual)
|
||||
KeyValueEqual(KeyEqual const& keyEqual) : KeyEqual(keyEqual)
|
||||
{
|
||||
}
|
||||
|
||||
// VFALCO NOTE WE might want only to enable these overloads
|
||||
// if KeyEqual has is_transparent
|
||||
#if 0
|
||||
template <class K>
|
||||
bool operator() (K const& k, element const& e) const
|
||||
{
|
||||
return this->member() (k, extract (e.value));
|
||||
}
|
||||
|
||||
template <class K>
|
||||
bool operator() (element const& e, K const& k) const
|
||||
{
|
||||
return this->member() (extract (e.value), k);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
operator()(Key const& k, element const& e) const
|
||||
{
|
||||
return this->member()(k, extract(e.value));
|
||||
return KeyEqual::operator()(k, extract(e.value));
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& e, Key const& k) const
|
||||
{
|
||||
return this->member()(extract(e.value), k);
|
||||
return KeyEqual::operator()(extract(e.value), k);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& lhs, element const& rhs) const
|
||||
{
|
||||
return this->member()(extract(lhs.value), extract(rhs.value));
|
||||
return KeyEqual::operator()(extract(lhs.value), extract(rhs.value));
|
||||
}
|
||||
|
||||
KeyEqual&
|
||||
key_eq()
|
||||
{
|
||||
return this->member();
|
||||
return *this;
|
||||
}
|
||||
|
||||
KeyEqual const&
|
||||
key_eq() const
|
||||
{
|
||||
return this->member();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ public:
|
||||
Keylet const& key,
|
||||
std::function<void(std::shared_ptr<SLE> const&)> const& describe)
|
||||
{
|
||||
if (key.type != ltOFFER)
|
||||
if (key.type != ltOFFER && key.type != ltOPTION_OFFER)
|
||||
{
|
||||
assert(!"Only Offers are appended to book directories. "
|
||||
"Call dirInsert() instead.");
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/ledger/detail/ApplyViewBase.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
|
||||
// #include <ripple/app/paths/impl/AmountSpec.h>
|
||||
// #include <ripple/ledger/PaymentSandbox.h>
|
||||
// #include <ripple/ledger/View.h>
|
||||
// #include <ripple/protocol/Feature.h>
|
||||
// #include <ripple/protocol/SField.h>
|
||||
// #include <ripple/protocol/STAccount.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace detail {
|
||||
|
||||
@@ -91,17 +91,10 @@ private:
|
||||
using value_type = map_type::value_type;
|
||||
|
||||
struct Transform
|
||||
#ifdef _LIBCPP_VERSION
|
||||
: std::unary_function<
|
||||
map_type::right_map::const_iterator::value_type const&,
|
||||
beast::IP::Endpoint const&>
|
||||
#endif
|
||||
{
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument_type =
|
||||
map_type::right_map::const_iterator::value_type const&;
|
||||
using result_type = beast::IP::Endpoint const&;
|
||||
#endif
|
||||
|
||||
explicit Transform() = default;
|
||||
|
||||
|
||||
@@ -69,14 +69,9 @@ public:
|
||||
public:
|
||||
// Iterator transformation to extract the endpoint from Element
|
||||
struct Transform
|
||||
#ifdef _LIBCPP_VERSION
|
||||
: public std::unary_function<Element, Endpoint>
|
||||
#endif
|
||||
{
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument = Element;
|
||||
using result_type = Endpoint;
|
||||
#endif
|
||||
|
||||
explicit Transform() = default;
|
||||
|
||||
@@ -239,15 +234,9 @@ public:
|
||||
|
||||
template <bool IsConst>
|
||||
struct Transform
|
||||
#ifdef _LIBCPP_VERSION
|
||||
: public std::
|
||||
unary_function<typename lists_type::value_type, Hop<IsConst>>
|
||||
#endif
|
||||
{
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument = typename lists_type::value_type;
|
||||
using result_type = Hop<IsConst>;
|
||||
#endif
|
||||
|
||||
explicit Transform() = default;
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace detail {
|
||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
||||
// the actual number of amendments. A LogicError on startup will verify this.
|
||||
static constexpr std::size_t numFeatures = 70;
|
||||
static constexpr std::size_t numFeatures = 71;
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -358,6 +358,7 @@ extern uint256 const fixXahauV2;
|
||||
extern uint256 const featureRemit;
|
||||
extern uint256 const featureZeroB2M;
|
||||
extern uint256 const fixNSDelete;
|
||||
extern uint256 const featureOptions;
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -297,6 +297,27 @@ import_vlseq(PublicKey const& key) noexcept;
|
||||
Keylet
|
||||
uritoken(AccountID const& issuer, Blob const& uri);
|
||||
|
||||
Keylet
|
||||
broker() noexcept;;
|
||||
|
||||
Keylet
|
||||
brokerStateDir(AccountID const& id, AccountID const& account) noexcept;
|
||||
|
||||
Keylet
|
||||
brokerState(AccountID const& id, AccountID const& issuer, uint256 const& ns) noexcept;
|
||||
|
||||
Keylet
|
||||
option(AccountID const& issuer, std::uint32_t expiration) noexcept;
|
||||
|
||||
Keylet
|
||||
optionBook(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration) noexcept;
|
||||
|
||||
Keylet
|
||||
optionOffer(AccountID const& id, std::uint32_t seq) noexcept;
|
||||
|
||||
Keylet
|
||||
optionQuality(Keylet const& k, std::uint64_t q) noexcept;
|
||||
|
||||
} // namespace keylet
|
||||
|
||||
// Everything below is deprecated and should be removed in favor of keylets:
|
||||
@@ -311,6 +332,18 @@ getQualityNext(uint256 const& uBase);
|
||||
std::uint64_t
|
||||
getQuality(uint256 const& uBase);
|
||||
|
||||
uint256
|
||||
getOptionBookBase(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration);
|
||||
|
||||
uint256
|
||||
getOptionQualityNext(uint256 const& uBase);
|
||||
|
||||
// VFALCO This name could be better
|
||||
std::uint64_t
|
||||
getOptionQuality(uint256 const& uBase);
|
||||
|
||||
/** The initial directory page for a specific quality */
|
||||
|
||||
uint256
|
||||
getTicketIndex(AccountID const& account, std::uint32_t uSequence);
|
||||
|
||||
|
||||
@@ -254,6 +254,18 @@ enum LedgerEntryType : std::uint16_t
|
||||
\sa keylet::emitted
|
||||
*/
|
||||
ltEMITTED_TXN = 'E',
|
||||
|
||||
/** A ledger object
|
||||
|
||||
\sa keylet::broker_state
|
||||
*/
|
||||
ltOPTION = 'X',
|
||||
|
||||
/** A ledger object
|
||||
|
||||
\sa keylet::broker_state
|
||||
*/
|
||||
ltOPTION_OFFER = 'y',
|
||||
};
|
||||
// clang-format off
|
||||
|
||||
@@ -315,6 +327,12 @@ enum LedgerSpecificFlags {
|
||||
|
||||
// ltURI_TOKEN
|
||||
lsfBurnable = 0x00000001, // True, issuer can burn the token
|
||||
|
||||
// ltOPTION_OFFER
|
||||
lsfType = 0x00010000,
|
||||
lsfAction = 0x00020000,
|
||||
lsfPosition = 0x00040000,
|
||||
lsfCovered = 0x00080000,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
103
src/ripple/protocol/Option.h
Normal file
103
src/ripple/protocol/Option.h
Normal file
@@ -0,0 +1,103 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PROTOCOL_OPTION_H_INCLUDED
|
||||
#define RIPPLE_PROTOCOL_OPTION_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/CountedObject.h>
|
||||
#include <ripple/protocol/Issue.h>
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Option final : public CountedObject<Option>
|
||||
{
|
||||
public:
|
||||
Issue issue;
|
||||
uint64_t strike;
|
||||
uint32_t expiration;
|
||||
|
||||
Option()
|
||||
{
|
||||
}
|
||||
|
||||
Option(
|
||||
Issue issue_,
|
||||
uint64_t strike_,
|
||||
uint32_t expiration_)
|
||||
: issue(issue_)
|
||||
, strike(strike_)
|
||||
, expiration(expiration_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::string
|
||||
to_string(Option const& option);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, Option const& x);
|
||||
|
||||
template <class Hasher>
|
||||
void
|
||||
hash_append(Hasher& h, Option const& o)
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, o.issue, o.strike, o.expiration);
|
||||
}
|
||||
|
||||
/** Equality comparison. */
|
||||
/** @{ */
|
||||
[[nodiscard]] inline constexpr bool
|
||||
operator==(Option const& lhs, Option const& rhs)
|
||||
{
|
||||
return (lhs.issue == rhs.issue) && (lhs.strike == rhs.strike) && (lhs.expiration == rhs.expiration);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Strict weak ordering. */
|
||||
/** @{ */
|
||||
[[nodiscard]] inline constexpr std::weak_ordering
|
||||
operator<=>(Option const& lhs, Option const& rhs)
|
||||
{
|
||||
if (auto const c{lhs.strike <=> rhs.strike}; c != 0)
|
||||
return c;
|
||||
return lhs.strike <=> rhs.strike;
|
||||
}
|
||||
/** @} */
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace boost {
|
||||
|
||||
template <>
|
||||
struct hash<ripple::Option> : std::hash<ripple::Option>
|
||||
{
|
||||
explicit hash() = default;
|
||||
|
||||
using Base = std::hash<ripple::Option>;
|
||||
// VFALCO NOTE broken in vs2012
|
||||
// using Base::Base; // inherit ctors
|
||||
};
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -171,8 +171,7 @@ STValidation::STValidation(
|
||||
{
|
||||
if (checkSignature && !isValid())
|
||||
{
|
||||
JLOG(debugLog().error()) << "Invalid signature in validation: "
|
||||
<< getJson(JsonOptions::none);
|
||||
JLOG(debugLog().error()) << "Invalid signature in validation";
|
||||
Throw<std::runtime_error>("Invalid signature in validation");
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,21 @@ enum ClaimRewardFlags : uint32_t {
|
||||
tfOptOut = 0x00000001,
|
||||
};
|
||||
|
||||
// OptionCreate flags:
|
||||
enum OptionCreateFlags : uint32_t {
|
||||
tfType = 0x00010000,
|
||||
tfAction = 0x00020000,
|
||||
tfPosition = 0x00040000,
|
||||
};
|
||||
constexpr std::uint32_t tfOptionCreateMask =
|
||||
~(tfUniversal | tfType | tfAction | tfPosition);
|
||||
|
||||
// OptionExpire flags:
|
||||
enum OptionExpireFlags : uint32_t {
|
||||
tfOptionExpire = 0x00010000,
|
||||
};
|
||||
constexpr std::uint32_t tfOptionExpireMask = ~(tfUniversal | tfOptionExpire);
|
||||
|
||||
// clang-format on
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -185,6 +185,9 @@ enum TxType : std::uint16_t
|
||||
ttUNL_MODIFY = 102,
|
||||
ttEMIT_FAILURE = 103,
|
||||
ttUNL_REPORT = 104,
|
||||
ttOPTION_LIST = 105,
|
||||
ttOPTION_CREATE = 106,
|
||||
ttOPTION_EXECUTE = 107,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -464,6 +464,7 @@ REGISTER_FIX (fixXahauV2, Supported::yes, VoteBehavior::De
|
||||
REGISTER_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(ZeroB2M, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FIX (fixNSDelete, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(Options, Supported::yes, VoteBehavior::DefaultNo);
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
|
||||
@@ -72,6 +72,9 @@ enum class LedgerNameSpace : std::uint16_t {
|
||||
URI_TOKEN = 'U',
|
||||
IMPORT_VLSEQ = 'I',
|
||||
UNL_REPORT = 'R',
|
||||
OPTION = 'X',
|
||||
OPTION_DIR = 'Y',
|
||||
OPTION_OFFER = 'y',
|
||||
|
||||
// No longer used or supported. Left here to reserve the space
|
||||
// to avoid accidental reuse.
|
||||
@@ -120,6 +123,33 @@ getQuality(uint256 const& uBase)
|
||||
return boost::endian::big_to_native(((std::uint64_t*)uBase.end())[-1]);
|
||||
}
|
||||
|
||||
uint256
|
||||
getOptionBookBase(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration)
|
||||
{
|
||||
auto const index = indexHash(
|
||||
LedgerNameSpace::BOOK_DIR,
|
||||
issuer, currency, strike, expiration);
|
||||
|
||||
// Return with quality 0.
|
||||
auto k = keylet::optionQuality({ltDIR_NODE, index}, 0);
|
||||
return k.key;
|
||||
}
|
||||
|
||||
uint256
|
||||
getOptionQualityNext(uint256 const& uBase)
|
||||
{
|
||||
static constexpr uint256 nextq(
|
||||
"0000000000000000000000000000000000000000000000010000000000000000");
|
||||
return uBase + nextq;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
getOptionQuality(uint256 const& uBase)
|
||||
{
|
||||
// VFALCO [base_uint] This assumes a certain storage format
|
||||
return boost::endian::big_to_native(((std::uint64_t*)uBase.end())[-1]);
|
||||
}
|
||||
|
||||
uint256
|
||||
getTicketIndex(AccountID const& account, std::uint32_t ticketSeq)
|
||||
{
|
||||
@@ -443,6 +473,42 @@ uritoken(AccountID const& issuer, Blob const& uri)
|
||||
LedgerNameSpace::URI_TOKEN, issuer, Slice{uri.data(), uri.size()})};
|
||||
}
|
||||
|
||||
Keylet
|
||||
option(AccountID const& issuer, std::uint32_t expiration) noexcept
|
||||
{
|
||||
return {ltOPTION,indexHash(LedgerNameSpace::OPTION, issuer, expiration)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
optionBook(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration) noexcept
|
||||
{
|
||||
return {ltDIR_NODE, getOptionBookBase(issuer, currency, strike, expiration)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
optionQuality(Keylet const& k, std::uint64_t q) noexcept
|
||||
{
|
||||
assert(k.type == ltDIR_NODE);
|
||||
|
||||
// Indexes are stored in big endian format: they print as hex as stored.
|
||||
// Most significant bytes are first and the least significant bytes
|
||||
// represent adjacent entries. We place the quality, in big endian format,
|
||||
// in the 8 right most bytes; this way, incrementing goes to the next entry
|
||||
// for indexes.
|
||||
uint256 x = k.key;
|
||||
|
||||
// FIXME This is ugly and we can and should do better...
|
||||
((std::uint64_t*)x.end())[-1] = boost::endian::native_to_big(q);
|
||||
|
||||
return {ltDIR_NODE, x};
|
||||
}
|
||||
|
||||
Keylet
|
||||
optionOffer(AccountID const& id, std::uint32_t seq) noexcept
|
||||
{
|
||||
return {ltOPTION_OFFER, indexHash(LedgerNameSpace::OPTION_OFFER, id, seq)};
|
||||
}
|
||||
|
||||
} // namespace keylet
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -84,6 +84,8 @@ LedgerFormats::LedgerFormats()
|
||||
{sfIndexNext, soeOPTIONAL},
|
||||
{sfIndexPrevious, soeOPTIONAL},
|
||||
{sfNFTokenID, soeOPTIONAL},
|
||||
{sfBaseFee, soeOPTIONAL},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -363,6 +365,35 @@ LedgerFormats::LedgerFormats()
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::Option,
|
||||
ltOPTION,
|
||||
{
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfExpiration, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED}
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::OptionOffer,
|
||||
ltOPTION_OFFER,
|
||||
{
|
||||
{sfOwner, soeREQUIRED},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfOfferID, soeREQUIRED}, // OptionID
|
||||
{sfAmount, soeREQUIRED}, // Locked Amount
|
||||
{sfTakerPays, soeREQUIRED}, // Premium
|
||||
{sfQualityIn, soeREQUIRED}, // Quantity
|
||||
{sfQualityOut, soeREQUIRED}, // To Seal
|
||||
{sfBookDirectory, soeREQUIRED},
|
||||
{sfBookNode, soeREQUIRED},
|
||||
{sfInvoiceID, soeOPTIONAL}, // MatchID
|
||||
{sfCheckID, soeOPTIONAL}, // SwapID
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED}
|
||||
},
|
||||
commonFields);
|
||||
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
|
||||
37
src/ripple/protocol/impl/Option.cpp
Normal file
37
src/ripple/protocol/impl/Option.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Option.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
std::string
|
||||
to_string(Option const& option)
|
||||
{
|
||||
return to_string(option.issue) + "/" + to_string(option.strike) + "/" + to_string(option.expiration);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, Option const& x)
|
||||
{
|
||||
os << to_string(x);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
@@ -456,6 +456,35 @@ TxFormats::TxFormats()
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::OptionList,
|
||||
ttOPTION_LIST,
|
||||
{
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfExpiration, soeREQUIRED},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::OptionCreate,
|
||||
ttOPTION_CREATE,
|
||||
{
|
||||
{sfOfferID, soeREQUIRED}, // Option ID
|
||||
{sfAmount, soeREQUIRED}, // Premium
|
||||
{sfQualityIn, soeREQUIRED}, // Quantity
|
||||
{sfInvoiceID, soeOPTIONAL}, // Swap ID
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::OptionExecute,
|
||||
ttOPTION_EXECUTE,
|
||||
{
|
||||
{sfOfferID, soeREQUIRED},
|
||||
{sfInvoiceID, soeOPTIONAL},
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
}
|
||||
|
||||
TxFormats const&
|
||||
|
||||
@@ -111,8 +111,13 @@ JSS(Offer); // ledger type.
|
||||
JSS(OfferCancel); // transaction type.
|
||||
JSS(OfferCreate); // transaction type.
|
||||
JSS(OfferSequence); // field.
|
||||
JSS(Option); // ledger type.
|
||||
JSS(OptionCreate); // transaction type.
|
||||
JSS(OptionExecute); // transaction type.
|
||||
JSS(OptionList); // transaction type.
|
||||
JSS(OptionOffer); // ledger type.
|
||||
JSS(Paths); // in/out: TransactionSign
|
||||
JSS(PayChannel); // ledger type.
|
||||
JSS(PayChannel); // ledger type.E
|
||||
JSS(Payment); // transaction type.
|
||||
JSS(PaymentChannelClaim); // transaction type.
|
||||
JSS(PaymentChannelCreate); // transaction type.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/app/reporting/P2pProxy.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/Role.h>
|
||||
|
||||
@@ -398,7 +398,7 @@ SHAMapInnerNode::canonicalizeChild(
|
||||
void
|
||||
SHAMapInnerNode::invariants(bool is_root) const
|
||||
{
|
||||
unsigned count = 0;
|
||||
[[maybe_unused]] unsigned count = 0;
|
||||
auto [numAllocated, hashes, children] =
|
||||
hashesAndChildren_.getHashesAndChildren();
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
459
src/test/app/Option_test.cpp
Normal file
459
src/test/app/Option_test.cpp
Normal file
@@ -0,0 +1,459 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2016 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct Option_test : public beast::unit_test::suite
|
||||
{
|
||||
// testDebug("PRE", env, { alice, bob }, {});
|
||||
void
|
||||
testDebug(
|
||||
std::string const& testNumber,
|
||||
jtx::Env const& env,
|
||||
std::vector<jtx::Account> const& accounts,
|
||||
std::vector<jtx::IOU> const& ious)
|
||||
{
|
||||
std::cout << "DEBUG: " << testNumber << "\n";
|
||||
for (std::size_t a = 0; a < accounts.size(); ++a)
|
||||
{
|
||||
auto const bal = env.balance(accounts[a]);
|
||||
std::cout << "account: " << accounts[a].human() << "BAL: " << bal
|
||||
<< "\n";
|
||||
for (std::size_t i = 0; i < ious.size(); ++i)
|
||||
{
|
||||
auto const iouBal = env.balance(accounts[a], ious[i]);
|
||||
std::cout << "account: " << accounts[a].human()
|
||||
<< "IOU: " << iouBal << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static STAmount
|
||||
lockedValue(
|
||||
jtx::Env const& env,
|
||||
jtx::Account const& account,
|
||||
std::uint32_t const& seq)
|
||||
{
|
||||
auto const sle = env.le(keylet::optionOffer(account, seq));
|
||||
if (sle->isFieldPresent(sfAmount))
|
||||
return (*sle)[sfAmount];
|
||||
return STAmount(0);
|
||||
}
|
||||
|
||||
Json::Value
|
||||
optionlist(
|
||||
jtx::Account const& account,
|
||||
std::uint32_t expiration,
|
||||
STAmount const& amount)
|
||||
{
|
||||
using namespace jtx;
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::OptionList;
|
||||
jv[jss::Account] = account.human();
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::none);
|
||||
jv[sfExpiration.jsonName] = expiration;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
optioncreate(
|
||||
jtx::Account const& account,
|
||||
uint256 const& optionId,
|
||||
uint32_t const& quantity,
|
||||
STAmount const& amount)
|
||||
{
|
||||
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;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
optionexecute(
|
||||
jtx::Account const& account,
|
||||
uint256 const& optionId,
|
||||
uint256 const& offerId)
|
||||
{
|
||||
using namespace jtx;
|
||||
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);
|
||||
;
|
||||
return jv;
|
||||
}
|
||||
|
||||
static uint256
|
||||
getOptionIndex(
|
||||
AccountID const& issuer,
|
||||
std::uint32_t expiration)
|
||||
{
|
||||
return keylet::option(issuer, expiration).key;
|
||||
}
|
||||
|
||||
static uint256
|
||||
getOfferIndex(AccountID const& account, std::uint32_t sequence)
|
||||
{
|
||||
return keylet::optionOffer(account, sequence).key;
|
||||
}
|
||||
|
||||
void
|
||||
testBookBuy(FeatureBitset features)
|
||||
{
|
||||
testcase("book buy");
|
||||
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
test::jtx::Env env{*this, network::makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const gw = Account("gw");
|
||||
IOU const USD(gw["USD"]);
|
||||
|
||||
env.fund(XRP(1000), gw, alice, bob);
|
||||
env.close();
|
||||
env.trust(USD(100000), alice, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(10000)));
|
||||
env(pay(gw, bob, USD(10000)));
|
||||
env.close();
|
||||
|
||||
// Alice offers to sell 100 XRP for 110 USD.
|
||||
// Create an sell: TakerPays 100, TakerGets 110/USD
|
||||
env(offer(alice, XRP(100), USD(110)), txflags(tfSell)); // <- Best
|
||||
// Alice offers to sell 100 XRP for 100 USD.
|
||||
// Create an sell: TakerPays 100, TakerGets 100/USD
|
||||
env(offer(alice, XRP(100), USD(100)), txflags(tfSell));
|
||||
// Alice offers to sell 100 XRP for 90 USD.
|
||||
// Create an sell: TakerPays 100, TakerGets 90/USD
|
||||
env(offer(alice, XRP(100), USD(90)), txflags(tfSell));
|
||||
// Alice offers to sell 100 XRP for 80 USD.
|
||||
// Create an sell: TakerPays 100, TakerGets 80/USD
|
||||
env(offer(alice, XRP(100), USD(80)), txflags(tfSell));
|
||||
// Alice offers to sell 100 XRP for 70 USD.
|
||||
// Create an sell: TakerPays 100, TakerGets 70/USD
|
||||
env(offer(alice, XRP(100), USD(70)), txflags(tfSell)); // <- Worst
|
||||
env.close();
|
||||
|
||||
// Bob offers to buy 110 USD for 100 XRP.
|
||||
// env(offer(bob, USD(110), XRP(100))); // <- Best
|
||||
|
||||
Book const book1{
|
||||
xrpIssue(),
|
||||
USD.issue(),
|
||||
};
|
||||
|
||||
const uint256 uBookBase1 = getBookBase(book1);
|
||||
const uint256 uBookEnd1 = getQualityNext(uBookBase1);
|
||||
auto view1 = env.closed();
|
||||
auto key1 = view1->succ(uBookBase1, uBookEnd1);
|
||||
if (key1)
|
||||
{
|
||||
auto sleOfferDir1 = view1->read(keylet::page(key1.value()));
|
||||
uint256 offerIndex1;
|
||||
unsigned int bookEntry1;
|
||||
cdirFirst(
|
||||
*view1,
|
||||
sleOfferDir1->key(),
|
||||
sleOfferDir1,
|
||||
bookEntry1,
|
||||
offerIndex1);
|
||||
auto sleOffer1 = view1->read(keylet::offer(offerIndex1));
|
||||
auto const dir1 =
|
||||
to_string(sleOffer1->getFieldH256(sfBookDirectory));
|
||||
auto const uTipIndex1 = sleOfferDir1->key();
|
||||
STAmount dirRate1 = amountFromQuality(getQuality(uTipIndex1));
|
||||
auto const rate1 = dirRate1 / 1'000'000;
|
||||
std::cout << "dirRate1: " << dirRate1 << "\n";
|
||||
std::cout << "rate1: " << rate1 << "\n";
|
||||
std::cout << "rate1=: " << (100 / rate1) << "\n";
|
||||
BEAST_EXPECT(100 / rate1 == 110);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testBookSell(FeatureBitset features)
|
||||
{
|
||||
testcase("book sell");
|
||||
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
test::jtx::Env env{*this, network::makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const gw = Account("gw");
|
||||
IOU const USD(gw["USD"]);
|
||||
|
||||
env.fund(XRP(1000), gw, alice, bob);
|
||||
env.close();
|
||||
env.trust(USD(100000), alice, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(10000)));
|
||||
env(pay(gw, bob, USD(10000)));
|
||||
env.close();
|
||||
|
||||
// Bob offers to buy 70 XRP for 100 USD.
|
||||
// Create an buy: TakerPays 100/USD, TakerGets 70
|
||||
env(offer(bob, USD(100), XRP(70))); // <- Worst
|
||||
// Bob offers to buy 80 XRP for 100 USD.
|
||||
// Create an buy: TakerPays 100/USD, TakerGets 80
|
||||
env(offer(bob, USD(100), XRP(80)));
|
||||
// Bob offers to buy 90 XRP for 100 USD.
|
||||
// Create an buy: TakerPays 100/USD, TakerGets 90
|
||||
env(offer(bob, USD(100), XRP(90)));
|
||||
// Bob offers to buy 100 XRP for 100 USD.
|
||||
// Create an buy: TakerPays 100/USD, TakerGets 100
|
||||
env(offer(bob, USD(100), XRP(100)));
|
||||
// Bob offers to buy 110 XRP for 100 USD.
|
||||
// Create an buy: TakerPays 100/USD, TakerGets 110
|
||||
env(offer(bob, USD(100), XRP(110))); // <- Best
|
||||
env.close();
|
||||
|
||||
// Alice offers to sell 110 XRP for 100 USD.
|
||||
// env(offer(alice, XRP(110), USD(100))); // <- Best
|
||||
|
||||
Book const bookOpp{
|
||||
USD.issue(),
|
||||
xrpIssue(),
|
||||
};
|
||||
|
||||
const uint256 uBookBaseOpp = getBookBase(bookOpp);
|
||||
const uint256 uBookEndOpp = getQualityNext(uBookBaseOpp);
|
||||
auto view = env.closed();
|
||||
auto key = view->succ(uBookBaseOpp, uBookEndOpp);
|
||||
if (key)
|
||||
{
|
||||
auto sleOfferDir = view->read(keylet::page(key.value()));
|
||||
uint256 offerIndex;
|
||||
unsigned int bookEntry;
|
||||
cdirFirst(
|
||||
*view, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex);
|
||||
auto sleOffer = view->read(keylet::offer(offerIndex));
|
||||
auto const dir = to_string(sleOffer->getFieldH256(sfBookDirectory));
|
||||
auto const uTipIndex = sleOfferDir->key();
|
||||
STAmount dirRate = amountFromQuality(getQuality(uTipIndex));
|
||||
auto const rate = dirRate * 1'000'000;
|
||||
std::cout << "dirRate: " << dirRate << "\n";
|
||||
std::cout << "rate: " << rate << "\n";
|
||||
std::cout << "rate=: " << (100 / rate) << "\n";
|
||||
BEAST_EXPECT(100 / rate == 110);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testOptionBookBuy(FeatureBitset features)
|
||||
{
|
||||
testcase("option book buy");
|
||||
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
test::jtx::Env env{*this, network::makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const broker = Account("broker");
|
||||
auto const gw = Account("gw");
|
||||
IOU const USD(gw["USD"]);
|
||||
|
||||
env.fund(XRP(1000), gw, alice, bob, broker);
|
||||
env.close();
|
||||
env.trust(USD(100000), alice, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, USD(10000)));
|
||||
env(pay(gw, bob, USD(10000)));
|
||||
env.close();
|
||||
|
||||
AccountID const zeroAcct{AccountID{}};
|
||||
auto const _exp = env.now() + 1s;
|
||||
auto const expiration = _exp.time_since_epoch().count();
|
||||
uint256 const optionId{getOptionIndex(zeroAcct, expiration)};
|
||||
env(optionlist(alice, expiration, XRP(10)), ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
env(optioncreate(alice, optionId, 100, XRP(70)),
|
||||
txflags(tfAction),
|
||||
ter(tesSUCCESS));
|
||||
env(optioncreate(alice, optionId, 100, XRP(80)),
|
||||
txflags(tfAction),
|
||||
ter(tesSUCCESS));
|
||||
env(optioncreate(alice, optionId, 100, XRP(90)),
|
||||
txflags(tfAction),
|
||||
ter(tesSUCCESS));
|
||||
env(optioncreate(alice, optionId, 100, XRP(100)),
|
||||
txflags(tfAction),
|
||||
ter(tesSUCCESS));
|
||||
env(optioncreate(alice, optionId, 100, XRP(110)),
|
||||
txflags(tfAction),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
STAmount strikePrice = XRP(10);
|
||||
const uint256 uBookBaseOpp = getOptionBookBase(
|
||||
strikePrice.getIssuer(),
|
||||
strikePrice.getCurrency(),
|
||||
strikePrice.mantissa(),
|
||||
expiration);
|
||||
std::cout << "BOOK BASE: " << uBookBaseOpp << "\n";
|
||||
const uint256 uBookEndOpp = getOptionQualityNext(uBookBaseOpp);
|
||||
std::cout << "BOOK BASE END: " << uBookEndOpp << "\n";
|
||||
auto view = env.closed();
|
||||
auto key = view->succ(uBookBaseOpp, uBookEndOpp);
|
||||
if (key)
|
||||
{
|
||||
auto sleOfferDir = view->read(keylet::page(key.value()));
|
||||
uint256 offerIndex;
|
||||
unsigned int bookEntry;
|
||||
cdirFirst(
|
||||
*view, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex);
|
||||
auto sleOffer = view->read(keylet::unchecked(offerIndex));
|
||||
STAmount premium = sleOffer->getFieldAmount(sfAmount);
|
||||
auto const dir = to_string(sleOffer->getFieldH256(sfBookDirectory));
|
||||
std::cout << "dir: " << dir << "\n";
|
||||
auto const uTipIndex = sleOfferDir->key();
|
||||
auto const optionQuality = getOptionQuality(uTipIndex);
|
||||
std::cout << "optionQuality: " << optionQuality << "\n";
|
||||
STAmount dirRate = STAmount(premium.issue(), optionQuality);
|
||||
std::cout << "dirRate: " << dirRate << "\n";
|
||||
// BEAST_EXPECT(100 / rate == 110);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testEnabled(FeatureBitset features)
|
||||
{
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
testcase("enabled");
|
||||
|
||||
test::jtx::Env env{*this, network::makeNetworkConfig(21337), features};
|
||||
// test::jtx::Env env{
|
||||
// *this,
|
||||
// network::makeNetworkConfig(21337),
|
||||
// features,
|
||||
// nullptr,
|
||||
// beast::severities::kWarning};
|
||||
|
||||
auto const feeDrops = env.current()->fees().base;
|
||||
|
||||
auto const writer = Account("alice");
|
||||
auto const buyer = Account("bob");
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
env.fund(XRP(100000), writer, buyer, gw);
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(0 == 0);
|
||||
AccountID const zeroAcct{AccountID{}};
|
||||
|
||||
auto const _exp = env.now() + 1s;
|
||||
auto const expiration = _exp.time_since_epoch().count();
|
||||
uint256 const optionId{getOptionIndex(zeroAcct, expiration)};
|
||||
|
||||
auto preWriter = env.balance(writer);
|
||||
auto preBuyer = env.balance(buyer);
|
||||
|
||||
auto const strikePrice = 10;
|
||||
env(optionlist(writer, expiration, XRP(strikePrice)),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(env.balance(writer) == preWriter - feeDrops);
|
||||
|
||||
// Call - Sell - Open
|
||||
auto const premium = 1;
|
||||
auto const quantity = 100;
|
||||
auto const seq = env.seq(writer);
|
||||
env(optioncreate(writer, optionId, quantity, XRP(premium)),
|
||||
txflags(tfAction),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(
|
||||
env.balance(writer) ==
|
||||
preWriter - (feeDrops * 2) - XRP(quantity));
|
||||
BEAST_EXPECT(
|
||||
lockedValue(env, writer, seq) == XRP(quantity));
|
||||
|
||||
preWriter = env.balance(writer);
|
||||
preBuyer = env.balance(buyer);
|
||||
|
||||
// Call - Buy - Open
|
||||
auto const seq1 = env.seq(buyer);
|
||||
env(optioncreate(buyer, optionId, quantity, XRP(premium)),
|
||||
ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
uint256 const offerId{getOfferIndex(buyer, seq1)};
|
||||
BEAST_EXPECT(
|
||||
env.balance(buyer) ==
|
||||
preBuyer - feeDrops - XRP(quantity * premium));
|
||||
BEAST_EXPECT(
|
||||
env.balance(writer) ==
|
||||
preWriter + XRP(quantity * premium));
|
||||
|
||||
preWriter = env.balance(writer);
|
||||
preBuyer = env.balance(buyer);
|
||||
|
||||
// Execute Option
|
||||
env(optionexecute(buyer, optionId, offerId), ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(
|
||||
env.balance(buyer) ==
|
||||
preBuyer - feeDrops - XRP(quantity * strikePrice) + XRP(quantity));
|
||||
BEAST_EXPECT(
|
||||
env.balance(writer) ==
|
||||
preWriter + XRP(quantity * strikePrice));
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace test::jtx;
|
||||
auto const sa = supported_amendments();
|
||||
// testBookBuy(sa);
|
||||
// testBookSell(sa);
|
||||
// testOptionBookBuy(sa);
|
||||
testEnabled(sa);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Option, app, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx/escrow.h>
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx/paychan.h>
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <ripple/app/misc/AmendmentTable.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/json/json_reader.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
Reference in New Issue
Block a user