Skip to content

Commit 7aa8b29

Browse files
authored
Merge branch 'main' into genDocs
2 parents 720ddfb + 9211ff6 commit 7aa8b29

File tree

17 files changed

+119
-58
lines changed

17 files changed

+119
-58
lines changed

.ci-config/rippled.cfg

+6
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,9 @@ fixReducedOffersV2
184184
DeepFreeze
185185
DynamicNFT
186186
PermissionedDomains
187+
188+
# This section can be used to simulate various FeeSettings scenarios for rippled node in standalone mode
189+
[voting]
190+
reference_fee = 200 # 200 drops
191+
account_reserve = 20000000 # 20 XRP
192+
owner_reserve = 5000000 # 5 XRP

packages/ripple-binary-codec/HISTORY.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
## Unreleased
44

55
### Fixed
6-
- add `MPTAmount` support in `Issue` (rippled internal type)
6+
* add `MPTCurrency` support in `Issue` (rippled internal type)
7+
* Throw an error during serialization if a field is unknown, rather than silently throwing it away.
78

89
## 2.3.0 (2025-2-13)
910

packages/ripple-binary-codec/src/types/st-object.ts

+18-7
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,25 @@ class STObject extends SerializedType {
116116
return Object.assign(acc, handled ?? { [key]: val })
117117
}, {})
118118

