Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e5b21f0
Merge pull request #692 from Xahau/sync-2.4.0-rebased
RichardAH Feb 24, 2026
65837f4
fix: Add AMMv1_3 amendment (#5203)
gregtatcam Jun 2, 2025
ec65e62
Merge fixAMMv1_3 amendment into featureAMM amendment
tequdev Feb 19, 2026
8673599
fixAMMClawbackRounding: adjust last holder's LPToken balance (#5513)
yinyiqian1 Jul 11, 2025
8cfee6c
Merge fixAMMClawbackRounding amendment into featureAMMClawback amendment
tequdev Feb 19, 2026
a29e248
URITokenTransferFee Amendment
tequdev Mar 17, 2026
f96d9b6
Add tests for Hooks fee
tequdev Jan 20, 2026
1ba444a
Updated tests to align with the changes merged into the dev branch.
tequdev Feb 17, 2026
9bfca63
Update util_keylet fee test
tequdev Feb 24, 2026
2d29518
fix: typo `SignersListSet`
tequdev Mar 5, 2026
8c4c158
output ccache configuration in release-builder
tequdev Nov 27, 2025
f90ed41
enable ccache direct_mode
tequdev Nov 27, 2025
25123b3
chore: replace levelization shell script with python
sublimator Mar 13, 2026
4150f03
chore: use improved levelization script with threading and argparse
sublimator Mar 13, 2026
7f6ac75
Revert "chore: use improved levelization script with threading and ar…
sublimator Mar 13, 2026
66f7294
Test: hint build_test_hooks.sh when hook wasm is empty in hso()
tequdev Apr 1, 2026
05a3e04
Fix BEAST_ENHANCED_LOGGING not working and restore original behavior
tequdev Mar 6, 2026
cd00ed7
change build instructions url
alloynetworks Mar 13, 2026
d8e8124
Merge branch 'dev' into uritoken-xfer-fee
tequdev Apr 27, 2026
45de67f
Add sfAccount != sfTransferFeeRecipient check
tequdev May 23, 2026
f1c45ff
Merge branch 'dev' into uritoken-xfer-fee
tequdev May 23, 2026
c836f81
Add check for sfTransferFeeRecipient != AMMAccount
tequdev May 23, 2026
7068163
Add LCOV_EXCL_LINE
tequdev May 23, 2026
27eb1c5
Merge branch 'dev' into uritoken-xfer-fee
tequdev Jul 2, 2026
af071c0
Update test
tequdev Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hook/sfcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@
#define sfLockingChainDoor ((8U << 16U) + 22U)
#define sfIssuingChainDoor ((8U << 16U) + 23U)
#define sfSubject ((8U << 16U) + 24U)
#define sfTransferFeeRecipient ((8U << 16U) + 98U)
#define sfInform ((8U << 16U) + 99U)
#define sfIndexes ((19U << 16U) + 1U)
#define sfHashes ((19U << 16U) + 2U)
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/features.macro
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.

XRPL_FEATURE(URITokenTransferFee, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (HookMap, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FIX (GuardDepth32, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(NamedHooks, Supported::yes, VoteBehavior::DefaultNo)
Expand Down
2 changes: 2 additions & 0 deletions include/xrpl/protocol/detail/ledger_entries.macro
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ LEDGER_ENTRY(ltURI_TOKEN, 0x0055, URIToken, uri_token, ({
{sfDigest, soeOPTIONAL},
{sfAmount, soeOPTIONAL},
{sfDestination, soeOPTIONAL},
{sfTransferFee, soeOPTIONAL},
{sfTransferFeeRecipient, soeOPTIONAL},
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
}))
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/sfields.macro
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ TYPED_SFIELD(sfAttestationRewardAccount, ACCOUNT, 21)
TYPED_SFIELD(sfLockingChainDoor, ACCOUNT, 22)
TYPED_SFIELD(sfIssuingChainDoor, ACCOUNT, 23)
TYPED_SFIELD(sfSubject, ACCOUNT, 24)
TYPED_SFIELD(sfTransferFeeRecipient, ACCOUNT, 98)
TYPED_SFIELD(sfInform, ACCOUNT, 99)

// vector of 256-bit
Expand Down
2 changes: 2 additions & 0 deletions include/xrpl/protocol/detail/transactions.macro
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ TRANSACTION(ttURITOKEN_MINT, 45, URITokenMint, ({
{sfDigest, soeOPTIONAL},
{sfAmount, soeOPTIONAL},
{sfDestination, soeOPTIONAL},
{sfTransferFee, soeOPTIONAL},
{sfTransferFeeRecipient, soeOPTIONAL},
}))

TRANSACTION(ttURITOKEN_BURN, 46, URITokenBurn, ({
Expand Down
2 changes: 2 additions & 0 deletions src/libxrpl/protocol/InnerObjectFormats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ InnerObjectFormats::InnerObjectFormats()
{sfURI, soeREQUIRED},
{sfDigest, soeOPTIONAL},
{sfFlags, soeOPTIONAL},
{sfTransferFee, soeOPTIONAL},
{sfTransferFeeRecipient, soeOPTIONAL},
});

add(sfRemark.jsonName,
Expand Down
123 changes: 123 additions & 0 deletions src/test/app/Remit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2844,6 +2844,128 @@ struct Remit_test : public beast::unit_test::suite
}
}

void
testURITokenTransferFee(FeatureBitset features)
{
testcase("uritoken transfer fee");
using namespace test::jtx;

// Remit with TransferFee, amendment disabled
{
auto const noXferFee = features - featureURITokenTransferFee;
Env env{*this, noXferFee};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(10000), alice, bob);
env.close();

std::string const uri(8, '?');
env(remit::remit(alice, bob),
remit::uri(uri, std::nullopt, std::nullopt, 5000),
ter(temDISABLED));
}

// Remit with TransferFee = 0 or > 50000 fails
{
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(10000), alice, bob);
env.close();

std::string const uri(4, '?');
for (auto const fee : {0, 50001})
{
env(remit::remit(alice, bob),
remit::uri(uri, std::nullopt, std::nullopt, fee),
ter(temBAD_TRANSFER_FEE));
}
}

// Remit with TransferFeeRecipient but no TransferFee
{
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(10000), alice, bob, carol);
env.close();

std::string const uri(6, '?');
env(remit::remit(alice, bob),
remit::uri(
uri, std::nullopt, std::nullopt, std::nullopt, carol),
ter(temMALFORMED));
}

// Remit with TransferFeeRecipient non-existent account
{
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const phantom = Account("phantom");
env.fund(XRP(10000), alice, bob);
env.close();

std::string const uri(7, '?');
env(remit::remit(alice, bob),
remit::uri(uri, std::nullopt, std::nullopt, 2500, phantom),
ter(tecNO_TARGET));
}

// Remit with MintURIToken including TransferFee
{
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(10000), alice, bob);
env.close();

std::string const uri(2, '?');
auto const tid =
keylet::uritoken(alice, Blob(uri.begin(), uri.end())).key;

// Remit with inline mint including TransferFee
env(remit::remit(alice, bob),
remit::uri(uri, std::nullopt, std::nullopt, 5000));
env.close();

// Verify the minted URIToken has TransferFee
auto const sleU = env.le(Keylet{ltURI_TOKEN, tid});
BEAST_EXPECT(sleU);
BEAST_EXPECT(sleU->isFieldPresent(sfTransferFee));
BEAST_EXPECT(sleU->getFieldU16(sfTransferFee) == 5000);
BEAST_EXPECT(sleU->getAccountID(sfIssuer) == alice.id());
BEAST_EXPECT(sleU->getAccountID(sfOwner) == bob.id());
}

// Remit with TransferFee and TransferFeeRecipient
{
Env env{*this, features};
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(10000), alice, bob, carol);
env.close();

std::string const uri(3, '?');
auto const tid =
keylet::uritoken(alice, Blob(uri.begin(), uri.end())).key;

env(remit::remit(alice, bob),
remit::uri(uri, std::nullopt, std::nullopt, 10000, carol));
env.close();

auto const sleU = env.le(Keylet{ltURI_TOKEN, tid});
BEAST_EXPECT(sleU);
BEAST_EXPECT(sleU->isFieldPresent(sfTransferFee));
BEAST_EXPECT(sleU->getFieldU16(sfTransferFee) == 10000);
BEAST_EXPECT(sleU->isFieldPresent(sfTransferFeeRecipient));
BEAST_EXPECT(
sleU->getAccountID(sfTransferFeeRecipient) == carol.id());
}
}

void
testOptionals(FeatureBitset features)
{
Expand Down Expand Up @@ -2944,6 +3066,7 @@ struct Remit_test : public beast::unit_test::suite
testTLDeepFreeze(features);
testRippling(features);
testURIToken(features);
testURITokenTransferFee(features);
testOptionals(features);
testDestAMM(features);
}
Expand Down
Loading
Loading