xrpl 4.4.0-smartescrow.0 → 4.5.0-smartescrow.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/build/xrpl-latest-min.js +1 -1
  2. package/build/xrpl-latest-min.js.map +1 -1
  3. package/build/xrpl-latest.js +191 -26
  4. package/build/xrpl-latest.js.map +1 -1
  5. package/dist/npm/models/common/index.d.ts +16 -0
  6. package/dist/npm/models/common/index.d.ts.map +1 -1
  7. package/dist/npm/models/ledger/AccountRoot.d.ts +3 -1
  8. package/dist/npm/models/ledger/AccountRoot.d.ts.map +1 -1
  9. package/dist/npm/models/ledger/AccountRoot.js +1 -0
  10. package/dist/npm/models/ledger/AccountRoot.js.map +1 -1
  11. package/dist/npm/models/ledger/DirectoryNode.d.ts +1 -0
  12. package/dist/npm/models/ledger/DirectoryNode.d.ts.map +1 -1
  13. package/dist/npm/models/ledger/Escrow.d.ts +2 -0
  14. package/dist/npm/models/ledger/Escrow.d.ts.map +1 -1
  15. package/dist/npm/models/ledger/MPToken.d.ts +1 -0
  16. package/dist/npm/models/ledger/MPToken.d.ts.map +1 -1
  17. package/dist/npm/models/ledger/MPTokenIssuance.d.ts +1 -0
  18. package/dist/npm/models/ledger/MPTokenIssuance.d.ts.map +1 -1
  19. package/dist/npm/models/ledger/Offer.d.ts +10 -1
  20. package/dist/npm/models/ledger/Offer.d.ts.map +1 -1
  21. package/dist/npm/models/ledger/Offer.js +1 -0
  22. package/dist/npm/models/ledger/Offer.js.map +1 -1
  23. package/dist/npm/models/methods/bookOffers.d.ts +1 -0
  24. package/dist/npm/models/methods/bookOffers.d.ts.map +1 -1
  25. package/dist/npm/models/methods/pathFind.d.ts +2 -0
  26. package/dist/npm/models/methods/pathFind.d.ts.map +1 -1
  27. package/dist/npm/models/methods/ripplePathFind.d.ts +1 -0
  28. package/dist/npm/models/methods/ripplePathFind.d.ts.map +1 -1
  29. package/dist/npm/models/methods/subscribe.d.ts +4 -0
  30. package/dist/npm/models/methods/subscribe.d.ts.map +1 -1
  31. package/dist/npm/models/transactions/MPTokenIssuanceCreate.d.ts +1 -1
  32. package/dist/npm/models/transactions/MPTokenIssuanceCreate.d.ts.map +1 -1
  33. package/dist/npm/models/transactions/MPTokenIssuanceCreate.js +14 -5
  34. package/dist/npm/models/transactions/MPTokenIssuanceCreate.js.map +1 -1
  35. package/dist/npm/models/transactions/accountSet.d.ts +2 -1
  36. package/dist/npm/models/transactions/accountSet.d.ts.map +1 -1
  37. package/dist/npm/models/transactions/accountSet.js +1 -0
  38. package/dist/npm/models/transactions/accountSet.js.map +1 -1
  39. package/dist/npm/models/transactions/common.d.ts +4 -0
  40. package/dist/npm/models/transactions/common.d.ts.map +1 -1
  41. package/dist/npm/models/transactions/common.js +134 -1
  42. package/dist/npm/models/transactions/common.js.map +1 -1
  43. package/dist/npm/models/transactions/escrowCreate.d.ts +2 -1
  44. package/dist/npm/models/transactions/escrowCreate.d.ts.map +1 -1
  45. package/dist/npm/models/transactions/escrowCreate.js +1 -6
  46. package/dist/npm/models/transactions/escrowCreate.js.map +1 -1
  47. package/dist/npm/models/transactions/escrowFinish.d.ts +5 -0
  48. package/dist/npm/models/transactions/escrowFinish.d.ts.map +1 -1
  49. package/dist/npm/models/transactions/escrowFinish.js.map +1 -1
  50. package/dist/npm/models/transactions/index.d.ts +1 -1
  51. package/dist/npm/models/transactions/index.d.ts.map +1 -1
  52. package/dist/npm/models/transactions/index.js +2 -1
  53. package/dist/npm/models/transactions/index.js.map +1 -1
  54. package/dist/npm/models/transactions/metadata.d.ts +2 -1
  55. package/dist/npm/models/transactions/metadata.d.ts.map +1 -1
  56. package/dist/npm/models/transactions/metadata.js.map +1 -1
  57. package/dist/npm/models/transactions/offerCreate.d.ts +4 -1
  58. package/dist/npm/models/transactions/offerCreate.d.ts.map +1 -1
  59. package/dist/npm/models/transactions/offerCreate.js +10 -0
  60. package/dist/npm/models/transactions/offerCreate.js.map +1 -1
  61. package/dist/npm/models/transactions/payment.d.ts +1 -0
  62. package/dist/npm/models/transactions/payment.d.ts.map +1 -1
  63. package/dist/npm/models/transactions/payment.js +4 -3
  64. package/dist/npm/models/transactions/payment.js.map +1 -1
  65. package/dist/npm/models/transactions/vaultCreate.d.ts.map +1 -1
  66. package/dist/npm/models/transactions/vaultCreate.js +13 -4
  67. package/dist/npm/models/transactions/vaultCreate.js.map +1 -1
  68. package/dist/npm/models/transactions/vaultDeposit.d.ts +2 -2
  69. package/dist/npm/models/transactions/vaultDeposit.d.ts.map +1 -1
  70. package/dist/npm/models/transactions/vaultDeposit.js.map +1 -1
  71. package/dist/npm/models/transactions/vaultWithdraw.d.ts +2 -2
  72. package/dist/npm/models/transactions/vaultWithdraw.d.ts.map +1 -1
  73. package/dist/npm/models/transactions/vaultWithdraw.js.map +1 -1
  74. package/dist/npm/snippets/src/permissionedDEX.d.ts +2 -0
  75. package/dist/npm/snippets/src/permissionedDEX.d.ts.map +1 -0
  76. package/dist/npm/snippets/src/permissionedDEX.js +173 -0
  77. package/dist/npm/snippets/src/permissionedDEX.js.map +1 -0
  78. package/dist/npm/snippets/tsconfig.tsbuildinfo +1 -1
  79. package/dist/npm/src/models/common/index.d.ts +16 -0
  80. package/dist/npm/src/models/common/index.d.ts.map +1 -1
  81. package/dist/npm/src/models/ledger/AccountRoot.d.ts +3 -1
  82. package/dist/npm/src/models/ledger/AccountRoot.d.ts.map +1 -1
  83. package/dist/npm/src/models/ledger/AccountRoot.js +1 -0
  84. package/dist/npm/src/models/ledger/AccountRoot.js.map +1 -1
  85. package/dist/npm/src/models/ledger/DirectoryNode.d.ts +1 -0
  86. package/dist/npm/src/models/ledger/DirectoryNode.d.ts.map +1 -1
  87. package/dist/npm/src/models/ledger/Escrow.d.ts +2 -0
  88. package/dist/npm/src/models/ledger/Escrow.d.ts.map +1 -1
  89. package/dist/npm/src/models/ledger/MPToken.d.ts +1 -0
  90. package/dist/npm/src/models/ledger/MPToken.d.ts.map +1 -1
  91. package/dist/npm/src/models/ledger/MPTokenIssuance.d.ts +1 -0
  92. package/dist/npm/src/models/ledger/MPTokenIssuance.d.ts.map +1 -1
  93. package/dist/npm/src/models/ledger/Offer.d.ts +10 -1
  94. package/dist/npm/src/models/ledger/Offer.d.ts.map +1 -1
  95. package/dist/npm/src/models/ledger/Offer.js +1 -0
  96. package/dist/npm/src/models/ledger/Offer.js.map +1 -1
  97. package/dist/npm/src/models/methods/bookOffers.d.ts +1 -0
  98. package/dist/npm/src/models/methods/bookOffers.d.ts.map +1 -1
  99. package/dist/npm/src/models/methods/pathFind.d.ts +2 -0
  100. package/dist/npm/src/models/methods/pathFind.d.ts.map +1 -1
  101. package/dist/npm/src/models/methods/ripplePathFind.d.ts +1 -0
  102. package/dist/npm/src/models/methods/ripplePathFind.d.ts.map +1 -1
  103. package/dist/npm/src/models/methods/subscribe.d.ts +4 -0
  104. package/dist/npm/src/models/methods/subscribe.d.ts.map +1 -1
  105. package/dist/npm/src/models/transactions/MPTokenIssuanceCreate.d.ts +1 -1
  106. package/dist/npm/src/models/transactions/MPTokenIssuanceCreate.d.ts.map +1 -1
  107. package/dist/npm/src/models/transactions/MPTokenIssuanceCreate.js +14 -5
  108. package/dist/npm/src/models/transactions/MPTokenIssuanceCreate.js.map +1 -1
  109. package/dist/npm/src/models/transactions/accountSet.d.ts +2 -1
  110. package/dist/npm/src/models/transactions/accountSet.d.ts.map +1 -1
  111. package/dist/npm/src/models/transactions/accountSet.js +1 -0
  112. package/dist/npm/src/models/transactions/accountSet.js.map +1 -1
  113. package/dist/npm/src/models/transactions/common.d.ts +4 -0
  114. package/dist/npm/src/models/transactions/common.d.ts.map +1 -1
  115. package/dist/npm/src/models/transactions/common.js +134 -1
  116. package/dist/npm/src/models/transactions/common.js.map +1 -1
  117. package/dist/npm/src/models/transactions/escrowCreate.d.ts +2 -1
  118. package/dist/npm/src/models/transactions/escrowCreate.d.ts.map +1 -1
  119. package/dist/npm/src/models/transactions/escrowCreate.js +1 -6
  120. package/dist/npm/src/models/transactions/escrowCreate.js.map +1 -1
  121. package/dist/npm/src/models/transactions/escrowFinish.d.ts +5 -0
  122. package/dist/npm/src/models/transactions/escrowFinish.d.ts.map +1 -1
  123. package/dist/npm/src/models/transactions/escrowFinish.js.map +1 -1
  124. package/dist/npm/src/models/transactions/index.d.ts +1 -1
  125. package/dist/npm/src/models/transactions/index.d.ts.map +1 -1
  126. package/dist/npm/src/models/transactions/index.js +2 -1
  127. package/dist/npm/src/models/transactions/index.js.map +1 -1
  128. package/dist/npm/src/models/transactions/metadata.d.ts +2 -1
  129. package/dist/npm/src/models/transactions/metadata.d.ts.map +1 -1
  130. package/dist/npm/src/models/transactions/metadata.js.map +1 -1
  131. package/dist/npm/src/models/transactions/offerCreate.d.ts +4 -1
  132. package/dist/npm/src/models/transactions/offerCreate.d.ts.map +1 -1
  133. package/dist/npm/src/models/transactions/offerCreate.js +10 -0
  134. package/dist/npm/src/models/transactions/offerCreate.js.map +1 -1
  135. package/dist/npm/src/models/transactions/payment.d.ts +1 -0
  136. package/dist/npm/src/models/transactions/payment.d.ts.map +1 -1
  137. package/dist/npm/src/models/transactions/payment.js +4 -3
  138. package/dist/npm/src/models/transactions/payment.js.map +1 -1
  139. package/dist/npm/src/models/transactions/vaultCreate.d.ts.map +1 -1
  140. package/dist/npm/src/models/transactions/vaultCreate.js +13 -4
  141. package/dist/npm/src/models/transactions/vaultCreate.js.map +1 -1
  142. package/dist/npm/src/models/transactions/vaultDeposit.d.ts +2 -2
  143. package/dist/npm/src/models/transactions/vaultDeposit.d.ts.map +1 -1
  144. package/dist/npm/src/models/transactions/vaultDeposit.js.map +1 -1
  145. package/dist/npm/src/models/transactions/vaultWithdraw.d.ts +2 -2
  146. package/dist/npm/src/models/transactions/vaultWithdraw.d.ts.map +1 -1
  147. package/dist/npm/src/models/transactions/vaultWithdraw.js.map +1 -1
  148. package/package.json +4 -4
  149. package/src/models/common/index.ts +25 -0
  150. package/src/models/ledger/AccountRoot.ts +10 -0
  151. package/src/models/ledger/DirectoryNode.ts +3 -0
  152. package/src/models/ledger/Escrow.ts +13 -1
  153. package/src/models/ledger/MPToken.ts +1 -0
  154. package/src/models/ledger/MPTokenIssuance.ts +1 -0
  155. package/src/models/ledger/Offer.ts +21 -0
  156. package/src/models/methods/bookOffers.ts +7 -0
  157. package/src/models/methods/pathFind.ts +10 -0
  158. package/src/models/methods/ripplePathFind.ts +5 -0
  159. package/src/models/methods/subscribe.ts +21 -0
  160. package/src/models/transactions/MPTokenIssuanceCreate.ts +33 -10
  161. package/src/models/transactions/accountSet.ts +2 -0
  162. package/src/models/transactions/common.ts +236 -1
  163. package/src/models/transactions/escrowCreate.ts +8 -12
  164. package/src/models/transactions/escrowFinish.ts +7 -0
  165. package/src/models/transactions/index.ts +1 -1
  166. package/src/models/transactions/metadata.ts +3 -0
  167. package/src/models/transactions/offerCreate.ts +25 -0
  168. package/src/models/transactions/payment.ts +18 -6
  169. package/src/models/transactions/vaultCreate.ts +30 -6
  170. package/src/models/transactions/vaultDeposit.ts +3 -2
  171. package/src/models/transactions/vaultWithdraw.ts +3 -2
