Skip to content

Commit a504482

Browse files
authored
XLS-81D Permissioned DEX (#3032)
* Implementation of XLS-81d PermissionedDEX; Transaction and Request models are implemented;
1 parent bc7fc4b commit a504482

File tree

15 files changed

+660
-11
lines changed

15 files changed

+660
-11
lines changed

.ci-config/rippled.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ fixInvalidTxFlags
190190
# 2.5.0 Amendments
191191
PermissionDelegation
192192
Batch
193+
PermissionedDEX
193194
TokenEscrow
194195
SingleAssetVault
195196

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ describe('Hash128', function () {
3232
new Error('Invalid Hash length 17'),
3333
)
3434
})
35+
36+
it(`throws when constructed from non-hexadecimal string`, () => {
37+
expect(() => Hash128.from('Z'.repeat(32))).toThrow(
38+
new Error('Invalid hash string ' + 'Z'.repeat(32)),
39+
)
40+
})
3541
})
3642
describe('Hash160', function () {
3743
it('has a static width member', function () {
@@ -56,6 +62,12 @@ describe('Hash160', function () {
5662
Hash160.from('100000000000000000000000000000000000000000'),
5763
).toThrow(new Error('Invalid Hash length 21'))
5864
})
65+
66+
it(`throws when constructed from non-hexadecimal string`, () => {
67+
expect(() => Hash160.from('Z'.repeat(40))).toThrow(
68+
new Error('Invalid hash string ' + 'Z'.repeat(40)),
69+
)
70+
})
5971
})
6072

6173
describe('Hash192', function () {
@@ -83,6 +95,12 @@ describe('Hash192', function () {
8395
Hash192.from('10000000000000000000000000000000000000000000000000'),
8496
).toThrow(new Error('Invalid Hash length 25'))
8597
})
98+
99+
it(`throws when constructed from non-hexadecimal string`, () => {
100+
expect(() => Hash192.from('Z'.repeat(48))).toThrow(
101+
new Error('Invalid hash string ' + 'Z'.repeat(48)),
102+
)
103+
})
86104
})
87105

88106
describe('Hash256', function () {
@@ -105,6 +123,12 @@ describe('Hash256', function () {
105123
expect(h.nibblet(4)).toBe(0x0b)
106124
expect(h.nibblet(5)).toBe(0xd)
107125
})
126+
127+
it(`throws when constructed from non-hexadecimal string`, () => {
128+
expect(() => Hash256.from('Z'.repeat(64))).toThrow(
129+
new Error('Invalid hash string ' + 'Z'.repeat(64)),
130+
)
131+
})
108132
})
109133

110134
describe('Currency', function () {

packages/xrpl/HISTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
55
## Unreleased
66

77
### Added
8+
* Support for `PermissionedDEX` (XLS-81d)
89
* Support for `Token Escrow` (XLS-85)
910
* Support for `Single Asset Vault` (XLS-65)
1011
* Adds `XRPLNumber` amount type used in Vault transactions. This supports integer, decimal, or scientific notation strings.

packages/xrpl/src/models/ledger/DirectoryNode.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,7 @@ export default interface DirectoryNode
4545
TakerGetsCurrency?: string
4646
/** The issuer of the TakerGets amount from the offers in this directory. */
4747
TakerGetsIssuer?: string
48+
49+
/** The domain that the offer directory is a part of. */
50+
DomainID?: string
4851
}

packages/xrpl/src/models/ledger/Offer.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@ import { Amount } from '../common'
22

33
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
44

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+
518
export default interface Offer extends BaseLedgerEntry, HasPreviousTxnID {
619
LedgerEntryType: 'Offer'
720
/** A bit-map of boolean flags enabled for this Offer. */
@@ -34,9 +47,17 @@ export default interface Offer extends BaseLedgerEntry, HasPreviousTxnID {
3447
OwnerNode: string
3548
/** The time this Offer expires, in seconds since the Ripple Epoch. */
3649
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[]
3757
}
3858

3959
export enum OfferFlags {
4060
lsfPassive = 0x00010000,
4161
lsfSell = 0x00020000,
62+
lsfHybrid = 0x00040000,
4263
}

packages/xrpl/src/models/methods/bookOffers.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export interface BookOffersRequest extends BaseRequest, LookupByLedgerRequest {
3939
* currency amounts.
4040
*/
4141
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
4249
}
4350

4451
export interface BookOffer extends Offer {

packages/xrpl/src/models/methods/pathFind.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export interface PathFindCreateRequest extends BasePathFindRequest {
3030
* about, or to check the overall cost to make a payment along a certain path.
3131
*/
3232
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
3338
}
3439

3540
/** Stop sending pathfinding information. */
@@ -97,6 +102,11 @@ export interface PathFindResponse extends BaseResponse {
97102
* Continues to send updates each time a new ledger closes.
98103
*/
99104
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
100110
/**
101111
* The ID provided in the WebSocket request is included again at this
102112
* level.

packages/xrpl/src/models/methods/ripplePathFind.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ export interface RipplePathFindRequest
3838
* and optional issuer field, like how currency amounts are specified.
3939
*/
4040
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
4146
}
4247

4348
export interface RipplePathFindPathOption {

packages/xrpl/src/models/methods/subscribe.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ export interface SubscribeBook {
3939
snapshot?: boolean
4040
/** If true, return both sides of the order book. The default is false. */
4141
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
4248
}
4349

4450
/**

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,3 +683,16 @@ export function containsDuplicates(
683683

684684
return false
685685
}
686+
687+
const _DOMAIN_ID_LENGTH = 64
688+
689+
/**
690+
* Utility method used across OfferCreate and Payment transactions to validate the DomainID.
691+
*
692+
* @param domainID - The domainID is a 64-character string that is used to identify a domain.
693+
*
694+
* @returns true if the domainID is a valid 64-character string, false otherwise
695+
*/
696+
export function isDomainID(domainID: unknown): domainID is string {
697+
return isString(domainID) && domainID.length === _DOMAIN_ID_LENGTH
698+
}

0 commit comments

Comments
 (0)