mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-04 18:55:49 +00:00
Compare commits
8 Commits
mysql
...
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_fallbackStyle": "{ ColumnLimit: 0 }",
|
||||
"[cpp]":{
|
||||
"editor.wordBasedSuggestions": false,
|
||||
"editor.wordBasedSuggestions": "off",
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.semanticHighlighting.enabled": true,
|
||||
"editor.tabSize": 4,
|
||||
|
||||
@@ -88,6 +88,7 @@ target_sources (xrpl_core PRIVATE
|
||||
src/ripple/protocol/impl/Issue.cpp
|
||||
src/ripple/protocol/impl/Keylet.cpp
|
||||
src/ripple/protocol/impl/LedgerFormats.cpp
|
||||
src/ripple/protocol/impl/Option.cpp
|
||||
src/ripple/protocol/impl/PublicKey.cpp
|
||||
src/ripple/protocol/impl/Quality.cpp
|
||||
src/ripple/protocol/impl/Rate2.cpp
|
||||
@@ -219,6 +220,7 @@ install (
|
||||
src/ripple/protocol/Keylet.h
|
||||
src/ripple/protocol/KnownFormats.h
|
||||
src/ripple/protocol/LedgerFormats.h
|
||||
src/ripple/protocol/Option.h
|
||||
src/ripple/protocol/Protocol.h
|
||||
src/ripple/protocol/PublicKey.h
|
||||
src/ripple/protocol/Quality.h
|
||||
@@ -446,6 +448,9 @@ target_sources (rippled PRIVATE
|
||||
src/ripple/app/tx/impl/NFTokenCreateOffer.cpp
|
||||
src/ripple/app/tx/impl/NFTokenMint.cpp
|
||||
src/ripple/app/tx/impl/OfferStream.cpp
|
||||
src/ripple/app/tx/impl/OptionCreate.cpp
|
||||
src/ripple/app/tx/impl/OptionExercise.cpp
|
||||
src/ripple/app/tx/impl/OptionList.cpp
|
||||
src/ripple/app/tx/impl/PayChan.cpp
|
||||
src/ripple/app/tx/impl/Payment.cpp
|
||||
src/ripple/app/tx/impl/SetAccount.cpp
|
||||
@@ -629,6 +634,7 @@ target_sources (rippled PRIVATE
|
||||
src/ripple/rpc/handlers/NFTOffers.cpp
|
||||
src/ripple/rpc/handlers/NodeToShard.cpp
|
||||
src/ripple/rpc/handlers/NoRippleCheck.cpp
|
||||
src/ripple/rpc/handlers/OptionBookOffers.cpp
|
||||
src/ripple/rpc/handlers/OwnerInfo.cpp
|
||||
src/ripple/rpc/handlers/PathFind.cpp
|
||||
src/ripple/rpc/handlers/PayChanClaim.cpp
|
||||
@@ -732,6 +738,7 @@ if (tests)
|
||||
src/test/app/NFTokenBurn_test.cpp
|
||||
src/test/app/NFTokenDir_test.cpp
|
||||
src/test/app/OfferStream_test.cpp
|
||||
src/test/app/Option_test.cpp
|
||||
src/test/app/Offer_test.cpp
|
||||
src/test/app/OversizeMeta_test.cpp
|
||||
src/test/app/Path_test.cpp
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#include <ripple/app/hook/applyHook.h>
|
||||
#include <ripple/app/ledger/OpenLedger.h>
|
||||
#include <ripple/app/ledger/TransactionMaster.h>
|
||||
#include <ripple/app/misc/HashRouter.h>
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/app/misc/Transaction.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/app/tx/impl/Import.h>
|
||||
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/Slice.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/st.h>
|
||||
#include <ripple/protocol/tokens.h>
|
||||
#include <boost/multiprecision/cpp_dec_float.hpp>
|
||||
#include <any>
|
||||
|
||||
@@ -85,9 +85,11 @@ OrderBookDB::update(std::shared_ptr<ReadView const> const& ledger)
|
||||
return;
|
||||
}
|
||||
|
||||
decltype(optionBooks_) optionBooks;
|
||||
decltype(allBooks_) allBooks;
|
||||
decltype(xrpBooks_) xrpBooks;
|
||||
|
||||
optionBooks.reserve(optionBooks_.size());
|
||||
allBooks.reserve(allBooks_.size());
|
||||
xrpBooks.reserve(xrpBooks_.size());
|
||||
|
||||
@@ -161,6 +163,14 @@ OrderBookDB::addOrderBook(Book const& book)
|
||||
xrpBooks_.insert(book.in);
|
||||
}
|
||||
|
||||
void
|
||||
OrderBookDB::addOptionOrderBook(Option const& option)
|
||||
{
|
||||
std::lock_guard sl(mLock);
|
||||
|
||||
optionBooks_.insert(option.issue);
|
||||
}
|
||||
|
||||
// return list of all orderbooks that want this issuerID and currencyID
|
||||
std::vector<Book>
|
||||
OrderBookDB::getBooksByTakerPays(Issue const& issue)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <ripple/app/ledger/AcceptedLedgerTx.h>
|
||||
#include <ripple/app/ledger/BookListeners.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/protocol/Option.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace ripple {
|
||||
@@ -40,6 +41,9 @@ public:
|
||||
void
|
||||
addOrderBook(Book const&);
|
||||
|
||||
void
|
||||
addOptionOrderBook(Option const&);
|
||||
|
||||
/** @return a list of all orderbooks that want this issuerID and currencyID.
|
||||
*/
|
||||
std::vector<Book>
|
||||
@@ -55,6 +59,7 @@ public:
|
||||
|
||||
BookListeners::pointer
|
||||
getBookListeners(Book const&);
|
||||
|
||||
BookListeners::pointer
|
||||
makeBookListeners(Book const&);
|
||||
|
||||
@@ -71,6 +76,9 @@ private:
|
||||
// Maps order books by "issue in" to "issue out":
|
||||
hardened_hash_map<Issue, hardened_hash_set<Issue>> allBooks_;
|
||||
|
||||
// does an order book to XRP exist
|
||||
hash_set<Issue> optionBooks_;
|
||||
|
||||
// does an order book to XRP exist
|
||||
hash_set<Issue> xrpBooks_;
|
||||
|
||||
|
||||
@@ -350,6 +350,15 @@ public:
|
||||
unsigned int iLimit,
|
||||
Json::Value const& jvMarker,
|
||||
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.
|
||||
bool
|
||||
@@ -4581,6 +4590,114 @@ NetworkOPsImp::getBookPage(
|
||||
|
||||
#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
|
||||
NetworkOPsImp::collect_metrics()
|
||||
{
|
||||
|
||||
@@ -163,6 +163,15 @@ public:
|
||||
unsigned int iLimit,
|
||||
Json::Value const& jvMarker,
|
||||
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]))
|
||||
drops_ -= (*before)[sfAmount].xrp().drops();
|
||||
break;
|
||||
case ltOPTION_OFFER:
|
||||
if (isXRP((*before)[sfLockedBalance]))
|
||||
drops_ -= (*before)[sfLockedBalance].xrp().drops();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -131,6 +135,10 @@ XRPNotCreated::visitEntry(
|
||||
if (!isDelete && isXRP((*after)[sfAmount]))
|
||||
drops_ += (*after)[sfAmount].xrp().drops();
|
||||
break;
|
||||
case ltOPTION_OFFER:
|
||||
if (!isDelete && isXRP((*after)[sfLockedBalance]))
|
||||
drops_ += (*after)[sfLockedBalance].xrp().drops();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -492,6 +500,8 @@ LedgerEntryTypesMatch::visitEntry(
|
||||
case ltURI_TOKEN:
|
||||
case ltIMPORT_VLSEQ:
|
||||
case ltUNL_REPORT:
|
||||
case ltOPTION:
|
||||
case ltOPTION_OFFER:
|
||||
break;
|
||||
default:
|
||||
invalidTypeAdded_ = true;
|
||||
|
||||
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/CashCheck.h>
|
||||
#include <ripple/app/tx/impl/Change.h>
|
||||
#include <ripple/app/tx/impl/ClaimReward.h>
|
||||
#include <ripple/app/tx/impl/CreateCheck.h>
|
||||
#include <ripple/app/tx/impl/CreateOffer.h>
|
||||
#include <ripple/app/tx/impl/CreateTicket.h>
|
||||
@@ -37,8 +38,12 @@
|
||||
#include <ripple/app/tx/impl/NFTokenCancelOffer.h>
|
||||
#include <ripple/app/tx/impl/NFTokenCreateOffer.h>
|
||||
#include <ripple/app/tx/impl/NFTokenMint.h>
|
||||
#include <ripple/app/tx/impl/OptionCreate.h>
|
||||
#include <ripple/app/tx/impl/OptionExercise.h>
|
||||
#include <ripple/app/tx/impl/OptionList.h>
|
||||
#include <ripple/app/tx/impl/PayChan.h>
|
||||
#include <ripple/app/tx/impl/Payment.h>
|
||||
#include <ripple/app/tx/impl/Remit.h>
|
||||
#include <ripple/app/tx/impl/SetAccount.h>
|
||||
#include <ripple/app/tx/impl/SetHook.h>
|
||||
#include <ripple/app/tx/impl/SetRegularKey.h>
|
||||
@@ -122,6 +127,12 @@ invoke_preflight(PreflightContext const& ctx)
|
||||
return invoke_preflight_helper<EscrowFinish>(ctx);
|
||||
case ttESCROW_CANCEL:
|
||||
return invoke_preflight_helper<EscrowCancel>(ctx);
|
||||
case ttOPTION_CREATE:
|
||||
return invoke_preflight_helper<OptionCreate>(ctx);
|
||||
case ttOPTION_EXERCISE:
|
||||
return invoke_preflight_helper<OptionExercise>(ctx);
|
||||
case ttOPTION_LIST:
|
||||
return invoke_preflight_helper<OptionList>(ctx);
|
||||
case ttPAYCHAN_CLAIM:
|
||||
return invoke_preflight_helper<PayChanClaim>(ctx);
|
||||
case ttPAYCHAN_CREATE:
|
||||
@@ -243,6 +254,12 @@ invoke_preclaim(PreclaimContext const& ctx)
|
||||
return invoke_preclaim<EscrowFinish>(ctx);
|
||||
case ttESCROW_CANCEL:
|
||||
return invoke_preclaim<EscrowCancel>(ctx);
|
||||
case ttOPTION_CREATE:
|
||||
return invoke_preclaim<OptionCreate>(ctx);
|
||||
case ttOPTION_EXERCISE:
|
||||
return invoke_preclaim<OptionExercise>(ctx);
|
||||
case ttOPTION_LIST:
|
||||
return invoke_preclaim<OptionList>(ctx);
|
||||
case ttPAYCHAN_CLAIM:
|
||||
return invoke_preclaim<PayChanClaim>(ctx);
|
||||
case ttPAYCHAN_CREATE:
|
||||
@@ -326,6 +343,12 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
return EscrowFinish::calculateBaseFee(view, tx);
|
||||
case ttESCROW_CANCEL:
|
||||
return EscrowCancel::calculateBaseFee(view, tx);
|
||||
case ttOPTION_CREATE:
|
||||
return OptionCreate::calculateBaseFee(view, tx);
|
||||
case ttOPTION_EXERCISE:
|
||||
return OptionExercise::calculateBaseFee(view, tx);
|
||||
case ttOPTION_LIST:
|
||||
return OptionList::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_CLAIM:
|
||||
return PayChanClaim::calculateBaseFee(view, tx);
|
||||
case ttPAYCHAN_CREATE:
|
||||
@@ -469,6 +492,18 @@ invoke_apply(ApplyContext& ctx)
|
||||
EscrowCancel p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttOPTION_CREATE: {
|
||||
OptionCreate p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttOPTION_EXERCISE: {
|
||||
OptionExercise p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttOPTION_LIST: {
|
||||
OptionList p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttPAYCHAN_CLAIM: {
|
||||
PayChanClaim p(ctx);
|
||||
return p();
|
||||
|
||||
@@ -30,7 +30,7 @@ mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
|
||||
{
|
||||
using namespace boost::multiprecision;
|
||||
|
||||
uint128_t result;
|
||||
boost::multiprecision::uint128_t result;
|
||||
result = multiply(result, value, mul);
|
||||
|
||||
result /= div;
|
||||
|
||||
@@ -145,111 +145,78 @@ private:
|
||||
};
|
||||
|
||||
// VFALCO TODO This should only be enabled for maps.
|
||||
class pair_value_compare
|
||||
: public beast::detail::empty_base_optimization<Compare>
|
||||
#ifdef _LIBCPP_VERSION
|
||||
,
|
||||
public std::binary_function<value_type, value_type, bool>
|
||||
#endif
|
||||
class pair_value_compare : public Compare
|
||||
{
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument = value_type;
|
||||
using second_argument = value_type;
|
||||
using result_type = bool;
|
||||
#endif
|
||||
|
||||
bool
|
||||
operator()(value_type const& lhs, value_type const& rhs) const
|
||||
{
|
||||
return this->member()(lhs.first, rhs.first);
|
||||
return Compare::operator()(lhs.first, rhs.first);
|
||||
}
|
||||
|
||||
pair_value_compare()
|
||||
{
|
||||
}
|
||||
|
||||
pair_value_compare(pair_value_compare const& other)
|
||||
: beast::detail::empty_base_optimization<Compare>(other)
|
||||
pair_value_compare(pair_value_compare const& other) : Compare(other)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend aged_ordered_container;
|
||||
|
||||
pair_value_compare(Compare const& compare)
|
||||
: beast::detail::empty_base_optimization<Compare>(compare)
|
||||
pair_value_compare(Compare const& compare) : Compare(compare)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Compares value_type against element, used in insert_check
|
||||
// VFALCO TODO hoist to remove template argument dependencies
|
||||
class KeyValueCompare
|
||||
: public beast::detail::empty_base_optimization<Compare>
|
||||
#ifdef _LIBCPP_VERSION
|
||||
,
|
||||
public std::binary_function<Key, element, bool>
|
||||
#endif
|
||||
class KeyValueCompare : public Compare
|
||||
{
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument = Key;
|
||||
using second_argument = element;
|
||||
using result_type = bool;
|
||||
#endif
|
||||
|
||||
KeyValueCompare() = default;
|
||||
|
||||
KeyValueCompare(Compare const& compare)
|
||||
: beast::detail::empty_base_optimization<Compare>(compare)
|
||||
KeyValueCompare(Compare const& compare) : Compare(compare)
|
||||
{
|
||||
}
|
||||
|
||||
// VFALCO NOTE WE might want only to enable these overloads
|
||||
// if Compare has is_transparent
|
||||
#if 0
|
||||
template <class K>
|
||||
bool operator() (K const& k, element const& e) const
|
||||
{
|
||||
return this->member() (k, extract (e.value));
|
||||
}
|
||||
|
||||
template <class K>
|
||||
bool operator() (element const& e, K const& k) const
|
||||
{
|
||||
return this->member() (extract (e.value), k);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
operator()(Key const& k, element const& e) const
|
||||
{
|
||||
return this->member()(k, extract(e.value));
|
||||
return Compare::operator()(k, extract(e.value));
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& e, Key const& k) const
|
||||
{
|
||||
return this->member()(extract(e.value), k);
|
||||
return Compare::operator()(extract(e.value), k);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& x, element const& y) const
|
||||
{
|
||||
return this->member()(extract(x.value), extract(y.value));
|
||||
return Compare::operator()(extract(x.value), extract(y.value));
|
||||
}
|
||||
|
||||
Compare&
|
||||
compare()
|
||||
{
|
||||
return beast::detail::empty_base_optimization<Compare>::member();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Compare const&
|
||||
compare() const
|
||||
{
|
||||
return beast::detail::empty_base_optimization<Compare>::member();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -148,115 +148,84 @@ private:
|
||||
};
|
||||
|
||||
// VFALCO TODO hoist to remove template argument dependencies
|
||||
class ValueHash : private beast::detail::empty_base_optimization<Hash>
|
||||
#ifdef _LIBCPP_VERSION
|
||||
,
|
||||
public std::unary_function<element, std::size_t>
|
||||
#endif
|
||||
class ValueHash : public Hash
|
||||
{
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using argument_type = element;
|
||||
using result_type = size_t;
|
||||
#endif
|
||||
|
||||
ValueHash()
|
||||
{
|
||||
}
|
||||
|
||||
ValueHash(Hash const& h)
|
||||
: beast::detail::empty_base_optimization<Hash>(h)
|
||||
ValueHash(Hash const& h) : Hash(h)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t
|
||||
operator()(element const& e) const
|
||||
{
|
||||
return this->member()(extract(e.value));
|
||||
return Hash::operator()(extract(e.value));
|
||||
}
|
||||
|
||||
Hash&
|
||||
hash_function()
|
||||
{
|
||||
return this->member();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Hash const&
|
||||
hash_function() const
|
||||
{
|
||||
return this->member();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Compares value_type against element, used in find/insert_check
|
||||
// VFALCO TODO hoist to remove template argument dependencies
|
||||
class KeyValueEqual
|
||||
: private beast::detail::empty_base_optimization<KeyEqual>
|
||||
#ifdef _LIBCPP_VERSION
|
||||
,
|
||||
public std::binary_function<Key, element, bool>
|
||||
#endif
|
||||
class KeyValueEqual : public KeyEqual
|
||||
{
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument_type = Key;
|
||||
using second_argument_type = element;
|
||||
using result_type = bool;
|
||||
#endif
|
||||
|
||||
KeyValueEqual()
|
||||
{
|
||||
}
|
||||
|
||||
KeyValueEqual(KeyEqual const& keyEqual)
|
||||
: beast::detail::empty_base_optimization<KeyEqual>(keyEqual)
|
||||
KeyValueEqual(KeyEqual const& keyEqual) : KeyEqual(keyEqual)
|
||||
{
|
||||
}
|
||||
|
||||
// VFALCO NOTE WE might want only to enable these overloads
|
||||
// if KeyEqual has is_transparent
|
||||
#if 0
|
||||
template <class K>
|
||||
bool operator() (K const& k, element const& e) const
|
||||
{
|
||||
return this->member() (k, extract (e.value));
|
||||
}
|
||||
|
||||
template <class K>
|
||||
bool operator() (element const& e, K const& k) const
|
||||
{
|
||||
return this->member() (extract (e.value), k);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
operator()(Key const& k, element const& e) const
|
||||
{
|
||||
return this->member()(k, extract(e.value));
|
||||
return KeyEqual::operator()(k, extract(e.value));
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& e, Key const& k) const
|
||||
{
|
||||
return this->member()(extract(e.value), k);
|
||||
return KeyEqual::operator()(extract(e.value), k);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& lhs, element const& rhs) const
|
||||
{
|
||||
return this->member()(extract(lhs.value), extract(rhs.value));
|
||||
return KeyEqual::operator()(extract(lhs.value), extract(rhs.value));
|
||||
}
|
||||
|
||||
KeyEqual&
|
||||
key_eq()
|
||||
{
|
||||
return this->member();
|
||||
return *this;
|
||||
}
|
||||
|
||||
KeyEqual const&
|
||||
key_eq() const
|
||||
{
|
||||
return this->member();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ public:
|
||||
Keylet const& key,
|
||||
std::function<void(std::shared_ptr<SLE> const&)> const& describe)
|
||||
{
|
||||
if (key.type != ltOFFER)
|
||||
if (key.type != ltOFFER && key.type != ltOPTION_OFFER)
|
||||
{
|
||||
assert(!"Only Offers are appended to book directories. "
|
||||
"Call dirInsert() instead.");
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/ledger/detail/ApplyViewBase.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
|
||||
// #include <ripple/app/paths/impl/AmountSpec.h>
|
||||
// #include <ripple/ledger/PaymentSandbox.h>
|
||||
// #include <ripple/ledger/View.h>
|
||||
// #include <ripple/protocol/Feature.h>
|
||||
// #include <ripple/protocol/SField.h>
|
||||
// #include <ripple/protocol/STAccount.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace detail {
|
||||
|
||||
@@ -91,17 +91,10 @@ private:
|
||||
using value_type = map_type::value_type;
|
||||
|
||||
struct Transform
|
||||
#ifdef _LIBCPP_VERSION
|
||||
: std::unary_function<
|
||||
map_type::right_map::const_iterator::value_type const&,
|
||||
beast::IP::Endpoint const&>
|
||||
#endif
|
||||
{
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument_type =
|
||||
map_type::right_map::const_iterator::value_type const&;
|
||||
using result_type = beast::IP::Endpoint const&;
|
||||
#endif
|
||||
|
||||
explicit Transform() = default;
|
||||
|
||||
|
||||
@@ -69,14 +69,9 @@ public:
|
||||
public:
|
||||
// Iterator transformation to extract the endpoint from Element
|
||||
struct Transform
|
||||
#ifdef _LIBCPP_VERSION
|
||||
: public std::unary_function<Element, Endpoint>
|
||||
#endif
|
||||
{
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument = Element;
|
||||
using result_type = Endpoint;
|
||||
#endif
|
||||
|
||||
explicit Transform() = default;
|
||||
|
||||
@@ -239,15 +234,9 @@ public:
|
||||
|
||||
template <bool IsConst>
|
||||
struct Transform
|
||||
#ifdef _LIBCPP_VERSION
|
||||
: public std::
|
||||
unary_function<typename lists_type::value_type, Hop<IsConst>>
|
||||
#endif
|
||||
{
|
||||
#ifndef _LIBCPP_VERSION
|
||||
using first_argument = typename lists_type::value_type;
|
||||
using result_type = Hop<IsConst>;
|
||||
#endif
|
||||
|
||||
explicit Transform() = default;
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace detail {
|
||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
||||
// the actual number of amendments. A LogicError on startup will verify this.
|
||||
static constexpr std::size_t numFeatures = 70;
|
||||
static constexpr std::size_t numFeatures = 71;
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -358,6 +358,7 @@ extern uint256 const fixXahauV2;
|
||||
extern uint256 const featureRemit;
|
||||
extern uint256 const featureZeroB2M;
|
||||
extern uint256 const fixNSDelete;
|
||||
extern uint256 const featureOptions;
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -297,6 +297,24 @@ import_vlseq(PublicKey const& key) noexcept;
|
||||
Keylet
|
||||
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
|
||||
|
||||
// Everything below is deprecated and should be removed in favor of keylets:
|
||||
@@ -311,6 +329,18 @@ getQualityNext(uint256 const& uBase);
|
||||
std::uint64_t
|
||||
getQuality(uint256 const& uBase);
|
||||
|
||||
uint256
|
||||
getOptionBookBase(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration);
|
||||
|
||||
uint256
|
||||
getOptionQualityNext(uint256 const& uBase);
|
||||
|
||||
// VFALCO This name could be better
|
||||
std::uint64_t
|
||||
getOptionQuality(uint256 const& uBase);
|
||||
|
||||
/** The initial directory page for a specific quality */
|
||||
|
||||
uint256
|
||||
getTicketIndex(AccountID const& account, std::uint32_t uSequence);
|
||||
|
||||
|
||||
@@ -254,6 +254,18 @@ enum LedgerEntryType : std::uint16_t
|
||||
\sa keylet::emitted
|
||||
*/
|
||||
ltEMITTED_TXN = 'E',
|
||||
|
||||
/** A ledger object
|
||||
|
||||
\sa keylet::broker_state
|
||||
*/
|
||||
ltOPTION = 'X',
|
||||
|
||||
/** A ledger object
|
||||
|
||||
\sa keylet::broker_state
|
||||
*/
|
||||
ltOPTION_OFFER = 'y',
|
||||
};
|
||||
// clang-format off
|
||||
|
||||
@@ -315,6 +327,12 @@ enum LedgerSpecificFlags {
|
||||
|
||||
// ltURI_TOKEN
|
||||
lsfBurnable = 0x00000001, // True, issuer can burn the token
|
||||
|
||||
// ltOPTION_OFFER
|
||||
lsfType = 0x00010000,
|
||||
lsfAction = 0x00020000,
|
||||
lsfPosition = 0x00040000,
|
||||
lsfCovered = 0x00080000,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
103
src/ripple/protocol/Option.h
Normal file
103
src/ripple/protocol/Option.h
Normal file
@@ -0,0 +1,103 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PROTOCOL_OPTION_H_INCLUDED
|
||||
#define RIPPLE_PROTOCOL_OPTION_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/CountedObject.h>
|
||||
#include <ripple/protocol/Issue.h>
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Option final : public CountedObject<Option>
|
||||
{
|
||||
public:
|
||||
Issue issue;
|
||||
uint64_t strike;
|
||||
uint32_t expiration;
|
||||
|
||||
Option()
|
||||
{
|
||||
}
|
||||
|
||||
Option(
|
||||
Issue issue_,
|
||||
uint64_t strike_,
|
||||
uint32_t expiration_)
|
||||
: issue(issue_)
|
||||
, strike(strike_)
|
||||
, expiration(expiration_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::string
|
||||
to_string(Option const& option);
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, Option const& x);
|
||||
|
||||
template <class Hasher>
|
||||
void
|
||||
hash_append(Hasher& h, Option const& o)
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, o.issue, o.strike, o.expiration);
|
||||
}
|
||||
|
||||
/** Equality comparison. */
|
||||
/** @{ */
|
||||
[[nodiscard]] inline constexpr bool
|
||||
operator==(Option const& lhs, Option const& rhs)
|
||||
{
|
||||
return (lhs.issue == rhs.issue) && (lhs.strike == rhs.strike) && (lhs.expiration == rhs.expiration);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Strict weak ordering. */
|
||||
/** @{ */
|
||||
[[nodiscard]] inline constexpr std::weak_ordering
|
||||
operator<=>(Option const& lhs, Option const& rhs)
|
||||
{
|
||||
if (auto const c{lhs.strike <=> rhs.strike}; c != 0)
|
||||
return c;
|
||||
return lhs.strike <=> rhs.strike;
|
||||
}
|
||||
/** @} */
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace boost {
|
||||
|
||||
template <>
|
||||
struct hash<ripple::Option> : std::hash<ripple::Option>
|
||||
{
|
||||
explicit hash() = default;
|
||||
|
||||
using Base = std::hash<ripple::Option>;
|
||||
// VFALCO NOTE broken in vs2012
|
||||
// using Base::Base; // inherit ctors
|
||||
};
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -410,6 +410,8 @@ extern SF_UINT32 const sfRewardLgrLast;
|
||||
extern SF_UINT32 const sfFirstNFTokenSequence;
|
||||
extern SF_UINT32 const sfImportSequence;
|
||||
extern SF_UINT32 const sfXahauActivationLgrSeq;
|
||||
extern SF_UINT32 const sfQuantity;
|
||||
extern SF_UINT32 const sfOpenInterest;
|
||||
|
||||
// 64-bit integers (common)
|
||||
extern SF_UINT64 const sfIndexNext;
|
||||
@@ -442,6 +444,7 @@ extern SF_UINT160 const sfTakerPaysCurrency;
|
||||
extern SF_UINT160 const sfTakerPaysIssuer;
|
||||
extern SF_UINT160 const sfTakerGetsCurrency;
|
||||
extern SF_UINT160 const sfTakerGetsIssuer;
|
||||
extern SF_UINT160 const sfCurrency;
|
||||
|
||||
// 256-bit (common)
|
||||
extern SF_UINT256 const sfLedgerHash;
|
||||
@@ -483,6 +486,8 @@ extern SF_UINT256 const sfURITokenID;
|
||||
extern SF_UINT256 const sfGovernanceFlags;
|
||||
extern SF_UINT256 const sfGovernanceMarks;
|
||||
extern SF_UINT256 const sfEmittedTxnID;
|
||||
extern SF_UINT256 const sfOptionID;
|
||||
extern SF_UINT256 const sfSwapID;
|
||||
|
||||
// currency amount (common)
|
||||
extern SF_AMOUNT const sfAmount;
|
||||
@@ -496,6 +501,7 @@ extern SF_AMOUNT const sfFee;
|
||||
extern SF_AMOUNT const sfSendMax;
|
||||
extern SF_AMOUNT const sfDeliverMin;
|
||||
extern SF_AMOUNT const sfLockedBalance;
|
||||
extern SF_AMOUNT const sfStrikePrice;
|
||||
|
||||
// currency amount (uncommon)
|
||||
extern SF_AMOUNT const sfMinimumOffer;
|
||||
|
||||
@@ -171,8 +171,7 @@ STValidation::STValidation(
|
||||
{
|
||||
if (checkSignature && !isValid())
|
||||
{
|
||||
JLOG(debugLog().error()) << "Invalid signature in validation: "
|
||||
<< getJson(JsonOptions::none);
|
||||
JLOG(debugLog().error()) << "Invalid signature in validation";
|
||||
Throw<std::runtime_error>("Invalid signature in validation");
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,21 @@ enum ClaimRewardFlags : uint32_t {
|
||||
tfOptOut = 0x00000001,
|
||||
};
|
||||
|
||||
// OptionCreate flags:
|
||||
enum OptionCreateFlags : uint32_t {
|
||||
tfType = 0x00010000, // 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
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -185,6 +185,9 @@ enum TxType : std::uint16_t
|
||||
ttUNL_MODIFY = 102,
|
||||
ttEMIT_FAILURE = 103,
|
||||
ttUNL_REPORT = 104,
|
||||
ttOPTION_LIST = 105,
|
||||
ttOPTION_CREATE = 106,
|
||||
ttOPTION_EXERCISE = 107,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -464,6 +464,7 @@ REGISTER_FIX (fixXahauV2, Supported::yes, VoteBehavior::De
|
||||
REGISTER_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(ZeroB2M, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FIX (fixNSDelete, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(Options, Supported::yes, VoteBehavior::DefaultNo);
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
|
||||
@@ -72,6 +72,9 @@ enum class LedgerNameSpace : std::uint16_t {
|
||||
URI_TOKEN = 'U',
|
||||
IMPORT_VLSEQ = 'I',
|
||||
UNL_REPORT = 'R',
|
||||
OPTION = 'X',
|
||||
OPTION_DIR = 'Y',
|
||||
OPTION_OFFER = 'y',
|
||||
|
||||
// No longer used or supported. Left here to reserve the space
|
||||
// to avoid accidental reuse.
|
||||
@@ -120,6 +123,33 @@ getQuality(uint256 const& uBase)
|
||||
return boost::endian::big_to_native(((std::uint64_t*)uBase.end())[-1]);
|
||||
}
|
||||
|
||||
uint256
|
||||
getOptionBookBase(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration)
|
||||
{
|
||||
auto const index = indexHash(
|
||||
LedgerNameSpace::BOOK_DIR,
|
||||
issuer, currency, strike, expiration);
|
||||
|
||||
// Return with quality 0.
|
||||
auto k = keylet::optionQuality({ltDIR_NODE, index}, 0);
|
||||
return k.key;
|
||||
}
|
||||
|
||||
uint256
|
||||
getOptionQualityNext(uint256 const& uBase)
|
||||
{
|
||||
static constexpr uint256 nextq(
|
||||
"0000000000000000000000000000000000000000000000010000000000000000");
|
||||
return uBase + nextq;
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
getOptionQuality(uint256 const& uBase)
|
||||
{
|
||||
// VFALCO [base_uint] This assumes a certain storage format
|
||||
return boost::endian::big_to_native(((std::uint64_t*)uBase.end())[-1]);
|
||||
}
|
||||
|
||||
uint256
|
||||
getTicketIndex(AccountID const& account, std::uint32_t ticketSeq)
|
||||
{
|
||||
@@ -443,6 +473,42 @@ uritoken(AccountID const& issuer, Blob const& uri)
|
||||
LedgerNameSpace::URI_TOKEN, issuer, Slice{uri.data(), uri.size()})};
|
||||
}
|
||||
|
||||
Keylet
|
||||
option(AccountID const& issuer, 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 ripple
|
||||
|
||||
@@ -84,6 +84,8 @@ LedgerFormats::LedgerFormats()
|
||||
{sfIndexNext, soeOPTIONAL},
|
||||
{sfIndexPrevious, soeOPTIONAL},
|
||||
{sfNFTokenID, soeOPTIONAL},
|
||||
{sfBaseFee, soeOPTIONAL},
|
||||
{sfExpiration, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -363,6 +365,38 @@ LedgerFormats::LedgerFormats()
|
||||
},
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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(sfOpenInterest, "OpenInterest", UINT32, 94);
|
||||
CONSTRUCT_TYPED_SFIELD(sfQuantity, "Quantity", UINT32, 95);
|
||||
CONSTRUCT_TYPED_SFIELD(sfXahauActivationLgrSeq, "XahauActivationLgrSeq",UINT32, 96);
|
||||
CONSTRUCT_TYPED_SFIELD(sfImportSequence, "ImportSequence", UINT32, 97);
|
||||
CONSTRUCT_TYPED_SFIELD(sfRewardTime, "RewardTime", UINT32, 98);
|
||||
@@ -195,6 +197,7 @@ CONSTRUCT_TYPED_SFIELD(sfTakerPaysCurrency, "TakerPaysCurrency", UINT160,
|
||||
CONSTRUCT_TYPED_SFIELD(sfTakerPaysIssuer, "TakerPaysIssuer", UINT160, 2);
|
||||
CONSTRUCT_TYPED_SFIELD(sfTakerGetsCurrency, "TakerGetsCurrency", UINT160, 3);
|
||||
CONSTRUCT_TYPED_SFIELD(sfTakerGetsIssuer, "TakerGetsIssuer", UINT160, 4);
|
||||
CONSTRUCT_TYPED_SFIELD(sfCurrency, "Currency", UINT160, 5);
|
||||
|
||||
// 256-bit (common)
|
||||
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(sfGovernanceMarks, "GovernanceMarks", UINT256, 98);
|
||||
CONSTRUCT_TYPED_SFIELD(sfEmittedTxnID, "EmittedTxnID", UINT256, 97);
|
||||
CONSTRUCT_TYPED_SFIELD(sfOptionID, "OptionID", UINT256, 96);
|
||||
CONSTRUCT_TYPED_SFIELD(sfSwapID, "SwapID", UINT256, 95);
|
||||
|
||||
// currency amount (common)
|
||||
CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1);
|
||||
@@ -261,6 +266,7 @@ CONSTRUCT_TYPED_SFIELD(sfLockedBalance, "LockedBalance", AMOUNT,
|
||||
CONSTRUCT_TYPED_SFIELD(sfBaseFeeDrops, "BaseFeeDrops", AMOUNT, 22);
|
||||
CONSTRUCT_TYPED_SFIELD(sfReserveBaseDrops, "ReserveBaseDrops", AMOUNT, 23);
|
||||
CONSTRUCT_TYPED_SFIELD(sfReserveIncrementDrops, "ReserveIncrementDrops", AMOUNT, 24);
|
||||
CONSTRUCT_TYPED_SFIELD(sfStrikePrice, "StrikePrice", AMOUNT, 25);
|
||||
|
||||
// variable length (common)
|
||||
CONSTRUCT_TYPED_SFIELD(sfPublicKey, "PublicKey", VL, 1);
|
||||
|
||||
@@ -456,6 +456,37 @@ TxFormats::TxFormats()
|
||||
{sfTicketSequence, soeOPTIONAL},
|
||||
},
|
||||
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&
|
||||
|
||||
@@ -111,8 +111,13 @@ JSS(Offer); // ledger type.
|
||||
JSS(OfferCancel); // transaction type.
|
||||
JSS(OfferCreate); // transaction type.
|
||||
JSS(OfferSequence); // field.
|
||||
JSS(Option); // ledger type.
|
||||
JSS(OptionCreate); // transaction type.
|
||||
JSS(OptionExercise); // transaction type.
|
||||
JSS(OptionList); // transaction type.
|
||||
JSS(OptionOffer); // ledger type.
|
||||
JSS(Paths); // in/out: TransactionSign
|
||||
JSS(PayChannel); // ledger type.
|
||||
JSS(PayChannel); // ledger type.E
|
||||
JSS(Payment); // transaction type.
|
||||
JSS(PaymentChannelClaim); // transaction type.
|
||||
JSS(PaymentChannelCreate); // transaction type.
|
||||
@@ -510,6 +515,7 @@ JSS(open); // out: handlers/Ledger
|
||||
JSS(open_ledger_cost); // out: SubmitTransaction
|
||||
JSS(open_ledger_fee); // out: TxQ
|
||||
JSS(open_ledger_level); // out: TxQ
|
||||
JSS(option); // out: AccountObjects
|
||||
JSS(owner); // in: LedgerEntry, out: NetworkOPs
|
||||
JSS(owner_funds); // in/out: Ledger, NetworkOPs, AcceptedLedgerTx
|
||||
JSS(page_index);
|
||||
@@ -630,6 +636,7 @@ JSS(stop_history_tx_only); // in: Unsubscribe, stop history tx stream
|
||||
JSS(storedSeqs); // out: NodeToShardStatus
|
||||
JSS(streams); // in: Subscribe, Unsubscribe
|
||||
JSS(strict); // in: AccountCurrencies, AccountInfo
|
||||
JSS(strike_price); // in: NetworkOPs
|
||||
JSS(sub_index); // in: LedgerEntry
|
||||
JSS(subcommand); // in: PathFind
|
||||
JSS(success); // rpc
|
||||
|
||||
@@ -189,8 +189,8 @@ doAccountObjects(RPC::JsonContext& context)
|
||||
}
|
||||
}
|
||||
|
||||
if (!ledger->exists(keylet::account(accountID)))
|
||||
return rpcError(rpcACT_NOT_FOUND);
|
||||
// if (!ledger->exists(keylet::account(accountID)))
|
||||
// return rpcError(rpcACT_NOT_FOUND);
|
||||
|
||||
std::optional<std::vector<LedgerEntryType>> typeFilter;
|
||||
|
||||
@@ -208,6 +208,7 @@ doAccountObjects(RPC::JsonContext& context)
|
||||
{jss::hook, ltHOOK},
|
||||
{jss::payment_channel, ltPAYCHAN},
|
||||
{jss::uri_token, ltURI_TOKEN},
|
||||
{jss::option, ltOPTION},
|
||||
{jss::state, ltRIPPLE_STATE}};
|
||||
|
||||
typeFilter.emplace();
|
||||
|
||||
@@ -103,6 +103,8 @@ doNodeToShard(RPC::JsonContext&);
|
||||
Json::Value
|
||||
doNoRippleCheck(RPC::JsonContext&);
|
||||
Json::Value
|
||||
doOptionBookOffers(RPC::JsonContext&);
|
||||
Json::Value
|
||||
doOwnerInfo(RPC::JsonContext&);
|
||||
Json::Value
|
||||
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/misc/NetworkOPs.h>
|
||||
#include <ripple/app/reporting/P2pProxy.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/Role.h>
|
||||
|
||||
@@ -118,6 +118,7 @@ Handler const handlerArray[]{
|
||||
{"nft_sell_offers", byRef(&doNFTSellOffers), Role::USER, NO_CONDITION},
|
||||
{"node_to_shard", byRef(&doNodeToShard), Role::ADMIN, 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},
|
||||
{"peers", byRef(&doPeers), Role::ADMIN, NO_CONDITION},
|
||||
{"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};
|
||||
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{
|
||||
{{jss::account, ltACCOUNT_ROOT},
|
||||
{jss::amendments, ltAMENDMENTS},
|
||||
@@ -1083,6 +1083,7 @@ chooseLedgerEntryType(Json::Value const& params)
|
||||
{jss::hashes, ltLEDGER_HASHES},
|
||||
{jss::import_vlseq, ltIMPORT_VLSEQ},
|
||||
{jss::offer, ltOFFER},
|
||||
{jss::option, ltOPTION},
|
||||
{jss::payment_channel, ltPAYCHAN},
|
||||
{jss::uri_token, ltURI_TOKEN},
|
||||
{jss::signer_list, ltSIGNER_LIST},
|
||||
|
||||
@@ -398,7 +398,7 @@ SHAMapInnerNode::canonicalizeChild(
|
||||
void
|
||||
SHAMapInnerNode::invariants(bool is_root) const
|
||||
{
|
||||
unsigned count = 0;
|
||||
[[maybe_unused]] unsigned count = 0;
|
||||
auto [numAllocated, hashes, children] =
|
||||
hashesAndChildren_.getHashesAndChildren();
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
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 <test/jtx/escrow.h>
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx/paychan.h>
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <ripple/app/misc/AmendmentTable.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/json/json_reader.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
Reference in New Issue
Block a user