@@ -2,6 +2,19 @@ import { Amount } from '../common'
2
2
 
3
3
  import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
4
4
 
5
+ export interface Book {
6
+ Book: {
7
+ /** The ID of the offer directory that links to this offer. */
8
+ BookDirectory: string
9
+
10
+ /**
11
+ * A hint indicating which page of the offer directory links to this entry,
12
+ * in case the directory consists of multiple pages.
13
+ */
14
+ BookNode: string
15
+ }
16
+ }
17
+
5
18
  export default interface Offer extends BaseLedgerEntry, HasPreviousTxnID {
6
19
  LedgerEntryType: 'Offer'
7
20
  /** A bit-map of boolean flags enabled for this Offer. */
@@ -34,9 +47,17 @@ export default interface Offer extends BaseLedgerEntry, HasPreviousTxnID {
34
47
  OwnerNode: string
35
48
  /** The time this Offer expires, in seconds since the Ripple Epoch. */
36
49
  Expiration?: number
50
+ /** The domain that the offer must be a part of. */
51
+ DomainID?: string
52
+ /**
53
+ * An additional list of order book directories that this offer belongs to.
54
+ * Currently this field only applicable to hybrid offers.
55
+ */
56
+ AdditionalBooks?: Book[]
37
57
  }
38
58
 
39
59
  export enum OfferFlags {
40
60
  lsfPassive = 0x00010000,
41
61
  lsfSell = 0x00020000,
62
+ lsfHybrid = 0x00040000,
42
63
  }
@@ -39,6 +39,13 @@ export interface BookOffersRequest extends BaseRequest, LookupByLedgerRequest {
39
39
  * currency amounts.
40
40
  */
41
41
  taker_pays: BookOfferCurrency
42
+ /**
43
+ * The object ID of a PermissionedDomain object. If this field is provided,
44
+ * the response will include only valid domain offers associated with that
45
+ * specific domain. If omitted, the response will include only hybrid and open
46
+ * offers for the trading pair, excluding all domain-specific offers.
47
+ */
48
+ domain?: string
42
49
  }
43
50
 
44
51
  export interface BookOffer extends Offer {
@@ -30,6 +30,11 @@ export interface PathFindCreateRequest extends BasePathFindRequest {
30
30
  * about, or to check the overall cost to make a payment along a certain path.
31
31
  */
32
32
  paths?: Path[]
33
+ /**
34
+ * The object ID of a PermissionedDomain object. If this field is included,
35
+ * then only valid paths for this domain will be returned.
36
+ */
37
+ domain?: string
33
38
  }
34
39
 
35
40
  /** Stop sending pathfinding information. */
@@ -97,6 +102,11 @@ export interface PathFindResponse extends BaseResponse {
97
102
  * Continues to send updates each time a new ledger closes.
98
103
  */
99
104
  full_reply: boolean
105
+ /**
106
+ * The object ID of a PermissionedDomain object, if the orderbook shown is
107
+ * for a specific domain.
108
+ */
109
+ domain?: string
100
110
  /**
101
111
  * The ID provided in the WebSocket request is included again at this
102
112
  * level.
@@ -38,6 +38,11 @@ export interface RipplePathFindRequest
38
38
  * and optional issuer field, like how currency amounts are specified.
39
39
  */
40
40
  source_currencies?: SourceCurrencyAmount[]
41
+ /**
42
+ * The object ID of a PermissionedDomain object. If this field is included,
43
+ * then only valid paths for this domain will be returned.
44
+ */
45
+ domain?: string
41
46
  }
42
47
 
43
48
  export interface RipplePathFindPathOption {
@@ -39,6 +39,12 @@ export interface SubscribeBook {
39
39
  snapshot?: boolean
40
40
  /** If true, return both sides of the order book. The default is false. */
41
41
  both?: boolean
42
+ /**
43
+ * The object ID of a PermissionedDomain object. If this field is included,
44
+ * then the offers will be filtered to only show the valid domain offers for
45
+ * that domain.
46
+ */
47
+ domain?: string
42
48
  }
43
49
 
44
50
  /**
@@ -136,6 +142,11 @@ export interface LedgerStream extends BaseStream {
136
142
  * connected but has not yet obtained a ledger from the network.
137
143
  */
138
144
  validated_ledgers?: string
145
+
146
+ /**
147
+ * The network from which the ledger stream is received.
148
+ */
149
+ network_id?: number
139
150
  }
140
151
 
141
152
  /**
@@ -175,6 +186,11 @@ export interface LedgerStreamResponse {
175
186
  * connected but has not yet obtained a ledger from the network.
176
187
  */
177
188
  validated_ledgers?: string
189
+
190
+ /**
191
+ * The network from which the ledger stream is received.
192
+ */
193
+ network_id?: number
178
194
  }
179
195
 
180
196
  /**
@@ -259,6 +275,11 @@ export interface ValidationStream extends BaseStream {
259
275
  * validator is using a token, this is an ephemeral public key.
260
276
  */
261
277
  validation_public_key: string
278
+
279
+ /**
280
+ * The network from which the validations stream is received.
281
+ */
282
+ network_id?: number
262
283
  }
