diff --git a/package-lock.json b/package-lock.json index dedb0fffb..13f9ffd64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,8 @@ "@vitejs/plugin-react": "^4.2.1", "@xrplf/isomorphic": "^1.0.0-beta.1", "@xrplf/prettier-config": "^1.9.1", + "@xrplkit/txmeta": "^1.3.1", + "@xrplkit/xfl": "^2.1.0", "assert": "^2.1.0", "autoprefixer": "^10.4.17", "axios": "^1.6.5", @@ -59,7 +61,8 @@ "vite-plugin-html": "^3.2.0", "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^4.2.0", - "xrpl-client": "^2.1.0" + "xrpl-client": "^2.1.0", + "xrpl-tx-path-parser": "^1.0.5" }, "devDependencies": { "@babel/eslint-parser": "^7.22.6", @@ -5351,7 +5354,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dev": true, "dependencies": { "@noble/hashes": "1.3.2" }, @@ -5521,7 +5523,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", - "dev": true, "dependencies": { "@noble/curves": "~1.2.0", "@noble/hashes": "~1.3.2", @@ -5535,7 +5536,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", - "dev": true, "dependencies": { "@noble/hashes": "~1.3.0", "@scure/base": "~1.1.0" @@ -6878,12 +6878,43 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@xrplf/secret-numbers/-/secret-numbers-1.0.0.tgz", "integrity": "sha512-qsCLGyqe1zaq9j7PZJopK+iGTGRbk6akkg6iZXJJgxKwck0C5x5Gnwlb1HKYGOwPKyrXWpV6a2YmcpNpUFctGg==", - "dev": true, "dependencies": { "@xrplf/isomorphic": "^1.0.0", "ripple-keypairs": "^2.0.0" } }, + "node_modules/@xrplkit/amount": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@xrplkit/amount/-/amount-1.2.3.tgz", + "integrity": "sha512-poV7ym7EhjAlEGym9Kc3AHuy3uuhVW3qnl2KDr8D4zk7hANj7aG6l1XdtWIAGAgBpHnvPEaOG7+llPRRv1irZw==", + "dependencies": { + "@xrplkit/xfl": "2.0.2" + } + }, + "node_modules/@xrplkit/amount/node_modules/@xrplkit/xfl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@xrplkit/xfl/-/xfl-2.0.2.tgz", + "integrity": "sha512-OUZZzSoXSxYxuhBcz08Wb9Xme71PkFYejk3vq4jPzdXreVhF71ZrM6nSObHFgasC8/srWKoqiS37CnB2Ht3k/g==" + }, + "node_modules/@xrplkit/txmeta": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@xrplkit/txmeta/-/txmeta-1.3.1.tgz", + "integrity": "sha512-iJNtLgXTpbuYBI0VDsXJhi3MHxmsCdWsI3OumETk+21HjxDw1AmPGmVHWu8q8turFth1qzRIAtHjxLHDl307sg==", + "dependencies": { + "@xrplkit/amount": "1.2.3", + "@xrplkit/xfl": "2.0.2" + } + }, + "node_modules/@xrplkit/txmeta/node_modules/@xrplkit/xfl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@xrplkit/xfl/-/xfl-2.0.2.tgz", + "integrity": "sha512-OUZZzSoXSxYxuhBcz08Wb9Xme71PkFYejk3vq4jPzdXreVhF71ZrM6nSObHFgasC8/srWKoqiS37CnB2Ht3k/g==" + }, + "node_modules/@xrplkit/xfl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@xrplkit/xfl/-/xfl-2.1.0.tgz", + "integrity": "sha512-CgRztE77c0hsZ57aPxwQg0vC6gSP/3Hz7cdzUQJSMBTy+b3VK/rGs+rMrmd9CY68jvVyTrhRpq6EgUPDBsywIg==" + }, "node_modules/abab": { "version": "2.0.5", "dev": true, @@ -9825,10 +9856,9 @@ } }, "node_modules/decimal.js": { - "version": "10.3.1", - "dev": true, - "license": "MIT", - "peer": true + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "node_modules/decimal.js-light": { "version": "2.5.1", @@ -23893,7 +23923,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-2.0.0.tgz", "integrity": "sha512-zakENc9A5dlW85uzrmQHrJehymhL59ftggboRNrjxFDJdlNJ6DSE210P3ys/9kL0oVtOzFnTrOPFfxHZeOsA/Q==", - "dev": true, "dependencies": { "@xrplf/isomorphic": "^1.0.0", "bignumber.js": "^9.0.0", @@ -23907,7 +23936,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-2.0.0.tgz", "integrity": "sha512-b5rfL2EZiffmklqZk1W+dvSy97v3V/C7936WxCCgDynaGPp7GE6R2XO7EU9O2LlM/z95rj870IylYnOQs+1Rag==", - "dev": true, "dependencies": { "@noble/curves": "^1.0.0", "@xrplf/isomorphic": "^1.0.0", @@ -27964,7 +27992,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-3.0.0.tgz", "integrity": "sha512-QC+dNx3tvMEn9IrxcXFFa0rWwvBwACkGFNKl+W2miMGYnlgSiIsnjdqwtG2WRs0Pyxs5dd9nBTQHyQ1BPxZ78A==", - "dev": true, "dependencies": { "@scure/bip32": "^1.3.1", "@scure/bip39": "^1.2.1", @@ -27990,11 +28017,20 @@ "websocket": "^1.0.34" } }, + "node_modules/xrpl-tx-path-parser": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/xrpl-tx-path-parser/-/xrpl-tx-path-parser-1.0.5.tgz", + "integrity": "sha512-eQTdv/OKe5MIjOuiYB6FseUtiD8WOqYTmAGfOkHn4DWn233ES/970Yr10QPTrQoWtoA5forroXSiXBse6xxvow==", + "dependencies": { + "@xrplkit/txmeta": "^1.3.1", + "decimal.js": "^10.4.3", + "xrpl": "^3.0.0" + } + }, "node_modules/xrpl/node_modules/cross-fetch": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dev": true, "dependencies": { "node-fetch": "^2.6.12" } @@ -28002,8 +28038,7 @@ "node_modules/xrpl/node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, "node_modules/y18n": { "version": "4.0.3", @@ -31558,7 +31593,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dev": true, "requires": { "@noble/hashes": "1.3.2" } @@ -31665,7 +31699,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", - "dev": true, "requires": { "@noble/curves": "~1.2.0", "@noble/hashes": "~1.3.2", @@ -31676,7 +31709,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", - "dev": true, "requires": { "@noble/hashes": "~1.3.0", "@scure/base": "~1.1.0" @@ -32576,12 +32608,47 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@xrplf/secret-numbers/-/secret-numbers-1.0.0.tgz", "integrity": "sha512-qsCLGyqe1zaq9j7PZJopK+iGTGRbk6akkg6iZXJJgxKwck0C5x5Gnwlb1HKYGOwPKyrXWpV6a2YmcpNpUFctGg==", - "dev": true, "requires": { "@xrplf/isomorphic": "^1.0.0", "ripple-keypairs": "^2.0.0" } }, + "@xrplkit/amount": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@xrplkit/amount/-/amount-1.2.3.tgz", + "integrity": "sha512-poV7ym7EhjAlEGym9Kc3AHuy3uuhVW3qnl2KDr8D4zk7hANj7aG6l1XdtWIAGAgBpHnvPEaOG7+llPRRv1irZw==", + "requires": { + "@xrplkit/xfl": "2.0.2" + }, + "dependencies": { + "@xrplkit/xfl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@xrplkit/xfl/-/xfl-2.0.2.tgz", + "integrity": "sha512-OUZZzSoXSxYxuhBcz08Wb9Xme71PkFYejk3vq4jPzdXreVhF71ZrM6nSObHFgasC8/srWKoqiS37CnB2Ht3k/g==" + } + } + }, + "@xrplkit/txmeta": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@xrplkit/txmeta/-/txmeta-1.3.1.tgz", + "integrity": "sha512-iJNtLgXTpbuYBI0VDsXJhi3MHxmsCdWsI3OumETk+21HjxDw1AmPGmVHWu8q8turFth1qzRIAtHjxLHDl307sg==", + "requires": { + "@xrplkit/amount": "1.2.3", + "@xrplkit/xfl": "2.0.2" + }, + "dependencies": { + "@xrplkit/xfl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@xrplkit/xfl/-/xfl-2.0.2.tgz", + "integrity": "sha512-OUZZzSoXSxYxuhBcz08Wb9Xme71PkFYejk3vq4jPzdXreVhF71ZrM6nSObHFgasC8/srWKoqiS37CnB2Ht3k/g==" + } + } + }, + "@xrplkit/xfl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@xrplkit/xfl/-/xfl-2.1.0.tgz", + "integrity": "sha512-CgRztE77c0hsZ57aPxwQg0vC6gSP/3Hz7cdzUQJSMBTy+b3VK/rGs+rMrmd9CY68jvVyTrhRpq6EgUPDBsywIg==" + }, "abab": { "version": "2.0.5", "dev": true @@ -34648,9 +34715,9 @@ } }, "decimal.js": { - "version": "10.3.1", - "dev": true, - "peer": true + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "decimal.js-light": { "version": "2.5.1" @@ -44266,7 +44333,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-2.0.0.tgz", "integrity": "sha512-zakENc9A5dlW85uzrmQHrJehymhL59ftggboRNrjxFDJdlNJ6DSE210P3ys/9kL0oVtOzFnTrOPFfxHZeOsA/Q==", - "dev": true, "requires": { "@xrplf/isomorphic": "^1.0.0", "bignumber.js": "^9.0.0", @@ -44277,7 +44343,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-2.0.0.tgz", "integrity": "sha512-b5rfL2EZiffmklqZk1W+dvSy97v3V/C7936WxCCgDynaGPp7GE6R2XO7EU9O2LlM/z95rj870IylYnOQs+1Rag==", - "dev": true, "requires": { "@noble/curves": "^1.0.0", "@xrplf/isomorphic": "^1.0.0", @@ -46953,7 +47018,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-3.0.0.tgz", "integrity": "sha512-QC+dNx3tvMEn9IrxcXFFa0rWwvBwACkGFNKl+W2miMGYnlgSiIsnjdqwtG2WRs0Pyxs5dd9nBTQHyQ1BPxZ78A==", - "dev": true, "requires": { "@scure/bip32": "^1.3.1", "@scure/bip39": "^1.2.1", @@ -46971,7 +47035,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dev": true, "requires": { "node-fetch": "^2.6.12" } @@ -46979,8 +47042,7 @@ "eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" } } }, @@ -46993,6 +47055,16 @@ "websocket": "^1.0.34" } }, + "xrpl-tx-path-parser": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/xrpl-tx-path-parser/-/xrpl-tx-path-parser-1.0.5.tgz", + "integrity": "sha512-eQTdv/OKe5MIjOuiYB6FseUtiD8WOqYTmAGfOkHn4DWn233ES/970Yr10QPTrQoWtoA5forroXSiXBse6xxvow==", + "requires": { + "@xrplkit/txmeta": "^1.3.1", + "decimal.js": "^10.4.3", + "xrpl": "^3.0.0" + } + }, "y18n": { "version": "4.0.3", "dev": true, diff --git a/package.json b/package.json index 83aec7b08..a6324de82 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "@vitejs/plugin-react": "^4.2.1", "@xrplf/isomorphic": "^1.0.0-beta.1", "@xrplf/prettier-config": "^1.9.1", + "@xrplkit/txmeta": "^1.3.1", + "@xrplkit/xfl": "^2.1.0", "assert": "^2.1.0", "autoprefixer": "^10.4.17", "axios": "^1.6.5", @@ -54,7 +56,8 @@ "vite-plugin-html": "^3.2.0", "vite-plugin-svgr": "^4.2.0", "vite-tsconfig-paths": "^4.2.0", - "xrpl-client": "^2.1.0" + "xrpl-client": "^2.1.0", + "xrpl-tx-path-parser": "^1.0.5" }, "devDependencies": { "@babel/eslint-parser": "^7.22.6", diff --git a/public/locales/ca-CA/translations.json b/public/locales/ca-CA/translations.json index a4b62daf5..8c4191dd4 100644 --- a/public/locales/ca-CA/translations.json +++ b/public/locales/ca-CA/translations.json @@ -75,6 +75,7 @@ "not_found_default_title": "No s'ha trobat la pàgina", "not_found_check_url": "Si us plau, comproba l'URL", "not_found": "No s'ha trobat", + "breakdown": "Breakdown", "buy": "Compra", "sell": "Ven", "price": "Preu", @@ -525,6 +526,17 @@ "indicate_unl": "indica un validador en una UNL", "transaction_tokens_involved": " i ", "transaction_tokens_swapped": " per ", + "graph_dependent_currency": "*Graph is dependent on the currency used.", + "incomplete_transaction": "Incomplete transaction", + "liquidity_source": "Liquidity Source", + "balance_changes": "Balance Changes", + "direct": "direct", + "dex": "dex", + "amm": "amm", + "rippling": "rippling", + "sold": "sold", + "bought": "bought", + "no_cross": "The offer has not crossed anything yet", "oracle_document_id": null, "provider": null, "last_update_time": null, diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index c7e8e0642..21fa5cf11 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -76,6 +76,7 @@ "not_found_default_title": "Page Not Found", "not_found_check_url": "Please double check your URL", "not_found": "Not found", + "breakdown": "Breakdown", "buy": "Buy", "sell": "Sell", "price": "Price", @@ -525,6 +526,17 @@ "indicate_unl": "indicates a validator on an UNL", "transaction_tokens_involved": " and ", "transaction_tokens_swapped": " for ", + "graph_dependent_currency": "*Graph is dependent on the currency used.", + "incomplete_transaction": "Incomplete transaction", + "liquidity_source": "Liquidity Source", + "balance_changes": "Balance Changes", + "direct": "direct", + "dex": "dex", + "amm": "amm", + "rippling": "rippling", + "sold": "sold", + "bought": "bought", + "no_cross": "The offer has not crossed anything yet", "oracle_document_id": "Oracle Document ID", "provider": "Provider", "last_update_time": "Last Update Time", diff --git a/src/containers/Transactions/BreakDownTab/breakDownTab.scss b/src/containers/Transactions/BreakDownTab/breakDownTab.scss new file mode 100644 index 000000000..16acfc3d3 --- /dev/null +++ b/src/containers/Transactions/BreakDownTab/breakDownTab.scss @@ -0,0 +1,143 @@ +@import '../../shared/css/variables'; + +.breakdown-body { + margin-top: 20px; + color: $black-40; + font-size: 12px; + letter-spacing: 0px; +} + +.detail-section { + padding: 5px 15px; + border-bottom: 1px solid $black-70; + margin: 0px 0px 30px; + + &:last-child { + border: none; + } + + &.no-border { + border: none; + } +} + +.title { + margin-bottom: 4px; + color: $white; + font-size: 16px; + text-transform: capitalize; + @include bold; + + span { + margin-left: 8px; + font-size: 10px; + line-height: 18px; + } +} + +.badge { + padding: 5px; + padding-top: 3px; + border-radius: 5px; + color: $black; + + &.amm { + background-color: #ff198b; + + } + + &.dex { + background-color: #19a3ff; + } + + &.direct { + background-color: #9a52ff; + } + + &.rippling { + background-color: #32e685; + } +} + +.flex { + display: flex; +} + +.rectangle-container { + min-height: 200px; + // margin-top: 100px; +} + +:root{ + --max-height: 80%; +} + +// Build the cylinder +$circle-height: 9px; + +.rectangle-row { + position: relative; + display: block; + height: 50px; +} + +.rectangle { + position: relative; + left: 0; + display: block; + width: $circle-height; + height: 30px; + box-sizing: border-box; + border-top: solid 2px var(--cylinder-color); + border-bottom: solid 2px var(--cylinder-color); + margin-top: 10px; + margin-left: 10px; + animation: grow 600ms 1.5s ease both; + background: linear-gradient(to right, #271527, var(--cylinder-color)); + // transform: rotate(90deg); +} + +.margin-text { + position: relative; + display: block; + border-bottom: dashed 1px $white; + font-weight: bold; + text-align: right; + text-transform: capitalize; + // transform: rotate(-180deg); + // writing-mode: vertical-rl; +} + +.rectangle::before, +.rectangle::after { + position: absolute; + top: -2px; + right: -3px; + width: $circle-height; + height: 30px; + box-sizing: border-box; + border: solid 2px var(--cylinder-color); + border-radius: 50%; + content: ''; +} + +.rectangle::before { + left: calc(-1 * $circle-height / 2); + background: #271527; +} + +.rectangle::after { + z-index: -1; + right: calc(-1 * $circle-height / 2); + background: var(--cylinder-color); +} + +@keyframes grow { + 0% { + width: 0; + } + + 100% { + width: calc(var(--max-height) * var(--percent-value)); // Change this to make things ✨ + } +} diff --git a/src/containers/Transactions/BreakDownTab/index.tsx b/src/containers/Transactions/BreakDownTab/index.tsx new file mode 100644 index 000000000..697caaa14 --- /dev/null +++ b/src/containers/Transactions/BreakDownTab/index.tsx @@ -0,0 +1,312 @@ +import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' +import pathParser from 'xrpl-tx-path-parser' +import { Account } from '../../shared/components/Account' +import { Amount } from '../../shared/components/Amount' +import { formatAmount } from '../../../rippled/lib/txSummary/formatAmount' +import './breakDownTab.scss' + +export const BreakDownTab: FC<{ data: any }> = ({ data }) => { + const { t } = useTranslation() + const [selectedView, setView] = useState('source') + + const hexToString = (hex: string) => { + let string: string = '' + for (let i = 0; i < hex.length; i += 2) { + const part = hex.substring(i, i + 2) + const code = parseInt(part, 16) + if (!isNaN(code) && code !== 0) { + string += String.fromCharCode(code) + } + } + return string + } + const BalanceChange: FC<{ + data: any + label: Boolean + type: String + }> = ({ data, label, type }) => { + const balances: string[] = [] + data.forEach((change: any, index: any) => { + const amount = change + amount.value *= -1 + + let balanceLabel: string = + type === 'direct' + ? 'recieved' + : amount.value < 0 + ? t('sold') + : t('bought') + if (type === 'direct') { + amount.value *= -1 + } + if (!label) { + balanceLabel = '' + } + balances.push( +
  • + {balanceLabel}{' '} + +
  • , + ) + }) + + return
      {balances}
    + } + // eslint-disable-next-line react/no-unstable-nested-components + const Transaction: FC<{ parsed: any; account: any }> = ({ + parsed, + account, + }) => { + const changes: string[] = [] + parsed.accountBalanceChanges.forEach((change, index) => { + if (account !== change.account) { + let type: string = '' + let spanClass: string = '' + if (change.isDirect) { + type = 'direct' + spanClass = 'badge direct' + } + if (change.isAMM) { + type = 'amm' + spanClass = 'badge amm' + } + if (change.isOffer) { + type = 'dex' + spanClass = 'badge dex' + } + if (change.isRippling) { + type = 'rippling' + spanClass = 'badge rippling' + } + changes.push( +

    + {type}{' '} + +

    , + + , + ) + } + }) + + return
    {changes}
    + } + + // eslint-disable-next-line react/no-unstable-nested-components + const Cylindars: FC<{ parsed: any; account: any }> = ({ + parsed, + account, + }) => { + const sum = { + amm: 0, + rippling: 0, + dex: 0, + direct: 0, + total: 0, + } + parsed.accountBalanceChanges.forEach((change) => { + if (account !== change.account) { + change.balances.forEach((balance) => { + if ( + (selectedView === 'source' && + parsed.destinationAmount.currency === balance.currency) || + (selectedView === 'destination' && + parsed.sourceAmount.currency === balance.currency) + ) { + if (change.isAMM) { + sum.amm += balance.value * -1 + sum.total += balance.value * -1 + } + if (change.isOffer) { + sum.dex += balance.value * -1 + sum.total += balance.value * -1 + } + if (change.isDirect) { + sum.direct += balance.value * 1 + sum.total += balance.value * 1 + } + if (change.isRippling && balance.value * 1 > 0) { + sum.rippling += + selectedView === 'destination' + ? balance.value * -1 + : balance.value * 1 + } + } + }) + } + }) + + const NON_STANDARD_CODE_LENGTH = 40 + const LP_TOKEN_IDENTIFIER = '03' + const destinationCurrencyCode = + parsed.destinationAmount.currency?.length === NON_STANDARD_CODE_LENGTH && + parsed.destinationAmount.currency?.substring(0, 2) !== LP_TOKEN_IDENTIFIER + ? hexToString(parsed.destinationAmount.currency) + : parsed.destinationAmount.currency + const sourceCurrencyCode = + parsed.sourceAmount.currency?.length === NON_STANDARD_CODE_LENGTH && + parsed.sourceAmount.currency?.substring(0, 2) !== LP_TOKEN_IDENTIFIER + ? hexToString(parsed.sourceAmount.currency) + : parsed.sourceAmount.currency + + return ( +
    + {/*
    + AMM: {sum.amm} {(sum.amm / sum.total) * 100} +
    +
    + RIPPLING: {sum.rippling} {(sum.rippling / sum.total) * 100} +
    +
    + DEX: {sum.dex} {(sum.dex / sum.total) * 100} +
    +
    + DIRECT: {sum.direct} {(sum.direct / sum.total) * 100} +
    +
    TOTAL: {sum.total}
    */} + + +

    {t('graph_dependent_currency')}

    +
    +
    + + {t('amm')} ({Math.round((sum.amm / sum.total) * 100)}%) + +
    +
    +
    + + {t('rippling')} ({Math.round((sum.rippling / sum.total) * 100)}%) + +
    +
    +
    + + {t('dex')} ({Math.round((sum.dex / sum.total) * 100)}%) + +
    +
    +
    + + {t('direct')} ({Math.round((sum.direct / sum.total) * 100)}%) + +
    +
    +
    + + {t('total')}{' '} + + +
    +
    + ) + } + + const renderData = () => { + if (data === undefined || data.tx === undefined) { + return null + } + + const mutated = data + mutated.tx.meta = data.meta + try { + const parsed = pathParser(mutated.tx) + if ( + parsed.sourceAmount.value === '0' && + mutated.tx.TransactionType === 'OfferCreate' + ) { + return

    {t('no_cross')}

    + } + return ( +
    +
    +
    {mutated.tx.TransactionType}
    +
    + Source: +
    +
    + +
    +
    +
    + Destination: +
    +
    + +
    +
    +
    +
    {t('liquidity_source')}
    + +
    +
    +
    {t('balance_changes')}
    + +
    + {/* < + // debug.... + div className="detail-section"> +
    {JSON.stringify(parsed, null, 2)}
    +
    */} +
    + ) + } catch (e) { + // console.log(e) + return

    {t('incomplete_transaction')}

    + } + } + + return
    {renderData()}
    +} diff --git a/src/containers/Transactions/index.tsx b/src/containers/Transactions/index.tsx index b5de8319f..3fb30d601 100644 --- a/src/containers/Transactions/index.tsx +++ b/src/containers/Transactions/index.tsx @@ -9,6 +9,7 @@ import { Tabs } from '../shared/components/Tabs' import { NOT_FOUND, BAD_REQUEST, HASH_REGEX, CTID_REGEX } from '../shared/utils' import { SimpleTab } from './SimpleTab' import { DetailTab } from './DetailTab' +import { BreakDownTab } from './BreakDownTab' import './transaction.scss' import { AnalyticsFields, useAnalytics } from '../shared/analytics' import SocketContext from '../shared/SocketContext' @@ -114,7 +115,12 @@ export const Transaction = () => { } function renderTabs() { - const tabs = ['simple', 'detailed', 'raw'] + const tabs = + data === undefined || + data.raw.tx.TransactionType === 'OfferCreate' || + data.raw.tx.TransactionType === 'Payment' + ? ['simple', 'breakdown', 'detailed', 'raw'] + : ['simple', 'detailed', 'raw'] const mainPath = buildPath(TRANSACTION_ROUTE, { identifier }) return } @@ -125,6 +131,9 @@ export const Transaction = () => { let body switch (tab) { + case 'breakdown': + body = + break case 'detailed': body = break diff --git a/src/containers/Transactions/test/BreakDownTab.test.tsx b/src/containers/Transactions/test/BreakDownTab.test.tsx new file mode 100644 index 000000000..928f7ddf7 --- /dev/null +++ b/src/containers/Transactions/test/BreakDownTab.test.tsx @@ -0,0 +1,101 @@ +import { mount } from 'enzyme' +import { I18nextProvider } from 'react-i18next' +import { BrowserRouter as Router } from 'react-router-dom' + +import EnableAmendment from './mock_data/EnableAmendment.json' +import Payment from './mock_data/PaymentBreakdown.json' +import { BreakDownTab } from '../BreakDownTab' +import i18n from '../../../i18n/testConfig' + +describe('BreakdownTab container', () => { + const createWrapper = (tx) => + mount( + + + + + , + ) + + it('renders EnableAmendment without crashing', () => { + const wrapper = createWrapper(EnableAmendment) + wrapper.unmount() + }) + + it('renders breakdown tab information', () => { + const wrapper = createWrapper(Payment) + + // console.log(Payment) + + expect(wrapper.find('.breakdown-body').length).toBe(1) + expect(wrapper.find('.detail-section').length).toBe(3) + + expect(wrapper.find('.source-account').length).toBe(1) + expect(wrapper.find('.source-amount').length).toBe(1) + expect(wrapper.find('.destination-account').length).toBe(1) + expect(wrapper.find('.destination-amount').length).toBe(1) + + expect( + wrapper.contains(
    liquidity_source
    ), + ).toBe(true) + + expect(wrapper.contains()).toBe( + true, + ) + expect( + wrapper.contains(), + ).toBe(true) + + expect(wrapper.contains(

    graph_dependent_currency

    )).toBe(true) + + expect( + wrapper.contains(amm (100%)), + ).toBe(true) + expect( + wrapper.contains(rippling (100%)), + ).toBe(true) + expect( + wrapper.contains(dex (0%)), + ).toBe(true) + expect( + wrapper.contains(direct (0%)), + ).toBe(true) + + expect(wrapper.contains(
    balance_changes
    )).toBe( + true, + ) + + expect(wrapper.contains(dex)).toBe(false) + expect(wrapper.contains(direct)).toBe( + false, + ) + expect( + wrapper.contains(rippling), + ).toBe(true) + expect(wrapper.contains(amm)).toBe(true) + + const balanceChanges = wrapper.find('.balance-changes') + expect(balanceChanges.find('BalanceChange')).toHaveLength(2) + // console.log(wrapper.debug()) + + expect( + balanceChanges.contains( + 26.79746261, + ), + ).toBe(true) + expect( + balanceChanges.contains( + -26.877855, + ), + ).toBe(true) + expect( + balanceChanges.contains( + -50.624692, + ), + ).toBe(true) + + wrapper.unmount() + }) + + // console.log(balanceChanges.debug()) +}) diff --git a/src/containers/Transactions/test/Transaction.test.tsx b/src/containers/Transactions/test/Transaction.test.tsx index 774d3da56..6f65b23e5 100644 --- a/src/containers/Transactions/test/Transaction.test.tsx +++ b/src/containers/Transactions/test/Transaction.test.tsx @@ -135,10 +135,11 @@ describe('Transaction container', () => { ) expect(summary.contains()).toBe(true) expect(wrapper.find('.tabs').length).toBe(1) - expect(wrapper.find('a.tab').length).toBe(3) + expect(wrapper.find('a.tab').length).toBe(4) expect(wrapper.find('a.tab').at(0).props().title).toBe('simple') - expect(wrapper.find('a.tab').at(1).props().title).toBe('detailed') - expect(wrapper.find('a.tab').at(2).props().title).toBe('raw') + expect(wrapper.find('a.tab').at(1).props().title).toBe('breakdown') + expect(wrapper.find('a.tab').at(2).props().title).toBe('detailed') + expect(wrapper.find('a.tab').at(3).props().title).toBe('raw') expect(wrapper.find('a.tab.selected').text()).toEqual('simple') wrapper.unmount() }) diff --git a/src/containers/Transactions/test/mock_data/PaymentBreakdown.json b/src/containers/Transactions/test/mock_data/PaymentBreakdown.json new file mode 100644 index 000000000..6336382c0 --- /dev/null +++ b/src/containers/Transactions/test/mock_data/PaymentBreakdown.json @@ -0,0 +1,286 @@ +{ + "tx": { + "Account": "r3GphWJaShhd1RNFnCaGMVyMawYcKHXCvo", + "Amount": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "1000000000000000e2" + }, + "DeliverMax": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "1000000000000000e2" + }, + "DeliverMin": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "25.639817695" + }, + "Destination": "r3GphWJaShhd1RNFnCaGMVyMawYcKHXCvo", + "Fee": "12", + "Flags": 131072, + "LastLedgerSequence": 88214940, + "Memos": [ + { + "Memo": { + "MemoData": "53574150205472616E73616374696F6E20696E69746961746564207669612058506D61726B65742E636F6D" + } + } + ], + "SendMax": "50624692", + "Sequence": 88214147, + "SigningPubKey": "02F00DADAD94C694359D9FB84163D390CDC418BDA6313A139945C09D2277B39B55", + "SourceTag": 20221212, + "TransactionType": "Payment", + "TxnSignature": "30450221009510B1D5C84A82D870C2827F6A82E6E8AE930778A75D2623D1E8D8280368D6D602207B74F93375275265AB59988748DD1D34F758960EE8FEE5C572A8E84A71205C0F", + "ctid": "C5420D9400120000", + "date": 1716579292000, + "hash": "undefined", + "inLedger": "undefined", + "ledger_index": "undefined", + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Account": "r3GphWJaShhd1RNFnCaGMVyMawYcKHXCvo", + "Balance": "15877383", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 88214148 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "4ACE281E26640B951CB4A23768CA6C7E1B0F602053B3C86A653847FE5A5AA1ED", + "PreviousFields": { "Balance": "66502087", "Sequence": 88214147 }, + "PreviousTxnID": "6947EB21D0A745A96F9B2F9B5BABFFE43CE66AF8488365DBC112BC38F8FAC0EA", + "PreviousTxnLgrSeq": 88214779 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-26.79746261485543" + }, + "Flags": 2228224, + "HighLimit": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "r3GphWJaShhd1RNFnCaGMVyMawYcKHXCvo", + "value": "775905" + }, + "HighNode": "0", + "LowLimit": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "0" + }, + "LowNode": "14f" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "5F1DFBA426DB53A57ECEFE3EAEB804107DA1B899D4A90C794E577E5082D7C125", + "PreviousFields": { + "Balance": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0" + } + }, + "PreviousTxnID": "6947EB21D0A745A96F9B2F9B5BABFFE43CE66AF8488365DBC112BC38F8FAC0EA", + "PreviousTxnLgrSeq": 88214779 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "AMMID": "383669860F36CDD7BF543C0711DD523E35F60ACA22C8AAD8FDDBB2632C4C5821", + "Account": "rGHt6LT5v9DVaEAmFzj5ciuxuj41ZjLofs", + "Balance": "534441055888", + "Flags": 26214400, + "OwnerCount": 1, + "Sequence": 86795379 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "AEAC617A4934A4E1029581A159AF8EB3356E4DE3C52324D9D5A73C00B8828BCF", + "PreviousFields": { "Balance": "534390431196" }, + "PreviousTxnID": "1FADA966F7F1E8465E17DE2F83216663BE8360B172AC3A0D13FC81757C64AC42", + "PreviousTxnLgrSeq": 88214747 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-284983.085646821" + }, + "Flags": 16908288, + "HighLimit": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rGHt6LT5v9DVaEAmFzj5ciuxuj41ZjLofs", + "value": "0" + }, + "HighNode": "0", + "LowLimit": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "0" + }, + "LowNode": "10a" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "CDE55A8290DF221A643F51798BE6294A69384066D1ECFB7C2B9BEE2004B38D63", + "PreviousFields": { + "Balance": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-285009.9635018237" + } + }, + "PreviousTxnID": "1FADA966F7F1E8465E17DE2F83216663BE8360B172AC3A0D13FC81757C64AC42", + "PreviousTxnLgrSeq": 88214747 + } + } + ], + "DeliveredAmount": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "26.79746261485543" + }, + "TransactionIndex": 18, + "TransactionResult": "tesSUCCESS", + "delivered_amount": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "26.79746261485543" + } + }, + "validated": "undefined", + "metaData": "undefined", + "status": "undefined" + }, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Account": "r3GphWJaShhd1RNFnCaGMVyMawYcKHXCvo", + "Balance": "15877383", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 88214148 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "4ACE281E26640B951CB4A23768CA6C7E1B0F602053B3C86A653847FE5A5AA1ED", + "PreviousFields": { "Balance": "66502087", "Sequence": 88214147 }, + "PreviousTxnID": "6947EB21D0A745A96F9B2F9B5BABFFE43CE66AF8488365DBC112BC38F8FAC0EA", + "PreviousTxnLgrSeq": 88214779 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-26.79746261485543" + }, + "Flags": 2228224, + "HighLimit": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "r3GphWJaShhd1RNFnCaGMVyMawYcKHXCvo", + "value": "775905" + }, + "HighNode": "0", + "LowLimit": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "0" + }, + "LowNode": "14f" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "5F1DFBA426DB53A57ECEFE3EAEB804107DA1B899D4A90C794E577E5082D7C125", + "PreviousFields": { + "Balance": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0" + } + }, + "PreviousTxnID": "6947EB21D0A745A96F9B2F9B5BABFFE43CE66AF8488365DBC112BC38F8FAC0EA", + "PreviousTxnLgrSeq": 88214779 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "AMMID": "383669860F36CDD7BF543C0711DD523E35F60ACA22C8AAD8FDDBB2632C4C5821", + "Account": "rGHt6LT5v9DVaEAmFzj5ciuxuj41ZjLofs", + "Balance": "534441055888", + "Flags": 26214400, + "OwnerCount": 1, + "Sequence": 86795379 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "AEAC617A4934A4E1029581A159AF8EB3356E4DE3C52324D9D5A73C00B8828BCF", + "PreviousFields": { "Balance": "534390431196" }, + "PreviousTxnID": "1FADA966F7F1E8465E17DE2F83216663BE8360B172AC3A0D13FC81757C64AC42", + "PreviousTxnLgrSeq": 88214747 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-284983.085646821" + }, + "Flags": 16908288, + "HighLimit": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rGHt6LT5v9DVaEAmFzj5ciuxuj41ZjLofs", + "value": "0" + }, + "HighNode": "0", + "LowLimit": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "0" + }, + "LowNode": "10a" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "CDE55A8290DF221A643F51798BE6294A69384066D1ECFB7C2B9BEE2004B38D63", + "PreviousFields": { + "Balance": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-285009.9635018237" + } + }, + "PreviousTxnID": "1FADA966F7F1E8465E17DE2F83216663BE8360B172AC3A0D13FC81757C64AC42", + "PreviousTxnLgrSeq": 88214747 + } + } + ], + "DeliveredAmount": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "26.79746261485543" + }, + "TransactionIndex": 18, + "TransactionResult": "tesSUCCESS", + "delivered_amount": { + "currency": "5553444300000000000000000000000000000000", + "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "value": "26.79746261485543" + } + }, + "hash": "2C9C11811DFC51263267F7D5AA81C807876B905A99AE52321C582D8E186B7E12", + "ledger_index": 88214932, + "date": 1716579292000 +}