Skip to content

Commit 7260f2f

Browse files
committed
Additional validations and tests for MPTokenIssuanceSet txn
1 parent 13f9ec9 commit 7260f2f

File tree

3 files changed

+260
-12
lines changed

3 files changed

+260
-12
lines changed

packages/xrpl/src/models/transactions/MPTokenIssuanceSet.ts

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { ValidationError } from '../../errors'
22
import { isFlagEnabled, isHex } from '../utils'
3+
// eslint-disable-next-line import/no-cycle -- this method is needed to convert txn flags to number
4+
import { convertTxFlagsToNumber } from '../utils/flags'
35

46
import {
57
BaseTransaction,
@@ -12,9 +14,12 @@ import {
1214
GlobalFlagsInterface,
1315
isNumber,
1416
MAX_MPT_META_BYTE_LENGTH,
17+
isDomainID,
1518
} from './common'
1619
import { MAX_TRANSFER_FEE } from './MPTokenIssuanceCreate'
1720

21+
import type { Transaction } from '.'
22+
1823
/**
1924
* Transaction Flags for an MPTokenIssuanceSet Transaction.
2025
*
@@ -133,9 +138,10 @@ export interface MPTokenIssuanceSet extends BaseTransaction {
133138
MPTokenMetadata?: string
134139
TransferFee?: number
135140
MutableFlags?: number
141+
DomainID?: string
136142
}
137143

138-
/* eslint-disable max-lines-per-function -- All validation rules are needed */
144+
/* eslint-disable max-lines-per-function, max-statements -- All validation rules are needed */
139145
/**
140146
* Verify the form and type of an MPTokenIssuanceSet at runtime.
141147
*
@@ -149,7 +155,13 @@ export function validateMPTokenIssuanceSet(tx: Record<string, unknown>): void {
149155
validateOptionalField(tx, 'MPTokenMetadata', isString)
150156
validateOptionalField(tx, 'TransferFee', isNumber)
151157
validateOptionalField(tx, 'MutableFlags', isNumber)
158+
validateOptionalField(tx, 'DomainID', isDomainID)
152159

160+
if (tx.DomainID != null && tx.Holder != null) {
161+
throw new ValidationError(
162+
'MPTokenIssuanceSet: Cannot set both DomainID and Holder fields.',
163+
)
164+
}
153165
if (
154166
tx.MutableFlags != null &&
155167
// eslint-disable-next-line no-bitwise -- Need bitwise operations to replicate rippled behavior
@@ -174,6 +186,80 @@ export function validateMPTokenIssuanceSet(tx: Record<string, unknown>): void {
174186
throw new ValidationError('MPTokenIssuanceSet: flag conflict')
175187
}
176188

189+
if (tx.Holder != null && tx.Holder === tx.Account) {
190+
throw new ValidationError(
191+
'MPTokenIssuanceSet: Holder cannot be the same as the Account.',
192+
)
193+
}
194+
195+
const isMutate =
196+
tx.MutableFlags != null ||
197+
tx.MPTokenMetadata != null ||
198+
tx.TransferFee != null
199+
if (
200+
(tx.Flags === 0 || tx.Flags === undefined) &&
201+
tx.DomainID == null &&
202+
!isMutate
203+
) {
204+
throw new ValidationError(
205+
'MPTokenIssuanceSet: Transaction does not change the state of the MPTokenIssuance ledger object.',
206+
)
207+
}
208+
209+
if (isMutate && tx.Holder != null) {
210+
throw new ValidationError(
211+
'MPTokenIssuanceSet: Holder field is not allowed when mutating MPTokenIssuance.',
212+
)
213+
}
214+
215+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Pseudo-Txn missing in BaseTransaction type.
216+
if (isMutate && convertTxFlagsToNumber(tx as Transaction) !== 0) {
217+
throw new ValidationError(
218+
'MPTokenIssuanceSet: Can not set flags when mutating MPTokenIssuance.',
219+
)
220+
}
221+
222+
const MPTMutabilityFlags: Array<{ setFlag: number; clearFlag: number }> = [
223+
{
224+
setFlag: MPTokenIssuanceSetMutableFlags.tmfMPTSetCanLock,
225+
clearFlag: MPTokenIssuanceSetMutableFlags.tmfMPTClearCanLock,
226+
},
227+
{
228+
setFlag: MPTokenIssuanceSetMutableFlags.tmfMPTSetRequireAuth,
229+
clearFlag: MPTokenIssuanceSetMutableFlags.tmfMPTClearRequireAuth,
230+
},
231+
{
232+
setFlag: MPTokenIssuanceSetMutableFlags.tmfMPTSetCanEscrow,
233+
clearFlag: MPTokenIssuanceSetMutableFlags.tmfMPTClearCanEscrow,
234+
},
235+
{
236+
setFlag: MPTokenIssuanceSetMutableFlags.tmfMPTSetCanTrade,
237+
clearFlag: MPTokenIssuanceSetMutableFlags.tmfMPTClearCanTrade,
238+
},
239+
{
240+
setFlag: MPTokenIssuanceSetMutableFlags.tmfMPTSetCanTransfer,
241+
clearFlag: MPTokenIssuanceSetMutableFlags.tmfMPTClearCanTransfer,
242+
},
243+
{
244+
setFlag: MPTokenIssuanceSetMutableFlags.tmfMPTSetCanClawback,
245+
clearFlag: MPTokenIssuanceSetMutableFlags.tmfMPTClearCanClawback,
246+
},
247+
]
248+
249+
// Can not set and clear the same flag
250+
if (tx.MutableFlags != null) {
251+
for (const flagPair of MPTMutabilityFlags) {
252+
if (
253+
isFlagEnabled(tx.MutableFlags, flagPair.setFlag) &&
254+
isFlagEnabled(tx.MutableFlags, flagPair.clearFlag)
255+
) {
256+
throw new ValidationError(
257+
'MPTokenIssuanceSet: Can not set and clear the same flag.',
258+
)
259+
}
260+
}
261+
}
262+
177263
if (typeof tx.TransferFee === 'number') {
178264
if (tx.TransferFee < 0 || tx.TransferFee > MAX_TRANSFER_FEE) {
179265
throw new ValidationError(
@@ -192,4 +278,4 @@ export function validateMPTokenIssuanceSet(tx: Record<string, unknown>): void {
192278
)
193279
}
194280
}
195-
/* eslint-enable max-lines-per-function */
281+
/* eslint-enable max-lines-per-function, max-statements */

packages/xrpl/src/models/utils/flags.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { BatchFlags } from '../transactions/batch'
1212
import { GlobalFlags } from '../transactions/common'
1313
import { MPTokenAuthorizeFlags } from '../transactions/MPTokenAuthorize'
1414
import { MPTokenIssuanceCreateFlags } from '../transactions/MPTokenIssuanceCreate'
15+
// eslint-disable-next-line import/no-cycle -- this method is needed to map txn flags
1516
import { MPTokenIssuanceSetFlags } from '../transactions/MPTokenIssuanceSet'
1617
import { NFTokenCreateOfferFlags } from '../transactions/NFTokenCreateOffer'
1718
import { NFTokenMintFlags } from '../transactions/NFTokenMint'

packages/xrpl/test/models/MPTokenIssuanceSet.test.ts

Lines changed: 171 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,6 @@ describe('MPTokenIssuanceSet', function () {
4343

4444
assertValid(validMPTokenIssuanceSet)
4545

46-
// It's fine to not specify any flag, it means only tx fee is deducted
47-
validMPTokenIssuanceSet = {
48-
TransactionType: 'MPTokenIssuanceSet',
49-
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
50-
MPTokenIssuanceID: TOKEN_ID,
51-
Holder: 'rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG',
52-
} as any
53-
54-
assertValid(validMPTokenIssuanceSet)
55-
5646
assertValid({
5747
TransactionType: 'MPTokenIssuanceSet',
5848
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
@@ -190,4 +180,175 @@ describe('MPTokenIssuanceSet', function () {
190180
`MPTokenIssuanceSet: MPTokenMetadata (hex format) must be non-empty and no more than ${MAX_MPT_META_BYTE_LENGTH} bytes.`,
191181
)
192182
})
183+
184+
it(`Throws w/ invalid type of DomainID`, function () {
185+
const invalid = {
186+
TransactionType: 'MPTokenIssuanceSet',
187+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
188+
MPTokenIssuanceID: TOKEN_ID,
189+
DomainID: 1,
190+
} as any
191+
192+
assertInvalid(invalid, 'MPTokenIssuanceSet: invalid field DomainID')
193+
})
194+
195+
it(`throws w/ identical holder and account ID`, function () {
196+
const invalid = {
197+
TransactionType: 'MPTokenIssuanceSet',
198+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
199+
MPTokenIssuanceID: TOKEN_ID,
200+
Holder: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
201+
} as any
202+
203+
assertInvalid(
204+
invalid,
205+
'MPTokenIssuanceSet: Holder cannot be the same as the Account.',
206+
)
207+
})
208+
209+
it(`Throws w/ no changes to the MPTokenIssuance ledger object`, function () {
210+
const noOpMPTokenIssuanceSet = {
211+
TransactionType: 'MPTokenIssuanceSet',
212+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
213+
MPTokenIssuanceID: TOKEN_ID,
214+
Holder: 'rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG',
215+
} as any
216+
217+
assertInvalid(
218+
noOpMPTokenIssuanceSet,
219+
'MPTokenIssuanceSet: Transaction does not change the state of the MPTokenIssuance ledger object.',
220+
)
221+
})
222+
223+
it(`Throws w/ Holder field and mutating the MPTokenIssuance ledger object`, function () {
224+
const invalid = {
225+
TransactionType: 'MPTokenIssuanceSet',
226+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
227+
MPTokenIssuanceID: TOKEN_ID,
228+
MutableFlags: MPTokenIssuanceSetMutableFlags.tmfMPTClearCanTransfer,
229+
Holder: 'rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG',
230+
} as any
231+
232+
assertInvalid(
233+
invalid,
234+
'MPTokenIssuanceSet: Holder field is not allowed when mutating MPTokenIssuance.',
235+
)
236+
})
237+
238+
it(`Throws w/ Flags field and mutating the MPTokenIssuance ledger object`, function () {
239+
const invalid = {
240+
TransactionType: 'MPTokenIssuanceSet',
241+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
242+
MPTokenIssuanceID: TOKEN_ID,
243+
MutableFlags: MPTokenIssuanceSetMutableFlags.tmfMPTClearCanTransfer,
244+
Flags: MPTokenIssuanceSetFlags.tfMPTLock,
245+
} as any
246+
247+
assertInvalid(
248+
invalid,
249+
'MPTokenIssuanceSet: Can not set flags when mutating MPTokenIssuance.',
250+
)
251+
})
252+
253+
it(`Throws w/ setting and clearing the tmfMPTCanLock flag`, function () {
254+
const invalid = {
255+
TransactionType: 'MPTokenIssuanceSet',
256+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
257+
MPTokenIssuanceID: TOKEN_ID,
258+
MutableFlags:
259+
// eslint-disable-next-line no-bitwise -- required to OR the flags
260+
MPTokenIssuanceSetMutableFlags.tmfMPTSetCanLock |
261+
MPTokenIssuanceSetMutableFlags.tmfMPTClearCanLock,
262+
} as any
263+
264+
assertInvalid(
265+
invalid,
266+
'MPTokenIssuanceSet: Can not set and clear the same flag.',
267+
)
268+
})
269+
270+
it(`Throws w/ setting and clearing the tmfMPTCanTransfer flag`, function () {
271+
const invalid = {
272+
TransactionType: 'MPTokenIssuanceSet',
273+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
274+
MPTokenIssuanceID: TOKEN_ID,
275+
MutableFlags:
276+
// eslint-disable-next-line no-bitwise -- required to OR the flags
277+
MPTokenIssuanceSetMutableFlags.tmfMPTSetCanTransfer |
278+
MPTokenIssuanceSetMutableFlags.tmfMPTClearCanTransfer,
279+
} as any
280+
281+
assertInvalid(
282+
invalid,
283+
'MPTokenIssuanceSet: Can not set and clear the same flag.',
284+
)
285+
})
286+
287+
it(`Throws w/ setting and clearing the tmfMPTCanClawback flag`, function () {
288+
const invalid = {
289+
TransactionType: 'MPTokenIssuanceSet',
290+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
291+
MPTokenIssuanceID: TOKEN_ID,
292+
MutableFlags:
293+
// eslint-disable-next-line no-bitwise -- required to OR the flags
294+
MPTokenIssuanceSetMutableFlags.tmfMPTSetCanClawback |
295+
MPTokenIssuanceSetMutableFlags.tmfMPTClearCanClawback,
296+
} as any
297+
298+
assertInvalid(
299+
invalid,
300+
'MPTokenIssuanceSet: Can not set and clear the same flag.',
301+
)
302+
})
303+
304+
it(`Throws w/ setting and clearing the tmfMPTCanEscrow flag`, function () {
305+
const invalid = {
306+
TransactionType: 'MPTokenIssuanceSet',
307+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
308+
MPTokenIssuanceID: TOKEN_ID,
309+
MutableFlags:
310+
// eslint-disable-next-line no-bitwise -- required to OR the flags
311+
MPTokenIssuanceSetMutableFlags.tmfMPTSetCanEscrow |
312+
MPTokenIssuanceSetMutableFlags.tmfMPTClearCanEscrow,
313+
} as any
314+
315+
assertInvalid(
316+
invalid,
317+
'MPTokenIssuanceSet: Can not set and clear the same flag.',
318+
)
319+
})
320+
321+
it(`Throws w/ setting and clearing the tmfMPTCanTrade flag`, function () {
322+
const invalid = {
323+
TransactionType: 'MPTokenIssuanceSet',
324+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
325+
MPTokenIssuanceID: TOKEN_ID,
326+
MutableFlags:
327+
// eslint-disable-next-line no-bitwise -- required to OR the flags
328+
MPTokenIssuanceSetMutableFlags.tmfMPTSetCanTrade |
329+
MPTokenIssuanceSetMutableFlags.tmfMPTClearCanTrade,
330+
} as any
331+
332+
assertInvalid(
333+
invalid,
334+
'MPTokenIssuanceSet: Can not set and clear the same flag.',
335+
)
336+
})
337+
338+
it(`Throws w/ setting and clearing the tmfMPTCanRequireAuth flag`, function () {
339+
const invalid = {
340+
TransactionType: 'MPTokenIssuanceSet',
341+
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
342+
MPTokenIssuanceID: TOKEN_ID,
343+
MutableFlags:
344+
// eslint-disable-next-line no-bitwise -- required to OR the flags
345+
MPTokenIssuanceSetMutableFlags.tmfMPTSetRequireAuth |
346+
MPTokenIssuanceSetMutableFlags.tmfMPTClearRequireAuth,
347+
} as any
348+
349+
assertInvalid(
350+
invalid,
351+
'MPTokenIssuanceSet: Can not set and clear the same flag.',
352+
)
353+
})
193354
})

0 commit comments

Comments
 (0)