263
284
 
264
285
  /**
@@ -8,6 +8,9 @@ import {
8
8
  validateOptionalField,
9
9
  isString,
10
10
  isNumber,
11
+ MAX_MPT_META_BYTE_LENGTH,
12
+ MPT_META_WARNING_HEADER,
13
+ validateMPTokenMetadata,
11
14
  } from './common'
12
15
  import type { TransactionMetadataBase } from './metadata'
13
16
 
@@ -104,10 +107,18 @@ export interface MPTokenIssuanceCreate extends BaseTransaction {
104
107
  * The field must NOT be present if the `tfMPTCanTransfer` flag is not set.
105
108
  */
106
109
  TransferFee?: number
110
+
107
111
  /**
108
- * Arbitrary metadata about this issuance, in hex format.
112
+ * Optional arbitrary metadata about this issuance, encoded as a hex string and limited to 1024 bytes.
113
+ *
114
+ * The decoded value must be a UTF-8 encoded JSON object that adheres to the
115
+ * XLS-89d MPTokenMetadata standard.
116
+ *
117
+ * While adherence to the XLS-89d format is not mandatory, non-compliant metadata
118
+ * may not be discoverable by ecosystem tools such as explorers and indexers.
109
119
  */
110
- MPTokenMetadata?: string | null
120
+ MPTokenMetadata?: string
121
+
111
122
  Flags?: number | MPTokenIssuanceCreateFlagsInterface
