Skip to content

Commit 1894c02

Browse files
committed
address PR comments from Cybele
1 parent 7e8cef2 commit 1894c02

File tree

6 files changed

+210
-6
lines changed

6 files changed

+210
-6
lines changed

packages/ripple-binary-codec/src/types/path-set.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Hop extends SerializedType {
109109

110110
if (type & TYPE_CURRENCY && type & TYPE_MPT) {
111111
throw new Error(
112-
'Invalid binary input: Currency and mpt_issuance_id are mutually exclusive in a path hop',
112+
'Currency and mpt_issuance_id are mutually exclusive in a path hop. The BinaryParser has a bitmask containing both Currency and mpt_issuance_id elements',
113113
)
114114
}
115115

packages/ripple-binary-codec/test/path-set.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,41 @@ describe('Path-Set binary-codec unit tests', () => {
216216
)
217217
})
218218

219+
it(`Path with MPT → Currency → MPT hops`, () => {
220+
const mptIssuanceId1 = '00000001B5F762798A53D543A014CAF8B297CFF8F2F937E8'
221+
const currencyCode = 'ABC'
222+
const mptIssuanceId2 = '000004C463C52827307480341125DA0577DEFC38405B0E3E'
223+
const path = [
224+
[
225+
{ mpt_issuance_id: mptIssuanceId1 } as HopObject,
226+
{ currency: currencyCode } as HopObject,
227+
{ mpt_issuance_id: mptIssuanceId2 } as HopObject,
228+
],
229+
]
230+
const serializedHexRepr = coreTypes.PathSet.from(path).toHex().toUpperCase()
231+
232+
expect(serializedHexRepr).toEqual(
233+
'40' /* MPT type code */ +
234+
mptIssuanceId1.toUpperCase() /* first 24-byte MPTID */ +
235+
'10' /* Currency type code */ +
236+
Currency.from(currencyCode)
237+
.toHex()
238+
.toUpperCase() /* 20-byte currency */ +
239+
'40' /* MPT type code */ +
240+
mptIssuanceId2.toUpperCase() /* second 24-byte MPTID */ +
241+
'00' /* PathSet terminator */,
242+
)
243+
244+
// test the round-trip equivalence
245+
expect(coreTypes.PathSet.from(path).toJSON()).toEqual(path)
246+
247+
// validate the de-serialization via the BinaryParser
248+
const parser = new BinaryParser(serializedHexRepr)
249+
expect(coreTypes.PathSet.fromParser(parser)).toEqual(
250+
coreTypes.PathSet.from(path),
251+
)
252+
})
253+
219254
it(`PathSet: Currency and MPT are mutually exclusive in a hop`, () => {
220255
const currencyCode = 'ABC'
221256
const mptIssuanceId = '00000001B5F762798A53D543A014CAF8B297CFF8F2F937E8'
@@ -244,7 +279,24 @@ describe('Path-Set binary-codec unit tests', () => {
244279

245280
const parser = new BinaryParser(invalidHex)
246281
expect(() => coreTypes.PathSet.fromParser(parser)).toThrow(
247-
'Currency and mpt_issuance_id are mutually exclusive',
282+
'Currency and mpt_issuance_id are mutually exclusive in a path hop. The BinaryParser has a bitmask containing both Currency and mpt_issuance_id elements',
283+
)
284+
})
285+
286+
it(`PathSet: Deserialization rejects type byte 0x51 (Account + Currency + MPT)`, () => {
287+
// Type byte 0x51 = TYPE_ACCOUNT (0x01) | TYPE_CURRENCY (0x10) | TYPE_MPT (0x40)
288+
const invalidHex =
289+
'51' +
290+
AccountID.from('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')
291+
.toHex()
292+
.toUpperCase() +
293+
Currency.from('ABC').toHex().toUpperCase() +
294+
'00000001B5F762798A53D543A014CAF8B297CFF8F2F937E8' +
295+
'00'
296+
297+
const parser = new BinaryParser(invalidHex)
298+
expect(() => coreTypes.PathSet.fromParser(parser)).toThrow(
299+
'Currency and mpt_issuance_id are mutually exclusive in a path hop. The BinaryParser has a bitmask containing both Currency and mpt_issuance_id elements',
248300
)
249301
})
250302
})

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ export interface AMMClawback extends BaseTransaction {
6767
Asset: IssuedCurrency | MPTCurrency
6868

6969
/**
70-
* Specifies the other asset in the AMM's pool. In JSON, this is an object with currency and
71-
* issuer fields (omit issuer for XRP), or mpt_issuance_id for MPT.
70+
* Specifies the other asset in the AMM's pool. In JSON, this can be XRP (no issuer),
71+
* an issued currency (with issuer), or an MPT (with mpt_issuance_id).
7272
*/
7373
Asset2: Currency
7474

packages/xrpl/test/integration/transactions/checkCash.test.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { assert } from 'chai'
22

3-
import { CheckCreate, CheckCash } from '../../../src'
3+
import { CheckCreate, CheckCash, LedgerEntry } from '../../../src'
44
import { createMPTIssuanceAndAuthorize } from '../mptUtils'
55
import serverUrl from '../serverUrl'
66
import {
@@ -111,7 +111,21 @@ describe('CheckCash', function () {
111111
1,
112112
'Should be exactly one check on the ledger',
113113
)
114-
const checkId = response1.result.account_objects[0].index
114+
115+
const checkObject = response1.result
116+
.account_objects[0] as LedgerEntry.Check
117+
const checkId = checkObject.index
118+
119+
// Verify the check ledger object contents
120+
assert.equal(checkObject.Account, testContext.wallet.classicAddress)
121+
assert.equal(
122+
checkObject.Destination,
123+
checkDestinationWallet.classicAddress,
124+
)
125+
assert.deepEqual(checkObject.SendMax, {
126+
mpt_issuance_id: mptIssuanceId,
127+
value: '50',
128+
})
115129

116130
// Cash the check with MPT
117131
const tx: CheckCash = {

packages/xrpl/test/integration/transactions/offerCreate.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,53 @@ describe('OfferCreate', function () {
162162
},
163163
TIMEOUT,
164164
)
165+
166+
it(
167+
'OfferCreate with MPT on TakerPays side',
168+
async () => {
169+
const issuerWallet = await generateFundedWallet(testContext.client)
170+
const sourceWallet = await generateFundedWallet(testContext.client)
171+
172+
const mptIssuanceId = await createMPTIssuanceAndAuthorize(
173+
testContext.client,
174+
issuerWallet,
175+
sourceWallet,
176+
// eslint-disable-next-line no-bitwise -- combining flags requires bitwise OR
177+
MPTokenIssuanceCreateFlags.tfMPTCanTrade |
178+
MPTokenIssuanceCreateFlags.tfMPTCanTransfer,
179+
)
180+
181+
// Create an offer: sell 100000 XRP drops for 10 MPT
182+
const tx: OfferCreate = {
183+
TransactionType: 'OfferCreate',
184+
Account: sourceWallet.classicAddress,
185+
TakerGets: '100000',
186+
TakerPays: {
187+
mpt_issuance_id: mptIssuanceId,
188+
value: '10',
189+
},
190+
}
191+
192+
await testTransaction(testContext.client, tx, sourceWallet)
193+
194+
// Confirm the offer exists on the ledger
195+
const accountOffersResponse = await testContext.client.request({
196+
command: 'account_offers',
197+
account: sourceWallet.classicAddress,
198+
})
199+
assert.lengthOf(
200+
accountOffersResponse.result.offers!,
201+
1,
202+
'Should be exactly one offer on the ledger',
203+
)
204+
205+
const offer = accountOffersResponse.result.offers![0]
206+
assert.equal(offer.taker_gets, '100000')
207+
assert.deepEqual(offer.taker_pays, {
208+
mpt_issuance_id: mptIssuanceId,
209+
value: '10',
210+
})
211+
},
212+
TIMEOUT,
213+
)
165214
})

packages/xrpl/test/integration/transactions/payment.test.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import {
66
MPTokenIssuanceCreate,
77
MPTokenIssuanceCreateFlags,
88
MPTokenAuthorize,
9+
AccountSet,
10+
AccountSetAsfFlags,
911
AMMCreate,
1012
TransactionMetadata,
13+
TrustSet,
1114
} from '../../../src'
1215
import { createMPTIssuanceAndAuthorize } from '../mptUtils'
1316
import serverUrl from '../serverUrl'
@@ -275,4 +278,90 @@ describe('Payment', function () {
275278
},
276279
TIMEOUT,
277280
)
281+
282+
it(
283+
'Payment with currency PathStep (no account)',
284+
async () => {
285+
const issuer = await generateFundedWallet(testContext.client)
286+
const lpWallet = await generateFundedWallet(testContext.client)
287+
const destination = await generateFundedWallet(testContext.client)
288+
289+
const currencyCode = 'USD'
290+
291+
// Enable default rippling on the issuer so payments can ripple through
292+
const enableRipplingTx: AccountSet = {
293+
TransactionType: 'AccountSet',
294+
Account: issuer.classicAddress,
295+
SetFlag: AccountSetAsfFlags.asfDefaultRipple,
296+
}
297+
await testTransaction(testContext.client, enableRipplingTx, issuer)
298+
299+
// LP sets up a trust line for USD and receives funds from the issuer
300+
const trustTx: TrustSet = {
301+
TransactionType: 'TrustSet',
302+
Account: lpWallet.classicAddress,
303+
LimitAmount: {
304+
currency: currencyCode,
305+
issuer: issuer.classicAddress,
306+
value: '100000',
307+
},
308+
}
309+
await testTransaction(testContext.client, trustTx, lpWallet)
310+
311+
const fundLpTx: Payment = {
312+
TransactionType: 'Payment',
313+
Account: issuer.classicAddress,
314+
Destination: lpWallet.classicAddress,
315+
Amount: {
316+
currency: currencyCode,
317+
issuer: issuer.classicAddress,
318+
value: '50000',
319+
},
320+
}
321+
await testTransaction(testContext.client, fundLpTx, issuer)
322+
323+
// Destination sets up a trust line for USD
324+
const destTrustTx: TrustSet = {
325+
TransactionType: 'TrustSet',
326+
Account: destination.classicAddress,
327+
LimitAmount: {
328+
currency: currencyCode,
329+
issuer: issuer.classicAddress,
330+
value: '100000',
331+
},
332+
}
333+
await testTransaction(testContext.client, destTrustTx, destination)
334+
335+
// Create AMM pool: XRP / USD
336+
const ammCreateTx: AMMCreate = {
337+
TransactionType: 'AMMCreate',
338+
Account: lpWallet.classicAddress,
339+
Amount: '1000000',
340+
Amount2: {
341+
currency: currencyCode,
342+
issuer: issuer.classicAddress,
343+
value: '1000',
344+
},
345+
TradingFee: 12,
346+
}
347+
await testTransaction(testContext.client, ammCreateTx, lpWallet)
348+
349+
// Cross-currency payment: XRP → USD via AMM with a currency-only path step
350+
const payTx: Payment = {
351+
TransactionType: 'Payment',
352+
Account: testContext.wallet.classicAddress,
353+
Destination: destination.classicAddress,
354+
Amount: {
355+
currency: currencyCode,
356+
issuer: issuer.classicAddress,
357+
value: '5',
358+
},
359+
SendMax: '500000',
360+
Paths: [[{ currency: currencyCode, issuer: issuer.classicAddress }]],
361+
}
362+
363+
await testTransaction(testContext.client, payTx, testContext.wallet)
364+
},
365+
TIMEOUT,
366+
)
278367
})

0 commit comments

Comments
 (0)