Skip to content

Commit

Permalink
[FOLD] Allow an NFT sell offer's destination to be a broker
Browse files Browse the repository at this point in the history
  • Loading branch information
scottschurr committed Mar 24, 2022
1 parent 5b0f667 commit 702526e
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 23 deletions.
15 changes: 8 additions & 7 deletions src/ripple/app/tx/impl/NFTokenAcceptOffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx)
return ret;

if (buy && sell)
{ // Brokered mode:
{
// Brokered mode:
auto const bo = ctx.view.read(keylet::nftoffer(*buy));
auto const so = ctx.view.read(keylet::nftoffer(*sell));

Expand All @@ -106,10 +107,10 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx)
return tecINSUFFICIENT_PAYMENT;

// If the seller specified a destination, that destination must be
// the buyer.
// the buyer or the broker.
if (auto const dest = so->at(~sfDestination))
{
if (*dest != bo->at(sfOwner))
if (*dest != bo->at(sfOwner) && *dest != ctx.tx[sfAccount])
return tecNFTOKEN_BUY_SELL_MISMATCH;
}

Expand Down Expand Up @@ -306,10 +307,10 @@ NFTokenAcceptOffer::doApply()
//
// It is important that the issuer's cut be calculated after the
// broker's portion is already removed. Calculating the issuer's
// cut before the broker's cut it removed can result in more money
// cut before the broker's cut is removed can result in more money
// being paid out than the seller authorized. That would be bad!

// Send the broker the amount they requested
// Send the broker the amount they requested.
if (auto const cut = ctx_.tx[~sfBrokerFee];
cut && cut.value() != beast::zero)
{
Expand All @@ -320,7 +321,7 @@ NFTokenAcceptOffer::doApply()
amount -= cut.value();
}

// Calculate the issuer's cut, if any:
// Calculate the issuer's cut, if any.
if (auto const fee = nft::getTransferFee(tokenID);
amount != beast::zero && fee != 0)
{
Expand All @@ -336,7 +337,7 @@ NFTokenAcceptOffer::doApply()
}
}

// And send whatever remains to the seller
// And send whatever remains to the seller.
if (amount > beast::zero)
{
if (auto const r = pay(buyer, seller, amount); !isTesSuccess(r))
Expand Down
72 changes: 56 additions & 16 deletions src/test/app/NFToken_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2649,8 +2649,9 @@ class NFToken_test : public beast::unit_test::suite
Account const issuer{"issuer"};
Account const minter{"minter"};
Account const buyer{"buyer"};
Account const broker{"broker"};

env.fund(XRP(1000), issuer, minter, buyer);
env.fund(XRP(1000), issuer, minter, buyer, broker);

// We want to explore how issuers vs minters fits into the permission
// scheme. So issuer issues and minter mints.
Expand Down Expand Up @@ -2775,47 +2776,86 @@ class NFToken_test : public beast::unit_test::suite
BEAST_EXPECT(ownerCount(env, buyer) == 0);
}

// Show that brokered mode cannot complete a transfer where the
// Destination doesn't match, but can complete if the Destination
// does match.
// Show that a sell offer's Destination can broker that sell offer
// to another account.
{
uint256 const offerMinterToBuyer =
uint256 const offerMinterToBroker =
keylet::nftoffer(minter, env.seq(minter)).key;
env(token::createOffer(minter, tokenID, drops(1)),
token::destination(buyer),
token::destination(broker),
txflags(tfSellToken));

uint256 const offerBuyerToMinter =
keylet::nftoffer(buyer, env.seq(buyer)).key;
env(token::createOffer(buyer, tokenID, drops(1)),
token::owner(minter));

uint256 const offerIssuerToMinter =
env.close();
BEAST_EXPECT(ownerCount(env, issuer) == 0);
BEAST_EXPECT(ownerCount(env, minter) == 2);
BEAST_EXPECT(ownerCount(env, buyer) == 1);

// issuer cannot broker the offers, because they are not the
// Destination.
env(token::brokerOffers(
issuer, offerBuyerToMinter, offerMinterToBroker),
ter(tecNFTOKEN_BUY_SELL_MISMATCH));
env.close();
BEAST_EXPECT(ownerCount(env, issuer) == 0);
BEAST_EXPECT(ownerCount(env, minter) == 2);
BEAST_EXPECT(ownerCount(env, buyer) == 1);

// Since broker is the sell offer's destination, they can broker
// the two offers.
env(token::brokerOffers(
broker, offerBuyerToMinter, offerMinterToBroker));
env.close();
BEAST_EXPECT(ownerCount(env, issuer) == 0);
BEAST_EXPECT(ownerCount(env, minter) == 0);
BEAST_EXPECT(ownerCount(env, buyer) == 1);
}

// Show that brokered mode cannot complete a transfer where the
// Destination doesn't match, but can complete if the Destination
// does match.
{
uint256 const offerBuyerToMinter =
keylet::nftoffer(buyer, env.seq(buyer)).key;
env(token::createOffer(buyer, tokenID, drops(1)),
token::destination(minter),
txflags(tfSellToken));

uint256 const offerMinterToBuyer =
keylet::nftoffer(minter, env.seq(minter)).key;
env(token::createOffer(minter, tokenID, drops(1)),
token::owner(buyer));

uint256 const offerIssuerToBuyer =
keylet::nftoffer(issuer, env.seq(issuer)).key;
env(token::createOffer(issuer, tokenID, drops(1)),
token::owner(minter));
token::owner(buyer));

env.close();
BEAST_EXPECT(ownerCount(env, issuer) == 1);
BEAST_EXPECT(ownerCount(env, minter) == 2);
BEAST_EXPECT(ownerCount(env, buyer) == 1);
BEAST_EXPECT(ownerCount(env, minter) == 1);
BEAST_EXPECT(ownerCount(env, buyer) == 2);

// Cannot broker offers when the sell destination is not the buyer.
env(token::brokerOffers(
buyer, offerIssuerToMinter, offerMinterToBuyer),
broker, offerIssuerToBuyer, offerBuyerToMinter),
ter(tecNFTOKEN_BUY_SELL_MISMATCH));
env.close();
BEAST_EXPECT(ownerCount(env, issuer) == 1);
BEAST_EXPECT(ownerCount(env, minter) == 2);
BEAST_EXPECT(ownerCount(env, buyer) == 1);
BEAST_EXPECT(ownerCount(env, minter) == 1);
BEAST_EXPECT(ownerCount(env, buyer) == 2);

// Broker is successful when destination is buyer.
env(token::brokerOffers(
issuer, offerBuyerToMinter, offerMinterToBuyer));
broker, offerMinterToBuyer, offerBuyerToMinter));
env.close();
BEAST_EXPECT(ownerCount(env, issuer) == 1);
BEAST_EXPECT(ownerCount(env, minter) == 0);
BEAST_EXPECT(ownerCount(env, buyer) == 1);
BEAST_EXPECT(ownerCount(env, minter) == 1);
BEAST_EXPECT(ownerCount(env, buyer) == 0);
}
}

Expand Down

0 comments on commit 702526e

Please sign in to comment.