112
123
  }
113
124
 
@@ -131,15 +142,13 @@ export function validateMPTokenIssuanceCreate(
131
142
  validateOptionalField(tx, 'TransferFee', isNumber)
132
143
  validateOptionalField(tx, 'AssetScale', isNumber)
133
144
 
134
- if (typeof tx.MPTokenMetadata === 'string' && tx.MPTokenMetadata === '') {
145
+ if (
146
+ typeof tx.MPTokenMetadata === 'string' &&
147
+ (!isHex(tx.MPTokenMetadata) ||
148
+ tx.MPTokenMetadata.length / 2 > MAX_MPT_META_BYTE_LENGTH)
149
+ ) {
135
150
  throw new ValidationError(
136
- 'MPTokenIssuanceCreate: MPTokenMetadata must not be empty string',
137
- )
138
- }
139
-
140
- if (typeof tx.MPTokenMetadata === 'string' && !isHex(tx.MPTokenMetadata)) {
141
- throw new ValidationError(
142
- 'MPTokenIssuanceCreate: MPTokenMetadata must be in hex format',
151
+ `MPTokenIssuanceCreate: MPTokenMetadata (hex format) must be non-empty and no more than ${MAX_MPT_META_BYTE_LENGTH} bytes.`,
143
152
  )
144
153
  }
145
154
 
@@ -178,5 +187,19 @@ export function validateMPTokenIssuanceCreate(
178
187
  )
179
188
  }
180
189
  }
