xrpl 4.4.0-smartescrow.0 → 4.4.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.
- package/build/xrpl-latest-min.js +1 -1
- package/build/xrpl-latest-min.js.map +1 -1
- package/build/xrpl-latest.js +407 -268
- package/build/xrpl-latest.js.map +1 -1
- package/dist/npm/Wallet/defaultFaucets.d.ts +1 -2
- package/dist/npm/Wallet/defaultFaucets.d.ts.map +1 -1
- package/dist/npm/Wallet/defaultFaucets.js +0 -3
- package/dist/npm/Wallet/defaultFaucets.js.map +1 -1
- package/dist/npm/models/common/index.d.ts +16 -0
- package/dist/npm/models/common/index.d.ts.map +1 -1
- package/dist/npm/models/ledger/AccountRoot.d.ts +3 -1
- package/dist/npm/models/ledger/AccountRoot.d.ts.map +1 -1
- package/dist/npm/models/ledger/AccountRoot.js +1 -0
- package/dist/npm/models/ledger/AccountRoot.js.map +1 -1
- package/dist/npm/models/ledger/DirectoryNode.d.ts +1 -0
- package/dist/npm/models/ledger/DirectoryNode.d.ts.map +1 -1
- package/dist/npm/models/ledger/Escrow.d.ts +2 -2
- package/dist/npm/models/ledger/Escrow.d.ts.map +1 -1
- package/dist/npm/models/ledger/FeeSettings.d.ts +0 -2
- package/dist/npm/models/ledger/FeeSettings.d.ts.map +1 -1
- package/dist/npm/models/ledger/MPToken.d.ts +1 -0
- package/dist/npm/models/ledger/MPToken.d.ts.map +1 -1
- package/dist/npm/models/ledger/MPTokenIssuance.d.ts +1 -0
- package/dist/npm/models/ledger/MPTokenIssuance.d.ts.map +1 -1
- package/dist/npm/models/ledger/Offer.d.ts +10 -1
- package/dist/npm/models/ledger/Offer.d.ts.map +1 -1
- package/dist/npm/models/ledger/Offer.js +1 -0
- package/dist/npm/models/ledger/Offer.js.map +1 -1
- package/dist/npm/models/methods/bookOffers.d.ts +1 -0
- package/dist/npm/models/methods/bookOffers.d.ts.map +1 -1
- package/dist/npm/models/methods/pathFind.d.ts +2 -0
- package/dist/npm/models/methods/pathFind.d.ts.map +1 -1
- package/dist/npm/models/methods/ripplePathFind.d.ts +1 -0
- package/dist/npm/models/methods/ripplePathFind.d.ts.map +1 -1
- package/dist/npm/models/methods/serverInfo.d.ts +0 -6
- package/dist/npm/models/methods/serverInfo.d.ts.map +1 -1
- package/dist/npm/models/methods/serverState.d.ts +0 -6
- package/dist/npm/models/methods/serverState.d.ts.map +1 -1
- package/dist/npm/models/methods/subscribe.d.ts +4 -0
- package/dist/npm/models/methods/subscribe.d.ts.map +1 -1
- package/dist/npm/models/transactions/MPTokenIssuanceCreate.d.ts +1 -1
- package/dist/npm/models/transactions/MPTokenIssuanceCreate.d.ts.map +1 -1
- package/dist/npm/models/transactions/MPTokenIssuanceCreate.js +14 -5
- package/dist/npm/models/transactions/MPTokenIssuanceCreate.js.map +1 -1
- package/dist/npm/models/transactions/accountSet.d.ts +2 -1
- package/dist/npm/models/transactions/accountSet.d.ts.map +1 -1
- package/dist/npm/models/transactions/accountSet.js +1 -0
- package/dist/npm/models/transactions/accountSet.js.map +1 -1
- package/dist/npm/models/transactions/common.d.ts +4 -0
- package/dist/npm/models/transactions/common.d.ts.map +1 -1
- package/dist/npm/models/transactions/common.js +134 -1
- package/dist/npm/models/transactions/common.js.map +1 -1
- package/dist/npm/models/transactions/escrowCreate.d.ts +2 -3
- package/dist/npm/models/transactions/escrowCreate.d.ts.map +1 -1
- package/dist/npm/models/transactions/escrowCreate.js +3 -10
- package/dist/npm/models/transactions/escrowCreate.js.map +1 -1
- package/dist/npm/models/transactions/escrowFinish.d.ts +0 -1
- package/dist/npm/models/transactions/escrowFinish.d.ts.map +1 -1
- package/dist/npm/models/transactions/escrowFinish.js.map +1 -1
- package/dist/npm/models/transactions/index.d.ts +1 -1
- package/dist/npm/models/transactions/index.d.ts.map +1 -1
- package/dist/npm/models/transactions/index.js +2 -1
- package/dist/npm/models/transactions/index.js.map +1 -1
- package/dist/npm/models/transactions/offerCreate.d.ts +4 -1
- package/dist/npm/models/transactions/offerCreate.d.ts.map +1 -1
- package/dist/npm/models/transactions/offerCreate.js +10 -0
- package/dist/npm/models/transactions/offerCreate.js.map +1 -1
- package/dist/npm/models/transactions/payment.d.ts +1 -0
- package/dist/npm/models/transactions/payment.d.ts.map +1 -1
- package/dist/npm/models/transactions/payment.js +4 -3
- package/dist/npm/models/transactions/payment.js.map +1 -1
- package/dist/npm/models/transactions/vaultCreate.d.ts.map +1 -1
- package/dist/npm/models/transactions/vaultCreate.js +13 -4
- package/dist/npm/models/transactions/vaultCreate.js.map +1 -1
- package/dist/npm/models/transactions/vaultDeposit.d.ts +2 -2
- package/dist/npm/models/transactions/vaultDeposit.d.ts.map +1 -1
- package/dist/npm/models/transactions/vaultDeposit.js.map +1 -1
- package/dist/npm/models/transactions/vaultWithdraw.d.ts +2 -2
- package/dist/npm/models/transactions/vaultWithdraw.d.ts.map +1 -1
- package/dist/npm/models/transactions/vaultWithdraw.js.map +1 -1
- package/dist/npm/snippets/src/permissionedDEX.d.ts +2 -0
- package/dist/npm/snippets/src/permissionedDEX.d.ts.map +1 -0
- package/dist/npm/snippets/src/permissionedDEX.js +173 -0
- package/dist/npm/snippets/src/permissionedDEX.js.map +1 -0
- package/dist/npm/snippets/tsconfig.tsbuildinfo +1 -1
- package/dist/npm/src/Wallet/defaultFaucets.d.ts +1 -2
- package/dist/npm/src/Wallet/defaultFaucets.d.ts.map +1 -1
- package/dist/npm/src/Wallet/defaultFaucets.js +0 -3
- package/dist/npm/src/Wallet/defaultFaucets.js.map +1 -1
- package/dist/npm/src/models/common/index.d.ts +16 -0
- package/dist/npm/src/models/common/index.d.ts.map +1 -1
- package/dist/npm/src/models/ledger/AccountRoot.d.ts +3 -1
- package/dist/npm/src/models/ledger/AccountRoot.d.ts.map +1 -1
- package/dist/npm/src/models/ledger/AccountRoot.js +1 -0
- package/dist/npm/src/models/ledger/AccountRoot.js.map +1 -1
- package/dist/npm/src/models/ledger/DirectoryNode.d.ts +1 -0
- package/dist/npm/src/models/ledger/DirectoryNode.d.ts.map +1 -1
- package/dist/npm/src/models/ledger/Escrow.d.ts +2 -2
- package/dist/npm/src/models/ledger/Escrow.d.ts.map +1 -1
- package/dist/npm/src/models/ledger/FeeSettings.d.ts +0 -2
- package/dist/npm/src/models/ledger/FeeSettings.d.ts.map +1 -1
- package/dist/npm/src/models/ledger/MPToken.d.ts +1 -0
- package/dist/npm/src/models/ledger/MPToken.d.ts.map +1 -1
- package/dist/npm/src/models/ledger/MPTokenIssuance.d.ts +1 -0
- package/dist/npm/src/models/ledger/MPTokenIssuance.d.ts.map +1 -1
- package/dist/npm/src/models/ledger/Offer.d.ts +10 -1
- package/dist/npm/src/models/ledger/Offer.d.ts.map +1 -1
- package/dist/npm/src/models/ledger/Offer.js +1 -0
- package/dist/npm/src/models/ledger/Offer.js.map +1 -1
- package/dist/npm/src/models/methods/bookOffers.d.ts +1 -0
- package/dist/npm/src/models/methods/bookOffers.d.ts.map +1 -1
- package/dist/npm/src/models/methods/pathFind.d.ts +2 -0
- package/dist/npm/src/models/methods/pathFind.d.ts.map +1 -1
- package/dist/npm/src/models/methods/ripplePathFind.d.ts +1 -0
- package/dist/npm/src/models/methods/ripplePathFind.d.ts.map +1 -1
- package/dist/npm/src/models/methods/serverInfo.d.ts +0 -6
- package/dist/npm/src/models/methods/serverInfo.d.ts.map +1 -1
- package/dist/npm/src/models/methods/serverState.d.ts +0 -6
- package/dist/npm/src/models/methods/serverState.d.ts.map +1 -1
- package/dist/npm/src/models/methods/subscribe.d.ts +4 -0
- package/dist/npm/src/models/methods/subscribe.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/MPTokenIssuanceCreate.d.ts +1 -1
- package/dist/npm/src/models/transactions/MPTokenIssuanceCreate.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/MPTokenIssuanceCreate.js +14 -5
- package/dist/npm/src/models/transactions/MPTokenIssuanceCreate.js.map +1 -1
- package/dist/npm/src/models/transactions/accountSet.d.ts +2 -1
- package/dist/npm/src/models/transactions/accountSet.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/accountSet.js +1 -0
- package/dist/npm/src/models/transactions/accountSet.js.map +1 -1
- package/dist/npm/src/models/transactions/common.d.ts +4 -0
- package/dist/npm/src/models/transactions/common.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/common.js +134 -1
- package/dist/npm/src/models/transactions/common.js.map +1 -1
- package/dist/npm/src/models/transactions/escrowCreate.d.ts +2 -3
- package/dist/npm/src/models/transactions/escrowCreate.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/escrowCreate.js +3 -10
- package/dist/npm/src/models/transactions/escrowCreate.js.map +1 -1
- package/dist/npm/src/models/transactions/escrowFinish.d.ts +0 -1
- package/dist/npm/src/models/transactions/escrowFinish.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/escrowFinish.js.map +1 -1
- package/dist/npm/src/models/transactions/index.d.ts +1 -1
- package/dist/npm/src/models/transactions/index.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/index.js +2 -1
- package/dist/npm/src/models/transactions/index.js.map +1 -1
- package/dist/npm/src/models/transactions/offerCreate.d.ts +4 -1
- package/dist/npm/src/models/transactions/offerCreate.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/offerCreate.js +10 -0
- package/dist/npm/src/models/transactions/offerCreate.js.map +1 -1
- package/dist/npm/src/models/transactions/payment.d.ts +1 -0
- package/dist/npm/src/models/transactions/payment.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/payment.js +4 -3
- package/dist/npm/src/models/transactions/payment.js.map +1 -1
- package/dist/npm/src/models/transactions/vaultCreate.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/vaultCreate.js +13 -4
- package/dist/npm/src/models/transactions/vaultCreate.js.map +1 -1
- package/dist/npm/src/models/transactions/vaultDeposit.d.ts +2 -2
- package/dist/npm/src/models/transactions/vaultDeposit.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/vaultDeposit.js.map +1 -1
- package/dist/npm/src/models/transactions/vaultWithdraw.d.ts +2 -2
- package/dist/npm/src/models/transactions/vaultWithdraw.d.ts.map +1 -1
- package/dist/npm/src/models/transactions/vaultWithdraw.js.map +1 -1
- package/dist/npm/src/sugar/autofill.d.ts.map +1 -1
- package/dist/npm/src/sugar/autofill.js +11 -32
- package/dist/npm/src/sugar/autofill.js.map +1 -1
- package/dist/npm/sugar/autofill.d.ts.map +1 -1
- package/dist/npm/sugar/autofill.js +11 -32
- package/dist/npm/sugar/autofill.js.map +1 -1
- package/package.json +4 -4
- package/src/Wallet/defaultFaucets.ts +0 -5
- package/src/models/common/index.ts +25 -0
- package/src/models/ledger/AccountRoot.ts +10 -0
- package/src/models/ledger/DirectoryNode.ts +3 -0
- package/src/models/ledger/Escrow.ts +12 -3
- package/src/models/ledger/FeeSettings.ts +0 -4
- package/src/models/ledger/MPToken.ts +1 -0
- package/src/models/ledger/MPTokenIssuance.ts +1 -0
- package/src/models/ledger/Offer.ts +21 -0
- package/src/models/methods/bookOffers.ts +7 -0
- package/src/models/methods/pathFind.ts +10 -0
- package/src/models/methods/ripplePathFind.ts +5 -0
- package/src/models/methods/serverInfo.ts +0 -9
- package/src/models/methods/serverState.ts +0 -10
- package/src/models/methods/subscribe.ts +21 -0
- package/src/models/transactions/MPTokenIssuanceCreate.ts +33 -10
- package/src/models/transactions/accountSet.ts +2 -0
- package/src/models/transactions/common.ts +236 -1
- package/src/models/transactions/escrowCreate.ts +10 -22
- package/src/models/transactions/escrowFinish.ts +0 -2
- package/src/models/transactions/index.ts +1 -1
- package/src/models/transactions/offerCreate.ts +25 -0
- package/src/models/transactions/payment.ts +18 -6
- package/src/models/transactions/vaultCreate.ts +30 -6
- package/src/models/transactions/vaultDeposit.ts +3 -2
- package/src/models/transactions/vaultWithdraw.ts +3 -2
- package/src/sugar/autofill.ts +25 -39
@@ -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
|
-
*
|
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
|
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 (
|
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
|
-
|
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
|
-
*
|
22
|
-
* Once escrowed,
|
23
|
-
*
|
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:
|
28
|
+
Amount: Amount | MPTAmount
|
26
29
|
/** Address to receive escrowed XRP. */
|
27
30
|
Destination: Account
|
28
31
|
/**
|
@@ -47,10 +50,6 @@ export interface EscrowCreate extends BaseTransaction {
|
|
47
50
|
* payment, such as a hosted recipient at the destination address.
|
48
51
|
*/
|
49
52
|
DestinationTag?: number
|
50
|
-
|
51
|
-
FinishFunction?: string
|
52
|
-
|
53
|
-
Data?: string
|
54
53
|
}
|
55
54
|
|
56
55
|
/**
|
@@ -62,14 +61,7 @@ export interface EscrowCreate extends BaseTransaction {
|
|
62
61
|
export function validateEscrowCreate(tx: Record<string, unknown>): void {
|
63
62
|
validateBaseTransaction(tx)
|
64
63
|
|
65
|
-
|
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
|
-
|
64
|
+
validateRequiredField(tx, 'Amount', isAmount)
|
73
65
|
validateRequiredField(tx, 'Destination', isAccount)
|
74
66
|
validateOptionalField(tx, 'DestinationTag', isNumber)
|
75
67
|
|
@@ -79,13 +71,9 @@ export function validateEscrowCreate(tx: Record<string, unknown>): void {
|
|
79
71
|
)
|
80
72
|
}
|
81
73
|
|
82
|
-
if (
|
83
|
-
tx.FinishAfter === undefined &&
|
84
|
-
tx.Condition === undefined &&
|
85
|
-
tx.FinishFunction === undefined
|
86
|
-
) {
|
74
|
+
if (tx.FinishAfter === undefined && tx.Condition === undefined) {
|
87
75
|
throw new ValidationError(
|
88
|
-
'EscrowCreate: Either
|
76
|
+
'EscrowCreate: Either Condition or FinishAfter must be specified',
|
89
77
|
)
|
90
78
|
}
|
91
79
|
|
@@ -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 {
|
@@ -14,10 +14,11 @@ import {
|
|
14
14
|
VAULT_DATA_MAX_BYTE_LENGTH,
|
15
15
|
XRPLNumber,
|
16
16
|
isXRPLNumber,
|
17
|
+
MAX_MPT_META_BYTE_LENGTH,
|
18
|
+
MPT_META_WARNING_HEADER,
|
19
|
+
validateMPTokenMetadata,
|
17
20
|
} from './common'
|
18
21
|
|
19
|
-
const META_MAX_BYTE_LENGTH = 1024
|
20
|
-
|
21
22
|
/**
|
22
23
|
* Enum representing withdrawal strategies for a Vault.
|
23
24
|
*/
|
@@ -71,6 +72,12 @@ export interface VaultCreate extends BaseTransaction {
|
|
71
72
|
|
72
73
|
/**
|
73
74
|
* Arbitrary metadata about the share MPT, in hex format, limited to 1024 bytes.
|
75
|
+
*
|
76
|
+
* The decoded value must be a UTF-8 encoded JSON object that adheres to the
|
77
|
+
* XLS-89d MPTokenMetadata standard.
|
78
|
+
*
|
79
|
+
* While adherence to the XLS-89d format is not mandatory, non-compliant metadata
|
80
|
+
* may not be discoverable by ecosystem tools such as explorers and indexers.
|
74
81
|
*/
|
75
82
|
MPTokenMetadata?: string
|
76
83
|
|
@@ -85,13 +92,14 @@ export interface VaultCreate extends BaseTransaction {
|
|
85
92
|
DomainID?: string
|
86
93
|
}
|
87
94
|
|
95
|
+
/* eslint-disable max-lines-per-function -- Not needed to reduce function */
|
96
|
+
/* eslint-disable max-statements -- required to do all field validations */
|
88
97
|
/**
|
89
98
|
* Verify the form and type of an {@link VaultCreate} at runtime.
|
90
99
|
*
|
91
100
|
* @param tx - A {@link VaultCreate} Transaction.
|
92
101
|
* @throws When the {@link VaultCreate} is malformed.
|
93
102
|
*/
|
94
|
-
// eslint-disable-next-line max-lines-per-function -- required to do all field validations
|
95
103
|
export function validateVaultCreate(tx: Record<string, unknown>): void {
|
96
104
|
validateBaseTransaction(tx)
|
97
105
|
|
@@ -119,13 +127,13 @@ export function validateVaultCreate(tx: Record<string, unknown>): void {
|
|
119
127
|
const metaHex = tx.MPTokenMetadata
|
120
128
|
if (!isHex(metaHex)) {
|
121
129
|
throw new ValidationError(
|
122
|
-
'VaultCreate: MPTokenMetadata must be a valid hex string',
|
130
|
+
'VaultCreate: MPTokenMetadata must be a valid non-empty hex string',
|
123
131
|
)
|
124
132
|
}
|
125
133
|
const metaByteLength = metaHex.length / 2
|
126
|
-
if (metaByteLength >
|
134
|
+
if (metaByteLength > MAX_MPT_META_BYTE_LENGTH) {
|
127
135
|
throw new ValidationError(
|
128
|
-
`VaultCreate: MPTokenMetadata exceeds ${
|
136
|
+
`VaultCreate: MPTokenMetadata exceeds ${MAX_MPT_META_BYTE_LENGTH} bytes (actual: ${metaByteLength})`,
|
129
137
|
)
|
130
138
|
}
|
131
139
|
}
|
@@ -139,4 +147,20 @@ export function validateVaultCreate(tx: Record<string, unknown>): void {
|
|
139
147
|
'VaultCreate: Cannot set DomainID unless tfVaultPrivate flag is set.',
|
140
148
|
)
|
141
149
|
}
|
150
|
+
|
151
|
+
if (tx.MPTokenMetadata != null) {
|
152
|
+
const validationMessages = validateMPTokenMetadata(tx.MPTokenMetadata)
|
153
|
+
|
154
|
+
if (validationMessages.length > 0) {
|
155
|
+
const message = [
|
156
|
+
MPT_META_WARNING_HEADER,
|
157
|
+
...validationMessages.map((msg) => `- ${msg}`),
|
158
|
+
].join('\n')
|
159
|
+
|
160
|
+
// eslint-disable-next-line no-console -- Required here.
|
161
|
+
console.warn(message)
|
162
|
+
}
|
163
|
+
}
|
142
164
|
}
|
165
|
+
/* eslint-enable max-lines-per-function */
|
166
|
+
/* eslint-enable max-statements */
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Amount } from '../common'
|
1
|
+
import { Amount, MPTAmount } from '../common'
|
2
2
|
|
3
3
|
import {
|
4
4
|
BaseTransaction,
|
@@ -24,7 +24,8 @@ export interface VaultDeposit extends BaseTransaction {
|
|
24
24
|
/**
|
25
25
|
* Asset amount to deposit.
|
26
26
|
*/
|
27
|
-
|
27
|
+
// TODO: remove MPTAmount when MPTv2 is released
|
28
|
+
Amount: Amount | MPTAmount
|
28
29
|
}
|
29
30
|
|
30
31
|
/**
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Amount } from '../common'
|
1
|
+
import { Amount, MPTAmount } from '../common'
|
2
2
|
|
3
3
|
import {
|
4
4
|
BaseTransaction,
|
@@ -27,7 +27,8 @@ export interface VaultWithdraw extends BaseTransaction {
|
|
27
27
|
/**
|
28
28
|
* The exact amount of Vault asset to withdraw.
|
29
29
|
*/
|
30
|
-
|
30
|
+
// TODO: remove MPTAmount when MPTv2 is released
|
31
|
+
Amount: Amount | MPTAmount
|
31
32
|
|
32
33
|
/**
|
33
34
|
* An account to receive the assets. It must be able to receive the asset.
|