Skip to content

Commit 9f7f01d

Browse files
Patel-Raj11achowdhry-ripplepdp2121dependabot[bot]khancode
authored
Add support for Token Escrow - XLS-0085-token-escrow (#1209)
## High Level Overview of Change XLS spec: https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0085-token-escrow <!-- Please include a summary/list of the changes. If too broad, please consider splitting into multiple PRs. --> ### Context of Change <!-- Please include the context of a change. If a bug fix, when was the bug introduced? What was the behavior? If a new feature, why was this architecture chosen? What were the alternatives? If a refactor, how is this better than the previous implementation? If there is a design document for this feature, please link it here. --> ### Type of Change <!-- Please check relevant options, delete irrelevant ones. --> - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Refactor (non-breaking change that only restructures code) - [x] Tests (You added tests for code that already exists, or your new feature included in this PR) - [ ] Documentation Updates - [ ] Translation Updates - [ ] Release ### Codebase Modernization <!-- In an effort to modernize the codebase, you should convert the files that you work with to React Hooks and TypeScript, and update tests to use the React Testing Library instead of Enzyme. If this is not possible (e.g. it's too many changes, touching too many files, etc.) please explain why here. --> - [ ] Updated files to React Hooks - [ ] Updated files to TypeScript - [ ] Updated tests to React Testing Library ## Before / After ### IOU <img width="1040" height="620" alt="image" src="https://github.com/user-attachments/assets/ff3f107f-924a-4525-8522-6908e8ea5a97" /> <img width="964" height="626" alt="image" src="https://github.com/user-attachments/assets/f88c2178-b62e-4db3-9c7e-7a005e7cd923" /> <img width="1278" height="210" alt="image" src="https://github.com/user-attachments/assets/a8cb3a88-bdcb-45ab-bb83-341e2f89233c" /> <img width="1004" height="619" alt="image" src="https://github.com/user-attachments/assets/8124182a-1f69-472e-8f93-34c9f2e8753b" /> <img width="901" height="623" alt="image" src="https://github.com/user-attachments/assets/4b022175-fcc3-45d7-836d-88247b98d891" /> <img width="1254" height="147" alt="image" src="https://github.com/user-attachments/assets/a7e200ed-6bd4-4c0e-a232-56ef7118a928" /> <img width="969" height="618" alt="image" src="https://github.com/user-attachments/assets/5914f9bc-a631-4a1f-9734-7f716142a086" /> <img width="890" height="621" alt="image" src="https://github.com/user-attachments/assets/cf95ef05-4dc7-40f2-b990-31e671a9fb0b" /> <img width="1223" height="132" alt="image" src="https://github.com/user-attachments/assets/50e24663-6210-4394-8a3d-5e9aab8eb2b4" /> ### MPT <img width="1003" height="619" alt="image" src="https://github.com/user-attachments/assets/7cd1a58c-7fd5-4157-8763-8653c3389056" /> <img width="710" height="627" alt="image" src="https://github.com/user-attachments/assets/c4648662-8bf6-4e23-8b70-1dccef67e4f2" /> <img width="1272" height="205" alt="image" src="https://github.com/user-attachments/assets/cb481879-3ddb-4464-96a8-9bd915e3a8ad" /> <img width="958" height="623" alt="image" src="https://github.com/user-attachments/assets/916cc1cc-067b-4239-b591-5e0cabe46ffb" /> <img width="782" height="626" alt="image" src="https://github.com/user-attachments/assets/ff34eb9a-1798-446b-a5ea-6113471dc90c" /> <img width="1250" height="165" alt="image" src="https://github.com/user-attachments/assets/48eadd8e-a604-424f-9878-59fff2c1553c" /> <img width="995" height="624" alt="image" src="https://github.com/user-attachments/assets/826f45e9-ccdc-41cf-a72a-94f8d4b508e1" /> <img width="871" height="634" alt="image" src="https://github.com/user-attachments/assets/32f7065e-37d9-4210-b8d7-9b850861d8ba" /> <img width="1267" height="187" alt="image" src="https://github.com/user-attachments/assets/648f6f1f-0602-49aa-8745-059ec30fa59d" /> ### XRP <img width="969" height="618" alt="image" src="https://github.com/user-attachments/assets/d57dba82-3147-440c-8b30-bde30df16305" /> <img width="812" height="611" alt="image" src="https://github.com/user-attachments/assets/bce362fc-be90-4211-bf64-115fa45bba6f" /> <img width="1252" height="187" alt="image" src="https://github.com/user-attachments/assets/c51fc16f-3a59-4652-88a1-353fa8c57e89" /> <img width="1004" height="608" alt="image" src="https://github.com/user-attachments/assets/351e7e00-c047-48b2-90ed-c341a6b6026a" /> <img width="938" height="616" alt="image" src="https://github.com/user-attachments/assets/0aec5c86-783a-4ddd-bcf5-0bf231e7d4ce" /> <img width="1334" height="181" alt="image" src="https://github.com/user-attachments/assets/59a492c4-3509-4717-9017-772578cb6712" /> <img width="1016" height="624" alt="image" src="https://github.com/user-attachments/assets/811eb6e2-1bc7-4ccc-8d97-f968abd4936f" /> <img width="981" height="598" alt="image" src="https://github.com/user-attachments/assets/93e25656-eb7f-43fd-ab28-d4b74d1a58d0" /> <img width="1333" height="149" alt="image" src="https://github.com/user-attachments/assets/ca033594-9a4f-48df-90ba-b61d23be9740" /> <!-- If just refactoring / back-end changes, this can be just an in-English description of the change at a technical level. If a UI change, screenshots should be included. --> ## Test Plan <!-- Please describe the tests that you ran to verify your changes and provide instructions so that others can reproduce. --> <!-- ## Future Tasks For future tasks related to PR. --> --------- Co-authored-by: achowdhry-ripple <achowdhry@ripple.com> Co-authored-by: pdp2121 <71317875+pdp2121@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Omar Khan <khancodegt@gmail.com> Co-authored-by: Phu Pham <ppham@ripple.com>
1 parent 4197879 commit 9f7f01d

22 files changed

+1728
-259
lines changed

src/containers/Transactions/test/DetailTab.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { mount } from 'enzyme'
22
import { BrowserRouter as Router } from 'react-router-dom'
33
import { I18nextProvider } from 'react-i18next'
44
import { QueryClientProvider } from 'react-query'
5-
import Transaction from '../../shared/components/Transaction/EscrowCreate/test/mock_data/EscrowCreate.json'
5+
import Transaction from './mock_data/EscrowCreate.json'
66
import FailedTransaction from '../../shared/components/Transaction/SignerListSet/test/mock_data/SignerListSet.json'
77
import HookPayment from './mock_data/HookPayment.json'
88
import EmittedPayment from './mock_data/EmittedPayment.json'
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"hash": "27D43BD746193512ABD670DBE9C646F341BDCEDB0741CF862F64190E2851CAD1",
3+
"ledger_index": 37471281,
4+
"date": "2018-03-25T04:10:21+00:00",
5+
"tx": {
6+
"TransactionType": "EscrowCreate",
7+
"Flags": 2147483648,
8+
"Sequence": 104,
9+
"FinishAfter": 636368460,
10+
"CancelAfter": 636368060,
11+
"Amount": "997500000",
12+
"Fee": "10",
13+
"SigningPubKey": "02D4BAE5988733A2BEA96021337CA85F0056D9AA47BCF0E16E0E527A0A28DB100D",
14+
"TxnSignature": "30440220286F11B955EF4CA3A033503DC2C8E42F259A6A3A196E362453EFBB04F8ABF62902204998A8A74EDB142456E673345AB7ABB7A36506D6C8E5694B5D707108991C32C4",
15+
"Condition": "A0258020886F982742772F414243855DC13B348FC78FB3D5119412C8A6480114E36A4451810120",
16+
"Account": "rLbgNAngLq3HABBXK4uPGCHrqeZwgaYi8q",
17+
"Destination": "rLbgNAngLq3HABBXK4uPGCHrqeZwgaYi7q",
18+
"Memos": [
19+
{
20+
"Memo": {
21+
"MemoType": "687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E65726963",
22+
"MemoData": ""
23+
}
24+
}
25+
]
26+
},
27+
"meta": {
28+
"TransactionIndex": 7,
29+
"AffectedNodes": [
30+
{
31+
"ModifiedNode": {
32+
"LedgerEntryType": "DirectoryNode",
33+
"LedgerIndex": "190D286E5687F825EA226482C56EB967011B258AB1F5B8F5028534E5030CA6B8",
34+
"FinalFields": {
35+
"Flags": 0,
36+
"RootIndex": "190D286E5687F825EA226482C56EB967011B258AB1F5B8F5028534E5030CA6B8",
37+
"Owner": "rLbgNAngLq3HABBXK4uPGCHrqeZwgaYi8q"
38+
}
39+
}
40+
},
41+
{
42+
"CreatedNode": {
43+
"LedgerEntryType": "Escrow",
44+
"LedgerIndex": "949D6E2DC8878166D6043348067D4672340C9710E950286E89D8385081B64C72",
45+
"NewFields": {
46+
"FinishAfter": 636368460,
47+
"Amount": "997500000",
48+
"Condition": "A0258020886F982742772F414243855DC13B348FC78FB3D5119412C8A6480114E36A4451810120",
49+
"Account": "rLbgNAngLq3HABBXK4uPGCHrqeZwgaYi8q",
50+
"Destination": "rLbgNAngLq3HABBXK4uPGCHrqeZwgaYi8q"
51+
}
52+
}
53+
},
54+
{
55+
"ModifiedNode": {
56+
"LedgerEntryType": "AccountRoot",
57+
"PreviousTxnLgrSeq": 37471281,
58+
"PreviousTxnID": "81D8CE5B4489301E2906632250D05B429F1849CC69F14F205C23C0E142C12B33",
59+
"LedgerIndex": "BEAB548BF4AA0ED98E8DD381D566A56C35C20B49A4E9B1A10A7F334B0B50CB4C",
60+
"PreviousFields": {
61+
"Sequence": 104,
62+
"OwnerCount": 25,
63+
"Balance": "8132542897"
64+
},
65+
"FinalFields": {
66+
"Flags": 0,
67+
"Sequence": 105,
68+
"OwnerCount": 26,
69+
"Balance": "7135042887",
70+
"Account": "rLbgNAngLq3HABBXK4uPGCHrqeZwgaYi8q"
71+
}
72+
}
73+
}
74+
],
75+
"TransactionResult": "tesSUCCESS"
76+
}
77+
}

