mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-13 23:25:48 +00:00
Compare commits
8 Commits
patch-ctid
...
options-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bae099ee63 | ||
|
|
dddc7a6f72 | ||
|
|
f4f3fc0231 | ||
|
|
0c9c93d861 | ||
|
|
2c5a9bd952 | ||
|
|
c77d53b810 | ||
|
|
ba2422929a | ||
|
|
625ca21d2f |
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_path": ".clang-format",
|
||||||
"C_Cpp.clang_format_fallbackStyle": "{ ColumnLimit: 0 }",
|
"C_Cpp.clang_format_fallbackStyle": "{ ColumnLimit: 0 }",
|
||||||
"[cpp]":{
|
"[cpp]":{
|
||||||
"editor.wordBasedSuggestions": false,
|
"editor.wordBasedSuggestions": "off",
|
||||||
"editor.suggest.insertMode": "replace",
|
"editor.suggest.insertMode": "replace",
|
||||||
"editor.semanticHighlighting.enabled": true,
|
"editor.semanticHighlighting.enabled": true,
|
||||||
"editor.tabSize": 4,
|
"editor.tabSize": 4,
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ target_sources (xrpl_core PRIVATE
|
|||||||
src/ripple/protocol/impl/Issue.cpp
|
src/ripple/protocol/impl/Issue.cpp
|
||||||
src/ripple/protocol/impl/Keylet.cpp
|
src/ripple/protocol/impl/Keylet.cpp
|
||||||
src/ripple/protocol/impl/LedgerFormats.cpp
|
src/ripple/protocol/impl/LedgerFormats.cpp
|
||||||
|
src/ripple/protocol/impl/Option.cpp
|
||||||
src/ripple/protocol/impl/PublicKey.cpp
|
src/ripple/protocol/impl/PublicKey.cpp
|
||||||
src/ripple/protocol/impl/Quality.cpp
|
src/ripple/protocol/impl/Quality.cpp
|
||||||
src/ripple/protocol/impl/Rate2.cpp
|
src/ripple/protocol/impl/Rate2.cpp
|
||||||
@@ -219,6 +220,7 @@ install (
|
|||||||
src/ripple/protocol/Keylet.h
|
src/ripple/protocol/Keylet.h
|
||||||
src/ripple/protocol/KnownFormats.h
|
src/ripple/protocol/KnownFormats.h
|
||||||
src/ripple/protocol/LedgerFormats.h
|
src/ripple/protocol/LedgerFormats.h
|
||||||
|
src/ripple/protocol/Option.h
|
||||||
src/ripple/protocol/Protocol.h
|
src/ripple/protocol/Protocol.h
|
||||||
src/ripple/protocol/PublicKey.h
|
src/ripple/protocol/PublicKey.h
|
||||||
src/ripple/protocol/Quality.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/NFTokenCreateOffer.cpp
|
||||||
src/ripple/app/tx/impl/NFTokenMint.cpp
|
src/ripple/app/tx/impl/NFTokenMint.cpp
|
||||||
src/ripple/app/tx/impl/OfferStream.cpp
|
src/ripple/app/tx/impl/OfferStream.cpp
|
||||||
|
src/ripple/app/tx/impl/OptionCreate.cpp
|
||||||
|
src/ripple/app/tx/impl/OptionExercise.cpp
|
||||||
|
src/ripple/app/tx/impl/OptionList.cpp
|
||||||
src/ripple/app/tx/impl/PayChan.cpp
|
src/ripple/app/tx/impl/PayChan.cpp
|
||||||
src/ripple/app/tx/impl/Payment.cpp
|
src/ripple/app/tx/impl/Payment.cpp
|
||||||
src/ripple/app/tx/impl/SetAccount.cpp
|
src/ripple/app/tx/impl/SetAccount.cpp
|
||||||
@@ -629,6 +634,7 @@ target_sources (rippled PRIVATE
|
|||||||
src/ripple/rpc/handlers/NFTOffers.cpp
|
src/ripple/rpc/handlers/NFTOffers.cpp
|
||||||
src/ripple/rpc/handlers/NodeToShard.cpp
|
src/ripple/rpc/handlers/NodeToShard.cpp
|
||||||
src/ripple/rpc/handlers/NoRippleCheck.cpp
|
src/ripple/rpc/handlers/NoRippleCheck.cpp
|
||||||
|
src/ripple/rpc/handlers/OptionBookOffers.cpp
|
||||||
src/ripple/rpc/handlers/OwnerInfo.cpp
|
src/ripple/rpc/handlers/OwnerInfo.cpp
|
||||||
src/ripple/rpc/handlers/PathFind.cpp
|
src/ripple/rpc/handlers/PathFind.cpp
|
||||||
src/ripple/rpc/handlers/PayChanClaim.cpp
|
src/ripple/rpc/handlers/PayChanClaim.cpp
|
||||||
@@ -732,6 +738,7 @@ if (tests)
|
|||||||
src/test/app/NFTokenBurn_test.cpp
|
src/test/app/NFTokenBurn_test.cpp
|
||||||
src/test/app/NFTokenDir_test.cpp
|
src/test/app/NFTokenDir_test.cpp
|
||||||
src/test/app/OfferStream_test.cpp
|
src/test/app/OfferStream_test.cpp
|
||||||
|
src/test/app/Option_test.cpp
|
||||||
src/test/app/Offer_test.cpp
|
src/test/app/Offer_test.cpp
|
||||||
src/test/app/OversizeMeta_test.cpp
|
src/test/app/OversizeMeta_test.cpp
|
||||||
src/test/app/Path_test.cpp
|
src/test/app/Path_test.cpp
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
#include <ripple/app/hook/applyHook.h>
|
#include <ripple/app/hook/applyHook.h>
|
||||||
#include <ripple/app/ledger/OpenLedger.h>
|
#include <ripple/app/ledger/OpenLedger.h>
|
||||||
#include <ripple/app/ledger/TransactionMaster.h>
|
#include <ripple/app/ledger/TransactionMaster.h>
|
||||||
|
#include <ripple/app/misc/HashRouter.h>
|
||||||
#include <ripple/app/misc/NetworkOPs.h>
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
#include <ripple/app/misc/Transaction.h>
|
#include <ripple/app/misc/Transaction.h>
|
||||||
#include <ripple/app/misc/TxQ.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/Log.h>
|
||||||
#include <ripple/basics/Slice.h>
|
#include <ripple/basics/Slice.h>
|
||||||
#include <ripple/protocol/ErrorCodes.h>
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
|
#include <ripple/protocol/TxFlags.h>
|
||||||
|
#include <ripple/protocol/st.h>
|
||||||
#include <ripple/protocol/tokens.h>
|
#include <ripple/protocol/tokens.h>
|
||||||
#include <boost/multiprecision/cpp_dec_float.hpp>
|
#include <boost/multiprecision/cpp_dec_float.hpp>
|
||||||
#include <any>
|
#include <any>
|
||||||
|
|||||||
@@ -85,9 +85,11 @@ OrderBookDB::update(std::shared_ptr<ReadView const> const& ledger)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decltype(optionBooks_) optionBooks;
|
||||||
decltype(allBooks_) allBooks;
|
decltype(allBooks_) allBooks;
|
||||||
decltype(xrpBooks_) xrpBooks;
|
decltype(xrpBooks_) xrpBooks;
|
||||||
|
|
||||||
|
optionBooks.reserve(optionBooks_.size());
|
||||||
allBooks.reserve(allBooks_.size());
|
allBooks.reserve(allBooks_.size());
|
||||||
xrpBooks.reserve(xrpBooks_.size());
|
xrpBooks.reserve(xrpBooks_.size());
|
||||||
|
|
||||||
@@ -161,6 +163,14 @@ OrderBookDB::addOrderBook(Book const& book)
|
|||||||
xrpBooks_.insert(book.in);
|
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
|
// return list of all orderbooks that want this issuerID and currencyID
|
||||||
std::vector<Book>
|
std::vector<Book>
|
||||||
OrderBookDB::getBooksByTakerPays(Issue const& issue)
|
OrderBookDB::getBooksByTakerPays(Issue const& issue)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <ripple/app/ledger/AcceptedLedgerTx.h>
|
#include <ripple/app/ledger/AcceptedLedgerTx.h>
|
||||||
#include <ripple/app/ledger/BookListeners.h>
|
#include <ripple/app/ledger/BookListeners.h>
|
||||||
#include <ripple/app/main/Application.h>
|
#include <ripple/app/main/Application.h>
|
||||||
|
#include <ripple/protocol/Option.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
@@ -40,6 +41,9 @@ public:
|
|||||||
void
|
void
|
||||||
addOrderBook(Book const&);
|
addOrderBook(Book const&);
|
||||||
|
|
||||||
|
void
|
||||||
|
addOptionOrderBook(Option const&);
|
||||||
|
|
||||||
/** @return a list of all orderbooks that want this issuerID and currencyID.
|
/** @return a list of all orderbooks that want this issuerID and currencyID.
|
||||||
*/
|
*/
|
||||||
std::vector<Book>
|
std::vector<Book>
|
||||||
@@ -55,6 +59,7 @@ public:
|
|||||||
|
|
||||||
BookListeners::pointer
|
BookListeners::pointer
|
||||||
getBookListeners(Book const&);
|
getBookListeners(Book const&);
|
||||||
|
|
||||||
BookListeners::pointer
|
BookListeners::pointer
|
||||||
makeBookListeners(Book const&);
|
makeBookListeners(Book const&);
|
||||||
|
|
||||||
@@ -71,6 +76,9 @@ private:
|
|||||||
// Maps order books by "issue in" to "issue out":
|
// Maps order books by "issue in" to "issue out":
|
||||||
hardened_hash_map<Issue, hardened_hash_set<Issue>> allBooks_;
|
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
|
// does an order book to XRP exist
|
||||||
hash_set<Issue> xrpBooks_;
|
hash_set<Issue> xrpBooks_;
|
||||||
|
|
||||||
|
|||||||
@@ -350,6 +350,15 @@ public:
|
|||||||
unsigned int iLimit,
|
unsigned int iLimit,
|
||||||
Json::Value const& jvMarker,
|
Json::Value const& jvMarker,
|
||||||
Json::Value& jvResult) override;
|
Json::Value& jvResult) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
getOptionBookPage(
|
||||||
|
std::shared_ptr<ReadView const>& lpLedger,
|
||||||
|
STAmount const&,
|
||||||
|
std::uint32_t const& expiration,
|
||||||
|
unsigned int iLimit,
|
||||||
|
Json::Value const& jvMarker,
|
||||||
|
Json::Value& jvResult) override;
|
||||||
|
|
||||||
// Ledger proposal/close functions.
|
// Ledger proposal/close functions.
|
||||||
bool
|
bool
|
||||||
@@ -4581,6 +4590,114 @@ NetworkOPsImp::getBookPage(
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
NetworkOPsImp::getOptionBookPage(
|
||||||
|
std::shared_ptr<ReadView const>& lpLedger,
|
||||||
|
STAmount const& strikePrice,
|
||||||
|
std::uint32_t const& expiration,
|
||||||
|
unsigned int iLimit,
|
||||||
|
Json::Value const& jvMarker,
|
||||||
|
Json::Value& jvResult)
|
||||||
|
{
|
||||||
|
Json::Value& jvOffers = (jvResult[jss::offers] = Json::Value(Json::arrayValue));
|
||||||
|
|
||||||
|
const uint256 uBookBase = getOptionBookBase(
|
||||||
|
strikePrice.getIssuer(),
|
||||||
|
strikePrice.getCurrency(),
|
||||||
|
strikePrice.mantissa(),
|
||||||
|
expiration);
|
||||||
|
const uint256 uBookEnd = getOptionQualityNext(uBookBase);
|
||||||
|
|
||||||
|
uint256 uTipIndex = uBookBase;
|
||||||
|
|
||||||
|
if (auto stream = m_journal.trace())
|
||||||
|
{
|
||||||
|
stream << "getBookPage:" << strikePrice;
|
||||||
|
stream << "getBookPage: uBookBase=" << uBookBase;
|
||||||
|
stream << "getBookPage: uBookEnd=" << uBookEnd;
|
||||||
|
stream << "getBookPage: uTipIndex=" << uTipIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadView const& view = *lpLedger;
|
||||||
|
|
||||||
|
bool bDone = false;
|
||||||
|
bool bDirectAdvance = true;
|
||||||
|
|
||||||
|
std::shared_ptr<SLE const> sleOfferDir;
|
||||||
|
uint256 offerIndex;
|
||||||
|
unsigned int uBookEntry;
|
||||||
|
STAmount saDirRate;
|
||||||
|
|
||||||
|
auto viewJ = app_.journal("View");
|
||||||
|
|
||||||
|
while (!bDone && iLimit-- > 0)
|
||||||
|
{
|
||||||
|
if (bDirectAdvance)
|
||||||
|
{
|
||||||
|
bDirectAdvance = false;
|
||||||
|
|
||||||
|
JLOG(m_journal.trace()) << "getBookPage: bDirectAdvance";
|
||||||
|
|
||||||
|
auto const ledgerIndex = view.succ(uTipIndex, uBookEnd);
|
||||||
|
if (ledgerIndex)
|
||||||
|
{
|
||||||
|
sleOfferDir = view.read(keylet::page(*ledgerIndex));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sleOfferDir.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sleOfferDir)
|
||||||
|
{
|
||||||
|
JLOG(m_journal.trace()) << "getBookPage: bDone";
|
||||||
|
bDone = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uTipIndex = sleOfferDir->key();
|
||||||
|
cdirFirst(view, uTipIndex, sleOfferDir, uBookEntry, offerIndex);
|
||||||
|
|
||||||
|
JLOG(m_journal.trace())
|
||||||
|
<< "getBookPage: uTipIndex=" << uTipIndex;
|
||||||
|
JLOG(m_journal.trace())
|
||||||
|
<< "getBookPage: offerIndex=" << offerIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bDone)
|
||||||
|
{
|
||||||
|
auto sleOffer = view.read(keylet::optionOffer(offerIndex));
|
||||||
|
|
||||||
|
if (sleOffer)
|
||||||
|
{
|
||||||
|
STAmount premium = sleOffer->getFieldAmount(sfAmount);
|
||||||
|
auto const optionQuality = getOptionQuality(uTipIndex);
|
||||||
|
STAmount saDirRate = STAmount(premium.issue(), optionQuality);
|
||||||
|
|
||||||
|
Json::Value jvOffer = sleOffer->getJson(JsonOptions::none);
|
||||||
|
|
||||||
|
Json::Value& jvOf = jvOffers.append(jvOffer);
|
||||||
|
jvOf[jss::quality] = saDirRate.getText();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JLOG(m_journal.warn()) << "Missing offer";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cdirNext(view, uTipIndex, sleOfferDir, uBookEntry, offerIndex))
|
||||||
|
{
|
||||||
|
bDirectAdvance = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JLOG(m_journal.trace())
|
||||||
|
<< "getBookPage: offerIndex=" << offerIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
NetworkOPsImp::collect_metrics()
|
NetworkOPsImp::collect_metrics()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -163,6 +163,15 @@ public:
|
|||||||
unsigned int iLimit,
|
unsigned int iLimit,
|
||||||
Json::Value const& jvMarker,
|
Json::Value const& jvMarker,
|
||||||
Json::Value& jvResult) = 0;
|
Json::Value& jvResult) = 0;
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
getOptionBookPage(
|
||||||
|
std::shared_ptr<ReadView const>& lpLedger,
|
||||||
|
STAmount const& strikePrice,
|
||||||
|
std::uint32_t const& expiration,
|
||||||
|
unsigned int iLimit,
|
||||||
|
Json::Value const& jvMarker,
|
||||||
|
Json::Value& jvResult) = 0;
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,10 @@ XRPNotCreated::visitEntry(
|
|||||||
if (isXRP((*before)[sfAmount]))
|
if (isXRP((*before)[sfAmount]))
|
||||||
drops_ -= (*before)[sfAmount].xrp().drops();
|
drops_ -= (*before)[sfAmount].xrp().drops();
|
||||||
break;
|
break;
|
||||||
|
case ltOPTION_OFFER:
|
||||||
|
if (isXRP((*before)[sfLockedBalance]))
|
||||||
|
drops_ -= (*before)[sfLockedBalance].xrp().drops();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -131,6 +135,10 @@ XRPNotCreated::visitEntry(
|
|||||||
if (!isDelete && isXRP((*after)[sfAmount]))
|
if (!isDelete && isXRP((*after)[sfAmount]))
|
||||||
drops_ += (*after)[sfAmount].xrp().drops();
|
drops_ += (*after)[sfAmount].xrp().drops();
|
||||||
break;
|
break;
|
||||||
|
case ltOPTION_OFFER:
|
||||||
|
if (!isDelete && isXRP((*after)[sfLockedBalance]))
|
||||||
|
drops_ += (*after)[sfLockedBalance].xrp().drops();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -492,6 +500,8 @@ LedgerEntryTypesMatch::visitEntry(
|
|||||||
case ltURI_TOKEN:
|
case ltURI_TOKEN:
|
||||||
case ltIMPORT_VLSEQ:
|
case ltIMPORT_VLSEQ:
|
||||||
case ltUNL_REPORT:
|
case ltUNL_REPORT:
|
||||||
|
case ltOPTION:
|
||||||
|
case ltOPTION_OFFER:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
invalidTypeAdded_ = true;
|
invalidTypeAdded_ = true;
|
||||||
|
|||||||
473
src/ripple/app/tx/impl/OptionCreate.cpp
Normal file
473
src/ripple/app/tx/impl/OptionCreate.cpp
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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[~sfSwapID];
|
||||||
|
std::uint32_t const quantity = ctx.tx.getFieldU32(sfQuantity);
|
||||||
|
|
||||||
|
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(sfOpenInterest) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// looking for oposite option position.
|
||||||
|
if (_isPut == isPut && _isSell != isSell)
|
||||||
|
{
|
||||||
|
uint256 const sealID = sleItem->getFieldH256(sfSwapID);
|
||||||
|
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(sfOptionID);
|
||||||
|
auto const flags = ctx_.tx.getFlags();
|
||||||
|
STAmount const premium = ctx_.tx.getFieldAmount(sfAmount);
|
||||||
|
std::uint32_t const quantity = ctx_.tx.getFieldU32(sfQuantity);
|
||||||
|
std::optional<uint256> const swapID = ctx_.tx[~sfSwapID];
|
||||||
|
|
||||||
|
STAmount const totalPremium = mulRound(premium, STAmount(premium.issue(), quantity), premium.issue(), false);
|
||||||
|
|
||||||
|
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(sfStrikePrice);
|
||||||
|
std::uint32_t const expiration = sleOptionAcc->getFieldU32(sfExpiration);
|
||||||
|
AccountID const issuer = sleOptionAcc->getAccountID(sfIssuer);
|
||||||
|
Currency const currency = Currency(sleOptionAcc->getFieldH160(sfCurrency));
|
||||||
|
// STAmount const quantityShares = STAmount({ issuer, currency }, isXRP(strikePrice) ? quantity * 1000000 : quantity);
|
||||||
|
STAmount const quantityShares = STAmount(Issue(currency, issuer), quantity);
|
||||||
|
|
||||||
|
if (strikePrice.issue() != totalPremium.issue() || strikePrice.issue() != totalPremium.issue())
|
||||||
|
return temBAD_ISSUER;
|
||||||
|
|
||||||
|
bool const isPut = (flags & tfType) != 0;
|
||||||
|
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.exponent(): " << strikePrice.exponent() << "\n";
|
||||||
|
std::cout << "OptionCreate.value(): " << strikePrice.value() << "\n";
|
||||||
|
std::cout << "OptionCreate.expiration: " << expiration << "\n";
|
||||||
|
|
||||||
|
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(sfSwapID, optionOfferKeylet.key);
|
||||||
|
uint32_t currSealed = sealedOption->getFieldU32(sfOpenInterest);
|
||||||
|
sealedOption->setFieldU32(sfOpenInterest, 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(sfOptionID, optionID);
|
||||||
|
optionOffer->setFieldU32(sfQuantity, quantity);
|
||||||
|
optionOffer->setFieldU32(sfOpenInterest, quantity);
|
||||||
|
optionOffer->setFieldAmount(sfAmount, premium); // Premium
|
||||||
|
optionOffer->setFieldAmount(sfLockedBalance, STAmount(0)); // Locked
|
||||||
|
if (sealID)
|
||||||
|
{
|
||||||
|
JLOG(j.warn()) << "Updating Option Offer: sealID";
|
||||||
|
optionOffer->setFieldH256(sfSwapID, *sealID);
|
||||||
|
optionOffer->setFieldU32(sfOpenInterest, 0);
|
||||||
|
}
|
||||||
|
if (isSell)
|
||||||
|
{
|
||||||
|
JLOG(j.warn()) << "Updating Option Offer: isSell";
|
||||||
|
// Update the locked balance
|
||||||
|
optionOffer->setFieldAmount(sfLockedBalance, 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(sfSwapID);
|
||||||
|
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(sfSwapID, optionOfferKeylet.key);
|
||||||
|
sb.update(swapOption);
|
||||||
|
|
||||||
|
// Update New Option
|
||||||
|
optionOffer->setFieldH256(sfSwapID, *swapID);
|
||||||
|
sb.update(optionOffer);
|
||||||
|
|
||||||
|
// Erase Swap Sealed Option
|
||||||
|
uint256 const swapSealedID = swapOption->getFieldH256(sfSwapID);
|
||||||
|
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 premium from the buyer
|
||||||
|
{
|
||||||
|
STAmount bal = mSourceBalance;
|
||||||
|
bal -= totalPremium.xrp();
|
||||||
|
if (bal < beast::zero || bal > mSourceBalance)
|
||||||
|
return tecINTERNAL;
|
||||||
|
sleSrcAcc->setFieldAmount(sfBalance, bal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the premium 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// // 1. & 2.
|
||||||
|
TER canBuyerXfer = trustTransferAllowed(
|
||||||
|
sb,
|
||||||
|
std::vector<AccountID>{srcAccID, oppAccID},
|
||||||
|
totalPremium.issue(),
|
||||||
|
j);
|
||||||
|
|
||||||
|
if (!isTesSuccess(canBuyerXfer))
|
||||||
|
{
|
||||||
|
return canBuyerXfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
STAmount availableBuyerFunds{accountFunds(
|
||||||
|
sb, srcAccID, totalPremium, fhZERO_IF_FROZEN, j)};
|
||||||
|
|
||||||
|
|
||||||
|
JLOG(j.warn()) << "availableBuyerFunds: " << availableBuyerFunds << "/n";
|
||||||
|
JLOG(j.warn()) << "totalPremium: " << totalPremium << "/n";
|
||||||
|
if (availableBuyerFunds < totalPremium)
|
||||||
|
return tecUNFUNDED_PAYMENT;
|
||||||
|
|
||||||
|
if (TER result = accountSend(sb, srcAccID, oppAccID, totalPremium, j, true); !isTesSuccess(result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.update(sleOppAcc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JLOG(j.warn()) << "Updating Option Balances: isSell";
|
||||||
|
if (isXRP(quantityShares))
|
||||||
|
{
|
||||||
|
// subtract the quantity 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
STAmount availableBuyerFunds{accountFunds(
|
||||||
|
sb, srcAccID, quantityShares, fhZERO_IF_FROZEN, j)};
|
||||||
|
|
||||||
|
if (availableBuyerFunds < quantityShares)
|
||||||
|
return tecUNFUNDED_PAYMENT;
|
||||||
|
|
||||||
|
std::shared_ptr<SLE> sleLine = ctx_.view().peek(keylet::line(srcAccID, quantityShares.getIssuer(), quantityShares.getCurrency()));
|
||||||
|
|
||||||
|
if (TER const result = trustAdjustLockedBalance(ctx_.view(), sleLine, quantityShares, 1, ctx_.journal, WetRun); !isTesSuccess(result))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
258
src/ripple/app/tx/impl/OptionExercise.cpp
Normal file
258
src/ripple/app/tx/impl/OptionExercise.cpp
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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/OptionExercise.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
|
||||||
|
OptionExercise::makeTxConsequences(PreflightContext const& ctx)
|
||||||
|
{
|
||||||
|
return TxConsequences{ctx.tx, TxConsequences::normal};
|
||||||
|
}
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
OptionExercise::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
|
||||||
|
OptionExercise::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(sfOptionID);
|
||||||
|
uint256 const offerID = ctx_.tx.getFieldH256(sfSwapID);
|
||||||
|
auto const flags = ctx_.tx.getFlags();
|
||||||
|
|
||||||
|
auto sleSrcAcc = ctx_.view().peek(keylet::account(srcAccID));
|
||||||
|
if (!sleSrcAcc)
|
||||||
|
return terNO_ACCOUNT;
|
||||||
|
|
||||||
|
auto optionOfferKeylet = ripple::keylet::unchecked(offerID);
|
||||||
|
auto sleOptionOffer = ctx_.view().peek(optionOfferKeylet);
|
||||||
|
if (!sleOptionOffer)
|
||||||
|
return tecNO_TARGET;
|
||||||
|
|
||||||
|
AccountID const ownrAccID = sleOptionOffer->getAccountID(sfOwner);
|
||||||
|
auto const sealedID = sleOptionOffer->getFieldH256(sfSwapID);
|
||||||
|
auto oppOfferKeylet = ripple::keylet::unchecked(sealedID);
|
||||||
|
auto sleSealedOffer = ctx_.view().peek(oppOfferKeylet);
|
||||||
|
if (!sleSealedOffer)
|
||||||
|
return tecNO_TARGET;
|
||||||
|
|
||||||
|
AccountID const oppAccID = sleSealedOffer->getAccountID(sfOwner);
|
||||||
|
auto sleOppAcc = ctx_.view().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 = ctx_.view().peek(keylet::unchecked(optionID));
|
||||||
|
if (!sleOption)
|
||||||
|
return tecINTERNAL;
|
||||||
|
|
||||||
|
STAmount const strikePrice = sleOption->getFieldAmount(sfStrikePrice);
|
||||||
|
STAmount const quantityShares = sleSealedOffer->getFieldAmount(sfLockedBalance);
|
||||||
|
std::uint32_t const quantity = sleOptionOffer->getFieldU32(sfQuantity);
|
||||||
|
STAmount const totalValue = mulRound(strikePrice, STAmount(strikePrice.issue(), quantity), strikePrice.issue(), false);
|
||||||
|
|
||||||
|
|
||||||
|
JLOG(j.warn()) << "OptionExercise: QUANTITY SHARES" << quantityShares << "\n";
|
||||||
|
JLOG(j.warn()) << "OptionExercise: TOTAL VALUE" << totalValue << "\n";
|
||||||
|
|
||||||
|
if (flags & tfOptionExpire)
|
||||||
|
{
|
||||||
|
JLOG(j.warn()) << "OptionExercise: EXPIRE OPTION";
|
||||||
|
ctx_.view().erase(sleSealedOffer);
|
||||||
|
ctx_.view().erase(sleOptionOffer);
|
||||||
|
// sb.apply(ctx_.rawView());
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (isPut)
|
||||||
|
{
|
||||||
|
case 0: {
|
||||||
|
JLOG(j.warn()) << "OptionExercise: EXERCISE CALL";
|
||||||
|
|
||||||
|
STAmount hBalance = mSourceBalance;
|
||||||
|
|
||||||
|
// subtract the total value from the buyer
|
||||||
|
// add the total value to the writer
|
||||||
|
if (isXRP(totalValue))
|
||||||
|
{
|
||||||
|
if (mSourceBalance < totalValue.xrp())
|
||||||
|
return tecUNFUNDED_PAYMENT;
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
hBalance -= totalValue.xrp();
|
||||||
|
if (hBalance < beast::zero || hBalance > mSourceBalance)
|
||||||
|
return tecINTERNAL;
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
STAmount wBalance = sleOppAcc->getFieldAmount(sfBalance);
|
||||||
|
STAmount prior = wBalance;
|
||||||
|
wBalance += totalValue.xrp();
|
||||||
|
if (wBalance < beast::zero || wBalance < prior)
|
||||||
|
return tecINTERNAL;
|
||||||
|
sleOppAcc->setFieldAmount(sfBalance, wBalance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 1. & 2.
|
||||||
|
TER canBuyerXfer = trustTransferAllowed(
|
||||||
|
ctx_.view(),
|
||||||
|
std::vector<AccountID>{srcAccID, oppAccID},
|
||||||
|
totalValue.issue(),
|
||||||
|
j);
|
||||||
|
|
||||||
|
if (!isTesSuccess(canBuyerXfer))
|
||||||
|
{
|
||||||
|
return canBuyerXfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
STAmount availableBuyerFunds{accountFunds(
|
||||||
|
ctx_.view(), srcAccID, totalValue, fhZERO_IF_FROZEN, j)};
|
||||||
|
|
||||||
|
if (availableBuyerFunds < totalValue)
|
||||||
|
return tecUNFUNDED_PAYMENT;
|
||||||
|
|
||||||
|
if (TER result = accountSend(
|
||||||
|
ctx_.view(), srcAccID, oppAccID, totalValue, j, true);
|
||||||
|
!isTesSuccess(result))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the shares to the buyer
|
||||||
|
if (isXRP(quantityShares))
|
||||||
|
{
|
||||||
|
STAmount prior = hBalance;
|
||||||
|
hBalance += quantityShares.xrp();
|
||||||
|
if (hBalance < beast::zero || hBalance < prior)
|
||||||
|
return tecINTERNAL;
|
||||||
|
sleSrcAcc->setFieldAmount(sfBalance, hBalance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Rate lockedRate = ripple::Rate(slep->getFieldU32(sfTransferRate));
|
||||||
|
// auto const issuerAccID = totalPremium.getIssuer();
|
||||||
|
// auto const xferRate = transferRate(view(), issuerAccID);
|
||||||
|
// // // update if issuer rate is less than locked rate
|
||||||
|
// // if (xferRate < lockedRate)
|
||||||
|
// // lockedRate = xferRate;
|
||||||
|
|
||||||
|
// all the significant complexity of checking the validity of this
|
||||||
|
// transfer and ensuring the lines exist etc is hidden away in this
|
||||||
|
// function, all we need to do is call it and return if unsuccessful.
|
||||||
|
TER const result = trustTransferLockedBalance(
|
||||||
|
ctx_.view(),
|
||||||
|
account_, // txn signing account
|
||||||
|
sleOppAcc, // src account
|
||||||
|
sleSrcAcc, // dst account
|
||||||
|
quantityShares, // xfer amount
|
||||||
|
-1,
|
||||||
|
parityRate,
|
||||||
|
j_,
|
||||||
|
WetRun // wet run;
|
||||||
|
);
|
||||||
|
if (!isTesSuccess(result))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
JLOG(j.warn()) << "OptionExercise: 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
|
||||||
|
ctx_.view().update(sleOppAcc);
|
||||||
|
ctx_.view().update(sleSrcAcc);
|
||||||
|
ctx_.view().erase(sleSealedOffer);
|
||||||
|
ctx_.view().erase(sleOptionOffer);
|
||||||
|
// sb.apply(ctx_.rawView());
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
XRPAmount
|
||||||
|
OptionExercise::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||||
|
{
|
||||||
|
return Transactor::calculateBaseFee(view, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
54
src/ripple/app/tx/impl/OptionExercise.h
Normal file
54
src/ripple/app/tx/impl/OptionExercise.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 OptionExercise : public Transactor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||||
|
|
||||||
|
explicit OptionExercise(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
|
||||||
109
src/ripple/app/tx/impl/OptionList.cpp
Normal file
109
src/ripple/app/tx/impl/OptionList.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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 strikePrice = ctx_.tx.getFieldAmount(sfStrikePrice);
|
||||||
|
AccountID const issuer = ctx_.tx.getAccountID(sfIssuer);
|
||||||
|
auto const currency = ctx_.tx.getFieldH160(sfCurrency);
|
||||||
|
|
||||||
|
auto sleSrcAcc = sb.peek(keylet::account(srcAccID));
|
||||||
|
if (!sleSrcAcc)
|
||||||
|
return terNO_ACCOUNT;
|
||||||
|
|
||||||
|
std::optional<Keylet> const optionKeylet = keylet::option(strikePrice.getIssuer(), strikePrice.getCurrency(), strikePrice.mantissa(), expiration);
|
||||||
|
if (sb.exists(*optionKeylet))
|
||||||
|
return tecDUPLICATE;
|
||||||
|
|
||||||
|
auto const sleOption = std::make_shared<SLE>(*optionKeylet);
|
||||||
|
|
||||||
|
auto const newPage = sb.dirInsert(
|
||||||
|
keylet::ownerDir(strikePrice.getIssuer()),
|
||||||
|
*optionKeylet,
|
||||||
|
describeOwnerDir(strikePrice.getIssuer()));
|
||||||
|
|
||||||
|
JLOG(j_.trace()) << "Adding Option to owner directory "
|
||||||
|
<< to_string(optionKeylet->key) << ": "
|
||||||
|
<< (newPage ? "success" : "failure");
|
||||||
|
|
||||||
|
if (!newPage)
|
||||||
|
return tecDIR_FULL;
|
||||||
|
|
||||||
|
(*sleOption)[sfOwnerNode] = *newPage;
|
||||||
|
(*sleOption)[sfStrikePrice] = strikePrice;
|
||||||
|
(*sleOption)[sfIssuer] = issuer;
|
||||||
|
(*sleOption)[sfCurrency] = currency;
|
||||||
|
(*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/CancelOffer.h>
|
||||||
#include <ripple/app/tx/impl/CashCheck.h>
|
#include <ripple/app/tx/impl/CashCheck.h>
|
||||||
#include <ripple/app/tx/impl/Change.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/CreateCheck.h>
|
||||||
#include <ripple/app/tx/impl/CreateOffer.h>
|
#include <ripple/app/tx/impl/CreateOffer.h>
|
||||||
#include <ripple/app/tx/impl/CreateTicket.h>
|
#include <ripple/app/tx/impl/CreateTicket.h>
|
||||||
@@ -37,8 +38,12 @@
|
|||||||
#include <ripple/app/tx/impl/NFTokenCancelOffer.h>
|
#include <ripple/app/tx/impl/NFTokenCancelOffer.h>
|
||||||
#include <ripple/app/tx/impl/NFTokenCreateOffer.h>
|
#include <ripple/app/tx/impl/NFTokenCreateOffer.h>
|
||||||
#include <ripple/app/tx/impl/NFTokenMint.h>
|
#include <ripple/app/tx/impl/NFTokenMint.h>
|
||||||
|
#include <ripple/app/tx/impl/OptionCreate.h>
|
||||||
|
#include <ripple/app/tx/impl/OptionExercise.h>
|
||||||
|
#include <ripple/app/tx/impl/OptionList.h>
|
||||||
#include <ripple/app/tx/impl/PayChan.h>
|
#include <ripple/app/tx/impl/PayChan.h>
|
||||||
#include <ripple/app/tx/impl/Payment.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/SetAccount.h>
|
||||||
#include <ripple/app/tx/impl/SetHook.h>
|
#include <ripple/app/tx/impl/SetHook.h>
|
||||||
#include <ripple/app/tx/impl/SetRegularKey.h>
|
#include <ripple/app/tx/impl/SetRegularKey.h>
|
||||||
@@ -122,6 +127,12 @@ invoke_preflight(PreflightContext const& ctx)
|
|||||||
return invoke_preflight_helper<EscrowFinish>(ctx);
|
return invoke_preflight_helper<EscrowFinish>(ctx);
|
||||||
case ttESCROW_CANCEL:
|
case ttESCROW_CANCEL:
|
||||||
return invoke_preflight_helper<EscrowCancel>(ctx);
|
return invoke_preflight_helper<EscrowCancel>(ctx);
|
||||||
|
case ttOPTION_CREATE:
|
||||||
|
return invoke_preflight_helper<OptionCreate>(ctx);
|
||||||
|
case ttOPTION_EXERCISE:
|
||||||
|
return invoke_preflight_helper<OptionExercise>(ctx);
|
||||||
|
case ttOPTION_LIST:
|
||||||
|
return invoke_preflight_helper<OptionList>(ctx);
|
||||||
case ttPAYCHAN_CLAIM:
|
case ttPAYCHAN_CLAIM:
|
||||||
return invoke_preflight_helper<PayChanClaim>(ctx);
|
return invoke_preflight_helper<PayChanClaim>(ctx);
|
||||||
case ttPAYCHAN_CREATE:
|
case ttPAYCHAN_CREATE:
|
||||||
@@ -243,6 +254,12 @@ invoke_preclaim(PreclaimContext const& ctx)
|
|||||||
return invoke_preclaim<EscrowFinish>(ctx);
|
return invoke_preclaim<EscrowFinish>(ctx);
|
||||||
case ttESCROW_CANCEL:
|
case ttESCROW_CANCEL:
|
||||||
return invoke_preclaim<EscrowCancel>(ctx);
|
return invoke_preclaim<EscrowCancel>(ctx);
|
||||||
|
case ttOPTION_CREATE:
|
||||||
|
return invoke_preclaim<OptionCreate>(ctx);
|
||||||
|
case ttOPTION_EXERCISE:
|
||||||
|
return invoke_preclaim<OptionExercise>(ctx);
|
||||||
|
case ttOPTION_LIST:
|
||||||
|
return invoke_preclaim<OptionList>(ctx);
|
||||||
case ttPAYCHAN_CLAIM:
|
case ttPAYCHAN_CLAIM:
|
||||||
return invoke_preclaim<PayChanClaim>(ctx);
|
return invoke_preclaim<PayChanClaim>(ctx);
|
||||||
case ttPAYCHAN_CREATE:
|
case ttPAYCHAN_CREATE:
|
||||||
@@ -326,6 +343,12 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
|||||||
return EscrowFinish::calculateBaseFee(view, tx);
|
return EscrowFinish::calculateBaseFee(view, tx);
|
||||||
case ttESCROW_CANCEL:
|
case ttESCROW_CANCEL:
|
||||||
return EscrowCancel::calculateBaseFee(view, tx);
|
return EscrowCancel::calculateBaseFee(view, tx);
|
||||||
|
case ttOPTION_CREATE:
|
||||||
|
return OptionCreate::calculateBaseFee(view, tx);
|
||||||
|
case ttOPTION_EXERCISE:
|
||||||
|
return OptionExercise::calculateBaseFee(view, tx);
|
||||||
|
case ttOPTION_LIST:
|
||||||
|
return OptionList::calculateBaseFee(view, tx);
|
||||||
case ttPAYCHAN_CLAIM:
|
case ttPAYCHAN_CLAIM:
|
||||||
return PayChanClaim::calculateBaseFee(view, tx);
|
return PayChanClaim::calculateBaseFee(view, tx);
|
||||||
case ttPAYCHAN_CREATE:
|
case ttPAYCHAN_CREATE:
|
||||||
@@ -469,6 +492,18 @@ invoke_apply(ApplyContext& ctx)
|
|||||||
EscrowCancel p(ctx);
|
EscrowCancel p(ctx);
|
||||||
return p();
|
return p();
|
||||||
}
|
}
|
||||||
|
case ttOPTION_CREATE: {
|
||||||
|
OptionCreate p(ctx);
|
||||||
|
return p();
|
||||||
|
}
|
||||||
|
case ttOPTION_EXERCISE: {
|
||||||
|
OptionExercise p(ctx);
|
||||||
|
return p();
|
||||||
|
}
|
||||||
|
case ttOPTION_LIST: {
|
||||||
|
OptionList p(ctx);
|
||||||
|
return p();
|
||||||
|
}
|
||||||
case ttPAYCHAN_CLAIM: {
|
case ttPAYCHAN_CLAIM: {
|
||||||
PayChanClaim p(ctx);
|
PayChanClaim p(ctx);
|
||||||
return p();
|
return p();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
|
|||||||
{
|
{
|
||||||
using namespace boost::multiprecision;
|
using namespace boost::multiprecision;
|
||||||
|
|
||||||
uint128_t result;
|
boost::multiprecision::uint128_t result;
|
||||||
result = multiply(result, value, mul);
|
result = multiply(result, value, mul);
|
||||||
|
|
||||||
result /= div;
|
result /= div;
|
||||||
|
|||||||
@@ -145,111 +145,78 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// VFALCO TODO This should only be enabled for maps.
|
// VFALCO TODO This should only be enabled for maps.
|
||||||
class pair_value_compare
|
class pair_value_compare : public Compare
|
||||||
: public beast::detail::empty_base_optimization<Compare>
|
|
||||||
#ifdef _LIBCPP_VERSION
|
|
||||||
,
|
|
||||||
public std::binary_function<value_type, value_type, bool>
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
#ifndef _LIBCPP_VERSION
|
|
||||||
using first_argument = value_type;
|
using first_argument = value_type;
|
||||||
using second_argument = value_type;
|
using second_argument = value_type;
|
||||||
using result_type = bool;
|
using result_type = bool;
|
||||||
#endif
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
operator()(value_type const& lhs, value_type const& rhs) const
|
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(pair_value_compare const& other)
|
pair_value_compare(pair_value_compare const& other) : Compare(other)
|
||||||
: beast::detail::empty_base_optimization<Compare>(other)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend aged_ordered_container;
|
friend aged_ordered_container;
|
||||||
|
|
||||||
pair_value_compare(Compare const& compare)
|
pair_value_compare(Compare const& compare) : Compare(compare)
|
||||||
: beast::detail::empty_base_optimization<Compare>(compare)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compares value_type against element, used in insert_check
|
// Compares value_type against element, used in insert_check
|
||||||
// VFALCO TODO hoist to remove template argument dependencies
|
// VFALCO TODO hoist to remove template argument dependencies
|
||||||
class KeyValueCompare
|
class KeyValueCompare : public Compare
|
||||||
: public beast::detail::empty_base_optimization<Compare>
|
|
||||||
#ifdef _LIBCPP_VERSION
|
|
||||||
,
|
|
||||||
public std::binary_function<Key, element, bool>
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
#ifndef _LIBCPP_VERSION
|
|
||||||
using first_argument = Key;
|
using first_argument = Key;
|
||||||
using second_argument = element;
|
using second_argument = element;
|
||||||
using result_type = bool;
|
using result_type = bool;
|
||||||
#endif
|
|
||||||
|
|
||||||
KeyValueCompare() = default;
|
KeyValueCompare() = default;
|
||||||
|
|
||||||
KeyValueCompare(Compare const& compare)
|
KeyValueCompare(Compare const& compare) : Compare(compare)
|
||||||
: beast::detail::empty_base_optimization<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
|
bool
|
||||||
operator()(Key const& k, element const& e) const
|
operator()(Key const& k, element const& e) const
|
||||||
{
|
{
|
||||||
return this->member()(k, extract(e.value));
|
return Compare::operator()(k, extract(e.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
operator()(element const& e, Key const& k) const
|
operator()(element const& e, Key const& k) const
|
||||||
{
|
{
|
||||||
return this->member()(extract(e.value), k);
|
return Compare::operator()(extract(e.value), k);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
operator()(element const& x, element const& y) const
|
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&
|
||||||
compare()
|
compare()
|
||||||
{
|
{
|
||||||
return beast::detail::empty_base_optimization<Compare>::member();
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Compare const&
|
Compare const&
|
||||||
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
|
// VFALCO TODO hoist to remove template argument dependencies
|
||||||
class ValueHash : private beast::detail::empty_base_optimization<Hash>
|
class ValueHash : public Hash
|
||||||
#ifdef _LIBCPP_VERSION
|
|
||||||
,
|
|
||||||
public std::unary_function<element, std::size_t>
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
#ifndef _LIBCPP_VERSION
|
|
||||||
using argument_type = element;
|
using argument_type = element;
|
||||||
using result_type = size_t;
|
using result_type = size_t;
|
||||||
#endif
|
|
||||||
|
|
||||||
ValueHash()
|
ValueHash()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueHash(Hash const& h)
|
ValueHash(Hash const& h) : Hash(h)
|
||||||
: beast::detail::empty_base_optimization<Hash>(h)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
operator()(element const& e) const
|
operator()(element const& e) const
|
||||||
{
|
{
|
||||||
return this->member()(extract(e.value));
|
return Hash::operator()(extract(e.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Hash&
|
Hash&
|
||||||
hash_function()
|
hash_function()
|
||||||
{
|
{
|
||||||
return this->member();
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hash const&
|
Hash const&
|
||||||
hash_function() const
|
hash_function() const
|
||||||
{
|
{
|
||||||
return this->member();
|
return *this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compares value_type against element, used in find/insert_check
|
// Compares value_type against element, used in find/insert_check
|
||||||
// VFALCO TODO hoist to remove template argument dependencies
|
// VFALCO TODO hoist to remove template argument dependencies
|
||||||
class KeyValueEqual
|
class KeyValueEqual : public KeyEqual
|
||||||
: private beast::detail::empty_base_optimization<KeyEqual>
|
|
||||||
#ifdef _LIBCPP_VERSION
|
|
||||||
,
|
|
||||||
public std::binary_function<Key, element, bool>
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
#ifndef _LIBCPP_VERSION
|
|
||||||
using first_argument_type = Key;
|
using first_argument_type = Key;
|
||||||
using second_argument_type = element;
|
using second_argument_type = element;
|
||||||
using result_type = bool;
|
using result_type = bool;
|
||||||
#endif
|
|
||||||
|
|
||||||
KeyValueEqual()
|
KeyValueEqual()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyValueEqual(KeyEqual const& keyEqual)
|
KeyValueEqual(KeyEqual const& keyEqual) : KeyEqual(keyEqual)
|
||||||
: beast::detail::empty_base_optimization<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
|
bool
|
||||||
operator()(Key const& k, element const& e) const
|
operator()(Key const& k, element const& e) const
|
||||||
{
|
{
|
||||||
return this->member()(k, extract(e.value));
|
return KeyEqual::operator()(k, extract(e.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
operator()(element const& e, Key const& k) const
|
operator()(element const& e, Key const& k) const
|
||||||
{
|
{
|
||||||
return this->member()(extract(e.value), k);
|
return KeyEqual::operator()(extract(e.value), k);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
operator()(element const& lhs, element const& rhs) const
|
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&
|
KeyEqual&
|
||||||
key_eq()
|
key_eq()
|
||||||
{
|
{
|
||||||
return this->member();
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEqual const&
|
KeyEqual const&
|
||||||
key_eq() const
|
key_eq() const
|
||||||
{
|
{
|
||||||
return this->member();
|
return *this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ public:
|
|||||||
Keylet const& key,
|
Keylet const& key,
|
||||||
std::function<void(std::shared_ptr<SLE> const&)> const& describe)
|
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. "
|
assert(!"Only Offers are appended to book directories. "
|
||||||
"Call dirInsert() instead.");
|
"Call dirInsert() instead.");
|
||||||
|
|||||||
@@ -19,6 +19,14 @@
|
|||||||
|
|
||||||
#include <ripple/basics/contract.h>
|
#include <ripple/basics/contract.h>
|
||||||
#include <ripple/ledger/detail/ApplyViewBase.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 ripple {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|||||||
@@ -91,17 +91,10 @@ private:
|
|||||||
using value_type = map_type::value_type;
|
using value_type = map_type::value_type;
|
||||||
|
|
||||||
struct Transform
|
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 =
|
using first_argument_type =
|
||||||
map_type::right_map::const_iterator::value_type const&;
|
map_type::right_map::const_iterator::value_type const&;
|
||||||
using result_type = beast::IP::Endpoint const&;
|
using result_type = beast::IP::Endpoint const&;
|
||||||
#endif
|
|
||||||
|
|
||||||
explicit Transform() = default;
|
explicit Transform() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -69,14 +69,9 @@ public:
|
|||||||
public:
|
public:
|
||||||
// Iterator transformation to extract the endpoint from Element
|
// Iterator transformation to extract the endpoint from Element
|
||||||
struct Transform
|
struct Transform
|
||||||
#ifdef _LIBCPP_VERSION
|
|
||||||
: public std::unary_function<Element, Endpoint>
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
#ifndef _LIBCPP_VERSION
|
|
||||||
using first_argument = Element;
|
using first_argument = Element;
|
||||||
using result_type = Endpoint;
|
using result_type = Endpoint;
|
||||||
#endif
|
|
||||||
|
|
||||||
explicit Transform() = default;
|
explicit Transform() = default;
|
||||||
|
|
||||||
@@ -239,15 +234,9 @@ public:
|
|||||||
|
|
||||||
template <bool IsConst>
|
template <bool IsConst>
|
||||||
struct Transform
|
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 first_argument = typename lists_type::value_type;
|
||||||
using result_type = Hop<IsConst>;
|
using result_type = Hop<IsConst>;
|
||||||
#endif
|
|
||||||
|
|
||||||
explicit Transform() = default;
|
explicit Transform() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace detail {
|
|||||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
// 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
|
// 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.
|
// 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.
|
/** Amendments that this server supports and the default voting behavior.
|
||||||
Whether they are enabled depends on the Rules defined in the validated
|
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 featureRemit;
|
||||||
extern uint256 const featureZeroB2M;
|
extern uint256 const featureZeroB2M;
|
||||||
extern uint256 const fixNSDelete;
|
extern uint256 const fixNSDelete;
|
||||||
|
extern uint256 const featureOptions;
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|
||||||
|
|||||||
@@ -297,6 +297,24 @@ import_vlseq(PublicKey const& key) noexcept;
|
|||||||
Keylet
|
Keylet
|
||||||
uritoken(AccountID const& issuer, Blob const& uri);
|
uritoken(AccountID const& issuer, Blob const& uri);
|
||||||
|
|
||||||
|
Keylet
|
||||||
|
option(AccountID const& issuer, Currency const& currency, std::uint64_t strike, 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;
|
||||||
|
|
||||||
|
inline Keylet
|
||||||
|
optionOffer(uint256 const& key) noexcept
|
||||||
|
{
|
||||||
|
return {ltOPTION_OFFER, key};
|
||||||
|
}
|
||||||
|
|
||||||
|
Keylet
|
||||||
|
optionQuality(Keylet const& k, std::uint64_t q) noexcept;
|
||||||
|
|
||||||
} // namespace keylet
|
} // namespace keylet
|
||||||
|
|
||||||
// Everything below is deprecated and should be removed in favor of keylets:
|
// Everything below is deprecated and should be removed in favor of keylets:
|
||||||
@@ -311,6 +329,18 @@ getQualityNext(uint256 const& uBase);
|
|||||||
std::uint64_t
|
std::uint64_t
|
||||||
getQuality(uint256 const& uBase);
|
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
|
uint256
|
||||||
getTicketIndex(AccountID const& account, std::uint32_t uSequence);
|
getTicketIndex(AccountID const& account, std::uint32_t uSequence);
|
||||||
|
|
||||||
|
|||||||
@@ -254,6 +254,18 @@ enum LedgerEntryType : std::uint16_t
|
|||||||
\sa keylet::emitted
|
\sa keylet::emitted
|
||||||
*/
|
*/
|
||||||
ltEMITTED_TXN = 'E',
|
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
|
// clang-format off
|
||||||
|
|
||||||
@@ -315,6 +327,12 @@ enum LedgerSpecificFlags {
|
|||||||
|
|
||||||
// ltURI_TOKEN
|
// ltURI_TOKEN
|
||||||
lsfBurnable = 0x00000001, // True, issuer can burn the 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
|
||||||
@@ -410,6 +410,8 @@ extern SF_UINT32 const sfRewardLgrLast;
|
|||||||
extern SF_UINT32 const sfFirstNFTokenSequence;
|
extern SF_UINT32 const sfFirstNFTokenSequence;
|
||||||
extern SF_UINT32 const sfImportSequence;
|
extern SF_UINT32 const sfImportSequence;
|
||||||
extern SF_UINT32 const sfXahauActivationLgrSeq;
|
extern SF_UINT32 const sfXahauActivationLgrSeq;
|
||||||
|
extern SF_UINT32 const sfQuantity;
|
||||||
|
extern SF_UINT32 const sfOpenInterest;
|
||||||
|
|
||||||
// 64-bit integers (common)
|
// 64-bit integers (common)
|
||||||
extern SF_UINT64 const sfIndexNext;
|
extern SF_UINT64 const sfIndexNext;
|
||||||
@@ -442,6 +444,7 @@ extern SF_UINT160 const sfTakerPaysCurrency;
|
|||||||
extern SF_UINT160 const sfTakerPaysIssuer;
|
extern SF_UINT160 const sfTakerPaysIssuer;
|
||||||
extern SF_UINT160 const sfTakerGetsCurrency;
|
extern SF_UINT160 const sfTakerGetsCurrency;
|
||||||
extern SF_UINT160 const sfTakerGetsIssuer;
|
extern SF_UINT160 const sfTakerGetsIssuer;
|
||||||
|
extern SF_UINT160 const sfCurrency;
|
||||||
|
|
||||||
// 256-bit (common)
|
// 256-bit (common)
|
||||||
extern SF_UINT256 const sfLedgerHash;
|
extern SF_UINT256 const sfLedgerHash;
|
||||||
@@ -483,6 +486,8 @@ extern SF_UINT256 const sfURITokenID;
|
|||||||
extern SF_UINT256 const sfGovernanceFlags;
|
extern SF_UINT256 const sfGovernanceFlags;
|
||||||
extern SF_UINT256 const sfGovernanceMarks;
|
extern SF_UINT256 const sfGovernanceMarks;
|
||||||
extern SF_UINT256 const sfEmittedTxnID;
|
extern SF_UINT256 const sfEmittedTxnID;
|
||||||
|
extern SF_UINT256 const sfOptionID;
|
||||||
|
extern SF_UINT256 const sfSwapID;
|
||||||
|
|
||||||
// currency amount (common)
|
// currency amount (common)
|
||||||
extern SF_AMOUNT const sfAmount;
|
extern SF_AMOUNT const sfAmount;
|
||||||
@@ -496,6 +501,7 @@ extern SF_AMOUNT const sfFee;
|
|||||||
extern SF_AMOUNT const sfSendMax;
|
extern SF_AMOUNT const sfSendMax;
|
||||||
extern SF_AMOUNT const sfDeliverMin;
|
extern SF_AMOUNT const sfDeliverMin;
|
||||||
extern SF_AMOUNT const sfLockedBalance;
|
extern SF_AMOUNT const sfLockedBalance;
|
||||||
|
extern SF_AMOUNT const sfStrikePrice;
|
||||||
|
|
||||||
// currency amount (uncommon)
|
// currency amount (uncommon)
|
||||||
extern SF_AMOUNT const sfMinimumOffer;
|
extern SF_AMOUNT const sfMinimumOffer;
|
||||||
|
|||||||
@@ -171,8 +171,7 @@ STValidation::STValidation(
|
|||||||
{
|
{
|
||||||
if (checkSignature && !isValid())
|
if (checkSignature && !isValid())
|
||||||
{
|
{
|
||||||
JLOG(debugLog().error()) << "Invalid signature in validation: "
|
JLOG(debugLog().error()) << "Invalid signature in validation";
|
||||||
<< getJson(JsonOptions::none);
|
|
||||||
Throw<std::runtime_error>("Invalid signature in validation");
|
Throw<std::runtime_error>("Invalid signature in validation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,6 +190,21 @@ enum ClaimRewardFlags : uint32_t {
|
|||||||
tfOptOut = 0x00000001,
|
tfOptOut = 0x00000001,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// OptionCreate flags:
|
||||||
|
enum OptionCreateFlags : uint32_t {
|
||||||
|
tfType = 0x00010000, // 0 Call 1 Put
|
||||||
|
tfAction = 0x00020000, // 0 Buy 2 Sell
|
||||||
|
tfPosition = 0x00040000, // 0 Open 4 Close
|
||||||
|
};
|
||||||
|
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
|
// clang-format on
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -185,6 +185,9 @@ enum TxType : std::uint16_t
|
|||||||
ttUNL_MODIFY = 102,
|
ttUNL_MODIFY = 102,
|
||||||
ttEMIT_FAILURE = 103,
|
ttEMIT_FAILURE = 103,
|
||||||
ttUNL_REPORT = 104,
|
ttUNL_REPORT = 104,
|
||||||
|
ttOPTION_LIST = 105,
|
||||||
|
ttOPTION_CREATE = 106,
|
||||||
|
ttOPTION_EXERCISE = 107,
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|||||||
@@ -464,6 +464,7 @@ REGISTER_FIX (fixXahauV2, Supported::yes, VoteBehavior::De
|
|||||||
REGISTER_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo);
|
REGISTER_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo);
|
||||||
REGISTER_FEATURE(ZeroB2M, Supported::yes, VoteBehavior::DefaultNo);
|
REGISTER_FEATURE(ZeroB2M, Supported::yes, VoteBehavior::DefaultNo);
|
||||||
REGISTER_FIX (fixNSDelete, 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
|
// The following amendments are obsolete, but must remain supported
|
||||||
// because they could potentially get enabled.
|
// because they could potentially get enabled.
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ enum class LedgerNameSpace : std::uint16_t {
|
|||||||
URI_TOKEN = 'U',
|
URI_TOKEN = 'U',
|
||||||
IMPORT_VLSEQ = 'I',
|
IMPORT_VLSEQ = 'I',
|
||||||
UNL_REPORT = 'R',
|
UNL_REPORT = 'R',
|
||||||
|
OPTION = 'X',
|
||||||
|
OPTION_DIR = 'Y',
|
||||||
|
OPTION_OFFER = 'y',
|
||||||
|
|
||||||
// No longer used or supported. Left here to reserve the space
|
// No longer used or supported. Left here to reserve the space
|
||||||
// to avoid accidental reuse.
|
// to avoid accidental reuse.
|
||||||
@@ -120,6 +123,33 @@ getQuality(uint256 const& uBase)
|
|||||||
return boost::endian::big_to_native(((std::uint64_t*)uBase.end())[-1]);
|
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
|
uint256
|
||||||
getTicketIndex(AccountID const& account, std::uint32_t ticketSeq)
|
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()})};
|
LedgerNameSpace::URI_TOKEN, issuer, Slice{uri.data(), uri.size()})};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keylet
|
||||||
|
option(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration) noexcept
|
||||||
|
{
|
||||||
|
return {ltOPTION, indexHash(LedgerNameSpace::OPTION, issuer, currency, strike, 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 keylet
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ LedgerFormats::LedgerFormats()
|
|||||||
{sfIndexNext, soeOPTIONAL},
|
{sfIndexNext, soeOPTIONAL},
|
||||||
{sfIndexPrevious, soeOPTIONAL},
|
{sfIndexPrevious, soeOPTIONAL},
|
||||||
{sfNFTokenID, soeOPTIONAL},
|
{sfNFTokenID, soeOPTIONAL},
|
||||||
|
{sfBaseFee, soeOPTIONAL},
|
||||||
|
{sfExpiration, soeOPTIONAL},
|
||||||
},
|
},
|
||||||
commonFields);
|
commonFields);
|
||||||
|
|
||||||
@@ -363,6 +365,38 @@ LedgerFormats::LedgerFormats()
|
|||||||
},
|
},
|
||||||
commonFields);
|
commonFields);
|
||||||
|
|
||||||
|
add(jss::Option,
|
||||||
|
ltOPTION,
|
||||||
|
{
|
||||||
|
{sfOwnerNode, soeREQUIRED},
|
||||||
|
{sfStrikePrice, soeREQUIRED},
|
||||||
|
{sfIssuer, soeREQUIRED},
|
||||||
|
{sfCurrency, soeREQUIRED},
|
||||||
|
{sfExpiration, soeREQUIRED},
|
||||||
|
{sfPreviousTxnID, soeREQUIRED},
|
||||||
|
{sfPreviousTxnLgrSeq, soeREQUIRED}
|
||||||
|
},
|
||||||
|
commonFields);
|
||||||
|
|
||||||
|
add(jss::OptionOffer,
|
||||||
|
ltOPTION_OFFER,
|
||||||
|
{
|
||||||
|
{sfOwner, soeREQUIRED},
|
||||||
|
{sfOwnerNode, soeREQUIRED},
|
||||||
|
{sfOptionID, soeREQUIRED}, // OptionID
|
||||||
|
{sfLockedBalance, soeREQUIRED}, // Locked Amount
|
||||||
|
{sfAmount, soeREQUIRED}, // Premium
|
||||||
|
{sfQuantity, soeREQUIRED}, // Quantity
|
||||||
|
{sfOpenInterest, soeREQUIRED}, // To Seal
|
||||||
|
{sfBookDirectory, soeREQUIRED},
|
||||||
|
{sfBookNode, soeREQUIRED},
|
||||||
|
{sfSwapID, soeOPTIONAL}, // MatchID
|
||||||
|
{sfCheckID, soeOPTIONAL}, // SwapID
|
||||||
|
{sfPreviousTxnID, soeREQUIRED},
|
||||||
|
{sfPreviousTxnLgrSeq, soeREQUIRED}
|
||||||
|
},
|
||||||
|
commonFields);
|
||||||
|
|
||||||
// clang-format on
|
// 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
|
||||||
@@ -157,6 +157,8 @@ CONSTRUCT_TYPED_SFIELD(sfLockCount, "LockCount", UINT32,
|
|||||||
|
|
||||||
CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50);
|
CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50);
|
||||||
|
|
||||||
|
CONSTRUCT_TYPED_SFIELD(sfOpenInterest, "OpenInterest", UINT32, 94);
|
||||||
|
CONSTRUCT_TYPED_SFIELD(sfQuantity, "Quantity", UINT32, 95);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfXahauActivationLgrSeq, "XahauActivationLgrSeq",UINT32, 96);
|
CONSTRUCT_TYPED_SFIELD(sfXahauActivationLgrSeq, "XahauActivationLgrSeq",UINT32, 96);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfImportSequence, "ImportSequence", UINT32, 97);
|
CONSTRUCT_TYPED_SFIELD(sfImportSequence, "ImportSequence", UINT32, 97);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfRewardTime, "RewardTime", UINT32, 98);
|
CONSTRUCT_TYPED_SFIELD(sfRewardTime, "RewardTime", UINT32, 98);
|
||||||
@@ -195,6 +197,7 @@ CONSTRUCT_TYPED_SFIELD(sfTakerPaysCurrency, "TakerPaysCurrency", UINT160,
|
|||||||
CONSTRUCT_TYPED_SFIELD(sfTakerPaysIssuer, "TakerPaysIssuer", UINT160, 2);
|
CONSTRUCT_TYPED_SFIELD(sfTakerPaysIssuer, "TakerPaysIssuer", UINT160, 2);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfTakerGetsCurrency, "TakerGetsCurrency", UINT160, 3);
|
CONSTRUCT_TYPED_SFIELD(sfTakerGetsCurrency, "TakerGetsCurrency", UINT160, 3);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfTakerGetsIssuer, "TakerGetsIssuer", UINT160, 4);
|
CONSTRUCT_TYPED_SFIELD(sfTakerGetsIssuer, "TakerGetsIssuer", UINT160, 4);
|
||||||
|
CONSTRUCT_TYPED_SFIELD(sfCurrency, "Currency", UINT160, 5);
|
||||||
|
|
||||||
// 256-bit (common)
|
// 256-bit (common)
|
||||||
CONSTRUCT_TYPED_SFIELD(sfLedgerHash, "LedgerHash", UINT256, 1);
|
CONSTRUCT_TYPED_SFIELD(sfLedgerHash, "LedgerHash", UINT256, 1);
|
||||||
@@ -236,6 +239,8 @@ CONSTRUCT_TYPED_SFIELD(sfURITokenID, "URITokenID", UINT256,
|
|||||||
CONSTRUCT_TYPED_SFIELD(sfGovernanceFlags, "GovernanceFlags", UINT256, 99);
|
CONSTRUCT_TYPED_SFIELD(sfGovernanceFlags, "GovernanceFlags", UINT256, 99);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfGovernanceMarks, "GovernanceMarks", UINT256, 98);
|
CONSTRUCT_TYPED_SFIELD(sfGovernanceMarks, "GovernanceMarks", UINT256, 98);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfEmittedTxnID, "EmittedTxnID", UINT256, 97);
|
CONSTRUCT_TYPED_SFIELD(sfEmittedTxnID, "EmittedTxnID", UINT256, 97);
|
||||||
|
CONSTRUCT_TYPED_SFIELD(sfOptionID, "OptionID", UINT256, 96);
|
||||||
|
CONSTRUCT_TYPED_SFIELD(sfSwapID, "SwapID", UINT256, 95);
|
||||||
|
|
||||||
// currency amount (common)
|
// currency amount (common)
|
||||||
CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1);
|
CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1);
|
||||||
@@ -261,6 +266,7 @@ CONSTRUCT_TYPED_SFIELD(sfLockedBalance, "LockedBalance", AMOUNT,
|
|||||||
CONSTRUCT_TYPED_SFIELD(sfBaseFeeDrops, "BaseFeeDrops", AMOUNT, 22);
|
CONSTRUCT_TYPED_SFIELD(sfBaseFeeDrops, "BaseFeeDrops", AMOUNT, 22);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfReserveBaseDrops, "ReserveBaseDrops", AMOUNT, 23);
|
CONSTRUCT_TYPED_SFIELD(sfReserveBaseDrops, "ReserveBaseDrops", AMOUNT, 23);
|
||||||
CONSTRUCT_TYPED_SFIELD(sfReserveIncrementDrops, "ReserveIncrementDrops", AMOUNT, 24);
|
CONSTRUCT_TYPED_SFIELD(sfReserveIncrementDrops, "ReserveIncrementDrops", AMOUNT, 24);
|
||||||
|
CONSTRUCT_TYPED_SFIELD(sfStrikePrice, "StrikePrice", AMOUNT, 25);
|
||||||
|
|
||||||
// variable length (common)
|
// variable length (common)
|
||||||
CONSTRUCT_TYPED_SFIELD(sfPublicKey, "PublicKey", VL, 1);
|
CONSTRUCT_TYPED_SFIELD(sfPublicKey, "PublicKey", VL, 1);
|
||||||
|
|||||||
@@ -456,6 +456,37 @@ TxFormats::TxFormats()
|
|||||||
{sfTicketSequence, soeOPTIONAL},
|
{sfTicketSequence, soeOPTIONAL},
|
||||||
},
|
},
|
||||||
commonFields);
|
commonFields);
|
||||||
|
|
||||||
|
add(jss::OptionList,
|
||||||
|
ttOPTION_LIST,
|
||||||
|
{
|
||||||
|
{sfStrikePrice, soeREQUIRED},
|
||||||
|
{sfIssuer, soeREQUIRED},
|
||||||
|
{sfCurrency, soeREQUIRED},
|
||||||
|
{sfExpiration, soeREQUIRED},
|
||||||
|
{sfTicketSequence, soeOPTIONAL},
|
||||||
|
},
|
||||||
|
commonFields);
|
||||||
|
|
||||||
|
add(jss::OptionCreate,
|
||||||
|
ttOPTION_CREATE,
|
||||||
|
{
|
||||||
|
{sfOptionID, soeREQUIRED}, // Option ID
|
||||||
|
{sfAmount, soeREQUIRED}, // Premium
|
||||||
|
{sfQuantity, soeREQUIRED}, // Quantity
|
||||||
|
{sfSwapID, soeOPTIONAL}, // Swap ID
|
||||||
|
{sfTicketSequence, soeOPTIONAL},
|
||||||
|
},
|
||||||
|
commonFields);
|
||||||
|
|
||||||
|
add(jss::OptionExercise,
|
||||||
|
ttOPTION_EXERCISE,
|
||||||
|
{
|
||||||
|
{sfOptionID, soeREQUIRED},
|
||||||
|
{sfSwapID, soeOPTIONAL},
|
||||||
|
{sfTicketSequence, soeOPTIONAL},
|
||||||
|
},
|
||||||
|
commonFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
TxFormats const&
|
TxFormats const&
|
||||||
|
|||||||
@@ -111,8 +111,13 @@ JSS(Offer); // ledger type.
|
|||||||
JSS(OfferCancel); // transaction type.
|
JSS(OfferCancel); // transaction type.
|
||||||
JSS(OfferCreate); // transaction type.
|
JSS(OfferCreate); // transaction type.
|
||||||
JSS(OfferSequence); // field.
|
JSS(OfferSequence); // field.
|
||||||
|
JSS(Option); // ledger type.
|
||||||
|
JSS(OptionCreate); // transaction type.
|
||||||
|
JSS(OptionExercise); // transaction type.
|
||||||
|
JSS(OptionList); // transaction type.
|
||||||
|
JSS(OptionOffer); // ledger type.
|
||||||
JSS(Paths); // in/out: TransactionSign
|
JSS(Paths); // in/out: TransactionSign
|
||||||
JSS(PayChannel); // ledger type.
|
JSS(PayChannel); // ledger type.E
|
||||||
JSS(Payment); // transaction type.
|
JSS(Payment); // transaction type.
|
||||||
JSS(PaymentChannelClaim); // transaction type.
|
JSS(PaymentChannelClaim); // transaction type.
|
||||||
JSS(PaymentChannelCreate); // transaction type.
|
JSS(PaymentChannelCreate); // transaction type.
|
||||||
@@ -510,6 +515,7 @@ JSS(open); // out: handlers/Ledger
|
|||||||
JSS(open_ledger_cost); // out: SubmitTransaction
|
JSS(open_ledger_cost); // out: SubmitTransaction
|
||||||
JSS(open_ledger_fee); // out: TxQ
|
JSS(open_ledger_fee); // out: TxQ
|
||||||
JSS(open_ledger_level); // out: TxQ
|
JSS(open_ledger_level); // out: TxQ
|
||||||
|
JSS(option); // out: AccountObjects
|
||||||
JSS(owner); // in: LedgerEntry, out: NetworkOPs
|
JSS(owner); // in: LedgerEntry, out: NetworkOPs
|
||||||
JSS(owner_funds); // in/out: Ledger, NetworkOPs, AcceptedLedgerTx
|
JSS(owner_funds); // in/out: Ledger, NetworkOPs, AcceptedLedgerTx
|
||||||
JSS(page_index);
|
JSS(page_index);
|
||||||
@@ -630,6 +636,7 @@ JSS(stop_history_tx_only); // in: Unsubscribe, stop history tx stream
|
|||||||
JSS(storedSeqs); // out: NodeToShardStatus
|
JSS(storedSeqs); // out: NodeToShardStatus
|
||||||
JSS(streams); // in: Subscribe, Unsubscribe
|
JSS(streams); // in: Subscribe, Unsubscribe
|
||||||
JSS(strict); // in: AccountCurrencies, AccountInfo
|
JSS(strict); // in: AccountCurrencies, AccountInfo
|
||||||
|
JSS(strike_price); // in: NetworkOPs
|
||||||
JSS(sub_index); // in: LedgerEntry
|
JSS(sub_index); // in: LedgerEntry
|
||||||
JSS(subcommand); // in: PathFind
|
JSS(subcommand); // in: PathFind
|
||||||
JSS(success); // rpc
|
JSS(success); // rpc
|
||||||
|
|||||||
@@ -189,8 +189,8 @@ doAccountObjects(RPC::JsonContext& context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ledger->exists(keylet::account(accountID)))
|
// if (!ledger->exists(keylet::account(accountID)))
|
||||||
return rpcError(rpcACT_NOT_FOUND);
|
// return rpcError(rpcACT_NOT_FOUND);
|
||||||
|
|
||||||
std::optional<std::vector<LedgerEntryType>> typeFilter;
|
std::optional<std::vector<LedgerEntryType>> typeFilter;
|
||||||
|
|
||||||
@@ -208,6 +208,7 @@ doAccountObjects(RPC::JsonContext& context)
|
|||||||
{jss::hook, ltHOOK},
|
{jss::hook, ltHOOK},
|
||||||
{jss::payment_channel, ltPAYCHAN},
|
{jss::payment_channel, ltPAYCHAN},
|
||||||
{jss::uri_token, ltURI_TOKEN},
|
{jss::uri_token, ltURI_TOKEN},
|
||||||
|
{jss::option, ltOPTION},
|
||||||
{jss::state, ltRIPPLE_STATE}};
|
{jss::state, ltRIPPLE_STATE}};
|
||||||
|
|
||||||
typeFilter.emplace();
|
typeFilter.emplace();
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ doNodeToShard(RPC::JsonContext&);
|
|||||||
Json::Value
|
Json::Value
|
||||||
doNoRippleCheck(RPC::JsonContext&);
|
doNoRippleCheck(RPC::JsonContext&);
|
||||||
Json::Value
|
Json::Value
|
||||||
|
doOptionBookOffers(RPC::JsonContext&);
|
||||||
|
Json::Value
|
||||||
doOwnerInfo(RPC::JsonContext&);
|
doOwnerInfo(RPC::JsonContext&);
|
||||||
Json::Value
|
Json::Value
|
||||||
doPathFind(RPC::JsonContext&);
|
doPathFind(RPC::JsonContext&);
|
||||||
|
|||||||
160
src/ripple/rpc/handlers/OptionBookOffers.cpp
Normal file
160
src/ripple/rpc/handlers/OptionBookOffers.cpp
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012-2014 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/app/main/Application.h>
|
||||||
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
|
#include <ripple/basics/Log.h>
|
||||||
|
#include <ripple/ledger/ReadView.h>
|
||||||
|
#include <ripple/net/RPCErr.h>
|
||||||
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
|
#include <ripple/protocol/UintTypes.h>
|
||||||
|
#include <ripple/protocol/jss.h>
|
||||||
|
#include <ripple/resource/Fees.h>
|
||||||
|
#include <ripple/rpc/Context.h>
|
||||||
|
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
Json::Value
|
||||||
|
doOptionBookOffers(RPC::JsonContext& context)
|
||||||
|
{
|
||||||
|
// VFALCO TODO Here is a terrible place for this kind of business
|
||||||
|
// logic. It needs to be moved elsewhere and documented,
|
||||||
|
// and encapsulated into a function.
|
||||||
|
if (context.app.getJobQueue().getJobCountGE(jtCLIENT) > 200)
|
||||||
|
return rpcError(rpcTOO_BUSY);
|
||||||
|
|
||||||
|
std::shared_ptr<ReadView const> lpLedger;
|
||||||
|
auto jvResult = RPC::lookupLedger(lpLedger, context);
|
||||||
|
|
||||||
|
if (!lpLedger)
|
||||||
|
return jvResult;
|
||||||
|
|
||||||
|
if (!context.params.isMember(jss::strike_price))
|
||||||
|
return RPC::missing_field_error(jss::strike_price);
|
||||||
|
|
||||||
|
Json::Value const& strike_price = context.params[jss::strike_price];
|
||||||
|
|
||||||
|
if (!strike_price.isObjectOrNull())
|
||||||
|
return RPC::object_field_error(jss::strike_price);
|
||||||
|
|
||||||
|
// if (!taker_gets.isObjectOrNull())
|
||||||
|
// return RPC::object_field_error(jss::taker_gets);
|
||||||
|
|
||||||
|
if (!strike_price.isMember(jss::currency))
|
||||||
|
return RPC::missing_field_error("strike_price.currency");
|
||||||
|
|
||||||
|
if (!strike_price[jss::currency].isString())
|
||||||
|
return RPC::expected_field_error("strike_price.currency", "string");
|
||||||
|
|
||||||
|
// if (!taker_gets.isMember(jss::currency))
|
||||||
|
// return RPC::missing_field_error("taker_gets.currency");
|
||||||
|
|
||||||
|
// if (!taker_gets[jss::currency].isString())
|
||||||
|
// return RPC::expected_field_error("taker_gets.currency", "string");
|
||||||
|
|
||||||
|
Currency pay_currency;
|
||||||
|
|
||||||
|
if (!to_currency(pay_currency, strike_price[jss::currency].asString()))
|
||||||
|
{
|
||||||
|
JLOG(context.j.info()) << "Bad strike_price currency.";
|
||||||
|
return RPC::make_error(
|
||||||
|
rpcSRC_CUR_MALFORMED,
|
||||||
|
"Invalid field 'strike_price.currency', bad currency.");
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountID pay_issuer;
|
||||||
|
|
||||||
|
if (strike_price.isMember(jss::issuer))
|
||||||
|
{
|
||||||
|
if (!strike_price[jss::issuer].isString())
|
||||||
|
return RPC::expected_field_error("strike_price.issuer", "string");
|
||||||
|
|
||||||
|
if (!to_issuer(pay_issuer, strike_price[jss::issuer].asString()))
|
||||||
|
return RPC::make_error(
|
||||||
|
rpcSRC_ISR_MALFORMED,
|
||||||
|
"Invalid field 'strike_price.issuer', bad issuer.");
|
||||||
|
|
||||||
|
if (pay_issuer == noAccount())
|
||||||
|
return RPC::make_error(
|
||||||
|
rpcSRC_ISR_MALFORMED,
|
||||||
|
"Invalid field 'strike_price.issuer', bad issuer account one.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pay_issuer = xrpAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::uint64_t> pay_value;
|
||||||
|
if (strike_price.isMember(jss::value))
|
||||||
|
{
|
||||||
|
if (!strike_price[jss::value].isString())
|
||||||
|
return RPC::expected_field_error("strike_price.value", "string");
|
||||||
|
|
||||||
|
pay_value = strike_price[jss::value].asInt();
|
||||||
|
if (!pay_value)
|
||||||
|
return RPC::invalid_field_error(jss::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isXRP(pay_currency) && !isXRP(pay_issuer))
|
||||||
|
return RPC::make_error(
|
||||||
|
rpcSRC_ISR_MALFORMED,
|
||||||
|
"Unneeded field 'taker_pays.issuer' for "
|
||||||
|
"XRP currency specification.");
|
||||||
|
|
||||||
|
if (!isXRP(pay_currency) && isXRP(pay_issuer))
|
||||||
|
return RPC::make_error(
|
||||||
|
rpcSRC_ISR_MALFORMED,
|
||||||
|
"Invalid field 'taker_pays.issuer', expected non-XRP issuer.");
|
||||||
|
|
||||||
|
std::optional<std::uint32_t> expiration;
|
||||||
|
if (context.params.isMember(jss::expiration))
|
||||||
|
{
|
||||||
|
if (!context.params[jss::expiration].isString())
|
||||||
|
return RPC::expected_field_error(jss::expiration, "string");
|
||||||
|
|
||||||
|
expiration = context.params[jss::expiration].asInt();
|
||||||
|
if (!expiration)
|
||||||
|
return RPC::invalid_field_error(jss::expiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int limit;
|
||||||
|
if (auto err = readLimitField(limit, RPC::Tuning::bookOffers, context))
|
||||||
|
return *err;
|
||||||
|
|
||||||
|
bool const bProof(context.params.isMember(jss::proof));
|
||||||
|
|
||||||
|
Json::Value const jvMarker(
|
||||||
|
context.params.isMember(jss::marker) ? context.params[jss::marker]
|
||||||
|
: Json::Value(Json::nullValue));
|
||||||
|
|
||||||
|
context.netOps.getOptionBookPage(
|
||||||
|
lpLedger,
|
||||||
|
STAmount(Issue(pay_currency, pay_issuer), pay_value ? *pay_value : 0),
|
||||||
|
expiration ? *expiration : 0,
|
||||||
|
limit,
|
||||||
|
jvMarker,
|
||||||
|
jvResult);
|
||||||
|
|
||||||
|
context.loadType = Resource::feeMediumBurdenRPC;
|
||||||
|
|
||||||
|
return jvResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <ripple/app/main/Application.h>
|
#include <ripple/app/main/Application.h>
|
||||||
#include <ripple/app/misc/NetworkOPs.h>
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
|
#include <ripple/app/reporting/P2pProxy.h>
|
||||||
#include <ripple/protocol/jss.h>
|
#include <ripple/protocol/jss.h>
|
||||||
#include <ripple/rpc/Context.h>
|
#include <ripple/rpc/Context.h>
|
||||||
#include <ripple/rpc/Role.h>
|
#include <ripple/rpc/Role.h>
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ Handler const handlerArray[]{
|
|||||||
{"nft_sell_offers", byRef(&doNFTSellOffers), Role::USER, NO_CONDITION},
|
{"nft_sell_offers", byRef(&doNFTSellOffers), Role::USER, NO_CONDITION},
|
||||||
{"node_to_shard", byRef(&doNodeToShard), Role::ADMIN, NO_CONDITION},
|
{"node_to_shard", byRef(&doNodeToShard), Role::ADMIN, NO_CONDITION},
|
||||||
{"noripple_check", byRef(&doNoRippleCheck), Role::USER, NO_CONDITION},
|
{"noripple_check", byRef(&doNoRippleCheck), Role::USER, NO_CONDITION},
|
||||||
|
{"option_book_offers", byRef(&doOptionBookOffers), Role::USER, NO_CONDITION},
|
||||||
{"owner_info", byRef(&doOwnerInfo), Role::USER, NEEDS_CURRENT_LEDGER},
|
{"owner_info", byRef(&doOwnerInfo), Role::USER, NEEDS_CURRENT_LEDGER},
|
||||||
{"peers", byRef(&doPeers), Role::ADMIN, NO_CONDITION},
|
{"peers", byRef(&doPeers), Role::ADMIN, NO_CONDITION},
|
||||||
{"path_find", byRef(&doPathFind), Role::USER, NEEDS_CURRENT_LEDGER},
|
{"path_find", byRef(&doPathFind), Role::USER, NEEDS_CURRENT_LEDGER},
|
||||||
|
|||||||
@@ -1067,7 +1067,7 @@ chooseLedgerEntryType(Json::Value const& params)
|
|||||||
std::pair<RPC::Status, LedgerEntryType> result{RPC::Status::OK, ltANY};
|
std::pair<RPC::Status, LedgerEntryType> result{RPC::Status::OK, ltANY};
|
||||||
if (params.isMember(jss::type))
|
if (params.isMember(jss::type))
|
||||||
{
|
{
|
||||||
static constexpr std::array<std::pair<char const*, LedgerEntryType>, 22>
|
static constexpr std::array<std::pair<char const*, LedgerEntryType>, 23>
|
||||||
types{
|
types{
|
||||||
{{jss::account, ltACCOUNT_ROOT},
|
{{jss::account, ltACCOUNT_ROOT},
|
||||||
{jss::amendments, ltAMENDMENTS},
|
{jss::amendments, ltAMENDMENTS},
|
||||||
@@ -1083,6 +1083,7 @@ chooseLedgerEntryType(Json::Value const& params)
|
|||||||
{jss::hashes, ltLEDGER_HASHES},
|
{jss::hashes, ltLEDGER_HASHES},
|
||||||
{jss::import_vlseq, ltIMPORT_VLSEQ},
|
{jss::import_vlseq, ltIMPORT_VLSEQ},
|
||||||
{jss::offer, ltOFFER},
|
{jss::offer, ltOFFER},
|
||||||
|
{jss::option, ltOPTION},
|
||||||
{jss::payment_channel, ltPAYCHAN},
|
{jss::payment_channel, ltPAYCHAN},
|
||||||
{jss::uri_token, ltURI_TOKEN},
|
{jss::uri_token, ltURI_TOKEN},
|
||||||
{jss::signer_list, ltSIGNER_LIST},
|
{jss::signer_list, ltSIGNER_LIST},
|
||||||
|
|||||||
@@ -398,7 +398,7 @@ SHAMapInnerNode::canonicalizeChild(
|
|||||||
void
|
void
|
||||||
SHAMapInnerNode::invariants(bool is_root) const
|
SHAMapInnerNode::invariants(bool is_root) const
|
||||||
{
|
{
|
||||||
unsigned count = 0;
|
[[maybe_unused]] unsigned count = 0;
|
||||||
auto [numAllocated, hashes, children] =
|
auto [numAllocated, hashes, children] =
|
||||||
hashesAndChildren_.getHashesAndChildren();
|
hashesAndChildren_.getHashesAndChildren();
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <ripple/app/ledger/LedgerMaster.h>
|
||||||
#include <ripple/protocol/Feature.h>
|
#include <ripple/protocol/Feature.h>
|
||||||
|
#include <ripple/protocol/jss.h>
|
||||||
#include <test/jtx.h>
|
#include <test/jtx.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <ripple/protocol/jss.h>
|
||||||
#include <test/jtx.h>
|
#include <test/jtx.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|||||||
621
src/test/app/Option_test.cpp
Normal file
621
src/test/app/Option_test.cpp
Normal file
@@ -0,0 +1,621 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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(sfLockedBalance))
|
||||||
|
return (*sle)[sfLockedBalance];
|
||||||
|
return STAmount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value
|
||||||
|
optionlist(
|
||||||
|
jtx::Account const& account,
|
||||||
|
std::uint32_t expiration,
|
||||||
|
STAmount const& strikePrice,
|
||||||
|
STAmount const& amount)
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = jss::OptionList;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv[sfStrikePrice.jsonName] = strikePrice.getJson(JsonOptions::none);
|
||||||
|
jv[sfIssuer.jsonName] = to_string(amount.getIssuer());
|
||||||
|
jv[sfCurrency.jsonName] = to_string(amount.getCurrency());
|
||||||
|
jv[sfExpiration.jsonName] = expiration;
|
||||||
|
return jv;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value
|
||||||
|
optioncreate(
|
||||||
|
jtx::Account const& account,
|
||||||
|
uint256 const& optionId,
|
||||||
|
uint32_t const& quantity,
|
||||||
|
STAmount const& premium)
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = jss::OptionCreate;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv[sfOptionID.jsonName] = to_string(optionId);
|
||||||
|
jv[jss::Amount] = premium.getJson(JsonOptions::none);
|
||||||
|
jv[sfQuantity.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::OptionExercise;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv[sfOptionID.jsonName] = to_string(optionId);
|
||||||
|
jv[sfSwapID.jsonName] = to_string(offerId);
|
||||||
|
;
|
||||||
|
return jv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint256
|
||||||
|
getOptionIndex(
|
||||||
|
AccountID const& issuer,
|
||||||
|
Currency const& currency,
|
||||||
|
std::uint64_t const& strike,
|
||||||
|
std::uint32_t expiration)
|
||||||
|
{
|
||||||
|
return keylet::option(issuer, currency, strike, expiration).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint256
|
||||||
|
getOfferIndex(AccountID const& account, std::uint32_t sequence)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
// {
|
||||||
|
// 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), 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::kTrace};
|
||||||
|
|
||||||
|
// 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), 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));
|
||||||
|
|
||||||
|
// auto jrr = getOptionBookOffers(env, XRP(strikePrice), expiration);
|
||||||
|
// std::cout << "RESULT: " << jrr << "\n";
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// auto jrr1 = getOptionBookOffers(env, XRP(strikePrice), expiration);
|
||||||
|
// std::cout << "RESULT1: " << jrr1 << "\n";
|
||||||
|
|
||||||
|
// // 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));
|
||||||
|
|
||||||
|
// auto jrrList = getOptionList(env, zeroAcct);
|
||||||
|
// std::cout << "RESULT LIST: " << jrrList << "\n";
|
||||||
|
// auto jrr2 = getOptionBookOffers(env, XRP(strikePrice), expiration);
|
||||||
|
// std::cout << "RESULT2: " << jrr2 << "\n";
|
||||||
|
// }
|
||||||
|
|
||||||
|
void
|
||||||
|
testIC(FeatureBitset features)
|
||||||
|
{
|
||||||
|
using namespace test::jtx;
|
||||||
|
using namespace std::literals;
|
||||||
|
testcase("ic");
|
||||||
|
|
||||||
|
// test::jtx::Env env{*this, network::makeNetworkConfig(21337), features};
|
||||||
|
test::jtx::Env env{
|
||||||
|
*this,
|
||||||
|
network::makeNetworkConfig(21337),
|
||||||
|
features,
|
||||||
|
nullptr,
|
||||||
|
beast::severities::kTrace};
|
||||||
|
|
||||||
|
auto const feeDrops = env.current()->fees().base;
|
||||||
|
|
||||||
|
auto const writer = Account("alice");
|
||||||
|
auto const buyer = Account("bob");
|
||||||
|
auto const gw = Account("gateway");
|
||||||
|
auto const GME = gw["GME"];
|
||||||
|
auto const USD = gw["USD"];
|
||||||
|
|
||||||
|
env.fund(XRP(100000), writer, buyer, gw);
|
||||||
|
env.close();
|
||||||
|
env.trust(USD(100000), writer, buyer);
|
||||||
|
env.close();
|
||||||
|
env(pay(gw, writer, USD(10000)));
|
||||||
|
env(pay(gw, buyer, USD(10000)));
|
||||||
|
env.close();
|
||||||
|
env.trust(GME(100000), writer, buyer);
|
||||||
|
env.close();
|
||||||
|
env(pay(gw, writer, USD(10000)));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
auto const _exp = env.now() + 1s;
|
||||||
|
auto const expiration = _exp.time_since_epoch().count();
|
||||||
|
uint256 const optionId{getOptionIndex(gw.id(), USD.currency, 20, expiration)};
|
||||||
|
|
||||||
|
auto preWriter = env.balance(writer);
|
||||||
|
auto preWriterGME = env.balance(writer, GME.issue());
|
||||||
|
auto preBuyer = env.balance(buyer);
|
||||||
|
auto preBuyerGME = env.balance(buyer, GME.issue());
|
||||||
|
|
||||||
|
auto const strikePrice = 20;
|
||||||
|
env(optionlist(writer, expiration, USD(strikePrice), GME(0)),
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
BEAST_EXPECT(env.balance(writer) == preWriter - feeDrops);
|
||||||
|
|
||||||
|
auto const strikePrice1 = 25;
|
||||||
|
env(optionlist(writer, expiration, USD(strikePrice1), GME(0)),
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Call - Sell - Open
|
||||||
|
auto const premium = 0.5;
|
||||||
|
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));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
env.balance(writer, GME.issue()) == preWriterGME - GME(quantity));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
lockedValue(env, writer, seq) == GME(quantity));
|
||||||
|
|
||||||
|
auto jrr = getOptionBookOffers(env, GME(strikePrice), expiration);
|
||||||
|
std::cout << "RESULT: " << jrr << "\n";
|
||||||
|
|
||||||
|
preWriter = env.balance(writer);
|
||||||
|
preWriterGME = env.balance(writer, GME.issue());
|
||||||
|
preBuyer = env.balance(buyer);
|
||||||
|
preBuyerGME = env.balance(buyer, GME.issue());
|
||||||
|
|
||||||
|
// // Call - Buy - Open
|
||||||
|
// auto const seq1 = env.seq(buyer);
|
||||||
|
// env(optioncreate(buyer, optionId, quantity, GME(premium)),
|
||||||
|
// ter(tesSUCCESS));
|
||||||
|
// env.close();
|
||||||
|
|
||||||
|
// uint256 const offerId{getOfferIndex(buyer, seq1)};
|
||||||
|
// BEAST_EXPECT(
|
||||||
|
// env.balance(buyer) ==
|
||||||
|
// preBuyer - feeDrops - GME(quantity * premium));
|
||||||
|
// BEAST_EXPECT(
|
||||||
|
// env.balance(writer) ==
|
||||||
|
// preWriter + GME(quantity * premium));
|
||||||
|
|
||||||
|
// preWriter = env.balance(writer);
|
||||||
|
// preBuyer = env.balance(buyer);
|
||||||
|
|
||||||
|
// auto jrr1 = getOptionBookOffers(env, GME(strikePrice), expiration);
|
||||||
|
// std::cout << "RESULT1: " << jrr1 << "\n";
|
||||||
|
|
||||||
|
// // Execute Option
|
||||||
|
// env(optionexecute(buyer, optionId, offerId), ter(tesSUCCESS));
|
||||||
|
// env.close();
|
||||||
|
|
||||||
|
// BEAST_EXPECT(
|
||||||
|
// env.balance(buyer) ==
|
||||||
|
// preBuyer - feeDrops - GME(quantity * strikePrice) + GME(quantity));
|
||||||
|
// BEAST_EXPECT(
|
||||||
|
// env.balance(writer) ==
|
||||||
|
// preWriter + GME(quantity * strikePrice));
|
||||||
|
|
||||||
|
// auto jrrList = getOptionList(env, zeroAcct);
|
||||||
|
// std::cout << "RESULT LIST: " << jrrList << "\n";
|
||||||
|
// auto jrr2 = getOptionBookOffers(env, GME(strikePrice), expiration);
|
||||||
|
// std::cout << "RESULT2: " << jrr2 << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
using namespace test::jtx;
|
||||||
|
auto const sa = supported_amendments();
|
||||||
|
// testBookBuy(sa);
|
||||||
|
// testBookSell(sa);
|
||||||
|
// testOptionBookBuy(sa);
|
||||||
|
// testEnabled(sa);
|
||||||
|
testIC(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 <ripple/protocol/jss.h>
|
||||||
#include <test/jtx/escrow.h>
|
#include <test/jtx/escrow.h>
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <ripple/protocol/TxFlags.h>
|
||||||
#include <ripple/protocol/jss.h>
|
#include <ripple/protocol/jss.h>
|
||||||
#include <test/jtx/paychan.h>
|
#include <test/jtx/paychan.h>
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <ripple/app/misc/AmendmentTable.h>
|
#include <ripple/app/misc/AmendmentTable.h>
|
||||||
#include <ripple/beast/unit_test.h>
|
#include <ripple/beast/unit_test.h>
|
||||||
|
#include <ripple/json/json_reader.h>
|
||||||
#include <ripple/protocol/Feature.h>
|
#include <ripple/protocol/Feature.h>
|
||||||
#include <ripple/protocol/jss.h>
|
#include <ripple/protocol/jss.h>
|
||||||
#include <test/jtx.h>
|
#include <test/jtx.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user