190
+
191
+ if (tx.MPTokenMetadata != null) {
192
+ const validationMessages = validateMPTokenMetadata(tx.MPTokenMetadata)
193
+
194
+ if (validationMessages.length > 0) {
195
+ const message = [
196
+ MPT_META_WARNING_HEADER,
197
+ ...validationMessages.map((msg) => `- ${msg}`),
198
+ ].join('\n')
199
+
200
+ // eslint-disable-next-line no-console -- Required here.
201
+ console.warn(message)
202
+ }
203
+ }
181
204
  }
182
205
  /* eslint-enable max-lines-per-function */
@@ -61,6 +61,8 @@ export enum AccountSetAsfFlags {
61
61
  asfDisallowIncomingTrustline = 15,
62
62
  /** Permanently gain the ability to claw back issued IOUs */
63
63
  asfAllowTrustLineClawback = 16,
64
+ /** Issuers allow their IOUs to be used as escrow amounts */
65
+ asfAllowTrustLineLocking = 17,
64
66
  }
65
67
 
66
68
  /**
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable max-lines -- common utility file */
2
- import { HEX_REGEX } from '@xrplf/isomorphic/utils'
2
+ import { HEX_REGEX, hexToString } from '@xrplf/isomorphic/utils'
3
3
  import { isValidClassicAddress, isValidXAddress } from 'ripple-address-codec'
4
4
  import { TRANSACTION_TYPES } from 'ripple-binary-codec'
5
5
 