src/containers/shared/components/Transaction/EscrowCancel/Description.tsx

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import { useTranslation, Trans } from 'react-i18next'
22
import type { EscrowCancel } from 'xrpl'
3-
import { findNode, normalizeAmount } from '../../../transactionUtils'
3+
import { findNode } from '../../../transactionUtils'
44
import { Account } from '../../Account'
55
import {
66
TransactionDescriptionComponent,
77
TransactionDescriptionProps,
88
} from '../types'
99
import { TRANSACTION_ROUTE } from '../../../../App/routes'
1010
import { RouteLink } from '../../../routing'
11+
import {
12+
formatAmount,
13+
isXRP,
14+
} from '../../../../../rippled/lib/txSummary/formatAmount'
15+
import { Amount } from '../../Amount'
1116

1217
const Description: TransactionDescriptionComponent = (
1318
props: TransactionDescriptionProps<EscrowCancel>,
1419
) => {
15-
const { t, i18n } = useTranslation()
16-
const language = i18n.resolvedLanguage
20+
const { t } = useTranslation()
1721
const { data } = props
1822
const deleted: any = findNode(data.meta, 'DeletedNode', 'Escrow')
1923
if (deleted == null) {
@@ -24,30 +28,31 @@ const Description: TransactionDescriptionComponent = (
2428
<div>
2529
{t('escrow_cancellation_desc')} <Account account={data.tx.Account} />
2630
</div>
27-
<div>
31+
<div data-testid="amount-line">
2832
<Trans i18nKey="escrow_cancellation_desc_2">
2933
The escrowed amount of
3034
<b>
31-
{normalizeAmount(deleted.FinalFields.Amount, language)}
32-
<small>XRP</small>
35+
<Amount value={formatAmount(deleted.FinalFields.Amount)} />
3336
</b>
3437
was returned to
3538
<Account account={data.tx.Owner} />
3639
</Trans>
37-
{data.tx.Owner === data.tx.Account && (
38-
<span>
39-
{' '}
40-
(
41-
<b>
42-
{normalizeAmount(
43-
deleted.FinalFields.Amount - parseInt(data.tx.Fee || '0', 10),
44-
language,
45-
)}
46-
<small>XRP</small>
47-
</b>{' '}
48-
{t('escrow_after_transaction_cost')})
49-
</span>
50-
)}
40+
{isXRP(deleted.FinalFields.Amount) &&
41+
data.tx.Owner === data.tx.Account && (
42+
<span>
43+
{' '}
44+
(
45+
<b>
46+
<Amount
47+
value={formatAmount(
48+
deleted.FinalFields.Amount -
49+
parseInt(data.tx.Fee || '0', 10),
50+
)}
51+
/>
52+
</b>{' '}
53+
{t('escrow_after_transaction_cost')})
54+
</span>
55+
)}
5156
</div>
5257
<Trans i18nKey="escrow_created_by_desc">
5358
The escrow was created by

src/containers/shared/components/Transaction/EscrowCancel/test/EscrowCancelDescription.test.tsx

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,67 @@
1-
import EscrowCancel from './mock_data/EscrowCancel.json'
1+
import { useQuery } from 'react-query'
2+
import mockEscrowCancel from './mock_data/EscrowCancel.json'
23
import { Description } from '../Description'
34
import { createDescriptionWrapperFactory } from '../../test'
5+
import i18n from '../../../../../../i18n/testConfigEnglish'
46

5-
const createWrapper = createDescriptionWrapperFactory(Description)
7+
const createWrapper = createDescriptionWrapperFactory(Description, i18n)
8+
9+
jest.mock('react-query', () => ({
10+
...jest.requireActual('react-query'),
11+
useQuery: jest.fn(),
12+
}))
13+
14+
function getTestByName(name: string) {
15+
return mockEscrowCancel[name]
16+
}
617

718
describe('EscrowCancelDescription', () => {
819
it('renders description for EscrowCancel', () => {
9-
const wrapper = createWrapper(EscrowCancel)
20+
const wrapper = createWrapper(
21+
getTestByName('EscrowCancel having XRP escrowed'),
22+
)
1023
expect(wrapper.html()).toBe(
11-
'<div>escrow_cancellation_desc <a data-testid="account" title="rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56" class="account" href="/accounts/rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56">rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56</a></div><div>The escrowed amount of<b>\uE900135.79<small>XRP</small></b>was returned to<a data-testid="account" title="rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56" class="account" href="/accounts/rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56">rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56</a><span> (<b>\uE900135.78999<small>XRP</small></b> escrow_after_transaction_cost)</span></div>The escrow was created by<a data-testid="account" title="rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56" class="account" href="/accounts/rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56">rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56</a>with transaction<a class="hash" href="/transactions/A979AD5C6A6C844913DA51D71BF5F0B8E254D9A211FA837C4B322C4A8FD358E6">A979AD...</a>',
24+
'<div>Cancellation was triggered by <a data-testid="account" title="rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56" class="account" href="/accounts/rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56">rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56</a></div><div data-testid="amount-line">The escrowed amount of <b><span class="amount" data-testid="amount"><span class="amount-localized" data-testid="amount-localized">135.79</span> <span class="currency" data-testid="currency">XRP</span></span></b> was returned to <a data-testid="account" title="rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56" class="account" href="/accounts/rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56">rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56</a><span> (<b><span class="amount" data-testid="amount"><span class="amount-localized" data-testid="amount-localized">135.78999</span> <span class="currency" data-testid="currency">XRP</span></span></b> after transaction cost)</span></div>The escrow was created by <a data-testid="account" title="rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56" class="account" href="/accounts/rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56">rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56</a> with transaction <a class="hash" href="/transactions/A979AD5C6A6C844913DA51D71BF5F0B8E254D9A211FA837C4B322C4A8FD358E6">A979AD...</a>',
25+
)
26+
wrapper.unmount()
27+
})
28+
29+
it('test XRP amount', () => {
30+
const wrapper = createWrapper(
31+
getTestByName('EscrowCancel having XRP escrowed'),
32+
)
33+
expect(wrapper.find('[data-testid="amount-line"]')).toHaveText(
34+
`The escrowed amount of \uE900135.79 XRP was returned to rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56 (\uE900135.78999 XRP after transaction cost)`,
35+
)
36+
37+
wrapper.unmount()
38+
})
39+
40+
it('test IOU amount', () => {
41+
const wrapper = createWrapper(
42+
getTestByName('EscrowCancel having IOU escrowed'),
43+
)
44+
expect(wrapper.find('[data-testid="amount-line"]')).toHaveText(
45+
'The escrowed amount of 1.00 ZZZ.rDb2kD2sibG5cxhz3VAoRFkmhPrca4JtL8 was returned to rDtpgHpsUFu2dHC6fMZnwgZvNEDZ9MG9YK',
46+
)
47+
wrapper.unmount()
48+
})
49+
50+
it('test MPT amount', () => {
51+
const data = {
52+
assetScale: 4,
53+
}
54+
55+
// @ts-ignore
56+
useQuery.mockImplementation(() => ({
57+
data,
58+
}))
59+
60+
const wrapper = createWrapper(
61+
getTestByName('EscrowCancel having MPT escrowed'),
62+
)
63+
expect(wrapper.find('[data-testid="amount-line"]')).toHaveText(
64+
'The escrowed amount of 0.0001 MPT (0044E49BC9FB70ADC1A604A5792643A38CA5887219C21C8C) was returned to r4ipomC348PqM2rGSBmhfRPXUH6CzKS1XJ',
1265
)
1366
wrapper.unmount()
1467
})

src/containers/shared/components/Transaction/EscrowCancel/test/EscrowCancelSimple.test.tsx

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1+
import { useQuery } from 'react-query'
12
import { createSimpleWrapperFactory } from '../../test/createWrapperFactory'
23
import { Simple } from '../Simple'
34
import mockEscrowCancel from './mock_data/EscrowCancel.json'
45

56
const createWrapper = createSimpleWrapperFactory(Simple)
67

8+
jest.mock('react-query', () => ({
9+
...jest.requireActual('react-query'),
10+
useQuery: jest.fn(),
11+
}))
12+
13+
function getTestByName(name: string) {
14+
return mockEscrowCancel[name]
15+
}
16+
717
describe('EscrowCancelSimple', () => {
818
it('renders with an expiration and offer', () => {
9-
const wrapper = createWrapper(mockEscrowCancel)
19+
const wrapper = createWrapper(
20+
getTestByName('EscrowCancel having XRP escrowed'),
21+
)
1022
expect(wrapper.find('[data-testid="escrow-amount"] .value')).toHaveText(
1123
`\uE900135.79 XRP`,
1224
)
@@ -18,4 +30,44 @@ describe('EscrowCancelSimple', () => {
1830
)
1931
wrapper.unmount()
2032
})
33+
34+
it('test XRP amount', () => {
35+
const wrapper = createWrapper(
36+
getTestByName('EscrowCancel having XRP escrowed'),
37+
)
38+
expect(wrapper.find('[data-testid="escrow-amount"] .value')).toHaveText(
39+
`\uE900135.79 XRP`,
40+
)
41+
42+
wrapper.unmount()
43+
})
44+
45+
it('test IOU amount', () => {
46+
const wrapper = createWrapper(
47+
getTestByName('EscrowCancel having IOU escrowed'),
48+
)
49+
expect(wrapper.find('[data-testid="escrow-amount"] .value')).toHaveText(
50+
'1.00 ZZZ.rDb2kD2sibG5cxhz3VAoRFkmhPrca4JtL8',
51+
)
52+
wrapper.unmount()
53+
})
54+
55+
it('test MPT amount', () => {
56+
const data = {
57+
assetScale: 4,
58+
}
59+
60+
// @ts-ignore
61+
useQuery.mockImplementation(() => ({
62+
data,
63+
}))
64+
65+
const wrapper = createWrapper(
66+
getTestByName('EscrowCancel having MPT escrowed'),
67+
)
68+
expect(wrapper.find('[data-testid="escrow-amount"] .value')).toHaveText(
69+
'0.0001 MPT (0044E49BC9FB70ADC1A604A5792643A38CA5887219C21C8C)',
70+
)
71+
wrapper.unmount()
72+
})
2173
})

src/containers/shared/components/Transaction/EscrowCancel/test/EscrowCancelTableDetail.test.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ const createWrapper = createTableDetailWrapperFactory(
1010

1111
describe('EscrowCancelTableDetail', () => {
1212
it('renders EscrowCancel without crashing', () => {
13-
const wrapper = createWrapper(mockEscrowCancel)
13+
const wrapper = createWrapper(
14+
mockEscrowCancel['EscrowCancel having XRP escrowed'],
15+
)
1416
expect(wrapper).toHaveText(
1517
'cancel escrow rpmqbo5FWoydTL2Ufh5YdtzmRjbeLyxt56 - 9',
1618
)

0 commit comments

Comments
 (0)