Compare commits

...

4 Commits

Author SHA1 Message Date
tequ
e454c61994 Support Cron Amendment (#32) 2025-10-17 18:44:07 +10:00
Denis Angell
b5f15ac075 cut v4.0.1 2025-10-02 19:09:24 +02:00
tequ
5ec5ad8e1e Deep Freeze XLS-77d (#2873) (#29)
Co-authored-by: Chenna Keshava B S <21219765+ckeshava@users.noreply.github.com>
Co-authored-by: Denis Angell <dangell@transia.co>
2025-10-02 18:51:18 +02:00
tequ
043620b637 feat: add Clawback amendment support (#2353) (#28)
* Add Clawback transaction
* Account flag lsfAllowTrustLineClawback
* Support bitwise flag checking of 64 bit flags

Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
2025-10-02 18:50:42 +02:00
24 changed files with 671 additions and 19 deletions

View File

@@ -63,11 +63,6 @@ online_delete=256
[debug_logfile]
/var/log/rippled/debug.log
[sntp_servers]
time.windows.com
time.apple.com
time.nist.gov
pool.ntp.org
[ips]
r.ripple.com 51235
@@ -167,6 +162,8 @@ fixXahauV1
fixXahauV2
fixXahauV3
PaychanAndEscrowForTokens
DeepFreeze
Clawback
[network_id]
21337

6
package-lock.json generated
View File

@@ -14746,7 +14746,7 @@
}
},
"packages/xahau": {
"version": "4.0.0",
"version": "4.0.1",
"license": "ISC",
"dependencies": {
"@scure/bip32": "^1.3.1",
@@ -14756,7 +14756,7 @@
"bignumber.js": "^9.0.0",
"eventemitter3": "^5.0.1",
"xahau-address-codec": "^5.0.0",
"xahau-binary-codec": "^2.1.0",
"xahau-binary-codec": "^2.1.1",
"xahau-keypairs": "^2.0.0"
},
"devDependencies": {
@@ -14789,7 +14789,7 @@
}
},
"packages/xahau-binary-codec": {
"version": "2.1.0",
"version": "2.1.1",
"license": "ISC",
"dependencies": {
"@xrplf/isomorphic": "^1.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "xahau-binary-codec",
"version": "2.1.0",
"version": "2.1.1",
"description": "XAH Ledger binary codec",
"files": [
"dist/*",

View File

@@ -29,6 +29,7 @@
"LEDGER_ENTRY_TYPES": {
"Invalid": -1,
"AccountRoot": 97,
"Cron": 65,
"DirectoryNode": 100,
"RippleState": 114,
"Ticket": 84,
@@ -798,6 +799,26 @@
"type": "UInt32"
}
],
[
"RepeatCount",
{
"nth": 94,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt32"
}
],
[
"DelaySeconds",
{
"nth": 95,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt32"
}
],
[
"XahauActivationLgrSeq",
{
@@ -1488,6 +1509,16 @@
"type": "Hash256"
}
],
[
"Cron",
{
"nth": 95,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Hash256"
}
],
[
"Amount",
{
@@ -2829,11 +2860,14 @@
"NFTokenCreateOffer": 27,
"NFTokenCancelOffer": 28,
"NFTokenAcceptOffer": 29,
"Clawback": 30,
"URITokenMint": 45,
"URITokenBurn": 46,
"URITokenBuy": 47,
"URITokenCreateSellOffer": 48,
"URITokenCancelSellOffer": 49,
"Cron": 92,
"CronSet": 93,
"SetRemarks": 94,
"Remit": 95,
"GenesisMint": 96,

View File

@@ -74,7 +74,13 @@ class XrplDefinitionsBase {
.filter(([_key, value]) => value >= 0)
.map(([key, _value]) => key)
const ignoreList = ['EnableAmendment', 'SetFee', 'UNLModify', 'EmitFailure']
const ignoreList = [
'EnableAmendment',
'SetFee',
'UNLModify',
'EmitFailure',
'Cron',
]
this.transactionMap = Object.assign(
{},
...Object.entries(enums.TRANSACTION_TYPES)

View File

@@ -4,8 +4,14 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
## Unreleased Changes
### Added
* Support for Cron Amendment
## 4.0.1 (2025-10-03)
### Added
* parseTransactionFlags as a utility function in the xrpl package to streamline transactions flags-to-map conversion
* Support for XLS-77d Deep-Freeze amendment
### Fixed
* `TransactionStream` model supports APIv2

View File

@@ -1,6 +1,6 @@
{
"name": "xahau",
"version": "4.0.0",
"version": "4.0.1",
"license": "ISC",
"description": "A TypeScript/JavaScript API for interacting with the XAH Ledger in Node.js and the browser",
"files": [
@@ -29,7 +29,7 @@
"bignumber.js": "^9.0.0",
"eventemitter3": "^5.0.1",
"xahau-address-codec": "^5.0.0",
"xahau-binary-codec": "^2.1.0",
"xahau-binary-codec": "^2.1.1",
"xahau-keypairs": "^2.0.0"
},
"devDependencies": {

View File

@@ -84,6 +84,8 @@ export default interface AccountRoot extends BaseLedgerEntry, HasPreviousTxnID {
GovernanceMarks?: string
AccountIndex?: number
TouchCount?: number
/* The cron job that is associated with this account. */
Cron?: string
}
/**
@@ -152,6 +154,11 @@ export interface AccountRootFlagsInterface {
* Disallow incoming Remit from other accounts.
*/
lsfDisallowIncomingRemit?: boolean
/**
* This address can claw back issued IOUs. Once enabled, cannot be disabled.
*/
lsfAllowTrustLineClawback?: boolean
}
export enum AccountRootFlags {
@@ -216,4 +223,8 @@ export enum AccountRootFlags {
* Disallow incoming Remits from other accounts.
*/
lsfDisallowIncomingRemit = 0x80000000,
/**
* This address can claw back issued IOUs. Once enabled, cannot be disabled.
*/
lsfAllowTrustLineClawback = 0x00001000,
}

View File

@@ -77,4 +77,8 @@ export enum RippleStateFlags {
lsfHighFreeze = 0x00800000,
// True, trust line to AMM. Used by client apps to identify payments via AMM.
lsfAMMNode = 0x01000000,
// True, low side has set deep freeze flag
lsfLowDeepFreeze = 0x02000000,
// True, high side has set deep freeze flag
lsfHighDeepFreeze = 0x04000000,
}

View File

@@ -61,6 +61,8 @@ export enum AccountSetAsfFlags {
asfDisallowIncomingTrustline = 15,
/** Disallow other accounts from sending incoming Remits */
asfDisallowIncomingRemit = 16,
/** Permanently gain the ability to claw back issued IOUs */
asfAllowTrustLineClawback = 17,
}
/**

View File

@@ -0,0 +1,49 @@
import { ValidationError } from '../../errors'
import { IssuedCurrencyAmount } from '../common'
import {
BaseTransaction,
validateBaseTransaction,
isIssuedCurrency,
} from './common'
/**
* The Clawback transaction is used by the token issuer to claw back
* issued tokens from a holder.
*/
export interface Clawback extends BaseTransaction {
TransactionType: 'Clawback'
/**
* Indicates the AccountID that submitted this transaction. The account MUST
* be the issuer of the currency.
*/
Account: string
/**
* The amount of currency to deliver, and it must be non-XRP. The nested field
* names MUST be lower-case. The `issuer` field MUST be the holder's address,
* whom to be clawed back.
*/
Amount: IssuedCurrencyAmount
}
/**
* Verify the form and type of an Clawback at runtime.
*
* @param tx - An Clawback Transaction.
* @throws When the Clawback is Malformed.
*/
export function validateClawback(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.Amount == null) {
throw new ValidationError('Clawback: missing field Amount')
}
if (!isIssuedCurrency(tx.Amount)) {
throw new ValidationError('Clawback: invalid Amount')
}
if (isIssuedCurrency(tx.Amount) && tx.Account === tx.Amount.issuer) {
throw new ValidationError('Clawback: invalid holder Account')
}
}

View File

@@ -0,0 +1,17 @@
import { BaseTransaction } from './common'
/**
* Cron job to be executed.
*
* @category Pseudo Transaction Models
*/
export interface Cron extends BaseTransaction {
TransactionType: 'Cron'
/**
* The ledger index where this pseudo-transaction appears.
* This distinguishes the pseudo-transaction from other occurrences of the same change.
*/
LedgerSequence: number
/** The owner of the cron job. */
Owner: string
}

View File

@@ -0,0 +1,68 @@
import { ValidationError } from '../../errors'
import { BaseTransaction, validateBaseTransaction } from './common'
/**
* Transaction Flags for an CronSet Transaction.
*
* @category Transaction Flags
*/
export enum CronSetFlags {
/**
* If set, indicates that the user would like to unset the cron job.
*/
tfCronUnset = 0x00000001,
}
/**
* CronSet is a transaction model that allows an account to set a cron job.
*
* @category Transaction Models
*/
export interface CronSet extends BaseTransaction {
TransactionType: 'CronSet'
Flags?: number | CronSetFlags
RepeatCount?: number
DelaySeconds?: number
}
const MAX_REPEAT_COUNT = 256
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- seconds in a year
const MIN_DELAY_SECONDS = 365 * 24 * 60 * 60
/**
* Verify the form and type of an CronSet at runtime.
*
* @param tx - An CronSet Transaction.
* @throws When the CronSet is Malformed.
*/
export function validateCronSet(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.Flags === CronSetFlags.tfCronUnset) {
if (tx.RepeatCount !== undefined || tx.DelaySeconds !== undefined) {
throw new ValidationError(
'CronSet: RepeatCount and DelaySeconds must not be set when Flags is set to tfCronUnset',
)
}
}
if (tx.RepeatCount !== undefined && typeof tx.RepeatCount !== 'number') {
throw new ValidationError('CronSet: RepeatCount must be a number')
}
if (tx.RepeatCount !== undefined && tx.RepeatCount > MAX_REPEAT_COUNT) {
throw new ValidationError(
`CronSet: RepeatCount must be less than ${MAX_REPEAT_COUNT}`,
)
}
if (tx.DelaySeconds !== undefined && typeof tx.DelaySeconds !== 'number') {
throw new ValidationError('CronSet: DelaySeconds must be a number')
}
if (tx.DelaySeconds !== undefined && tx.DelaySeconds > MIN_DELAY_SECONDS) {
throw new ValidationError(
`CronSet: DelaySeconds must be less than ${MIN_DELAY_SECONDS}`,
)
}
}

View File

@@ -17,6 +17,8 @@ export { CheckCancel } from './checkCancel'
export { CheckCash } from './checkCash'
export { CheckCreate } from './checkCreate'
export { ClaimReward, ClaimRewardFlags } from './claimReward'
export { Cron } from './cron'
export { CronSet, CronSetFlags } from './cronSet'
export { DepositPreauth } from './depositPreauth'
export { EscrowCancel } from './escrowCancel'
export { EscrowCreate } from './escrowCreate'
@@ -61,3 +63,4 @@ export { URITokenCreateSellOffer } from './uriTokenCreateSellOffer'
export { URITokenBuy } from './uriTokenBuy'
export { URITokenCancelSellOffer } from './uriTokenCancelSellOffer'
export { UNLModify } from './UNLModify'
export { Clawback } from './clawback'

View File

@@ -10,7 +10,10 @@ import { CheckCancel, validateCheckCancel } from './checkCancel'
import { CheckCash, validateCheckCash } from './checkCash'
import { CheckCreate, validateCheckCreate } from './checkCreate'
import { ClaimReward, validateClaimReward } from './claimReward'
import { Clawback, validateClawback } from './clawback'
import { BaseTransaction, isIssuedCurrency } from './common'
import { Cron } from './cron'
import { CronSet, validateCronSet } from './cronSet'
import { DepositPreauth, validateDepositPreauth } from './depositPreauth'
import { EnableAmendment } from './enableAmendment'
import { EscrowCancel, validateEscrowCancel } from './escrowCancel'
@@ -66,6 +69,8 @@ export type SubmittableTransaction =
| CheckCash
| CheckCreate
| ClaimReward
| Clawback
| CronSet
| DepositPreauth
| EscrowCancel
| EscrowCreate
@@ -96,7 +101,7 @@ export type SubmittableTransaction =
*
* @category Transaction Models
*/
export type PseudoTransaction = EnableAmendment | SetFee | UNLModify
export type PseudoTransaction = Cron | EnableAmendment | SetFee | UNLModify
/**
* All transactions that can live on the XAHL
@@ -204,6 +209,14 @@ export function validate(transaction: Record<string, unknown>): void {
validateClaimReward(tx)
break
case 'Clawback':
validateClawback(tx)
break
case 'CronSet':
validateCronSet(tx)
break
case 'DepositPreauth':
validateDepositPreauth(tx)
break

View File

@@ -30,6 +30,11 @@ export enum TrustSetFlags {
tfSetFreeze = 0x00100000,
/** Unfreeze the trust line. */
tfClearFreeze = 0x00200000,
/** Deep-Freeze the trustline -- disallow sending and receiving the said IssuedCurrency */
/** Allowed only if the trustline is already regularly frozen, or if tfSetFreeze is set in the same transaction. */
tfSetDeepFreeze = 0x00400000,
/** Clear a Deep-Frozen trustline */
tfClearDeepFreeze = 0x00800000,
}
/**
@@ -89,6 +94,11 @@ export interface TrustSetFlagsInterface extends GlobalFlags {
tfSetFreeze?: boolean
/** Unfreeze the trust line. */
tfClearFreeze?: boolean
/** Deep-Freeze the trustline -- disallow sending and receiving the said IssuedCurrency */
/** Allowed only if the trustline is already regularly frozen, or if tfSetFreeze is set in the same transaction. */
tfSetDeepFreeze?: boolean
/** Clear a Deep-Frozen trust line */
tfClearDeepFreeze?: boolean
}
/**

View File

@@ -8,6 +8,7 @@ import {
} from '../ledger/AccountRoot'
import { AccountSetTfFlags } from '../transactions/accountSet'
import { GlobalFlags } from '../transactions/common'
import { CronSetFlags } from '../transactions/cronSet'
import { OfferCreateFlags } from '../transactions/offerCreate'
import { PaymentFlags } from '../transactions/payment'
import { PaymentChannelClaimFlags } from '../transactions/paymentChannelClaim'
@@ -52,6 +53,7 @@ const txToFlag = {
PaymentChannelClaim: PaymentChannelClaimFlags,
Payment: PaymentFlags,
TrustSet: TrustSetFlags,
CronSet: CronSetFlags,
}
/**

View File

@@ -0,0 +1,115 @@
import { assert } from 'chai'
import {
AccountSet,
AccountSetAsfFlags,
TrustSet,
Payment,
Clawback,
} from '../../../src'
import serverUrl from '../serverUrl'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { generateFundedWallet, testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('Clawback', function () {
let testContext: XrplIntegrationTestContext
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const wallet2 = await generateFundedWallet(testContext.client)
const setupAccountSetTx: AccountSet = {
TransactionType: 'AccountSet',
Account: testContext.wallet.classicAddress,
SetFlag: AccountSetAsfFlags.asfAllowTrustLineClawback,
}
await testTransaction(
testContext.client,
setupAccountSetTx,
testContext.wallet,
)
const setupTrustSetTx: TrustSet = {
TransactionType: 'TrustSet',
Account: wallet2.classicAddress,
LimitAmount: {
currency: 'USD',
issuer: testContext.wallet.classicAddress,
value: '1000',
},
}
await testTransaction(testContext.client, setupTrustSetTx, wallet2)
const setupPaymentTx: Payment = {
TransactionType: 'Payment',
Account: testContext.wallet.classicAddress,
Destination: wallet2.classicAddress,
Amount: {
currency: 'USD',
issuer: testContext.wallet.classicAddress,
value: '1000',
},
}
await testTransaction(
testContext.client,
setupPaymentTx,
testContext.wallet,
)
// verify that line is created
const objectsResponse = await testContext.client.request({
command: 'account_objects',
account: wallet2.classicAddress,
type: 'state',
})
assert.lengthOf(
objectsResponse.result.account_objects,
1,
'Should be exactly one line on the ledger',
)
// actual test - clawback
const tx: Clawback = {
TransactionType: 'Clawback',
Account: testContext.wallet.classicAddress,
Amount: {
currency: 'USD',
issuer: wallet2.classicAddress,
value: '500',
},
}
await testTransaction(testContext.client, tx, testContext.wallet)
// verify amount clawed back
const linesResponse = await testContext.client.request({
command: 'account_lines',
account: wallet2.classicAddress,
})
assert.lengthOf(
linesResponse.result.lines,
1,
'Should be exactly one line on the ledger',
)
assert.equal(
'500',
linesResponse.result.lines[0].balance,
`Holder balance incorrect after Clawback`,
)
},
TIMEOUT,
)
})

View File

@@ -1,24 +1,36 @@
import { assert } from 'chai'
import { OfferCreate } from '../../../src'
import { OfferCreate, TrustSet, Wallet } from '../../../src'
import serverUrl from '../serverUrl'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { testTransaction } from '../utils'
import {
testTransaction,
generateFundedWallet,
submitTransaction,
} from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('OfferCreate', function () {
let testContext: XrplIntegrationTestContext
let wallet_deep_freeze_trustline: Wallet | undefined
beforeEach(async () => {
beforeAll(async () => {
testContext = await setupClient(serverUrl)
if (!wallet_deep_freeze_trustline) {
// eslint-disable-next-line require-atomic-updates -- race condition doesn't really matter
wallet_deep_freeze_trustline = await generateFundedWallet(
testContext.client,
)
}
})
afterEach(async () => teardownClient(testContext))
afterAll(async () => teardownClient(testContext))
it(
'base',
@@ -49,4 +61,52 @@ describe('OfferCreate', function () {
},
TIMEOUT,
)
it(
'OfferCreate with Deep-Frozen trustline must fail',
async () => {
assert(wallet_deep_freeze_trustline != null)
// deep-freeze the trust line
const trust_set_tx: TrustSet = {
TransactionType: 'TrustSet',
Account: testContext.wallet.classicAddress,
LimitAmount: {
currency: 'USD',
issuer: wallet_deep_freeze_trustline.classicAddress,
value: '10',
},
Flags: {
tfSetFreeze: true,
tfSetDeepFreeze: true,
},
}
await testTransaction(
testContext.client,
trust_set_tx,
testContext.wallet,
)
const offer_create_tx: OfferCreate = {
TransactionType: 'OfferCreate',
Account: testContext.wallet.classicAddress,
TakerGets: '13100000',
TakerPays: {
currency: 'USD',
issuer: wallet_deep_freeze_trustline.classicAddress,
value: '10',
},
}
const response = await submitTransaction({
client: testContext.client,
transaction: offer_create_tx,
wallet: testContext.wallet,
})
assert.equal(response.result.engine_result, 'tecFROZEN')
},
TIMEOUT,
)
})

View File

@@ -1,6 +1,8 @@
import { assert } from 'chai'
import { TrustSet, percentToQuality, Wallet } from '../../../src'
import { RippleState } from '../../../src/models/ledger/index'
import { RippleStateFlags } from '../../../src/models/ledger/RippleState'
import serverUrl from '../serverUrl'
import {
setupClient,
@@ -85,4 +87,60 @@ describe('TrustSet', function () {
},
TIMEOUT,
)
it(
'Create a Deep-Frozen trustline',
async () => {
assert(wallet2 != null)
// deep-freeze a trustline with the specified counter-party/currency-code
const tx: TrustSet = {
TransactionType: 'TrustSet',
Account: testContext.wallet.classicAddress,
LimitAmount: {
currency: 'USD',
issuer: wallet2.classicAddress,
value: '10',
},
Flags: {
tfSetFreeze: true,
tfSetDeepFreeze: true,
},
}
const response = await testTransaction(
testContext.client,
tx,
testContext.wallet,
)
assert.equal(response.result.engine_result, 'tesSUCCESS')
// assert that the trustline is frozen
const trustLine = await testContext.client.request({
command: 'account_lines',
account: testContext.wallet.classicAddress,
})
assert.equal(trustLine.result.lines[0].freeze, true)
// verify that the trust-line is deep-frozen
// this operation cannot be done with the account_lines RPC
const account_objects = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
})
const rippleState = account_objects.result
.account_objects[0] as RippleState
// Depending on the pseudo-random generation of accounts,
// either of the below leger-object flags must be set
const hasDeepFreeze =
// eslint-disable-next-line no-bitwise -- required to validate flag
(rippleState.Flags & RippleStateFlags.lsfHighDeepFreeze) |
// eslint-disable-next-line no-bitwise -- required to validate flag
(rippleState.Flags & RippleStateFlags.lsfLowDeepFreeze)
assert.isTrue(hasDeepFreeze !== 0)
},
TIMEOUT,
)
})

View File

@@ -0,0 +1,81 @@
import { assert } from 'chai'
import { validate, ValidationError } from '../../src'
/**
* Clawback Transaction Verification Testing.
*
* Providing runtime verification testing for each specific transaction type.
*/
describe('Clawback', function () {
it(`verifies valid Clawback`, function () {
const validClawback = {
TransactionType: 'Clawback',
Amount: {
currency: 'DSH',
issuer: 'rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX',
value: '43.11584856965009',
},
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
} as any
assert.doesNotThrow(() => validate(validClawback))
})
it(`throws w/ missing Amount`, function () {
const missingAmount = {
TransactionType: 'Clawback',
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
} as any
assert.throws(
() => validate(missingAmount),
ValidationError,
'Clawback: missing field Amount',
)
})
it(`throws w/ invalid Amount`, function () {
const invalidAmount = {
TransactionType: 'Clawback',
Amount: 100000000,
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
} as any
assert.throws(
() => validate(invalidAmount),
ValidationError,
'Clawback: invalid Amount',
)
const invalidStrAmount = {
TransactionType: 'Clawback',
Amount: '1234',
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
} as any
assert.throws(
() => validate(invalidStrAmount),
ValidationError,
'Clawback: invalid Amount',
)
})
it(`throws w/ invalid holder Account`, function () {
const invalidAccount = {
TransactionType: 'Clawback',
Amount: {
currency: 'DSH',
issuer: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
value: '43.11584856965009',
},
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm',
} as any
assert.throws(
() => validate(invalidAccount),
ValidationError,
'Clawback: invalid holder Account',
)
})
})

View File

@@ -0,0 +1,108 @@
import { assert } from 'chai'
import { validate, ValidationError } from '../../src'
import {
CronSetFlags,
validateCronSet,
} from '../../src/models/transactions/cronSet'
/**
* CronSet Transaction Verification Testing.
*
* Providing runtime verification testing for each specific transaction type.
*/
describe('CronSet', function () {
it(`verifies valid CronSet`, function () {
let validCronSet = {
TransactionType: 'CronSet',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
Fee: '100',
RepeatCount: 256,
DelaySeconds: 365 * 24 * 60 * 60,
} as any
assert.doesNotThrow(() => validateCronSet(validCronSet))
assert.doesNotThrow(() => validate(validCronSet))
validCronSet = {
TransactionType: 'CronSet',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
Fee: '100',
Flags: CronSetFlags.tfCronUnset,
} as any
assert.doesNotThrow(() => validateCronSet(validCronSet))
assert.doesNotThrow(() => validate(validCronSet))
validCronSet = {
TransactionType: 'CronSet',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
Fee: '100',
Flags: { tfCronUnset: true },
} as any
assert.doesNotThrow(() => validateCronSet(validCronSet))
assert.doesNotThrow(() => validate(validCronSet))
})
it(`throws w/ invalid Delete Operation`, function () {
const invalidDeleteOperation = {
TransactionType: 'CronSet',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
Flags: CronSetFlags.tfCronUnset,
RepeatCount: 1,
DelaySeconds: 1,
Fee: '100',
} as any
assert.throws(
() => validateCronSet(invalidDeleteOperation),
ValidationError,
'CronSet: RepeatCount and DelaySeconds must not be set when Flags is set to tfCronUnset',
)
assert.throws(
() => validate(invalidDeleteOperation),
ValidationError,
'CronSet: RepeatCount and DelaySeconds must not be set when Flags is set to tfCronUnset',
)
})
it(`throws w/ invalid RepeatCount`, function () {
const invalidRepeatCount = {
TransactionType: 'CronSet',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
RepeatCount: 257,
Fee: '100',
} as any
assert.throws(
() => validateCronSet(invalidRepeatCount),
ValidationError,
'CronSet: RepeatCount must be less than 256',
)
assert.throws(
() => validate(invalidRepeatCount),
ValidationError,
'CronSet: RepeatCount must be less than 256',
)
})
it(`throws w/ invalid DelaySeconds`, function () {
const invalidDelaySeconds = {
TransactionType: 'CronSet',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
DelaySeconds: 365 * 24 * 60 * 60 + 1,
Fee: '100',
} as any
assert.throws(
() => validateCronSet(invalidDelaySeconds),
ValidationError,
`CronSet: DelaySeconds must be less than ${365 * 24 * 60 * 60}`,
)
assert.throws(
() => validate(invalidDelaySeconds),
ValidationError,
`CronSet: DelaySeconds must be less than ${365 * 24 * 60 * 60}`,
)
})
})

View File

@@ -22,6 +22,11 @@ describe('TrustSet', function () {
},
QualityIn: 1234,
QualityOut: 4321,
// an example of deep-frozen trustline
Flags: {
tfSetFreeze: true,
tfSetDeepFreeze: true,
},
} as any
})

View File

@@ -168,7 +168,8 @@ describe('Models Utils', function () {
AccountRootFlags.lsfDisallowIncomingCheck |
AccountRootFlags.lsfDisallowIncomingPayChan |
AccountRootFlags.lsfDisallowIncomingTrustline |
AccountRootFlags.lsfDisallowIncomingRemit
AccountRootFlags.lsfDisallowIncomingRemit |
AccountRootFlags.lsfAllowTrustLineClawback
const parsed = parseAccountRootFlags(accountRootFlags)
@@ -186,7 +187,8 @@ describe('Models Utils', function () {
parsed.lsfDisallowIncomingCheck &&
parsed.lsfDisallowIncomingPayChan &&
parsed.lsfDisallowIncomingTrustline &&
parsed.lsfDisallowIncomingRemit,
parsed.lsfDisallowIncomingRemit &&
parsed.lsfAllowTrustLineClawback,
)
})
@@ -207,6 +209,7 @@ describe('Models Utils', function () {
assert.isUndefined(parsed.lsfDisallowIncomingPayChan)
assert.isUndefined(parsed.lsfDisallowIncomingTrustline)
assert.isUndefined(parsed.lsfDisallowIncomingRemit)
assert.isUndefined(parsed.lsfAllowTrustLineClawback)
})
it('parseTransactionFlags all enabled', function () {