119-
let sorted = Object.keys(xAddressDecoded)
120-
.map((f: string): FieldInstance => definitions.field[f] as FieldInstance)
121-
.filter(
122-
(f: FieldInstance): boolean =>
123-
f !== undefined &&
124-
xAddressDecoded[f.name] !== undefined &&
125-
f.isSerialized,
119+
function isValidFieldInstance(
120+
f: FieldInstance | undefined,
121+
): f is FieldInstance {
122+
return (
123+
f !== undefined &&
124+
xAddressDecoded[f.name] !== undefined &&
125+
f.isSerialized
126126
)
127+
}
128+
129+
let sorted = Object.keys(xAddressDecoded)
130+
.map((f: string): FieldInstance | undefined => {
131+
if (!(f in definitions.field)) {
132+
if (f[0] === f[0].toLowerCase()) return undefined
133+
throw new Error(`Field ${f} is not defined in the definitions`)
134+
}
135+
return definitions.field[f] as FieldInstance
136+
})
137+
.filter(isValidFieldInstance)
127138
.sort((a, b) => {
128139
return a.ordinal - b.ordinal
129140
})

packages/ripple-binary-codec/test/definitions.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ describe('encode and decode using new types as a parameter', function () {
3434
it('can encode and decode a new Field', function () {
3535
const tx = { ...txJson, NewFieldDefinition: 10 }
3636

37-
// Before updating the types, undefined fields will be ignored on encode
38-
expect(decode(encode(tx))).not.toEqual(tx)
37+
// Before updating the types, undefined fields will throw an error
38+
expect(() => encode(tx)).toThrow()
3939

4040
// Normally this would be generated directly from rippled with something like `server_definitions`.
4141
// Added here to make it easier to see what is actually changing in the definitions.json file.
@@ -72,8 +72,8 @@ describe('encode and decode using new types as a parameter', function () {
7272
],
7373
}
7474

75-
// Before updating the types, undefined fields will be ignored on encode
76-
expect(decode(encode(tx))).not.toEqual(tx)
75+
// Before updating the types, undefined fields will throw an error
76+
expect(() => encode(tx)).toThrow()
7777

7878
// Normally this would be generated directly from rippled with something like `server_definitions`.
7979
// Added here to make it easier to see what is actually changing in the definitions.json file.
@@ -141,8 +141,8 @@ describe('encode and decode using new types as a parameter', function () {
141141
},
142142
])
143143

144-
// Test that before updating the types this tx fails to decode correctly. Note that undefined fields are ignored on encode.
145-
expect(decode(encode(tx))).not.toEqual(tx)
144+
// Test that before updating the types this tx fails to decode correctly. Note that undefined fields will throw an error.
145+
expect(() => encode(tx)).toThrow()
146146

147147
class NewType extends UInt32 {
148148
// Should be the same as UInt32

packages/ripple-binary-codec/test/tx-encode-decode.test.ts

+9
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,13 @@ describe('encoding and decoding tx_json', function () {
116116
encode(my_tx)
117117
}).toThrow()
118118
})
119+
120+
it('throws when there is an unknown field', function () {
121+
const my_tx = Object.assign({}, tx_json, {
122+
BadField: 1,
123+
})
124+
expect(() => {
125+
encode(my_tx)
126+
}).toThrow()
127+
})
119128
})

packages/xrpl/HISTORY.md

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
1212
* `OracleSet` transaction accepts hexadecimal string values for `AssetPrice` field
1313
* `TransactionStream` model includes `hash` field in APIv2
1414
* `TransactionStream` model includes `close_time_iso` field only for APIv2
15+
* Adds `MPTCurrency` type
1516

1617
## 4.2.0 (2025-2-13)
1718

@@ -41,6 +42,7 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
4142
* `TransactionStream` model supports APIv2
4243
* `TransactionStream` model includes `close_time_iso` field
4344
* `Ledger` model includes `close_time_iso` field
45+
* Remove hard-coded reference to 10 drops as the reference transaction cost. Ensure tests passed for all transaction fee scenarios and `AMMCreate` transaction fee calculation is correct in case `owner_reserve` increases.
4446

4547
## 4.0.0 (2024-07-15)
4648

packages/xrpl/src/models/common/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ export interface IssuedCurrency {
1414
issuer: string
1515
}
1616

17-
export type Currency = IssuedCurrency | XRP
17+
export interface MPTCurrency {
18+
mpt_issuance_id: string
19+
}
20+
21+
export type Currency = IssuedCurrency | MPTCurrency | XRP
1822

1923
export interface IssuedCurrencyAmount extends IssuedCurrency {
2024
value: string

packages/xrpl/src/sugar/autofill.ts

+17-19
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,13 @@ export async function setNextValidSequenceNumber(
230230
}
231231

232232
/**
233-
* Fetches the account deletion fee from the server state using the provided client.
233+
* Fetches the owner reserve fee from the server state using the provided client.
234234
*
235235
* @param client - The client object used to make the request.
236-
* @returns A Promise that resolves to the account deletion fee as a BigNumber.
237-
* @throws {Error} Throws an error if the account deletion fee cannot be fetched.
236+
* @returns A Promise that resolves to the owner reserve fee as a BigNumber.
237+
* @throws {Error} Throws an error if the owner reserve fee cannot be fetched.
238238
*/
239-
async function fetchAccountDeleteFee(client: Client): Promise<BigNumber> {
239+
async function fetchOwnerReserveFee(client: Client): Promise<BigNumber> {
240240
const response = await client.request({ command: 'server_state' })
241241
const fee = response.result.state.validated_ledger?.reserve_inc
242242

@@ -260,45 +260,43 @@ export async function calculateFeePerTransactionType(
260260
tx: Transaction,
261261
signersCount = 0,
262262
): Promise<void> {
263-
// netFee is usually 0.00001 XRP (10 drops)
264263
const netFeeXRP = await getFeeXrp(client)
265264
const netFeeDrops = xrpToDrops(netFeeXRP)
266265
let baseFee = new BigNumber(netFeeDrops)
267266

268267
// EscrowFinish Transaction with Fulfillment
269268
if (tx.TransactionType === 'EscrowFinish' && tx.Fulfillment != null) {
270269
const fulfillmentBytesSize: number = Math.ceil(tx.Fulfillment.length / 2)
271-
// 10 drops × (33 + (Fulfillment size in bytes / 16))
272-
const product = new BigNumber(
270+
// BaseFee × (33 + (Fulfillment size in bytes / 16))
271+
baseFee = new BigNumber(
273272
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- expected use of magic numbers
274273
scaleValue(netFeeDrops, 33 + fulfillmentBytesSize / 16),
275274
)
276-
baseFee = product.dp(0, BigNumber.ROUND_CEIL)
277275
}
278276

279-
if (
280-
tx.TransactionType === 'AccountDelete' ||
281-
tx.TransactionType === 'AMMCreate'
282-
) {
283-
baseFee = await fetchAccountDeleteFee(client)
277+
const isSpecialTxCost = ['AccountDelete', 'AMMCreate'].includes(
278+
tx.TransactionType,
279+
)
280+
281+
if (isSpecialTxCost) {
282+
baseFee = await fetchOwnerReserveFee(client)
284283
}
285284

286285
/*
287286
* Multi-signed Transaction
288-
* 10 drops × (1 + Number of Signatures Provided)
287+
* BaseFee × (1 + Number of Signatures Provided)
289288
*/
290289
if (signersCount > 0) {
291290
baseFee = BigNumber.sum(baseFee, scaleValue(netFeeDrops, 1 + signersCount))
292291
}
293292

294293
const maxFeeDrops = xrpToDrops(client.maxFeeXRP)
295-
const totalFee =
296-
tx.TransactionType === 'AccountDelete'
297-
? baseFee
298-
: BigNumber.min(baseFee, maxFeeDrops)
294+
const totalFee = isSpecialTxCost
295+
? baseFee
296+
: BigNumber.min(baseFee, maxFeeDrops)
299297

300298
// Round up baseFee and return it as a string
301-
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-magic-numbers -- param reassign is safe, base 10 magic num
299+
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-magic-numbers, require-atomic-updates -- safe reassignment.
302300
tx.Fee = totalFee.dp(0, BigNumber.ROUND_CEIL).toString(10)
303301
}
304302

packages/xrpl/test/integration/requests/ammInfo.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ describe('AMMInfo', function () {
3737
assert.equal(amm.amount, '1250')
3838
assert.deepEqual(amm.amount2, {
3939
currency: asset2.currency,
40-
// @ts-expect-error: asset2.issuer should be defined at this point
4140
issuer: asset2.issuer,
4241
value: '250',
4342
})

packages/xrpl/test/integration/requests/fee.test.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import BigNumber from 'bignumber.js'
12
import { assert } from 'chai'
23
import omit from 'lodash/omit'
34

4-
import { FeeRequest } from '../../../src'
5+
import { FeeRequest, xrpToDrops } from '../../../src'
6+
import getFeeXrp from '../../../src/sugar/getFeeXrp'
57
import serverUrl from '../serverUrl'
68
import {
79
setupClient,
@@ -26,17 +28,21 @@ describe('fee', function () {
2628
const request: FeeRequest = {
2729
command: 'fee',
2830
}
31+
const referenceFee = xrpToDrops(await getFeeXrp(testContext.client, 1))
32+
const MEDIAN_FEE_MULTIPLIER = 500
2933
const response = await testContext.client.request(request)
3034
const expected = {
3135
id: 0,
3236
result: {
3337
current_ledger_size: '0',
3438
current_queue_size: '0',
3539
drops: {
36-
base_fee: '10',
37-
median_fee: '5000',
38-
minimum_fee: '10',
39-
open_ledger_fee: '10',
40+
base_fee: referenceFee,
41+
median_fee: new BigNumber(referenceFee)
42+
.times(MEDIAN_FEE_MULTIPLIER)
43+
.toString(),
44+
minimum_fee: referenceFee,
45+
open_ledger_fee: referenceFee,
4046
},
4147
expected_ledger_size: '1000',
4248
ledger_current_index: 2925,

packages/xrpl/test/integration/setup.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import {
44
AMMDeposit,
55
AMMDepositFlags,
66
Client,
7-
Currency,
7+
IssuedCurrency,
88
SignerListSet,
99
Wallet,
1010
XChainBridge,
1111
XChainCreateBridge,
12+
XRP,
1213
} from '../../src'
1314

1415
import serverUrl from './serverUrl'
@@ -24,8 +25,8 @@ export interface TestAMMPool {
2425
issuerWallet: Wallet
2526
lpWallet: Wallet
2627
testWallet: Wallet
27-
asset: Currency
28-
asset2: Currency
28+
asset: XRP
29+
asset2: IssuedCurrency
2930
}
3031

3132
interface TestBridge {

packages/xrpl/test/integration/transactions/ammCreate.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ describe('AMMCreate', function () {
3636
assert.equal(amm.amount, '250')
3737
assert.deepEqual(amm.amount2, {
3838
currency: asset2.currency,
39-
// @ts-expect-error: asset2.issuer should be defined at this point
4039
issuer: asset2.issuer,
4140
value: '250',
4241
})

packages/xrpl/test/integration/transactions/ammDeposit.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ describe('AMMDeposit', function () {
102102
Amount: '100',
103103
Amount2: {
104104
currency: asset2.currency,
105-
// @ts-expect-error: asset2.issuer should be defined at this point
106105
issuer: asset2.issuer,
107106
value: '100',
108107
},

packages/xrpl/test/integration/transactions/ammWithdraw.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ describe('AMMWithdraw', function () {
110110
Amount: '50',
111111
Amount2: {
112112
currency: asset2.currency,
113-
// @ts-expect-error: asset2.issuer should be defined at this point
114113
issuer: asset2.issuer,
115114
value: '50',
116115
},

packages/xrpl/test/integration/transactions/payment.test.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,20 @@ import {
1313
teardownClient,
1414
type XrplIntegrationTestContext,
1515
} from '../setup'
16-
import { generateFundedWallet, testTransaction } from '../utils'
16+
import {
17+
fetchAccountReserveFee,
18+
generateFundedWallet,
19+
testTransaction,
20+
} from '../utils'
1721

1822
// how long before each test case times out
1923
const TIMEOUT = 20000
2024

2125
describe('Payment', function () {
2226
let testContext: XrplIntegrationTestContext
2327
let paymentTx: Payment
24-
const AMOUNT = '10000000'
28+
let amount: string
29+
const DEFAULT_AMOUNT = '10000000'
2530
// This wallet is used for DeliverMax related tests
2631
let senderWallet: Wallet
2732

@@ -31,14 +36,17 @@ describe('Payment', function () {
3136
paymentTx = {
3237
TransactionType: 'Payment',
3338
Account: senderWallet.classicAddress,
34-
Amount: AMOUNT,
39+
Amount: amount,
3540
Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy',
3641
}
3742
})
3843

3944
beforeAll(async () => {
4045
testContext = await setupClient(serverUrl)
4146
senderWallet = await generateFundedWallet(testContext.client)
47+
// Make sure the amount sent satisfies minimum reserve requirement to fund an account.
48+
amount =
49+
(await fetchAccountReserveFee(testContext.client)) ?? DEFAULT_AMOUNT
4250
})
4351
afterAll(async () => teardownClient(testContext))
4452

@@ -66,7 +74,7 @@ describe('Payment', function () {
6674
)
6775

6876
assert.equal(result.result.engine_result_code, 0)
69-
assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
77+
assert.equal((result.result.tx_json as Payment).Amount, amount)
7078
},
7179
TIMEOUT,
7280
)
@@ -86,7 +94,7 @@ describe('Payment', function () {
8694
)
8795

8896
assert.equal(result.result.engine_result_code, 0)
89-
assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
97+
assert.equal((result.result.tx_json as Payment).Amount, amount)
9098
},
9199
TIMEOUT,
92100
)
@@ -104,7 +112,7 @@ describe('Payment', function () {
104112
)
105113

106114
assert.equal(result.result.engine_result_code, 0)
107-
assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
115+
assert.equal((result.result.tx_json as Payment).Amount, amount)
108116
},
109117
TIMEOUT,
110118
)

packages/xrpl/test/integration/transactions/xchainClaim.test.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
XChainCreateClaimID,
1010
xrpToDrops,
1111
} from '../../../src'
12+
import getFeeXrp from '../../../src/sugar/getFeeXrp'
1213
import serverUrl from '../serverUrl'
1314
import {
1415
setupBridge,
@@ -39,6 +40,7 @@ describe('XChainCreateBridge', function () {
3940
const destination = await generateFundedWallet(testContext.client)
4041
const otherChainSource = Wallet.generate()
4142
const amount = xrpToDrops(10)
43+
const netFee = xrpToDrops(await getFeeXrp(testContext.client))
4244

4345
const claimIdTx: XChainCreateClaimID = {
4446
TransactionType: 'XChainCreateClaimID',
@@ -103,7 +105,10 @@ describe('XChainCreateBridge', function () {
103105
)
104106
assert.equal(
105107
finalBalance,
106-
initialBalance + Number(amount) - Number(signatureReward) - 12,
108+
initialBalance +
109+
Number(amount) -
110+
Number(signatureReward) -
111+
Number(netFee),
107112
"The destination's balance should not change yet",
108113
)
109114
},

0 commit comments

Comments
 (0)