diff --git a/package-lock.json b/package-lock.json index 9ccc54054..429fca673 100644 --- a/package-lock.json +++ b/package-lock.json @@ -117,7 +117,7 @@ "ts-jest": "^26.5.6", "ts-node": "^10.9.1", "typescript": "^4.9.4", - "xrpl": "^2.8.1" + "xrpl": "^2.10.0" }, "engines": { "node": ">=18.0.0 <19", @@ -5408,6 +5408,15 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/brorand": { + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/@types/brorand/-/brorand-1.0.30.tgz", + "integrity": "sha512-moU/Mp0MA5vFNGj1/A7Z5TpNC1uyS82I6KZp0Oxk9OKC2XD0S6aQGLVv9ryBYAs259Cq7h9iM1jN9zbuCrUI9w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cheerio": { "version": "0.22.29", "dev": true, @@ -23163,9 +23172,9 @@ } }, "node_modules/ripple-binary-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.6.0.tgz", - "integrity": "sha512-fa0aMSbh1VOGEHIWCF/VuIvoMoQ/1HLJoBxm+oPNPIDyZJG1uRpLYph1pcvAlDuMutHM3ZHMzWjJpe3AaiMIUA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.9.0.tgz", + "integrity": "sha512-vJlY23rRrP9XPD9bo63lAvZphjj1OGtfLzEUxlYYD7SJvDbYEaiEsriC69LKOXMft5sCFRvCsLGOeSDAdZW9hw==", "dev": true, "dependencies": { "assert": "^2.0.0", @@ -27292,9 +27301,9 @@ "peer": true }, "node_modules/xrpl": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.8.1.tgz", - "integrity": "sha512-P6SwGpA3QDusaqjElo6HbNvMI+voTQbkSknbqwZCTG6cpwsvrYbKWqS/Kl2hoqvhhe8L9d8tdCdm4YXcGy/j8g==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.11.0.tgz", + "integrity": "sha512-TjkPu1GwKlSW9Ag7lFYnxuWcAHbK86nGu6lVLfMCcfiRYfZQJNG8X4/4Tk+uTGyyH6T/UqYA05t4Z6a23IsTCg==", "dev": true, "dependencies": { "bignumber.js": "^9.0.0", @@ -27303,9 +27312,10 @@ "https-proxy-agent": "^5.0.0", "lodash": "^4.17.4", "ripple-address-codec": "^4.3.0", - "ripple-binary-codec": "^1.6.0", + "ripple-binary-codec": "^1.9.0", "ripple-keypairs": "^1.3.0", - "ws": "^8.2.2" + "ws": "^8.2.2", + "xrpl-secret-numbers": "^0.3.3" }, "engines": { "node": ">=10.13.0" @@ -27320,6 +27330,17 @@ "websocket": "^1.0.34" } }, + "node_modules/xrpl-secret-numbers": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/xrpl-secret-numbers/-/xrpl-secret-numbers-0.3.4.tgz", + "integrity": "sha512-B3m0OLRsmNLQpN/BUR15+LC4yejM/pdneoWgijfBYbgjVVnpyCF5+Ur7zbAs4nCAlBUZYXnxp+o/rSNZkke9jQ==", + "dev": true, + "dependencies": { + "@types/brorand": "^1.0.30", + "brorand": "^1.1.0", + "ripple-keypairs": "^1.1.5" + } + }, "node_modules/xrpl/node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", @@ -30943,6 +30964,15 @@ "@babel/types": "^7.3.0" } }, + "@types/brorand": { + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/@types/brorand/-/brorand-1.0.30.tgz", + "integrity": "sha512-moU/Mp0MA5vFNGj1/A7Z5TpNC1uyS82I6KZp0Oxk9OKC2XD0S6aQGLVv9ryBYAs259Cq7h9iM1jN9zbuCrUI9w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/cheerio": { "version": "0.22.29", "dev": true, @@ -43212,9 +43242,9 @@ } }, "ripple-binary-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.6.0.tgz", - "integrity": "sha512-fa0aMSbh1VOGEHIWCF/VuIvoMoQ/1HLJoBxm+oPNPIDyZJG1uRpLYph1pcvAlDuMutHM3ZHMzWjJpe3AaiMIUA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.9.0.tgz", + "integrity": "sha512-vJlY23rRrP9XPD9bo63lAvZphjj1OGtfLzEUxlYYD7SJvDbYEaiEsriC69LKOXMft5sCFRvCsLGOeSDAdZW9hw==", "dev": true, "requires": { "assert": "^2.0.0", @@ -45977,9 +46007,9 @@ "peer": true }, "xrpl": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.8.1.tgz", - "integrity": "sha512-P6SwGpA3QDusaqjElo6HbNvMI+voTQbkSknbqwZCTG6cpwsvrYbKWqS/Kl2hoqvhhe8L9d8tdCdm4YXcGy/j8g==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.11.0.tgz", + "integrity": "sha512-TjkPu1GwKlSW9Ag7lFYnxuWcAHbK86nGu6lVLfMCcfiRYfZQJNG8X4/4Tk+uTGyyH6T/UqYA05t4Z6a23IsTCg==", "dev": true, "requires": { "bignumber.js": "^9.0.0", @@ -45988,9 +46018,10 @@ "https-proxy-agent": "^5.0.0", "lodash": "^4.17.4", "ripple-address-codec": "^4.3.0", - "ripple-binary-codec": "^1.6.0", + "ripple-binary-codec": "^1.9.0", "ripple-keypairs": "^1.3.0", - "ws": "^8.2.2" + "ws": "^8.2.2", + "xrpl-secret-numbers": "^0.3.3" }, "dependencies": { "ws": { @@ -46011,6 +46042,17 @@ "websocket": "^1.0.34" } }, + "xrpl-secret-numbers": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/xrpl-secret-numbers/-/xrpl-secret-numbers-0.3.4.tgz", + "integrity": "sha512-B3m0OLRsmNLQpN/BUR15+LC4yejM/pdneoWgijfBYbgjVVnpyCF5+Ur7zbAs4nCAlBUZYXnxp+o/rSNZkke9jQ==", + "dev": true, + "requires": { + "@types/brorand": "^1.0.30", + "brorand": "^1.1.0", + "ripple-keypairs": "^1.1.5" + } + }, "y18n": { "version": "4.0.3", "dev": true, diff --git a/package.json b/package.json index 079abea22..51f8b8343 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "ts-jest": "^26.5.6", "ts-node": "^10.9.1", "typescript": "^4.9.4", - "xrpl": "^2.8.1" + "xrpl": "^2.10.0" }, "resolutions": { "jest-environment-jsdom": "29.3.1" diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 3cb775eed..081066135 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -122,6 +122,8 @@ "the_channel_amount_is": "The channel amount is", "update_payment_channel": "It will update the payment channel", "the_channel_balance_is": "The channel balance claimed is", + "amm_delete_description": "Attempted to delete the AMM for and .", + "amm_delete_description_caveat": "If there were more than 512 trustlines, this only removes 512 trustlines instead.", "payment_channel_closed_description": "The payment channel will be closed, any remaining balance will be returned to the source account", "set_signer_list_description": "It sets the minimum signer quorum at {{quorum}} with the following list of signers", "unset_signer_list_description": "It removed all signers from the account", diff --git a/src/containers/shared/components/Currency.tsx b/src/containers/shared/components/Currency.tsx index 6cff4f6a6..e6473d5ee 100644 --- a/src/containers/shared/components/Currency.tsx +++ b/src/containers/shared/components/Currency.tsx @@ -25,6 +25,7 @@ const Currency = (props: Props) => { shortenIssuer = false, displaySymbol = true, } = props + const LPTokenIdentifier = '03' const currencyCode = currency?.length === NON_STANDARD_CODE_LENGTH && diff --git a/src/containers/shared/components/Transaction/AMMDelete/Description.tsx b/src/containers/shared/components/Transaction/AMMDelete/Description.tsx new file mode 100644 index 000000000..1895c9afb --- /dev/null +++ b/src/containers/shared/components/Transaction/AMMDelete/Description.tsx @@ -0,0 +1,31 @@ +import { Trans } from 'react-i18next' +import { type AMMDelete } from 'xrpl' +import { TransactionDescriptionProps } from '../types' +import Currency from '../../Currency' + +export const Description = ({ + data, +}: TransactionDescriptionProps) => { + const { Asset, Asset2 } = data.tx + + return ( +
+ , + Asset2: ( + + ), + }} + /> +
+ +
+ ) +} diff --git a/src/containers/shared/components/Transaction/AMMDelete/Simple.tsx b/src/containers/shared/components/Transaction/AMMDelete/Simple.tsx new file mode 100644 index 000000000..c84f65b71 --- /dev/null +++ b/src/containers/shared/components/Transaction/AMMDelete/Simple.tsx @@ -0,0 +1,24 @@ +import { useTranslation } from 'react-i18next' + +import { type AMMDelete } from 'xrpl' +import { SimpleRow } from '../SimpleRow' +import { TransactionSimpleProps } from '../types' +import Currency from '../../Currency' + +export const Simple = ({ data }: TransactionSimpleProps) => { + const { t } = useTranslation() + const { Asset, Asset2 } = data.instructions + + return ( + <> + + {/* @ts-expect-error -- Fixed by https://github.com/XRPLF/xrpl.js/pull/2451 */} + + + + {/* @ts-expect-error -- Fixed by https://github.com/XRPLF/xrpl.js/pull/2451 */} + + + + ) +} diff --git a/src/containers/shared/components/Transaction/AMMDelete/TableDetail.tsx b/src/containers/shared/components/Transaction/AMMDelete/TableDetail.tsx new file mode 100644 index 000000000..afa29d03e --- /dev/null +++ b/src/containers/shared/components/Transaction/AMMDelete/TableDetail.tsx @@ -0,0 +1,24 @@ +import { useTranslation } from 'react-i18next' +import { type AMMDelete } from 'xrpl' +import { TransactionTableDetailProps } from '../types' +import Currency from '../../Currency' + +export const TableDetail = ({ + instructions, +}: TransactionTableDetailProps) => { + const { t } = useTranslation() + const { Asset, Asset2 } = instructions + + return ( +
+
+ {t('asset')} + +
+
+ {t('asset2')} + +
+
+ ) +} diff --git a/src/containers/shared/components/Transaction/AMMDelete/index.ts b/src/containers/shared/components/Transaction/AMMDelete/index.ts new file mode 100644 index 000000000..ef0e659a1 --- /dev/null +++ b/src/containers/shared/components/Transaction/AMMDelete/index.ts @@ -0,0 +1,19 @@ +import { type AMMDelete } from 'xrpl' +import { + TransactionAction, + TransactionCategory, + TransactionMapping, +} from '../types' +import { Description } from './Description' + +import { Simple } from './Simple' +import { TableDetail } from './TableDetail' + +export const AMMDeleteTransaction: TransactionMapping = { + Description, + TableDetail, + Simple, + action: TransactionAction.CANCEL, + category: TransactionCategory.DEX, + parser: (tx: AMMDelete): AMMDelete => tx, +} diff --git a/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteDescription.test.tsx b/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteDescription.test.tsx new file mode 100644 index 000000000..b1b5e5cb7 --- /dev/null +++ b/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteDescription.test.tsx @@ -0,0 +1,22 @@ +import i18n from '../../../../../../i18n/testConfigEnglish' +import mockAMMDelete from './mock_data/AMMDelete.json' +import { Description } from '../Description' +import { createDescriptionWrapperFactory } from '../../test' + +const createWrapper = createDescriptionWrapperFactory(Description, i18n) + +describe('AMMDelete: Description', () => { + it('renders description for AMMDelete transaction', () => { + const wrapper = createWrapper(mockAMMDelete) + + expect(wrapper.find('[data-test="amm-delete-description"]')).toHaveText( + 'Attempted to delete the AMM for \uE900 XRP and FOO.rm5c42Crqpdch5fbuCdHmSMV1wrL9arV9.If there were more than 512 trustlines, this only removes 512 trustlines instead.', + ) + expect(wrapper.find('a')).toHaveProp( + 'href', + '/token/FOO.rm5c42Crqpdch5fbuCdHmSMV1wrL9arV9', + ) + + wrapper.unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteSimple.test.tsx b/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteSimple.test.tsx new file mode 100644 index 000000000..b516be24a --- /dev/null +++ b/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteSimple.test.tsx @@ -0,0 +1,21 @@ +import i18n from '../../../../../../i18n/testConfigEnglish' +import { expectSimpleRowText } from '../../test' + +import { createSimpleWrapperFactory } from '../../test/createWrapperFactory' +import { Simple } from '../Simple' +import mockAMMDelete from './mock_data/AMMDelete.json' + +const createWrapper = createSimpleWrapperFactory(Simple, i18n) + +describe('AMMDelete: Simple', () => { + it('renders', () => { + const wrapper = createWrapper(mockAMMDelete) // TOOD: - Make this look up asset 1 / asset 2 currency codes + expectSimpleRowText(wrapper, 'asset1', '\uE900 XRP') + expectSimpleRowText( + wrapper, + 'asset2', + 'FOO.rm5c42Crqpdch5fbuCdHmSMV1wrL9arV9', + ) + wrapper.unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteTableDetail.test.tsx b/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteTableDetail.test.tsx new file mode 100644 index 000000000..07278226f --- /dev/null +++ b/src/containers/shared/components/Transaction/AMMDelete/test/AMMDeleteTableDetail.test.tsx @@ -0,0 +1,18 @@ +import { TableDetail } from '../TableDetail' +import mockAMMDelete from './mock_data/AMMDelete.json' +import { createTableDetailWrapperFactory } from '../../test' +import i18n from '../../../../../../i18n/testConfigEnglish' + +const createWrapper = createTableDetailWrapperFactory(TableDetail, i18n) + +describe('AMMDelete: TableDetail', () => { + it('renders with an expiration and offer', () => { + const wrapper = createWrapper(mockAMMDelete) + + expect(wrapper.find('[data-test="asset"]')).toHaveText('asset\uE900 XRP') + expect(wrapper.find('[data-test="asset2"]')).toHaveText( + 'Asset 2FOO.rm5c42Crqpdch5fbuCdHmSMV1wrL9arV9', + ) + wrapper.unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/AMMDelete/test/mock_data/AMMDelete.json b/src/containers/shared/components/Transaction/AMMDelete/test/mock_data/AMMDelete.json new file mode 100644 index 000000000..36db32e27 --- /dev/null +++ b/src/containers/shared/components/Transaction/AMMDelete/test/mock_data/AMMDelete.json @@ -0,0 +1,48 @@ +{ + "tx": { + "Account": "rm5c42Crqpdch5fbuCdHmSMV1wrL9arV9", + "Asset": { + "currency": "XRP" + }, + "Asset2": { + "currency": "FOO", + "issuer": "rm5c42Crqpdch5fbuCdHmSMV1wrL9arV9" + }, + "Fee": "12", + "Flags": 0, + "LastLedgerSequence": 372572, + "Sequence": 372548, + "SigningPubKey": "ED6784394D134E202BCCD957A1A3C5A66647092F3929D388A878A16D1910875435", + "TransactionType": "AMMDelete", + "TxnSignature": "F9AA459D8CE593E6E2E69BB6A6F723A4822FD5F40314F642FA9EC5187F7FD937FBD3E8A214119D04C74358A3478DCB6EAABE02EFAC1E6E125E15310E18A36D0D", + "date": 1693268101000 + }, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Account": "rm5c42Crqpdch5fbuCdHmSMV1wrL9arV9", + "Balance": "9997998976", + "Flags": 8388608, + "OwnerCount": 1, + "Sequence": 372549 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "84CA74ECFDB34F014142013B4CD2FBE3942C7BA9BA7E1FC5A1CB1EF719173812", + "PreviousFields": { + "Balance": "9997998988", + "Sequence": 372548 + }, + "PreviousTxnID": "E5051DA09F143A719521D6ABBB3856EA3E2CA38EF1CFF0E7DF9FE1C31DD73B6D", + "PreviousTxnLgrSeq": 372552 + } + } + ], + "TransactionIndex": 0, + "TransactionResult": "tecAMM_NOT_EMPTY" + }, + "hash": "D159883D456646562F51F3E5A2754F7D880D39A6372EDF679A43A7DDB77F735C", + "ledger_index": 372554, + "date": 1693268101000 +} diff --git a/src/containers/shared/components/Transaction/README.md b/src/containers/shared/components/Transaction/README.md index 588605bfa..080721f14 100644 --- a/src/containers/shared/components/Transaction/README.md +++ b/src/containers/shared/components/Transaction/README.md @@ -7,7 +7,7 @@ Each transaction has its properties defined in `./{TransactionType}/index.ts`. - **TableDetail**: A React component that defines the body of transaction on the account, ledger, token, or nft page. - **action**: A TransactionCategory value used to determine shape color of the transaction. - **category**: A TransactionCategory value used to determine the color of the transaction. -- **parser**: A function which takes a transaction object and meta nodes. It returns additional properties to map onto a transaction. +- **parser**: An optional function which takes a transaction object and meta nodes. It returns additional properties to map onto a transaction. This is run when a transaction is received from the server and is useful for transforming complex properties derived from nodes. This object is then provided in `./index.ts` diff --git a/src/containers/shared/components/Transaction/index.ts b/src/containers/shared/components/Transaction/index.ts index c6e2bdf9a..77c03ab3b 100644 --- a/src/containers/shared/components/Transaction/index.ts +++ b/src/containers/shared/components/Transaction/index.ts @@ -1,5 +1,6 @@ import { AMMCreate } from './AMMCreate' import { AMMDeposit } from './AMMDeposit' +import { AMMDeleteTransaction as AMMDelete } from './AMMDelete' import { AMMWithdraw } from './AMMWithdraw' import { AMMBid } from './AMMBid' import { AMMVote } from './AMMVote' @@ -81,6 +82,7 @@ export const transactionTypes: { [key: string]: TransactionMapping } = { AMMDeposit, AMMBid, AMMVote, + AMMDelete, TrustSet, UNLModify, Clawback, diff --git a/src/containers/shared/types.ts b/src/containers/shared/types.ts index 877de0c0b..4b847a730 100644 --- a/src/containers/shared/types.ts +++ b/src/containers/shared/types.ts @@ -8,6 +8,7 @@ export interface IssuedCurrency { export interface XRP { currency: 'XRP' + issuer: never } export type Currency = IssuedCurrency | XRP