@@ -12,6 +12,7 @@ import {
12
12
  IssuedCurrency,
13
13
  IssuedCurrencyAmount,
14
14
  MPTAmount,
15
+ MPTokenMetadata,
15
16
  Memo,
16
17
  Signer,
17
18
  XChainBridge,
@@ -22,10 +23,50 @@ const MEMO_SIZE = 3
22
23
  export const MAX_AUTHORIZED_CREDENTIALS = 8
23
24
  const MAX_CREDENTIAL_BYTE_LENGTH = 64
24
25
  const MAX_CREDENTIAL_TYPE_LENGTH = MAX_CREDENTIAL_BYTE_LENGTH * 2
26
+ export const MAX_MPT_META_BYTE_LENGTH = 1024
25
27
 
26
28
  // Used for Vault transactions
27
29
  export const VAULT_DATA_MAX_BYTE_LENGTH = 256
28
30
 
31
+ // To validate MPTokenMetadata as per XLS-89d
32
+ const TICKER_REGEX = /^[A-Z0-9]{1,6}$/u
33
+
34
+ const MAX_MPT_META_TOP_LEVEL_FIELD_COUNT = 9
35
+
36
+ const MPT_META_URL_FIELD_COUNT = 3
37
+
38
+ const MPT_META_REQUIRED_FIELDS = [
39
+ 'ticker',
40
+ 'name',
41
+ 'icon',
42
+ 'asset_class',
43
+ 'issuer_name',
44
+ ]
45
+
46
+ const MPT_META_ASSET_CLASSES = [
47
+ 'rwa',
48
+ 'memes',
49
+ 'wrapped',
50
+ 'gaming',
51
+ 'defi',
52
+ 'other',
53
+ ]
54
+
55
+ const MPT_META_ASSET_SUB_CLASSES = [
56
+ 'stablecoin',
57
+ 'commodity',
58
+ 'real_estate',
59
+ 'private_credit',
60
+ 'equity',
61
+ 'treasury',
62
+ 'other',
63
+ ]
64
+
65
+ export const MPT_META_WARNING_HEADER =
66
+ 'MPTokenMetadata is not properly formatted as JSON as per the XLS-89d standard. ' +
67
+ "While adherence to this standard is not mandatory, such non-compliant MPToken's might not be discoverable " +
68
+ 'by Explorers and Indexers in the XRPL ecosystem.'
69
+
29
70
  function isMemo(obj: unknown): obj is Memo {
30
71
  if (!isRecord(obj)) {
31
72
  return false
@@ -683,3 +724,197 @@ export function containsDuplicates(
683
724
 
684
725
  return false
685
726
  }
727
+
728
+ const _DOMAIN_ID_LENGTH = 64
729
+
730
+ /**
731
+ * Utility method used across OfferCreate and Payment transactions to validate the DomainID.
732
+ *
733
+ * @param domainID - The domainID is a 64-character string that is used to identify a domain.
734
+ *
735
+ * @returns true if the domainID is a valid 64-character string, false otherwise
736
+ */
737
+ export function isDomainID(domainID: unknown): domainID is string {
738
+ return (
739
+ isString(domainID) &&
740
+ domainID.length === _DOMAIN_ID_LENGTH &&
741
+ isHex(domainID)
742
+ )
743
+ }
744
+
745
+ /* eslint-disable max-lines-per-function -- Required here as structure validation is verbose. */
746
+ /* eslint-disable max-statements -- Required here as structure validation is verbose. */
747
+
748
+ /**
749
+ * Validates if MPTokenMetadata adheres to XLS-89d standard.
750
+ *
751
+ * @param input - Hex encoded MPTokenMetadata.
752
+ * @returns Validation messages if MPTokenMetadata does not adheres to XLS-89d standard.
753
+ */
754
+ export function validateMPTokenMetadata(input: string): string[] {
755
+ const validationMessages: string[] = []
756
+
757
+ if (!isHex(input)) {
758
+ validationMessages.push(`MPTokenMetadata must be in hex format.`)
759
+ return validationMessages
760
+ }
761
+
762
+ if (input.length / 2 > MAX_MPT_META_BYTE_LENGTH) {
763
+ validationMessages.push(
764
+ `MPTokenMetadata must be max ${MAX_MPT_META_BYTE_LENGTH} bytes.`,
765
+ )
766
+ return validationMessages
767
+ }
768
+
769
+ let jsonMetaData: unknown
770
+
771
+ try {
772
+ jsonMetaData = JSON.parse(hexToString(input))
773
+ } catch (err) {
774
+ validationMessages.push(
775
+ `MPTokenMetadata is not properly formatted as JSON - ${String(err)}`,
776
+ )
777
+ return validationMessages
778
+ }
779
+
780
+ if (
781
+ jsonMetaData == null ||
782
+ typeof jsonMetaData !== 'object' ||
783
+ Array.isArray(jsonMetaData)
784
+ ) {
785
+ validationMessages.push(
786
+ 'MPTokenMetadata is not properly formatted as per XLS-89d.',
787
+ )
788
+ return validationMessages
789
+ }
790
+
791
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- It must be some JSON object.
792
+ const obj = jsonMetaData as Record<string, unknown>
793
+
794
+ // validating structure
795
+
796
+ // check for maximum number of fields
797
+ const fieldCount = Object.keys(obj).length
798
+ if (fieldCount > MAX_MPT_META_TOP_LEVEL_FIELD_COUNT) {
799
+ validationMessages.push(
800
+ `MPTokenMetadata must not contain more than ${MAX_MPT_META_TOP_LEVEL_FIELD_COUNT} top-level fields (found ${fieldCount}).`,
801
+ )
802
+ return validationMessages
803
+ }
804
+
805
+ const incorrectRequiredFields = MPT_META_REQUIRED_FIELDS.filter(
806
+ (field) => !isString(obj[field]),
807
+ )
808
+
809
+ if (incorrectRequiredFields.length > 0) {
810
+ incorrectRequiredFields.forEach((field) =>
811
+ validationMessages.push(`${field} is required and must be string.`),
812
+ )
813
+ return validationMessages
814
+ }
815
+
816
+ if (obj.desc != null && !isString(obj.desc)) {
817
+ validationMessages.push(`desc must be a string.`)
818
+ return validationMessages
819
+ }
820
+
821
+ if (obj.asset_subclass != null && !isString(obj.asset_subclass)) {
822
+ validationMessages.push(`asset_subclass must be a string.`)
823
+ return validationMessages
824
+ }
825
+
826
+ if (
827
+ obj.additional_info != null &&
828
+ !isString(obj.additional_info) &&
829
+ !isRecord(obj.additional_info)
830
+ ) {
831
+ validationMessages.push(`additional_info must be a string or JSON object.`)
832
+ return validationMessages
833
+ }
834
+
835
+ if (obj.urls != null) {
836
+ if (!Array.isArray(obj.urls)) {
837
+ validationMessages.push('urls must be an array as per XLS-89d.')
838
+ return validationMessages
839
+ }
840
+ if (!obj.urls.every(isValidMPTokenMetadataUrlStructure)) {
841
+ validationMessages.push(
842
+ 'One or more urls are not structured per XLS-89d.',
843
+ )
844
+ return validationMessages
845
+ }
846
+ }
847
+
848
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Required here.
849
+ const mptMPTokenMetadata = obj as unknown as MPTokenMetadata
850
+
851
+ // validating content
852
+ if (!TICKER_REGEX.test(mptMPTokenMetadata.ticker)) {
853
+ validationMessages.push(
854
+ `ticker should have uppercase letters (A-Z) and digits (0-9) only. Max 6 characters recommended.`,
855
+ )
856
+ }
857
+
858
+ if (!mptMPTokenMetadata.icon.startsWith('https://')) {
859
+ validationMessages.push(`icon should be a valid https url.`)
860
+ }
861
+
862
+ if (
863
+ !MPT_META_ASSET_CLASSES.includes(
864
+ mptMPTokenMetadata.asset_class.toLowerCase(),
865
+ )
866
+ ) {
867
+ validationMessages.push(
868
+ `asset_class should be one of ${MPT_META_ASSET_CLASSES.join(', ')}.`,
869
+ )
870
+ }
871
+
872
+ if (
873
+ mptMPTokenMetadata.asset_subclass != null &&
874
+ !MPT_META_ASSET_SUB_CLASSES.includes(
875
+ mptMPTokenMetadata.asset_subclass.toLowerCase(),
876
+ )
877
+ ) {
878
+ validationMessages.push(
879
+ `asset_subclass should be one of ${MPT_META_ASSET_SUB_CLASSES.join(
880
+ ', ',
881
+ )}.`,
882
+ )
883
+ }
884
+
885
+ if (
886
+ mptMPTokenMetadata.asset_class.toLowerCase() === 'rwa' &&
887
+ mptMPTokenMetadata.asset_subclass == null
888
+ ) {
889
+ validationMessages.push(
890
+ `asset_subclass is required when asset_class is rwa.`,
891
+ )
892
+ }
893
+
894
+ if (
895
+ mptMPTokenMetadata.urls != null &&
896
+ !mptMPTokenMetadata.urls.every((ele) => ele.url.startsWith('https://'))
897
+ ) {
898
+ validationMessages.push(`url should be a valid https url.`)
899
+ }
900
+
901
+ return validationMessages
902
+ }
903
+ /* eslint-enable max-lines-per-function */
904
+ /* eslint-enable max-statements */
905
+
906
+ function isValidMPTokenMetadataUrlStructure(input: unknown): boolean {
907
+ if (input == null) {
908
+ return false
909
+ }
910
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Required here.
911
+ const obj = input as Record<string, unknown>
912
+
913
+ return (
914
+ typeof obj === 'object' &&
915
+ isString(obj.url) &&
916
+ isString(obj.type) &&
917
+ isString(obj.title) &&
918
+ Object.keys(obj).length === MPT_META_URL_FIELD_COUNT
919
+ )
920
+ }
@@ -1,9 +1,11 @@
1
1
  import { ValidationError } from '../../errors'
2
+ import { Amount, MPTAmount } from '../common'
2
3
 
3
4
  import {
4
5
  Account,
5
6
  BaseTransaction,
6
7
  isAccount,
8
+ isAmount,
7
9
  isNumber,
8
10
  validateBaseTransaction,
9
11
  validateOptionalField,
@@ -18,11 +20,12 @@ import {
18
20
  export interface EscrowCreate extends BaseTransaction {
19
21
  TransactionType: 'EscrowCreate'
20
22
  /**
21
- * Amount of XRP, in drops, to deduct from the sender's balance and escrow.
22
- * Once escrowed, the XRP can either go to the Destination address (after the.
23
- * FinishAfter time) or returned to the sender (after the CancelAfter time).
23
+ * The amount to deduct from the sender's balance and and set aside in escrow.
24
+ * Once escrowed, this amount can either go to the Destination address (after any Finish times/conditions)
25
+ * or returned to the sender (after any cancellation times/conditions). Can represent XRP, in drops,
26
+ * an IOU token, or an MPT. Must always be a positive value.
24
27
  */
25
- Amount: string
28
+ Amount: Amount | MPTAmount
26
29
  /** Address to receive escrowed XRP. */
27
30
  Destination: Account
28
31
  /**
@@ -62,14 +65,7 @@ export interface EscrowCreate extends BaseTransaction {
62
65
  export function validateEscrowCreate(tx: Record<string, unknown>): void {
63
66
  validateBaseTransaction(tx)
64
67
 
65
- if (tx.Amount === undefined) {
66
- throw new ValidationError('EscrowCreate: missing field Amount')
67
- }
68
-
69
- if (typeof tx.Amount !== 'string') {
70
- throw new ValidationError('EscrowCreate: Amount must be a string')
71
- }
72
-
68
+ validateRequiredField(tx, 'Amount', isAmount)
73
69
  validateRequiredField(tx, 'Destination', isAccount)
74
70
  validateOptionalField(tx, 'DestinationTag', isNumber)
75
71
 
@@ -9,6 +9,7 @@ import {
9
9
  validateRequiredField,
10
10
  MAX_AUTHORIZED_CREDENTIALS,
11
11
  } from './common'
12
+ import { TransactionMetadataBase } from './metadata'
12
13
 
13
14
  /**
14
15
  * Deliver XRP from a held payment to the recipient.
@@ -42,6 +43,12 @@ export interface EscrowFinish extends BaseTransaction {
42
43
  ComputationAllowance?: number
43
44
  }
44
45
 
46
+ export interface EscrowFinishMetadata extends TransactionMetadataBase {
47
+ // if ComputationAllowance is present and the Smart Escrow runs
48
+ GasUsed?: number
49
+ WasmReturnCode?: number
50
+ }
51
+
45
52
  /**
46
53
  * Verify the form and type of an EscrowFinish at runtime.
47
54
  *
@@ -1,4 +1,4 @@
1
- export { BaseTransaction, isMPTAmount } from './common'
1
+ export { BaseTransaction, isMPTAmount, validateMPTokenMetadata } from './common'
2
2
  export {
3
3
  validate,
4
4
  PseudoTransaction,
@@ -1,6 +1,7 @@
1
1
  import { Amount, MPTAmount } from '../common'
2
2
 
3
3
  import { BaseTransaction } from './common'
4
+ import { EscrowFinish, EscrowFinishMetadata } from './escrowFinish'
4
5
  import {
5
6
  MPTokenIssuanceCreate,
6
7
  MPTokenIssuanceCreateMetadata,
@@ -105,4 +106,6 @@ export type TransactionMetadata<T extends BaseTransaction = Transaction> =
105
106
  ? NFTokenCancelOfferMetadata
106
107
  : T extends MPTokenIssuanceCreate
107
108
  ? MPTokenIssuanceCreateMetadata
109
+ : T extends EscrowFinish
110
+ ? EscrowFinishMetadata
108
111
  : TransactionMetadataBase
@@ -1,11 +1,14 @@
1
1
  import { ValidationError } from '../../errors'
2
2
  import { Amount } from '../common'
3
+ import { hasFlag } from '../utils'
3
4
 
4
5
  import {
5
6
  BaseTransaction,
6
7
  GlobalFlagsInterface,
7
8
  validateBaseTransaction,
8
9
  isAmount,
10
+ validateOptionalField,
11
+ isDomainID,
9
12
  } from './common'
10
13
 
11
14
  /**
@@ -42,6 +45,11 @@ export enum OfferCreateFlags {
42
45
  * the TakerPays amount in exchange.
43
46
  */
44
47
  tfSell = 0x00080000,
48
+ /**
49
+ * Indicates the offer is hybrid. (meaning it is part of both a domain and open order book)
50
+ * This flag cannot be set if the offer doesn't have a DomainID
51
+ */
52
+ tfHybrid = 0x00100000,
45
53
  }
46
54
 
47
55
  /**
@@ -83,6 +91,7 @@ export interface OfferCreateFlagsInterface extends GlobalFlagsInterface {
83
91
  tfImmediateOrCancel?: boolean
84
92
  tfFillOrKill?: boolean
85
93
  tfSell?: boolean
94
+ tfHybrid?: boolean
86
95
  }
87
96
 
88
97
  /**
@@ -106,6 +115,8 @@ export interface OfferCreate extends BaseTransaction {
106
115
  TakerGets: Amount
107
116
  /** The amount and type of currency being requested by the offer creator. */
108
117
  TakerPays: Amount
118
+ /** The domain that the offer must be a part of. */
119
+ DomainID?: string
109
120
  }
110
121
 
111
122
  /**
@@ -140,4 +151,18 @@ export function validateOfferCreate(tx: Record<string, unknown>): void {
140
151
  if (tx.OfferSequence !== undefined && typeof tx.OfferSequence !== 'number') {
141
152
  throw new ValidationError('OfferCreate: invalid OfferSequence')
142
153
  }
154
+
155
+ validateOptionalField(tx, 'DomainID', isDomainID, {
156
+ txType: 'OfferCreate',
157
+ paramName: 'DomainID',
158
+ })
159
+
160
+ if (
161
+ tx.DomainID == null &&
162
+ hasFlag(tx, OfferCreateFlags.tfHybrid, 'tfHybrid')
163
+ ) {
164
+ throw new ValidationError(
165
+ 'OfferCreate: tfHybrid flag cannot be set if DomainID is not present',
166
+ )
167
+ }
143
168
  }
@@ -8,6 +8,7 @@ import {
8
8
  GlobalFlagsInterface,
9
9
  validateBaseTransaction,
10
10
  isAccount,
11
+ isDomainID,
11
12
  validateRequiredField,
12
13
  validateOptionalField,
13
14
  isNumber,
@@ -160,6 +161,18 @@ export interface Payment extends BaseTransaction {
160
161
  * The credentials included must not be expired.
161
162
  */
162
163
  CredentialIDs?: string[]
164
+ /**
165
+ * The domain the sender intends to use. Both the sender and destination must
166
+ * be part of this domain. The DomainID can be included if the sender intends
167
+ * it to be a cross-currency payment (i.e. if the payment is going to interact
168
+ * with the DEX). The domain will only play it's role if there is a path that
169
+ * crossing an orderbook.
170
+ *
171
+ * Note: it's still possible that DomainID is included but the payment does
172
+ * not interact with DEX, it simply means that the DomainID will be ignored
173
+ * during payment paths.
174
+ */
175
+ DomainID?: string
163
176
  Flags?: number | PaymentFlagsInterface
164
177
  }
165
178
 
@@ -199,6 +212,11 @@ export function validatePayment(tx: Record<string, unknown>): void {
199
212
  throw new ValidationError('PaymentTransaction: InvoiceID must be a string')
200
213
  }
201
214
 
215
+ validateOptionalField(tx, 'DomainID', isDomainID, {
216
+ txType: 'PaymentTransaction',
217
+ paramName: 'DomainID',
218
+ })
219
+
202
220
  if (tx.Paths !== undefined && !isPaths(tx.Paths)) {
203
221
  throw new ValidationError('PaymentTransaction: invalid Paths')
204
222
  }
@@ -208,12 +226,6 @@ export function validatePayment(tx: Record<string, unknown>): void {
208
226
  }
209
227
 
210
228
  checkPartialPayment(tx)
211
-
212
- if (tx.DeliverMax != null) {
213
- throw new ValidationError(
214
- 'PaymentTransaction: Cannot have DeliverMax in a submitted transaction',
215
- )
216
- }
217
229
  }
218
230
 
219
231
  function checkPartialPayment(tx: Record<string, unknown>): void {