Compare commits

...

222 Commits

Author SHA1 Message Date
akcodez
3873ae0085 design QA and changes with Design team 2026-01-15 11:23:24 -08:00
akcodez
f62c99b387 fix tablet 2026-01-14 14:07:59 -08:00
akcodez
7383bf8044 add back bundle 2026-01-14 14:05:49 -08:00
akcodez
e12b1bf8dc complete pattern and showcase 2026-01-14 14:04:58 -08:00
akcodez
0b52e5f747 Add FeatureTwoColumn pattern and button enhancements
- Introduced the FeatureTwoColumn pattern for showcasing features in a two-column layout, supporting multiple color themes and responsive design.
- Implemented button behavior based on the number of links, with configurations for 1 to 5 links.
- Added `forceColor` prop to Button component to maintain button color across light and dark themes, particularly useful for colored backgrounds.
- Updated styles and documentation for both the FeatureTwoColumn pattern and Button component to reflect new features and usage guidelines.
2026-01-14 14:04:17 -08:00
akcodez
161e4305e6 add bun lock as build container is using bun 2026-01-14 10:30:58 -08:00
akcodez
8e7d7ecba1 resolve build erros 2026-01-14 10:27:07 -08:00
Aria Keshmiri
32f6a1de2d Merge pull request #3436 from XRPLF/go/feat/pattern/smallTiles
[feat][site] - Add SmallTilesSection component and styles
2026-01-14 10:22:42 -08:00
akcodez
e3459b336e merge main branch in rebundle css 2026-01-14 10:21:36 -08:00
Aria Keshmiri
1a1e1b30a6 Merge pull request #3445 from XRPLF/brand/merge-master-in
Brand/merge master in
2026-01-14 10:19:02 -08:00
akcodez
5eb98cacac merge master in, resolve conflicts 2026-01-14 10:06:48 -08:00
gabriel-ortiz
9a7f9479d4 Merge branch 'xrpl-brand-update-2026' into go/feat/pattern/smallTiles 2026-01-13 16:12:21 -08:00
Aria Keshmiri
c21785855f Merge pull request #3443 from XRPLF/brand-update-header
Brand update header
2026-01-13 13:19:10 -08:00
akcodez
887e1f38f5 Enhance Navbar and Submenu components for improved accessibility and keyboard navigation
- Added keyboard navigation support to NavItems, allowing users to close submenus with the Escape key and toggle with Enter/Space.
- Implemented onClose callback in Submenu components to facilitate submenu closure.
- Updated Navbar to handle submenu closing for keyboard navigation.
- Adjusted styles for better layout and responsiveness in submenus.
2026-01-13 12:30:51 -08:00
Aria Keshmiri
79294acb05 Merge pull request #3434 from XRPLF/pattern/header-hero-split-media
Pattern/header hero split media
2026-01-13 12:13:30 -08:00
akcodez
5cb06eaf86 merge xrpl branch in, resolve comments 2026-01-13 12:11:34 -08:00
Calvin Jhunjhuwala
fe0057aa9f css clean up 2026-01-13 12:07:29 -08:00
Calvin Jhunjhuwala
1e3ca30ace fixing per comments 2026-01-13 12:07:29 -08:00
Calvin Jhunjhuwala
66356984b4 added light image variant for example page 2026-01-13 12:07:29 -08:00
Calvin Jhunjhuwala
1fcc294ffb fixing button spacing + image background 2026-01-13 12:07:29 -08:00
Calvin Jhunjhuwala
0d0fc38344 cleaning up css 2026-01-13 12:07:28 -08:00
Calvin Jhunjhuwala
cd1759332d fixing errors 2026-01-13 12:07:28 -08:00
Calvin Jhunjhuwala
62d23ce36b updated and working showcase for call out media 2026-01-13 12:07:28 -08:00
Calvin Jhunjhuwala
678e168029 adding a README 2026-01-13 12:07:28 -08:00
akcodez
31a9cac20b merge main in, resolve comments 2026-01-13 12:06:52 -08:00
akcodez
79f40fb2c6 fix card 2026-01-13 12:05:49 -08:00
akcodez
9738402921 Focus should not activate button hover - cardimage 2026-01-13 12:05:49 -08:00
akcodez
e064ce02d0 Card - image : hovering card should not activate button hover state 2026-01-13 12:05:49 -08:00
akcodez
862a5c42d8 fix disasbled dark mode link color 2026-01-13 12:05:49 -08:00
akcodez
d29a5083d1 Refactor button typography for responsiveness and consistency
- Updated Button component styles to use responsive typography mixins for better adaptability across devices.
- Removed redundant media queries and adjusted font sizes and line heights for both button types.
- Ensured margin resets to maintain consistent spacing in various contexts.
- Enhanced CSS for improved readability and maintainability.
2026-01-13 12:05:49 -08:00
akcodez
60fc8eb22e Update CardImage and TileLogo components for improved responsiveness and layout
- Adjusted column spans in CardImage showcase to support a 4-column layout on larger screens.
- Updated TileLogo showcase to reflect changes in aspect ratios and responsive behavior, ensuring proper display across different screen sizes.
- Enhanced SCSS styles for both components to maintain consistency in appearance and functionality in light and dark themes.
- Refined documentation to clarify usage and responsive design principles for both CardImage and TileLogo components.
2026-01-13 12:05:49 -08:00
akcodez
ec4ef6e9fc Update HeaderHeroSplitMedia styles for aspect ratio and title alignment
- Changed tablet aspect ratio from 2:1 to 16:9 for better compatibility with an 8-column grid.
- Adjusted title alignment in the title-only layout from center to top for improved visual hierarchy.
2026-01-13 12:02:11 -08:00
akcodez
42552e4d24 add translations 2026-01-13 11:56:47 -08:00
akcodez
e46e4006d5 Refactor submenu styles for improved layout and spacing
- Updated .bds-submenu__section to use justify-content: space-between for better distribution of space between header and items.
- Removed gap property and set margin-top to 0 for .bds-submenu__tier2 to enhance visual alignment in Use Cases section.
2026-01-13 10:25:35 -08:00
akcodez
5cf22174dc Add LanguageDropdown component to Navbar and enhance LanguagePill functionality
- Introduced LanguageDropdown for improved language selection in Navbar and MobileMenu.
- Updated LanguagePill to display the current language dynamically and manage dropdown state.
- Enhanced styles for language-related components to support open/close states and transitions.
- Added mobile-specific dropdown positioning and styles for better user experience.
2026-01-13 10:20:37 -08:00
akcodez
7fd39abb2b Integrate search functionality into Navbar and MobileMenu components, utilizing Redocly's search dialog. Update AlertBanner to prevent hydration mismatch with null initial state for displayDate. Adjust NavControls and NetworkSubmenu to handle theme detection more effectively. 2026-01-13 10:05:02 -08:00
Calvin
d7e042bdb6 Merge pull request #3437 from XRPLF/layout/callout-media-banner
Layout/callout media banner
2026-01-13 09:40:31 -08:00
akcodez
9006dc3812 Refactor Navbar component structure by modularizing subcomponents and removing legacy exports for improved maintainability and clarity. 2026-01-13 09:33:25 -08:00
gabriel-ortiz
f3bef3784f Add theme mode mixin and update CardIcon focus styles
- Introduced a new SCSS mixin for theme mode that applies styles based on light or dark themes, with validation for mode parameters.
- Updated CardIcon styles to maintain background color on focus for both light and green themes, enhancing accessibility and user experience.
- Removed unnecessary import from SmallTilesSection styles to streamline the codebase.
2026-01-12 21:02:29 -08:00
Calvin Jhunjhuwala
b8286bf6b4 css clean up 2026-01-12 19:30:21 -08:00
Rome Reginelli
f7f5a2e6cf Merge pull request #3358 from XRPLF/timed_escrow_tutorial_linear
Update Escrow tutorials in new format
2026-01-12 15:41:25 -08:00
Calvin Jhunjhuwala
3feb69a1da fixing per comments 2026-01-12 14:28:57 -08:00
akcodez
e94be3ca20 PERFECT THE ANIMATION 2026-01-12 13:01:19 -08:00
akcodez
4776c45c33 Enhance Navbar with XRP Ledger text animation and update styles for dark mode and submenu transitions 2026-01-12 12:55:28 -08:00
akcodez
1278b1aca9 add proper icons 2026-01-12 11:25:24 -08:00
oeggert
5f40b9633e Merge pull request #3403 from XRPLF/mvadari-patch-1
Add `hash` request parameter to `server_definitions`
2026-01-12 10:26:35 -08:00
Mayukha Vadari
dfc84c21cb Merge branch 'master' into mvadari-patch-1 2026-01-12 13:24:03 -05:00
akcodez
da529fd71e add arrow moving logic 2026-01-12 10:05:17 -08:00
akcodez
e467e27448 add dark mode styles 2026-01-12 09:59:32 -08:00
Maria Shodunke
64b5a727e5 Merge pull request #3419 from XRPLF/update-dia-blog-post
Update DIA Oracles blog post
2026-01-12 10:05:46 +00:00
Calvin Jhunjhuwala
de84fa25a8 added light image variant for example page 2026-01-09 15:50:48 -08:00
Calvin Jhunjhuwala
b4ddfa7955 fixing button spacing + image background 2026-01-09 15:29:22 -08:00
akcodez
ce75b4388c minor qa changes 2026-01-09 12:58:07 -08:00
Rome Reginelli
8e325bf3bb Merge pull request #3440 from lmaisons/patch-2
Update reserve requirements in genesis ledger documentation
2026-01-09 09:30:25 -08:00
Luc des Trois Maisons
faf2a4fa4b Update reserve requirements in genesis ledger documentation
Taken from latest release at:
https://github.com/XRPLF/rippled/blob/3.0.0/src/xrpld/core/Config.h#L60-L78
2026-01-09 12:26:26 -05:00
Maria Shodunke
55c12bac85 Merge pull request #3380 from XRPLF/minor-get-started-improvements
Add minor improvements for Javascript code walkthrough
2026-01-09 10:55:02 +00:00
Rome Reginelli
0bee628274 Merge pull request #3435 from Silkjaer/patch-1
Replace 'XRP Ledger Foundation' with 'InFTF'
2026-01-08 17:12:10 -08:00
mDuo13
12b60d17e2 Address more of @maria-robobug's review comments 2026-01-08 17:09:12 -08:00
mDuo13
54ccc38d1a Escrow tutorials: link xrpl.js getting started in prereqs 2026-01-08 17:09:12 -08:00
Rome Reginelli
406741663c Apply suggestions from @maria-robobug review
Co-authored-by: Maria Shodunke <maria-robobug@users.noreply.github.com>
2026-01-08 17:09:12 -08:00
mDuo13
66652d1dab Refactor Escrow "how-to" tutorials into new format
- Update "Send a Time-Held Escrow" to "Send a Timed Escrow" with new
  code samples in JS and Python.
- Update "Send a Conditionally-Held Escrow" to "Send a Conditional
  Escrow" with new code samples in JS and Python.
- Update "Cancel an Expired Escrow" and "Look Up Escrows" with new code
  samples.
- Remove translations of the old tutorials, which are substantially
  different from the new tutorials.
- Remove websocket API examples that were used in the old tutorials.
- Remove the "Use Escrow as a Smart Contract" article, which was an
  awkward mix between a use case and a tutorial.
- Update sidebars and redirects for the removed and renamed tutorials.
2026-01-08 17:09:11 -08:00
Calvin Jhunjhuwala
46add22436 cleaning up css 2026-01-08 16:41:47 -08:00
Calvin Jhunjhuwala
93ca38ed76 fixing errors 2026-01-08 15:24:04 -08:00
Calvin Jhunjhuwala
e0430b9899 updated and working showcase for call out media 2026-01-08 14:08:51 -08:00
akcodez
5df5f38e83 fix animation 2026-01-08 13:18:17 -08:00
akcodez
2b15495835 udpate animations 2026-01-08 13:14:36 -08:00
gabriel-ortiz
ab366c79ef Add warning for empty cards in SmallTilesSection
- Implemented a console warning in the SmallTilesSection component to notify when no cards are provided, improving debugging and user feedback.
2026-01-08 11:29:01 -08:00
akcodez
1074670da7 fix nav heights 2026-01-08 11:27:42 -08:00
akcodez
1a2cb105f3 add all submenus 2026-01-08 11:23:41 -08:00
gabriel-ortiz
3badef78c1 Add SmallTilesSection component and styles
- Introduced the SmallTilesSection component for displaying multiple CardIcon components in a responsive grid layout.
- Implemented SCSS styles for the SmallTilesSection, including responsive grid behavior and spacing adjustments.
- Added documentation for the SmallTilesSection component, detailing usage, props, and examples.
- Updated main stylesheet to include SmallTilesSection styles.
2026-01-08 10:09:48 -08:00
Calvin Jhunjhuwala
a1f4c82e3a adding a README 2026-01-07 16:30:43 -08:00
Calvin Jhunjhuwala
478f5784ee updating callout media banner, styling for extra width 2026-01-07 16:28:21 -08:00
akcodez
471bf7f193 add basis for navbar 2026-01-07 13:48:30 -08:00
Thomas Silkjær
f853d9b38c Replace 'XRP Ledger Foundation' with 'InFTF'
Please replace XRP Ledger Foundation with InFTF. Thanks :)
2026-01-07 11:10:59 +01:00
akcodez
f346a80ce0 Refactor HeaderHeroSplitMedia styles for tablet and accent surfaces
- Updated spacing variables for tablet view to enhance layout consistency.
- Introduced accent-specific spacing for title and description groups on tablet and desktop.
- Adjusted margin and gap values for improved responsiveness and visual hierarchy.
2026-01-06 13:28:20 -08:00
akcodez
e849cc95b9 Update HeaderHeroSplitMedia styles for improved color consistency and dark mode support
- Refactored color variables to use design tokens for better maintainability.
- Ensured consistent text colors for titles and descriptions in both light and dark modes.
- Adjusted color values for better contrast and readability across themes.
2026-01-06 13:20:09 -08:00
akcodez
0bff1aab4c Add HeaderHeroSplitMedia styles and adjust typography margins
- Introduced new styles for the HeaderHeroSplitMedia component, including layout, typography, and responsive design adjustments.
- Updated margin-bottom for paragraph and label-r elements to enhance spacing consistency.
2026-01-06 13:15:12 -08:00
oeggert
84c1a5de16 Merge pull request #3426 from XRPLF/update-ledger_data
update ledger_data page
2026-01-06 12:35:01 -08:00
Aria Keshmiri
e6aa704841 Merge pull request #3431 from XRPLF/design-qa-review
Design qa review
2026-01-06 10:43:41 -08:00
Maria Shodunke
235fc80c2d Apply suggestions from code review
Co-authored-by: Amarantha Kulkarni <amarantha-k@users.noreply.github.com>
2026-01-06 11:09:08 +00:00
akcodez
9415ae085a fix card 2026-01-05 13:10:51 -08:00
akcodez
4cb232a068 Focus should not activate button hover - cardimage 2026-01-05 13:01:02 -08:00
akcodez
35958ebede Card - image : hovering card should not activate button hover state 2026-01-05 12:59:59 -08:00
akcodez
688ac5dc91 fix disasbled dark mode link color 2026-01-05 12:54:05 -08:00
Aria Keshmiri
e298a45902 Merge pull request #3430 from XRPLF/fix-typography-010526
fixing typography for label-r
2026-01-05 12:53:31 -08:00
akcodez
176e187c6a Refactor button typography for responsiveness and consistency
- Updated Button component styles to use responsive typography mixins for better adaptability across devices.
- Removed redundant media queries and adjusted font sizes and line heights for both button types.
- Ensured margin resets to maintain consistent spacing in various contexts.
- Enhanced CSS for improved readability and maintainability.
2026-01-05 12:44:14 -08:00
Calvin Jhunjhuwala
15046f431e fixing typography for label-r 2026-01-05 12:44:11 -08:00
akcodez
ecd4a1bb66 Merge remote-tracking branch 'origin/xrpl-brand-update-2026' into design-qa-review 2026-01-05 12:22:06 -08:00
Calvin
3fbed79209 Merge pull request #3421 from XRPLF/fix-bds-session-121125
Fix typography
2026-01-05 12:07:55 -08:00
Aria Keshmiri
fc472a4f77 Merge pull request #3417 from XRPLF/link-rename
rename link
2026-01-05 11:55:15 -08:00
Aria Keshmiri
bebc019daa Merge pull request #3420 from XRPLF/component/button-href-fix
Add link button functionality and styles
2026-01-05 11:55:04 -08:00
Calvin Jhunjhuwala
b3f235ded6 adding typography showcase page 2026-01-05 11:33:11 -08:00
akcodez
7b223aafc2 Update CardImage and TileLogo components for improved responsiveness and layout
- Adjusted column spans in CardImage showcase to support a 4-column layout on larger screens.
- Updated TileLogo showcase to reflect changes in aspect ratios and responsive behavior, ensuring proper display across different screen sizes.
- Enhanced SCSS styles for both components to maintain consistency in appearance and functionality in light and dark themes.
- Refined documentation to clarify usage and responsive design principles for both CardImage and TileLogo components.
2026-01-05 10:12:11 -08:00
Rome Reginelli
dee67b3e77 Merge pull request #3230 from XRPLF/tutorial_formatting
Revise tutorial and sample code guidelines
2025-12-19 16:40:49 -08:00
Oliver Eggert
fc8c6fb30e update ledger_data page 2025-12-19 15:57:29 -08:00
oeggert
affbab0e74 Merge pull request #3424 from XRPLF/remove-common-fields
Remove unnecessary common fields from tables
2025-12-19 14:50:20 -08:00
oeggert
3497c2e2eb Merge pull request #3425 from XRPLF/close_time_iso
Update close_time_iso fields
2025-12-19 14:50:04 -08:00
Oliver Eggert
60c583d5fe update ledger-clio method 2025-12-18 22:31:51 -08:00
Oliver Eggert
1facb7c2d3 update path_find_ and subscribe 2025-12-18 21:35:16 -08:00
Oliver Eggert
71e2dae458 update transaction_entry 2025-12-18 21:22:01 -08:00
Oliver Eggert
b0d0212024 update account_tx 2025-12-18 21:01:34 -08:00
Oliver Eggert
515add6246 update ledger and tx methods 2025-12-18 20:27:31 -08:00
mDuo13
16a7ece46d Tutorial guidelines: more edits per reviews 2025-12-18 17:49:56 -08:00
Rome Reginelli
3fe0afec39 Apply suggestions from @maria-robobug review
Co-authored-by: Maria Shodunke <maria-robobug@users.noreply.github.com>
2025-12-18 17:49:15 -08:00
Oliver Eggert
8ac405e3be remove common fields 2025-12-18 16:21:37 -08:00
Maria Shodunke
921827d6b2 Add custom oracle configurations details 2025-12-18 17:57:07 +00:00
Maria Shodunke
a4c9417a7b Merge pull request #3398 from tequdev/brandkit_target_blank
Update Navbar component to open brand kit link in a new tab
2025-12-16 18:33:26 +00:00
mDuo13
91e8962c25 Revise tutorial guidelines & template
(Includes suggestions from @oeggert review)

Co-authored-by: oeggert <117319296+oeggert@users.noreply.github.com>

More verify creds docs

Rework tutorial guidelines & template

Update verify credential (js) tutorial for reworked code sample

Remove now-unused tutorial structure screenshots
2025-12-15 16:15:09 -08:00
Calvin Jhunjhuwala
ffe0eff61a lg + xl separate for the display-lg font class 2025-12-11 15:29:01 -08:00
Calvin Jhunjhuwala
be0e324d0b working font styles and updated card grid 2025-12-11 15:01:08 -08:00
akcodez
fadfde1775 Add link button functionality and styles
- Introduced link button support in the Button component, allowing buttons to render as anchor elements when an `href` prop is provided.
- Updated Button showcase to demonstrate link buttons for internal and external navigation.
- Enhanced Button SCSS styles to ensure proper appearance and behavior for link buttons in both light and dark themes.
- Added documentation for link button usage in the Button showcase.
2025-12-11 11:23:23 -08:00
Maria Shodunke
b5f3449547 Merge pull request #3412 from XRPLF/dependabot/go_modules/_code-samples/secure-signing/go/golang.org/x/crypto-0.45.0
Bump golang.org/x/crypto from 0.35.0 to 0.45.0 in /_code-samples/secure-signing/go
2025-12-11 12:36:48 +00:00
Maria Shodunke
0d4c6e982a Update DIA Oracles blog post 2025-12-11 12:33:16 +00:00
Rome Reginelli
b5ff3d806e Merge pull request #3381 from XRPLF/add-code-sample-iterate-owner-dir
Add code sample showing how to walk an owner directory
2025-12-10 13:59:33 -08:00
akcodez
32899e9c41 rename link 2025-12-10 13:53:21 -08:00
mDuo13
152cc95a63 Improve owner directory code sample 2025-12-10 13:52:59 -08:00
Aria Keshmiri
15f48991c3 Merge pull request #3415 from XRPLF/component/card-image
Add CardImage component with showcase and documentation
2025-12-10 12:45:37 -08:00
akcodez
642c0dd2ce add bottom padding 2025-12-10 12:45:04 -08:00
akcodez
a08b24ed5d Enhance CardImage showcase with image scaling demo and responsive layout adjustments
- Added a new image scaling demonstration to the CardImage showcase, illustrating a zoom effect on hover.
- Updated layout of CardImage components to improve responsiveness, adjusting column spans for various screen sizes.
- Introduced a new image from Figma for the scaling demo and included detailed descriptions of the scaling behavior.
- Refined SCSS styles to support the new layout and ensure consistent spacing across different screen sizes.
2025-12-10 10:35:14 -08:00
Aria Keshmiri
74e8be5a13 Merge pull request #3413 from XRPLF/component/card-icon
Add CardIcon component with showcase and documentation
2025-12-10 09:37:01 -08:00
Maria Shodunke
898d067c02 Merge pull request #3386 from XRPLF/issue-mptoken-tutorial
Add tutorial for issuing an MPT (Javascript and Python)
2025-12-10 10:47:25 +00:00
oeggert
acc02da22e Merge pull request #3374 from XRPLF/rippled-3.0.0
3.0 Release Doc Updates
2025-12-09 23:29:43 -08:00
oeggert
ea16168700 Merge pull request #3407 from XRPLF/release-notes-3.0
Release notes 3.0
2025-12-09 16:38:59 -08:00
Oliver Eggert
ddafea0fe0 update date 2025-12-09 16:32:14 -08:00
oeggert
5d4904cfd6 Merge pull request #3377 from XRPLF/update-amendments-3.0
update known amendment page
2025-12-09 16:29:30 -08:00
Oliver Eggert
5e02c839ea update hash links and commit 2025-12-09 16:21:44 -08:00
Oliver Eggert
a44713a903 fix grammar 2025-12-09 15:03:09 -08:00
Oliver Eggert
3be9307845 add reviewer suggestions 2025-12-09 15:01:04 -08:00
oeggert
e197e4e034 Merge pull request #3414 from XRPLF/ledger-entry-update
add breaking change for ledger_entry
2025-12-09 13:46:51 -08:00
Calvin
7895d6dee9 Merge pull request #3410 from XRPLF/component/card-stat
[feat][site] Component/Card-Stat
2025-12-09 12:33:19 -08:00
akcodez
8e6d8f7c30 Add CardImage component with showcase and documentation
- Introduced the CardImage component, a responsive card designed to display an image, title, subtitle, and call-to-action button.
- Implemented three responsive size variants (LG, MD, SM) that adapt to viewport width.
- Added interactive states including default, hover, focus, pressed, and disabled.
- Created a comprehensive showcase page demonstrating all variants and states of the CardImage component.
- Included detailed documentation covering usage examples, props API, design tokens, and accessibility features.
- Added SCSS styles for the CardImage component, ensuring compatibility with both light and dark themes.
2025-12-09 12:29:03 -08:00
Calvin Jhunjhuwala
36785bc0f1 fixing css conflict 2025-12-09 12:28:14 -08:00
Oliver Eggert
337a576d2d add ammclawbackrounding 2025-12-09 12:10:12 -08:00
Oliver Eggert
1534ecfa26 add breaking change 2025-12-09 12:04:05 -08:00
Calvin Jhunjhuwala
977c37ef83 minor copy change, pushiong fixed color 2025-12-09 12:00:11 -08:00
Calvin Jhunjhuwala
52d895b1d0 edits for dark mode card 2025-12-09 11:56:34 -08:00
Oliver Eggert
9600871944 add breaking change info 2025-12-09 11:08:53 -08:00
akcodez
230ddcbe21 Add CardIcon component with showcase and documentation
- Introduced the CardIcon component, a clickable card featuring an icon and label text, supporting two color variants (Neutral and Green) and five interaction states (Default, Hover, Focused, Pressed, Disabled).
- Implemented responsive sizing that adapts at breakpoints (SM, MD, LG).
- Created a comprehensive showcase page demonstrating all variants, states, and usage examples.
- Added detailed documentation covering usage guidelines, best practices, and API reference.
- Included SCSS styles for the CardIcon component, ensuring compatibility with both light and dark themes.
2025-12-09 10:37:40 -08:00
Aria Keshmiri
1ee5828747 Merge pull request #3406 from XRPLF/component/card-offgrid
Add CardOffgrid component with showcase and documentation
2025-12-09 10:13:16 -08:00
Aria Keshmiri
314980a667 Merge pull request #3409 from XRPLF/component/tile-logo
Add TileLogo component with showcase and documentation
2025-12-09 10:13:06 -08:00
dependabot[bot]
a0b688689a Bump golang.org/x/crypto in /_code-samples/secure-signing/go
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.35.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.35.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-09 14:23:31 +00:00
Maria Shodunke
7f4004ec30 Merge pull request #3411 from XRPLF/dependabot/go_modules/_code-samples/use-tickets/go/golang.org/x/crypto-0.45.0
Bump golang.org/x/crypto from 0.35.0 to 0.45.0 in /_code-samples/use-tickets/go
2025-12-09 14:22:34 +00:00
dependabot[bot]
372a9128a1 Bump golang.org/x/crypto in /_code-samples/use-tickets/go
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.35.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.35.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-09 14:19:08 +00:00
Maria Shodunke
3a339849b3 Merge pull request #3402 from XRPLF/dependabot/go_modules/_code-samples/non-fungible-token/go/golang.org/x/crypto-0.45.0
Bump golang.org/x/crypto from 0.35.0 to 0.45.0 in /_code-samples/non-fungible-token/go
2025-12-09 14:18:12 +00:00
akcodez
2783d90cf6 Refactor CardOffgrid typography styles to utilize predefined classes
- Updated CardOffgrid component to use .subhead-lg-l and .body-l classes for title and description typography, respectively.
- Removed custom font size and line height definitions from SCSS and CSS files for improved consistency and maintainability.
- Adjusted related styles to ensure compatibility with the new typography approach.
2025-12-09 06:15:22 -08:00
akcodez
5fbdbb8d42 Refactor TileLogo showcase layout to use PageGrid components
- Replaced div-based layout with PageGridRow and PageGridCol components for better responsiveness and alignment.
- Updated all instances of logo displays within the TileLogo showcase to utilize the new grid structure.
- Ensured consistent spacing and alignment across different logo variants and states.
2025-12-09 06:05:54 -08:00
Maria Shodunke
8a4d1851db Fix mpt-generator html file and regenerate zip 2025-12-09 12:17:43 +00:00
Maria Shodunke
2faba938f7 Run standard command to fix JS code style 2025-12-09 12:13:00 +00:00
Maria Shodunke
7dadae868f Apply suggestions from code review
Co-authored-by: oeggert <117319296+oeggert@users.noreply.github.com>
2025-12-09 11:28:22 +00:00
Calvin Jhunjhuwala
b7ba976fb2 consolidating classes, still need superscript specs 2025-12-08 21:06:56 -08:00
Calvin Jhunjhuwala
a6eb9e63e5 complete showcase pagfe 2025-12-08 15:30:42 -08:00
oeggert
da422329f9 Update blog/2025/rippled-3.0.0.md
Co-authored-by: Maria Shodunke <maria-robobug@users.noreply.github.com>
2025-12-08 14:11:03 -08:00
Calvin Jhunjhuwala
7d694c76a5 working card stat, finalizing sup, cleaning up code cleanliness 2025-12-08 13:58:57 -08:00
akcodez
cbc56937e6 Refactor TileLogo component styles and class naming to align with BDS conventions
- Updated SCSS styles to replace 'tile-logo' namespace with 'bds-tile-logo' for consistency with the Brand Design System.
- Adjusted class names in the TileLogo component to reflect the new naming convention.
- Ensured all styles and variants (square, rectangle, neutral, green) are updated accordingly for both light and dark themes.
2025-12-08 10:57:02 -08:00
akcodez
1a9fa9b970 Add TileLogo component with showcase and documentation
- Introduced the TileLogo component, designed for displaying brand logos with interactive states.
- Implemented two shape variants (Square and Rectangle) and two color variants (Neutral and Green), with responsive sizing.
- Created a comprehensive showcase page demonstrating all variants, states, and usage examples.
- Added detailed documentation covering usage guidelines, best practices, and API reference.
- Included SCSS styles for the TileLogo component, ensuring compatibility with both light and dark themes.
2025-12-08 10:52:37 -08:00
akcodez
cd82ea5484 rm link 2025-12-08 10:13:05 -08:00
Oliver Eggert
fa40ba2b71 add fixincludekeyletfields 2025-12-05 14:13:27 -08:00
Oliver Eggert
506b1c7b21 add release notes 2025-12-05 13:42:51 -08:00
akcodez
57898ab010 Add CardOffgrid component with showcase and documentation
- Introduced the CardOffgrid component, designed for displaying feature highlights with customizable icons, titles, and descriptions.
- Implemented two color variants: neutral and green, with interactive states and a unique hover animation.
- Created a comprehensive showcase page demonstrating all variants and states of the CardOffgrid component.
- Added detailed documentation covering usage guidelines, best practices, and API reference.
- Included SCSS styles for the CardOffgrid component, ensuring compatibility with both light and dark themes.
2025-12-05 11:41:18 -08:00
Aria Keshmiri
2dbb111943 Merge pull request #3405 from XRPLF/component/divider
Add Divider component and documentation
2025-12-04 15:34:04 -08:00
akcodez
cb6323d153 Update Divider component to replace 'black' color variant with 'base' for improved theme adaptability
- Changed all instances of 'black' to 'base' in the Divider component and its documentation to reflect the new high-contrast color that adapts to light and dark themes.
- Updated showcase page and styles to ensure consistency with the new color naming convention.
2025-12-04 15:33:32 -08:00
akcodez
08941588aa fix colors 2025-12-04 13:51:04 -08:00
akcodez
e183369ef6 add height 2025-12-04 13:48:23 -08:00
akcodez
702e180de6 Add Divider component and documentation
- Introduced the Divider component following the XRPL Brand Design System, supporting horizontal and vertical orientations, three stroke weights (thin, regular, strong), and three color variants (gray, black, green).
- Created a comprehensive showcase page for the Divider component, demonstrating its usage and variations.
- Added detailed documentation for the Divider component, including props API, usage examples, and best practices.
- Included styles for the Divider component in SCSS format, ensuring compatibility with light and dark themes.
2025-12-04 12:20:18 -08:00
Calvin
a265f82980 Merge pull request #3404 from XRPLF/grid-optimization
[fix] Grid optimization
2025-12-03 16:17:54 -08:00
Calvin Jhunjhuwala
021899906d removing unnecessary npm package 2025-12-03 16:14:09 -08:00
Calvin Jhunjhuwala
f022c48f6c changing showcase file 2025-12-03 16:06:59 -08:00
Calvin Jhunjhuwala
3e07b8400d removing extra css 2025-12-03 16:02:08 -08:00
Calvin Jhunjhuwala
5433894f20 renaming class name to bds 2025-12-03 14:51:59 -08:00
Calvin Jhunjhuwala
a6d84de417 working grid for fill and auto 2025-12-03 13:34:05 -08:00
Calvin Jhunjhuwala
6f76d4ece5 clean up of grid, working on all screen sizes, logic built out 2025-12-03 13:09:39 -08:00
Aria Keshmiri
17778ad84b Merge pull request #3399 from XRPLF/component/button
Component/button
2025-12-03 10:58:04 -08:00
Aria Keshmiri
518585227d Merge pull request #3401 from XRPLF/component/link
Add BDS link styles and update existing link styles
2025-12-03 10:57:25 -08:00
akcodez
ad0631f701 add arialbel 2025-12-03 10:56:43 -08:00
Mayukha Vadari
ac623ef506 Add request parameters to server definitions
Updated server_definitions.md to include request parameters.
2025-12-03 10:50:37 -05:00
dependabot[bot]
8f21c5d402 Bump golang.org/x/crypto in /_code-samples/non-fungible-token/go
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.35.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.35.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-03 11:22:53 +00:00
akcodez
01c19628a9 fix link spacing 2025-12-02 15:27:52 -08:00
Oliver Eggert
938a85eac0 add lending protocol amendment and reviewer suggestion 2025-12-02 14:15:38 -08:00
akcodez
44614dba9d Refactor Button Styles for Responsive Design
Updated button styles to enhance responsiveness across devices by replacing fixed breakpoints with a mixin for the xl breakpoint. Adjusted typography and padding for smaller screens to ensure consistent appearance and usability. Improved comments for clarity on the import order and design tokens.
2025-12-02 12:40:27 -08:00
akcodez
621db81c7d add proper breakpoint sizing 2025-12-02 12:23:35 -08:00
akcodez
c01749eba2 rm deprecated linkCOlor 2025-12-02 11:52:23 -08:00
akcodez
2ff14e4224 Add BDS link styles and update existing link styles
- Introduced new styles for BDS link icons, including hover and focus states.
- Updated existing link styles to exclude BDS links from certain color and hover effects.
- Ensured consistent styling across light and dark themes for BDS links.
- Refactored landing page link styles to accommodate new BDS link classes.
2025-12-02 10:21:52 -08:00
Oliver Eggert
ad280c2c1b initial release notes 2025-12-01 17:03:41 -08:00
akcodez
7685c2eb1e Refactor Tertiary Button Styles for Dark Mode
Updated the styles for the tertiary button in dark mode, including hover, focus, active, and disabled states. Adjusted padding and added text decoration for better visual feedback. Enhanced responsiveness for smaller screens to ensure consistent behavior across devices.
2025-12-01 14:27:02 -08:00
akcodez
2de2bac211 Enhance dark mode styles for primary and secondary buttons, adding detailed states for hover, focus, active, and disabled conditions. Update CSS to reflect new design specifications for secondary button in dark mode. 2025-12-01 14:21:14 -08:00
akcodez
bdc69f047a add primary dark mode 2025-12-01 14:17:25 -08:00
akcodez
f20177b5f9 Add Tertiary Button Showcase Component
Introduced a new ButtonShowcaseTertiary component to demonstrate the BDS Tertiary Button, including its various states and usage examples. Removed the previous ButtonShowcase component to streamline the showcase and focus on the Tertiary variant. Updated Button component to support the new variant and adjusted related styles in the CSS for both green and black themes.
2025-12-01 14:07:29 -08:00
akcodez
73b2127f87 add proper focus and active / hover states 2025-12-01 13:45:21 -08:00
akcodez
32b309c878 merge main brand branch in 2025-12-01 12:03:09 -08:00
akcodez
9cf1b07954 add full primary button 2025-12-01 12:01:50 -08:00
Calvin
5b73ccb8be Merge pull request #3393 from XRPLF/bds-font-update
[fix][fonts] Clean up of fonts, folder re-structuring
2025-12-01 11:01:58 -08:00
tequ
316f6f4fd4 Update Navbar component to open brand kit link in a new tab 2025-12-01 11:31:41 +09:00
Calvin Jhunjhuwala
c4188c47d6 updating css for medium 2025-11-26 16:06:00 -08:00
Calvin Jhunjhuwala
2429574182 removing px reversion for medium 2025-11-26 16:03:51 -08:00
Calvin Jhunjhuwala
42ec50df27 removing fake button folder 2025-11-26 16:00:14 -08:00
Calvin Jhunjhuwala
37e96a9dae more documentation around page grid 2025-11-25 21:36:38 -08:00
Calvin Jhunjhuwala
f3ae760c40 move page grid css to page grid folder, removing more text class styling 2025-11-25 21:30:25 -08:00
Calvin Jhunjhuwala
97c302822a adding folder structure, cleaning up font, add readme for page grid 2025-11-25 17:29:58 -08:00
Oliver Eggert
682389e4e7 update binary format for signed int 2025-11-25 15:16:36 -08:00
Oliver Eggert
5c467f1c9b initialize 3.0 doc branch 2025-11-25 15:16:36 -08:00
Oliver Eggert
104d07c6bf add fixMPTDeliveredAmount amendment info 2025-11-25 15:13:15 -08:00
Maria Shodunke
f290941300 Update MPT Generator use case page 2025-11-25 18:22:53 +00:00
Maria Shodunke
a71a3fca10 Add tutorial for issuing an MPT (Javascript and Python). 2025-11-21 13:43:33 +00:00
Maria Shodunke
d89fdc79b8 Add minor improvements for javascript code walkthrough 2025-11-17 14:03:03 +00:00
mDuo13
c6df042c7b Add code sample showing how to walk an owner directory 2025-11-14 16:39:35 -08:00
Calvin Jhunjhuwala
e92929e148 removing old background images, updating font classes 2025-11-13 12:08:33 -08:00
oeggert
427d0ce441 Update resources/known-amendments.md
Co-authored-by: Rome Reginelli <rome@ripple.com>
2025-11-10 20:35:08 -08:00
oeggert
6be3d0117a Update resources/known-amendments.md
Co-authored-by: Rome Reginelli <rome@ripple.com>
2025-11-10 20:34:59 -08:00
Oliver Eggert
ed4b18586b update known amendment page 2025-11-05 21:14:14 -08:00
Oliver Eggert
864c412305 initialize 3.0 doc branch 2025-11-04 13:28:58 -08:00
Calvin Jhunjhuwala
9d3d11800a adding page grid component + stylersheet 2025-11-03 10:42:35 -08:00
Calvin Jhunjhuwala
a956d5ae78 removing empty card footer 2025-10-30 10:42:30 -07:00
Calvin Jhunjhuwala
52e070dcf6 removing background from css as well 2025-10-30 10:38:53 -07:00
Calvin Jhunjhuwala
605eb70aed removed all old fonts, remove all old backgrounds 2025-10-30 10:33:42 -07:00
akcodez
0c2a1bc249 Refactor color migration strategy to a clean implementation, removing backward compatibility aliases and consolidating color scales. Update SCSS files to reflect new design tokens and ensure all references are migrated. Adjust event card layout in community events page for improved responsiveness. Modify CSS styles for better alignment with updated color palette and remove unnecessary padding in card components. 2025-10-21 14:04:55 -07:00
akcodez
51e763b967 Enhance PostCSS configuration with expanded safelist for Bootstrap utility classes, update layout in about and resources pages, and refine color palette in CSS files for improved design consistency. Adjusted card styles and link behaviors to align with new design tokens. 2025-10-21 13:42:07 -07:00
akcodez
86998c82d6 Implement CSS optimization pipeline with PurgeCSS, Autoprefixer, and cssnano; reduce bundle size by 42% (uncompressed) and 39% (gzipped). Add analysis tool for CSS metrics and update build scripts in package.json. Create comprehensive documentation for CSS optimization process. 2025-10-21 10:06:53 -07:00
akcodez
c2287a7fe6 run css compiler 2025-10-21 09:23:47 -07:00
akcodez
f09ab44280 upgrade bootstrap, add new fonts 2025-10-21 09:22:02 -07:00
Calvin Jhunjhuwala
08807db2e9 ran css 2025-10-21 09:17:17 -07:00
Calvin Jhunjhuwala
201479ced6 new fonts added working across website 2025-10-21 09:09:56 -07:00
Calvin Jhunjhuwala
ce49c8b6ba updating bootstrap to v5 2025-10-17 16:28:07 -07:00
425 changed files with 69432 additions and 8539 deletions

2
.gitignore vendored
View File

@@ -8,6 +8,8 @@ yarn-error.log
*.iml
.venv/
_code-samples/*/js/package-lock.json
*.css.map
# PHP
composer.lock
.cursor/

View File

@@ -104,142 +104,16 @@ rippled tx C53ECF838647FA5A4C780377025FEC7999AB4182590510CA461444B207AB74A9 fals
{% tabs %}
{% tab label="WebSocket (Hash)" %}
{% code-snippet file="/_api-examples/tx/ws-response-hash.json" language="json" /%}
{% tab label="WebSocket" %}
{% code-snippet file="/_api-examples/tx/ws-response.json" language="json" /%}
{% /tab %}
{% tab label="WebSocket (CTID)" %}
{% code-snippet file="/_api-examples/tx/ws-response-ctid.json" language="json" /%}
{% /tab %}
{% tab label="JSON-RPC (Hash)" %}
{% code-snippet file="/_api-examples/tx/jsonrpc-response-hash.json" language="json" /%}
{% /tab %}
{% tab label="JSON-RPC (CTID)" %}
{% code-snippet file="/_api-examples/tx/jsonrpc-response-ctid.json" language="json" /%}
{% tab label="JSON-RPC" %}
{% code-snippet file="/_api-examples/tx/jsonrpc-response.json" language="json" prefix="200 OK\n\n" /%}
{% /tab %}
{% tab label="Commandline" %}
```json
{
"result" : {
"Account" : "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"Fee" : "12",
"Flags" : 0,
"LastLedgerSequence" : 56865248,
"OfferSequence" : 5037708,
"Sequence" : 5037710,
"SigningPubKey" : "03B51A3EDF70E4098DA7FB053A01C5A6A0A163A30ED1445F14F87C7C3295FCB3BE",
"TakerGets" : "15000000000",
"TakerPays" : {
"currency" : "CNY",
"issuer" : "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value" : "20160.75"
},
"TransactionType" : "OfferCreate",
"TxnSignature" : "3045022100A5023A0E64923616FCDB6D664F569644C7C9D1895772F986CD6B981B515B02A00220530C973E9A8395BC6FE2484948D2751F6B030FC7FB8575D1BFB406368AD554D9",
"date" : 648248020,
"hash" : "C53ECF838647FA5A4C780377025FEC7999AB4182590510CA461444B207AB74A9",
"inLedger" : 56865245,
"ledger_index" : 56865245,
"meta" : {
"AffectedNodes" : [
{
"ModifiedNode" : {
"FinalFields" : {
"ExchangeRate" : "4F04C66806CF7400",
"Flags" : 0,
"RootIndex" : "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"TakerGetsCurrency" : "0000000000000000000000000000000000000000",
"TakerGetsIssuer" : "0000000000000000000000000000000000000000",
"TakerPaysCurrency" : "000000000000000000000000434E590000000000",
"TakerPaysIssuer" : "CED6E99370D5C00EF4EBF72567DA99F5661BFB3A"
},
"LedgerEntryType" : "DirectoryNode",
"LedgerIndex" : "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400"
}
},
{
"ModifiedNode" : {
"FinalFields" : {
"Account" : "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"Balance" : "10404767991",
"Flags" : 0,
"OwnerCount" : 3,
"Sequence" : 5037711
},
"LedgerEntryType" : "AccountRoot",
"LedgerIndex" : "1DECD9844E95FFBA273F1B94BA0BF2564DDF69F2804497A6D7837B52050174A2",
"PreviousFields" : {
"Balance" : "10404768003",
"Sequence" : 5037710
},
"PreviousTxnID" : "4DC47B246B5EB9CCE92ABA8C482479E3BF1F946CABBEF74CA4DE36521D5F9008",
"PreviousTxnLgrSeq" : 56865244
}
},
{
"DeletedNode" : {
"FinalFields" : {
"Account" : "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"BookDirectory" : "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"BookNode" : "0000000000000000",
"Flags" : 0,
"OwnerNode" : "0000000000000000",
"PreviousTxnID" : "8F5FF57B404827F12BDA7561876A13C3E3B3095CBF75334DBFB5F227391A660C",
"PreviousTxnLgrSeq" : 56865244,
"Sequence" : 5037708,
"TakerGets" : "15000000000",
"TakerPays" : {
"currency" : "CNY",
"issuer" : "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value" : "20160.75"
}
},
"LedgerEntryType" : "Offer",
"LedgerIndex" : "26AAE6CA8D29E28A47C92ADF22D5D96A0216F0551E16936856DDC8CB1AAEE93B"
}
},
{
"ModifiedNode" : {
"FinalFields" : {
"Flags" : 0,
"IndexNext" : "0000000000000000",
"IndexPrevious" : "0000000000000000",
"Owner" : "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"RootIndex" : "47FAF5D102D8CE655574F440CDB97AC67C5A11068BB3759E87C2B9745EE94548"
},
"LedgerEntryType" : "DirectoryNode",
"LedgerIndex" : "47FAF5D102D8CE655574F440CDB97AC67C5A11068BB3759E87C2B9745EE94548"
}
},
{
"CreatedNode" : {
"LedgerEntryType" : "Offer",
"LedgerIndex" : "8BAEE3C7DE04A568E96007420FA11ABD0BC9AE44D35932BB5640E9C3FB46BC9B",
"NewFields" : {
"Account" : "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"BookDirectory" : "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"Sequence" : 5037710,
"TakerGets" : "15000000000",
"TakerPays" : {
"currency" : "CNY",
"issuer" : "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value" : "20160.75"
}
}
}
}
],
"TransactionIndex" : 0,
"TransactionResult" : "tesSUCCESS"
},
"status" : "success",
"validated" : true
}
}
```
{% code-snippet file="/_api-examples/tx/jsonrpc-response.json" language="json" prefix="Loading: \"/etc/opt/ripple/rippled.cfg\"\n2025-Dec-19 03:16:00.638871262 UTC HTTPClient:NFO Connecting to 127.0.0.1:5005\n\n" /%}
{% /tab %}
{% /tabs %}

View File

@@ -1,119 +0,0 @@
---
html: cancel-an-expired-escrow.html
parent: use-escrows.html
seo:
description: 有効期限切れのEscrowを取り消します。
labels:
- Escrow
- スマートコントラクト
---
# 有効期限切れEscrowの取消し
## 1.有効期限切れEscrowの確認
XRP LedgerのEscrowが有効期限切れとなるのは、その`CancelAfter`の時刻が検証済みレジャーの`close_time`よりも前である場合です。Escrowに`CancelAfter`時刻が指定されていない場合は、Escrowが有効期限切れになることはありません。最新の検証済みレジャーの閉鎖時刻は、[ledgerメソッド][]を使用して検索できます。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/ledger-request-expiration.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/ledger-response-expiration.json" language="json" /%}
{% /tab %}
{% /tabs %}
[account_objectsメソッド][]を使用してEscrowを検索し、`CancelAfter`の時刻と比較できます。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/account_objects-request-expiration.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/account_objects-response-expiration.json" language="json" /%}
{% /tab %}
{% /tabs %}
## 2.EscrowCancelトランザクションの送信
XRP Ledgerでは、[EscrowCancelトランザクション][]に[署名して送信する](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)ことで、***誰でも***有効期限切れのEscrowを取り消すことができます。トランザクションの`Owner`フィールドを、そのEscrowを作成した`EscrowCreate`トランザクションの`Account`に設定します。`OfferSequence`フィールドを、`EscrowCreate`トランザクションの`Sequence`に設定します。
{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%}
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcancel.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcancel.json" language="json" /%}
{% /tab %}
{% /tabs %}
トランザクションの識別用`hash`値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。
## 3.検証の待機
{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%}
## 4.最終結果の確認
EscrowCancelトランザクションの識別用ハッシュを指定した[txメソッド][]を使用してトランザクションの最終ステータスを確認します。トランザクションのメタデータで`LedgerEntryType``Escrow`である`DeletedNode`を探します。また、エスクローに預託された支払いの送金元の`ModifiedNode`(タイプが`AccountRoot`)も探します。オブジェクトの`FinalFields`に、`Balance`フィールドのXRP返金額の増分が表示されている必要があります。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcancel.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcancel.json" language="json" /%}
{% /tab %}
{% /tabs %}
上記の例では、`r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT`がEscrowの送金元であり、`Balance`が99999**8**9990 dropから99999**9**9990 dropに増加していることから、エスクローに預託されていた10,000 XRP dropが返金されたことがわかりますdrop = 0.01XRP
{% admonition type="success" name="ヒント" %}Escrowを実行する[EscrowFinishトランザクション][]で使用する`OfferSequence`が不明な場合は、Escrowの`PreviousTxnID`フィールドのトランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、そのEscrowを作成したトランザクションを検索します。Escrowを終了するときには、そのトランザクションの`Sequence`の値を`OfferSequence`の値として使用します。{% /admonition %}
{% raw-partial file="/@l10n/ja/docs/_snippets/common-links.md" /%}

View File

@@ -1,80 +0,0 @@
---
html: look-up-escrows.html
parent: use-escrows.html
seo:
description: 送金元または送金先のアドレスを使って保留中のEscrowを検索します。
labels:
- Escrow
- スマートコントラクト
---
# Escrowの検索
保留中のEscrowはすべて[Escrowオブジェクト](../../../../concepts/payment-types/escrow.md)としてレジャーに保管されます。
Escrowオブジェクトを検索するには、[account_objectsメソッド][]で[送金元のアドレス](#送金元のアドレスによるescrowの検索)または[送金先のアドレス](#送金先のアドレスによるescrowの検索)を使用して検索します。
## 送金元のアドレスによるEscrowの検索
[account_objectsメソッド][]を使用して、送金元アドレスからEscrowオブジェクトを検索できます。
たとえば、送金元アドレスが`rfztBskAVszuS3s5Kq7zDS74QtHrw893fm`である保留中のEscrowオブジェクトをすべて検索するとします。以下のリクエストの例に従ってこの検索を実行できます。この例では送金元アドレスは`account`の値です。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/account_objects-request.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンスは以下の例のようになります。このレスポンスには、送金元アドレスまたは送金先アドレスが`rfztBskAVszuS3s5Kq7zDS74QtHrw893fm`である保留中のEscrowオブジェクトがすべて含まれています。送金元アドレスは`Account`の値であり、送金先アドレスは`Destination`の値です。
この例では、2番目と4番目のEscrowオブジェクトが検索条件に一致しています。これは、これらのオブジェクトの`Account`(送金元のアドレス)の値が`rfztBskAVszuS3s5Kq7zDS74QtHrw893fm`に設定されているためです。
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/account_objects-response.json" language="json" /%}
{% /tab %}
{% /tabs %}
## 送金先のアドレスによるEscrowの検索
[account_objectsメソッド][]を使用して、送金先アドレスからEscrowオブジェクトを検索できます。
{% admonition type="info" name="注記" %}送金先のアドレスによる保留中のEscrowオブジェクトの検索は、[fix1523 Amendment][]が2017/11/14に有効化された後に作成されたEscrowについてのみ行うことができます。{% /admonition %}
たとえば、送金先アドレスが`rfztBskAVszuS3s5Kq7zDS74QtHrw893fm`である保留中のEscrowオブジェクトをすべて検索するとします。以下のリクエストの例に従ってこの検索を実行できます。この例では送金先アドレスは`account`の値です。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/account_objects-request.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンスは以下の例のようになります。レスポンスには送金先アドレスまたは送金元アドレスが`rfztBskAVszuS3s5Kq7zDS74QtHrw893fm`である保留中のEscrowオブジェクトがすべて含まれています。送金先アドレスは`Destination`の値であり、送金元アドレスは`Account`の値です。
この例では、1番目と3番目のEscrowオブジェクトが検索条件に一致しています。これは、これらのオブジェクトの`Destination`(送金先のアドレス)の値が`rfztBskAVszuS3s5Kq7zDS74QtHrw893fm`に設定されているためです。
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/account_objects-response.json" language="json" /%}
{% /tab %}
{% /tabs %}
{% raw-partial file="/@l10n/ja/docs/_snippets/common-links.md" /%}

View File

@@ -1,173 +0,0 @@
---
html: send-a-conditionally-held-escrow.html
parent: use-escrows.html
seo:
description: 満たされた条件に基づいてリリースとなるEscrowを作成します。
labels:
- Escrow
- スマートコントラクト
---
# 条件に基づくEscrowの送信
## 1.条件とフルフィルメントの生成
XRP Ledger EscrowにはPREIMAGE-SHA-256 [Crypto-Conditions](https://tools.ietf.org/html/draft-thomas-crypto-conditions-03)が必要です。条件とフルフィルメントを適切なフォーマットで計算するには、[five-bells-condition](https://github.com/interledgerjs/five-bells-condition)などのCrypto-conditionsライブラリを使用する必要があります。フルフィルメントについては、以下のフルフィルメントを生成するためのメソッドのいずれかを使用することが推奨されます。
- 暗号論的に安全な乱数ソースを使用して、32バイト以上のランダムバイトを生成します。
- Interledger Protocolの[PSK仕様](https://github.com/interledger/rfcs/blob/master/deprecated/0016-pre-shared-key/0016-pre-shared-key.md)に従い、ILPパケットのHMAC-SHA-256をフルフィルメントとして使用します。
ランダムなフルフィルメントと条件のJavaScriptコードの例:
```js
const cc = require('five-bells-condition')
const crypto = require('crypto')
const preimageData = crypto.randomBytes(32)
const myFulfillment = new cc.PreimageSha256()
myFulfillment.setPreimage(preimageData)
const condition = myFulfillment.getConditionBinary().toString('hex').toUpperCase()
console.log('Condition:', condition)
// (Random hexadecimal, 72 chars in length)
// keep secret until you want to finish executing the held payment:
const fulfillment = myFulfillment.serializeBinary().toString('hex').toUpperCase()
console.log('Fulfillment:', fulfillment)
// (Random hexadecimal, 78 chars in length)
```
後で使用できるように条件とフルフィルメントを保存します。保留中の支払いの実行が完了するまでは、フルフィルメントを公開しないでください。フルフィルメントを知っていれば誰でもEscrowを終了でき、保留中の資金を指定された送金先にリリースできます。
## 2.リリース時刻または取消し時刻の計算
条件付き`Escrow`トランザクションには、`CancelAfter`フィールドと`FinishAfter`フィールドのいずれか、または両方が含まれている必要があります。`CancelAfter`フィールドを使用すると、指定の時刻までに条件を満たすことができなかった場合に送金元へXRPを返金できます。`FinishAfter`フィールドに指定される時刻より前の時間は、正しいフルフィルメントが送信されてもEscrowを実行できません。いずれのフィールドでも、将来の時刻を指定する必要があります。
`CancelAfter`の時刻を24時間先に設定する例:
{% tabs %}
{% tab label="JavaScript" %}
```js
const rippleOffset = 946684800
const CancelAfter = Math.floor(Date.now() / 1000) + (24*60*60) - rippleOffset
console.log(CancelAfter)
// Example:556927412
```
{% /tab %}
{% tab label="Python 2/3" %}
```python
from time import time
ripple_offset = 946684800
cancel_after = int(time()) + (24*60*60) - 946684800
print(cancel_after)
# Example: 556927412
```
{% /tab %}
{% /tabs %}
{% admonition type="danger" name="警告" %}XRP Ledgerでは、時刻を**Rippleエポック2000-01-01T00:00:00Z以降の経過秒数**として指定する必要があります。`CancelAfter`または`FinishAfter`フィールドで、UNIX時刻を同等のRipple時刻に変換せずに使用すると、ロック解除時刻が**30年**先に設定されることになります。{% /admonition %}
## 3.EscrowCreateトランザクションの送信
[EscrowCreateトランザクション][]に[署名して送信](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)します。トランザクションの`Condition`フィールドを、保留中の支払いがリリースされる時刻に設定します。`Destination`を受取人に設定します。受取人と送金元のアドレスは同じでもかまいません。前の手順で算出した`CancelAfter`または`FinishAfter`の時刻も指定します。`Amount`を、Escrowする[XRPのdrop数][]の合計額に設定します。
{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%}
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcreate-condition.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcreate-condition.json" language="json" /%}
{% /tab %}
{% /tabs %}
## 4.検証の待機
{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%}
## 5.Escrowが作成されたことの確認
トランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、トランザクションの最終ステータスを確認します。特に、[Escrowレジャーオブジェクト](../../../../concepts/payment-types/escrow.md)が作成されたことを示す`CreatedNode`をトランザクションメタデータで探します。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcreate-condition.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcreate-condition.json" language="json" /%}
{% /tab %}
{% /tabs %}
## 6.EscrowFinishトランザクションの送信
`FinishAfter`の時刻が経過した後で資金のリリースを実行する[EscrowFinishトランザクション][]に[署名して送信](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)します。トランザクションの`Owner`フィールドにEscrowCreateトランザクションの`Account`アドレスを設定し、`OfferSequence` にEscrowCreateトランザクションの`Sequence`番号を設定します。`Condition`フィールドと`Fulfillment`フィールドに、ステップ1で生成した条件値とフルフィルメント値をそれぞれ16進数で設定します。フルフィルメントのサイズバイト数に基づいて`Fee`[トランザクションコスト](../../../../concepts/transactions/transaction-cost.md)の値を設定します。条件付きEscrowFinishでは、少なくとも330 dropXRPと、フルフィルメントのサイズで16バイトごとに10 dropが必要です。
{% admonition type="info" name="注記" %}EscrowCreateトランザクションに`FinishAfter`フィールドが含まれている場合、Escrowの条件として正しいフルフィルメントを指定しても、この時刻よりも前の時点ではこのトランザクションを実行できません。前に閉鎖されたレジャーの閉鎖時刻が`FinishAfter`の時刻よりも前である場合、EscrowFinishトランザクションは[結果コード](../../../../references/protocol/transactions/transaction-results/index.md)`tecNO_PERMISSION`で失敗します。{% /admonition %}
Escrowが有効期限切れの場合は、[Escrowの取消し](cancel-an-expired-escrow.md)だけが可能です。
{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%}
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowfinish-condition.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowfinish-condition.json" language="json" /%}
{% /tab %}
{% /tabs %}
トランザクションの識別用`hash`値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。
## 7.検証の待機
{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%}
## 8.最終結果の確認
EscrowFinishトランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、トランザクションの最終ステータスを確認します。特にトランザクションメタデータ内で、エスクローに預託された支払いの送金先の`ModifiedNode`(タイプが`AccountRoot`)を確認します。オブジェクトの`FinalFields`に、`Balance`フィールドのXRP返金額の増分が表示されている必要があります。
リクエスト:
{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowfinish-condition.json" language="json" /%}
レスポンス:
{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowfinish-condition.json" language="json" /%}
{% raw-partial file="/@l10n/ja/docs/_snippets/common-links.md" /%}

View File

@@ -1,188 +0,0 @@
---
html: send-a-time-held-escrow.html
parent: use-escrows.html
seo:
description: 指定した時間が経過することがリリースの唯一の条件であるEscrowを作成します。
labels:
- Escrow
- スマートコントラクト
---
# 時間に基づくEscrowの送信
[EscrowCreateトランザクション][]タイプでは、リリースの唯一の条件が特定時刻を経過することであるEscrowを作成できます。このためには、`FinishAfter`フィールドを使用し、`Condition`フィールドを省略します。
## 1.リリース時刻の計算
時刻を **[Rippleエポック以降の経過秒数][]** として指定する必要があります。Rippleエポックは、UNIXエポックの946684800秒後です。たとえば、2017年11月13日の午前0時UTCに資金をリリースする場合、以下のようになります。
{% tabs %}
{% tab label="JavaScript" %}
```js
// JavaScript Date() is natively expressed in milliseconds; convert to seconds
const release_date_unix = Math.floor( new Date("2017-11-13T00:00:00Z") / 1000 );
const release_date_ripple = release_date_unix - 946684800;
console.log(release_date_ripple);
// 563846400
```
{% /tab %}
{% tab label="Python 3" %}
```python
import datetime
release_date_utc = datetime.datetime(2017,11,13,0,0,0,tzinfo=datetime.timezone.utc)
release_date_ripple = int(release_date_utc.timestamp()) - 946684800
print(release_date_ripple)
# 563846400
```
{% /tab %}
{% /tabs %}
{% admonition type="danger" name="警告" %}`FinishAfter`フィールドで、UNIX時刻を同等のRipple時刻に変換せずに使用すると、ロック解除時刻が30年先に設定されることになります。{% /admonition %}
## 2.EscrowCreateトランザクションの送信
[EscrowCreateトランザクション][]に[署名して送信](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)します。トランザクションの`FinishAfter`フィールドを、保留中の支払いがリリースされる時刻に設定します。`Condition`フィールドを省略して、時刻を保留中の支払いをリリースする唯一の条件とします。`Destination`を受取人に設定します。受取人と送金元のアドレスは同じでもかまいません。`Amount`を、Escrowする[XRPのdrop数][]の合計額に設定します。
{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%}
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowcreate-time.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowcreate-time.json" language="json" /%}
{% /tab %}
{% /tabs %}
トランザクションの識別用`hash`値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。
## 3.検証の待機
{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%}
## 4.Escrowが作成されたことの確認
トランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、トランザクションの最終ステータスを確認します。[Escrowレジャーオブジェクト](../../../../concepts/payment-types/escrow.md)が作成されたことを示す`CreatedNode`をトランザクションメタデータで探します。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowcreate-time.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowcreate-time.json" language="json" /%}
{% /tab %}
{% /tabs %}
## 5.リリース時刻までの待機
`FinishAfter`時刻が指定されている保留中の支払いは、Escrowードの`FinishAfter`時刻よりも後の[`close_time`ヘッダーフィールド](../../../../references/protocol/ledger-data/ledger-header.md)の時刻でレジャーが閉鎖するまでは完了できません。
最新の検証済みレジャーの閉鎖時刻は、[ledgerメソッド][]を使用して検索できます。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/ledger-request.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/ledger-response.json" language="json" /%}
{% /tab %}
{% /tabs %}
## 6.EscrowFinishトランザクションの送信
`FinishAfter`の時刻が経過した後で資金のリリースを実行する[EscrowFinishトランザクション][]に[署名して送信](../../../../concepts/transactions/index.md#トランザクションへの署名とトランザクションの送信)します。トランザクションの`Owner`フィールドにEscrowCreateトランザクションの`Account`アドレスを設定し、`OfferSequence` にEscrowCreateトランザクションの`Sequence`番号を設定します。時刻のみに基づいて保留されているEscrowの場合は、`Condition`フィールドと`Fulfillment`フィールドを省略します。
{% admonition type="success" name="ヒント" %}XRP Ledgerの状態はトランザクションでしか変更できないため、EscrowFinishトランザクションが必要です。このトランザクションの送信者は、Escrowの受取人、Escrowの元としての送金人、またはその他のXRP Ledgerアドレスのいずれかです。{% /admonition %}
Escrowが有効期限切れの場合は、[Escrowの取消し](cancel-an-expired-escrow.md)だけが可能です。
{% partial file="/@l10n/ja/docs/_snippets/secret-key-warning.md" /%}
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-request-escrowfinish-time.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/submit-response-escrowfinish-time.json" language="json" /%}
{% /tab %}
{% /tabs %}
トランザクションの識別用`hash`値をメモしておきます。これにより、検証済みレジャーバージョンに記録されるときにその最終ステータスを確認できます。
## 7.検証の待機
{% partial file="/@l10n/ja/docs/_snippets/wait-for-validation.md" /%}
## 8.最終結果の確認
EscrowFinishトランザクションの識別用ハッシュを指定した[txメソッド][]を使用して、トランザクションの最終ステータスを確認します。特にトランザクションメタデータ内で、エスクローに預託された支払いの送金先の`ModifiedNode`(タイプが`AccountRoot`)を確認します。オブジェクトの`FinalFields`に、`Balance`フィールドのXRP返金額の増分が表示されている必要があります。
リクエスト:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/tx-request-escrowfinish-time.json" language="json" /%}
{% /tab %}
{% /tabs %}
レスポンス:
{% tabs %}
{% tab label="Websocket" %}
{% code-snippet file="/_api-examples/escrow/websocket/tx-response-escrowfinish-time.json" language="json" /%}
{% /tab %}
{% /tabs %}
{% raw-partial file="/@l10n/ja/docs/_snippets/common-links.md" /%}

View File

@@ -37,7 +37,7 @@ XRP Ledger上のスマートコントラクトは、条件付きで保有する
オラクルのプログラムが条件を満たしたことを検知した後、エスクローの受取人にfulfillmentの16進数値を渡します。この時点以降、オラクルはエスクローを終了させるなど、何も行いません。エスクローの受取人は、ほとんどの場合、エスクローを終了することになります。
[conditionとfulfillmentの生成](../../tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditionally-held-escrow.md#1-generate-condition-and-fulfillment)をご覧ください。
[条件に基づくEscrowの送信](../../tutorials/how-tos/use-specialized-payment-types/use-escrows/send-a-conditional-escrow.md)をご覧ください。
## 例

View File

@@ -1,55 +0,0 @@
---
html: tutorial-structure.html
parent: contribute-documentation.html
seo:
description: 一般的なチュートリアルの構成要素の要約です。
---
# チュートリアルの構成
各XRP Ledgerチュートリアルは、同一のフォーマットで構成されています。
1. チュートリアルで説明する機能の簡単な説明。
2. コードを実行するための前提条件(必要な場合)、またはサンプルコードへのリンク。
3. チュートリアルの機能の使用例。
4. サンプルコードの解説と、そのスクリプトの特徴的な要素の紹介。
5. 次のステップとして試すべき概念的な情報や優れたチュートリアルへのリンク。
セットアップ(前提条件)と使用方法とコード開発は分けて考えましょう。これらはそれぞれ異なる活動であり、それぞれ脳の異なる領域を動かします。この3つの要素を一度に考えようとすると、混乱につながります。
## 説明
![説明](/docs/img/tut-struct1.png)
そのサンプルが何を示しているかを記載してください。可能であれば、各サンプルには関連する特定のタスクを達成するための手順を記述してください。(NFTの売却オファーの作成、売却オファーの受け入れ、売却オファーの削除など)。チュートリアルで説明されている内容を理解するのに十分なコンセプトに関する情報を記載し、必要であれば、追加情報へのリンクも記載します。
## 前提条件
![前提条件](/docs/img/tut-struct2.png)
必要なソフトウェアと、チュートリアルを実行するために必要なすべてのサンプルコードへのリンクを提供します。必要であれば、サードパーティのツールの使い方を簡単に説明しますが、ユーザが自由に深く掘り下げることができるように、ソースとなるウェブサイトへのリンクを提供します。
## 使用例
![使用例](/docs/img/tut-struct3.png)
チュートリアルのアプリケーションの完成した動作例を提供することから始めましょう。これは、ソフトウェアを使って問題を解決するチャンスです。
 
チュートリアルの各ステップにはスクリーンショットを使用してください。これによって、ユーザは自分でコードを実行しなくてもチュートリアルを理解することができます。もちろん、コードを実行することが _望ましい_ ですが、これにりユーザに選択肢を与えることができます。
適切な条件におけるシナリオを記述してください。インターネットへの接続が途切れなければ、アプリケーションは問題なく動作するはずです。チュートリアルに関連しないトラブルシューティングの情報を提供しないでください。
## コード解説
![コード解説](/docs/img/tut-struct4.png)
コードを1ブロックずつ見ていきましょう。既に説明したトピックを繰り返さないでください。サンプルコードには、HTML構文のような基本的な部分のプログラミング方法については、その実装に独自なものがない限り、詳細な説明はしないでください。
強調すべき重要なことは、XRPLとのやりとりはすべてトランザクションかリクエストであり、すべてのトランザクションとリクエストは本質的に同じであるということです。私たちが提供するサンプルコードは、トランザクションやリクエストを準備する方法と、返された結果を処理する方法を示しています。1つのトランザクションやリクエストをどのように送信しどのようなレスポンスを返すかを知ることは、他のトランザクションやリクエストの処理について非常に良いヒントとなります。
(技術的には、リクエストに似た第3のカテゴリがあります。[Subscriptionメソッド](../../docs/references/http-websocket-apis/public-api-methods/subscription-methods/index.md)をご覧ください)。
## 関連項目
![関連項目](/docs/img/tut-struct5.png)
チュートリアルの最後には、追加の資料、概念的な情報、学習のにおいて有益な次のステップとなるチュートリアルへのリンクを提供します。

View File

@@ -1,449 +1,152 @@
import * as React from "react";
import { useThemeConfig, useThemeHooks } from "@redocly/theme/core/hooks";
import { LanguagePicker } from "@redocly/theme/components/LanguagePicker/LanguagePicker";
import { slugify } from "../../helpers";
import { Link } from "@redocly/theme/components/Link/Link";
import { ColorModeSwitcher } from "@redocly/theme/components/ColorModeSwitcher/ColorModeSwitcher";
import { Search } from "@redocly/theme/components/Search/Search";
import arrowUpRight from "../../../static/img/icons/arrow-up-right-custom.svg";
import moment from "moment-timezone";
import { useSearchDialog } from "@redocly/theme/core/hooks";
import { SearchDialog } from "@redocly/theme/components/Search/SearchDialog";
// @ts-ignore
// Import from modular components
import { AlertBanner } from "./components/AlertBanner";
import { NavLogo } from "./components/NavLogo";
import { NavItems } from "./components/NavItems";
import { NavControls, HamburgerButton } from "./controls";
import { DevelopSubmenu, UseCasesSubmenu, CommunitySubmenu, NetworkSubmenu } from "./submenus";
import { MobileMenu } from "./mobile-menu";
import { alertBanner } from "./constants/navigation";
const alertBanner = {
show: false,
message: "APEX 2025",
button: "REGISTER",
link: "https://www.xrpledgerapex.com/?utm_source=xrplwebsite&utm_medium=direct&utm_campaign=xrpl-event-ho-xrplapex-glb-2025-q1_xrplwebsite_ari_arp_bf_rsvp&utm_content=cta_btn_english_pencilbanner"
};
// Re-export AlertBanner for backwards compatibility
export { AlertBanner } from "./components/AlertBanner";
export function AlertBanner({ message, button, link, show }) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const bannerRef = React.useRef(null);
const [displayDate, setDisplayDate] = React.useState("JUNE 10-12");
React.useEffect(() => {
const calculateCountdown = () => {
// Calculate days until June 11, 2025 8AM Singapore time
// This will automatically adjust for the user's timezone
const target = moment.tz('2025-06-11 08:00:00', 'Asia/Singapore');
const now = moment();
const daysUntil = target.diff(now, 'days');
// Show countdown if event is in the future, otherwise show the provided date
let newDisplayDate = "JUNE 10-12";
if (daysUntil > 0) {
newDisplayDate = daysUntil === 1 ? 'IN 1 DAY' : `IN ${daysUntil} DAYS`;
} else if (daysUntil === 0) {
// Check if it's today
const hoursUntil = target.diff(now, 'hours');
newDisplayDate = hoursUntil > 0 ? 'TODAY' : "JUNE 10-12";
}
setDisplayDate(newDisplayDate);
};
// Calculate immediately
calculateCountdown();
// Update every hour
const interval = setInterval(calculateCountdown, 60 * 60 * 1000);
return () => clearInterval(interval);
}, []);
React.useEffect(() => {
const banner = bannerRef.current;
if (!banner) return;
const handleMouseEnter = () => {
banner.classList.add("has-hover");
};
// Attach the event listener
banner.addEventListener("mouseenter", handleMouseEnter);
// Clean up the event listener on unmount
return () => {
banner.removeEventListener("mouseenter", handleMouseEnter);
};
}, []);
if (show) {
return (
<a
href={link}
target="_blank"
ref={bannerRef}
className="top-banner fixed-top web-banner"
rel="noopener noreferrer"
aria-label="Get Tickets for the APEX 2025 Event"
>
<div className="banner-event-details">
<div className="event-info">{translate(message)}</div>
<div className="event-date">{displayDate}</div>
</div>
<div className="banner-button">
<div className="button-text">{translate(button)}</div>
<img
className="button-icon"
src={arrowUpRight}
alt="Get Tickets Icon"
/>
</div>
</a>
);
}
return null;
// Props interface for Navbar (extensible for future use)
interface NavbarProps {
className?: string;
}
export function Navbar(props) {
// const [isOpen, setIsOpen] = useMobileMenu(false);
const themeConfig = useThemeConfig();
const { useL10n } = useThemeHooks();
const { changeLanguage } = useL10n();
const menu = themeConfig.navbar?.items;
const logo = themeConfig.logo;
const { href, altText, items } = props;
const pathPrefix = "";
/**
* Main Navbar Component.
* Renders the complete navigation bar including:
* - Alert banner (when enabled)
* - Logo
* - Navigation items with desktop submenus
* - Control buttons (search, theme toggle, language)
* - Mobile menu
*/
export function Navbar(_props: NavbarProps = {}) {
const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false);
const [activeSubmenu, setActiveSubmenu] = React.useState<string | null>(null);
const [closingSubmenu, setClosingSubmenu] = React.useState<string | null>(null);
const submenuTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);
const closingTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);
const navItems = menu.map((item, index) => {
if (item.type === "group") {
return (
<NavDropdown
key={index}
label={item.label}
labelTranslationKey={item.labelTranslationKey}
items={item.items}
pathPrefix={pathPrefix}
/>
);
} else {
return (
<NavItem key={index}>
<Link to={item.link} className="nav-link">
{item.label}
</Link>
</NavItem>
);
// Use Redocly's search dialog hook - shared across navbar and mobile menu
const { isOpen: isSearchOpen, onOpen: onSearchOpen, onClose: onSearchClose } = useSearchDialog();
const handleHamburgerClick = () => {
setMobileMenuOpen(true);
};
const handleMobileMenuClose = () => {
setMobileMenuOpen(false);
};
const handleSubmenuMouseEnter = (itemLabel: string) => {
// Clear any pending close/closing timeouts
if (submenuTimeoutRef.current) {
clearTimeout(submenuTimeoutRef.current);
submenuTimeoutRef.current = null;
}
});
if (closingTimeoutRef.current) {
clearTimeout(closingTimeoutRef.current);
closingTimeoutRef.current = null;
}
// Cancel closing state and activate the new submenu
setClosingSubmenu(null);
setActiveSubmenu(itemLabel);
};
const handleSubmenuMouseLeave = () => {
submenuTimeoutRef.current = setTimeout(() => {
// Start closing animation
const currentSubmenu = activeSubmenu;
if (currentSubmenu) {
setClosingSubmenu(currentSubmenu);
setActiveSubmenu(null);
// After animation completes (300ms), clear closing state
closingTimeoutRef.current = setTimeout(() => {
setClosingSubmenu(null);
}, 350); // Slightly longer than animation to ensure completion
}
}, 150);
};
const handleSubmenuClose = () => {
// Close submenu immediately (for keyboard navigation)
if (activeSubmenu) {
setClosingSubmenu(activeSubmenu);
setActiveSubmenu(null);
// After animation completes, clear closing state
closingTimeoutRef.current = setTimeout(() => {
setClosingSubmenu(null);
}, 350);
}
};
// Handle scroll lock when submenu is open or closing
React.useEffect(() => {
if (activeSubmenu || closingSubmenu) {
document.body.classList.add('bds-submenu-open');
} else {
document.body.classList.remove('bds-submenu-open');
}
return () => {
document.body.classList.remove('bds-submenu-open');
};
}, [activeSubmenu, closingSubmenu]);
React.useEffect(() => {
// Turns out jQuery is necessary for firing events on Bootstrap v4
// dropdowns. These events set classes so that the search bar and other
// submenus collapse on mobile when you expand one submenu.
const dds = $("#topnav-pages .dropdown");
const top_main_nav = document.querySelector("#top-main-nav");
dds.on("show.bs.dropdown", (evt) => {
top_main_nav.classList.add("submenu-expanded");
});
dds.on("hidden.bs.dropdown", (evt) => {
top_main_nav.classList.remove("submenu-expanded");
});
// Close navbar on .dropdown-item click
const toggleNavbar = () => {
const navbarToggler = document.querySelector(".navbar-toggler");
const isNavbarCollapsed =
navbarToggler.getAttribute("aria-expanded") === "true";
if (isNavbarCollapsed) {
navbarToggler?.click(); // Simulate click to toggle navbar
return () => {
if (submenuTimeoutRef.current) {
clearTimeout(submenuTimeoutRef.current);
}
if (closingTimeoutRef.current) {
clearTimeout(closingTimeoutRef.current);
}
};
const dropdownItems = document.querySelectorAll(".dropdown-item");
dropdownItems.forEach((item) => {
item.addEventListener("click", toggleNavbar);
});
// Cleanup function to remove event listeners
return () => {
dropdownItems.forEach((item) => {
item.removeEventListener("click", toggleNavbar);
});
};
}, []);
const navbarClasses = [
"bds-navbar",
alertBanner.show ? "bds-navbar--with-banner" : ""
].filter(Boolean).join(" ");
return (
<>
<AlertBanner {...alertBanner} />
<NavWrapper belowAlertBanner={alertBanner.show}>
<LogoBlock to={href} img={logo} alt={altText} />
<NavControls>
<MobileMenuIcon />
</NavControls>
<TopNavCollapsible>
<NavItems>
{navItems}
<div id="topnav-search" className="nav-item search">
<Search className="topnav-search" />
</div>
<div id="topnav-language" className="nav-item">
<LanguagePicker
onChangeLanguage={changeLanguage}
onlyIcon
alignment="end"
/>
</div>
<div id="topnav-theme" className="nav-item">
<ColorModeSwitcher />
</div>
</NavItems>
</TopNavCollapsible>
</NavWrapper>
{/* Backdrop blur overlay when submenu is open or closing */}
<div
className={`bds-submenu-backdrop ${activeSubmenu || closingSubmenu ? 'bds-submenu-backdrop--active' : ''}`}
onClick={() => setActiveSubmenu(null)}
/>
<header
className={navbarClasses}
onMouseLeave={handleSubmenuMouseLeave}
>
<div className="bds-navbar__content">
<NavLogo />
<NavItems activeSubmenu={activeSubmenu} onSubmenuEnter={handleSubmenuMouseEnter} onSubmenuClose={handleSubmenuClose} />
<NavControls onSearch={onSearchOpen} />
<HamburgerButton onClick={handleHamburgerClick} />
</div>
{/* Submenus positioned relative to navbar */}
<div onMouseEnter={() => activeSubmenu && handleSubmenuMouseEnter(activeSubmenu)}>
<DevelopSubmenu isActive={activeSubmenu === 'Develop'} isClosing={closingSubmenu === 'Develop'} onClose={handleSubmenuClose} />
<UseCasesSubmenu isActive={activeSubmenu === 'Use Cases'} isClosing={closingSubmenu === 'Use Cases'} onClose={handleSubmenuClose} />
<CommunitySubmenu isActive={activeSubmenu === 'Community'} isClosing={closingSubmenu === 'Community'} onClose={handleSubmenuClose} />
<NetworkSubmenu isActive={activeSubmenu === 'Network'} isClosing={closingSubmenu === 'Network'} onClose={handleSubmenuClose} />
</div>
</header>
<MobileMenu isOpen={mobileMenuOpen} onClose={handleMobileMenuClose} onSearch={onSearchOpen} />
{/* Render SearchDialog when open - this is the actual search modal */}
{isSearchOpen && <SearchDialog onClose={onSearchClose} />}
</>
);
}
export function TopNavCollapsible({ children }) {
return (
<div
className="collapse navbar-collapse justify-content-between"
id="top-main-nav"
>
{children}
</div>
);
}
export function NavDropdown(props) {
const { label, items, pathPrefix, labelTranslationKey } = props;
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const dropdownGroups = items.map((item, index) => {
if (item.items) {
const groupLinks = item.items.map((item2, index2) => {
const cls2 = item2.external
? "dropdown-item external-link"
: "dropdown-item";
let item2_href = item2.link;
if (item2_href && !item2_href.match(/^https?:/)) {
item2_href = pathPrefix + item2_href;
}
//conditional specific for brand kit
if (item2.link === "/XRPL_Brand_Kit.zip") {
return (
<a key={index2} href="/XRPL_Brand_Kit.zip" className={cls2}>
{translate(item2.labelTranslationKey, item2.label)}
</a>
);
}
return (
<Link key={index2} className={cls2} to={item2_href}>
{translate(item2.labelTranslationKey, item2.label)}
</Link>
);
});
const clnm = "navcol col-for-" + slugify(item.label);
return (
<div key={index} className={clnm}>
<h5 className="dropdown-item">
{translate(item.labelTranslationKey, item.label)}
</h5>
{groupLinks}
</div>
);
} else if (item.icon) {
const hero_id = "dropdown-hero-for-" + slugify(label);
const img_alt = item.label + " icon";
let hero_href = item.link;
if (hero_href && !hero_href.match(/^https?:/)) {
hero_href = pathPrefix + hero_href;
}
const splitlabel = item.label.split(" || ");
let splittranslationkey = ["", ""];
if (item.labelTranslationKey) {
splittranslationkey = item.labelTranslationKey.split(" || ");
}
const newlabel = translate(splittranslationkey[0], splitlabel[0]);
const description = translate(splittranslationkey[1], splitlabel[1]); // splitlabel[1] might be undefined, that's ok
return (
<Link
key={index}
className="dropdown-item dropdown-hero"
id={hero_id}
to={hero_href}
>
<img id={item.hero} alt={img_alt} src={item.icon} />
<div className="dropdown-hero-text">
<h4>{newlabel}</h4>
<p>{description}</p>
</div>
</Link>
);
} else {
const cls = item.external
? "dropdown-item ungrouped external-link"
: "dropdown-item ungrouped";
let item_href = item.link;
if (item_href && !item_href.match(/^https?:/)) {
item_href = pathPrefix + item_href;
}
return (
<Link key={index} className={cls} to={item_href}>
{translate(item.labelTranslationKey, item.label)}
</Link>
);
}
});
const toggler_id = "topnav_" + slugify(label);
const dd_id = "topnav_dd_" + slugify(label);
return (
<li className="nav-item dropdown">
<a
className="nav-link dropdown-toggle"
href="#"
id={toggler_id}
role="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
<span>{translate(labelTranslationKey, label)}</span>
</a>
<div className="dropdown-menu" aria-labelledby={toggler_id} id={dd_id}>
{dropdownGroups}
</div>
</li>
);
}
export function NavWrapper(props) {
return (
<nav
className="top-nav navbar navbar-expand-lg navbar-dark fixed-top"
style={props.belowAlertBanner ? { marginTop: "52px" } : {}}
>
{props.children}
</nav>
);
}
export function NavControls(props) {
return (
<button
className="navbar-toggler collapsed"
type="button"
data-toggle="collapse"
data-target="#top-main-nav"
aria-controls="navbarHolder"
aria-expanded="false"
aria-label="Toggle navigation"
>
{props.children}
</button>
);
}
export function MobileMenuIcon() {
return (
<span className="navbar-toggler-icon">
<div></div>
</span>
);
}
export function GetStartedButton() {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
return (
<Link
className="btn btn-primary"
to={"/docs/tutorials"}
style={{ height: "38px", paddingTop: "11px" }}
>
{translate("Get Started")}
</Link>
);
}
export function NavItems(props) {
return (
<ul className="nav navbar-nav" id="topnav-pages">
{props.children}
</ul>
);
}
export function NavItem(props) {
return <li className="nav-item">{props.children}</li>;
}
export function LogoBlock(props) {
const { to, img, altText } = props;
return (
<Link className="navbar-brand" to="/">
<img className="logo" alt={"XRP LEDGER"} height="40" src="data:," />
</Link>
);
}
export class ThemeToggle extends React.Component {
auto_update_theme() {
const upc = window.localStorage.getItem("user-prefers-color");
let theme = "dark"; // Default to dark theme
if (!upc) {
// User hasn't saved a preference specifically for this site; check
// the browser-level preferences.
if (
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: light)").matches
) {
theme = "light";
}
} else {
// Follow user's saved setting.
theme = upc == "light" ? "light" : "dark";
}
const disable_theme = theme == "dark" ? "light" : "dark";
document.documentElement.classList.add(theme);
document.documentElement.classList.remove(disable_theme);
}
user_choose_theme() {
const new_theme = document.documentElement.classList.contains("dark")
? "light"
: "dark";
window.localStorage.setItem("user-prefers-color", new_theme);
document.body.style.transition = "background-color .2s ease";
const disable_theme = new_theme == "dark" ? "light" : "dark";
document.documentElement.classList.add(new_theme);
document.documentElement.classList.remove(disable_theme);
}
render() {
return (
<div className="nav-item" id="topnav-theme">
<form className="form-inline">
<div
className="custom-control custom-theme-toggle form-inline-item"
title=""
data-toggle="tooltip"
data-placement="left"
data-original-title="Toggle Dark Mode"
>
<input
type="checkbox"
className="custom-control-input"
id="css-toggle-btn"
onClick={this.user_choose_theme}
/>
<label className="custom-control-label" htmlFor="css-toggle-btn">
<span className="d-lg-none">Light/Dark Theme</span>
</label>
</div>
</form>
</div>
);
}
componentDidMount() {
this.auto_update_theme();
}
}

View File

@@ -0,0 +1,82 @@
import * as React from "react";
import { useThemeHooks } from "@redocly/theme/core/hooks";
import moment from "moment-timezone";
import { arrowUpRight } from "../constants/icons";
interface AlertBannerProps {
message: string;
button: string;
link: string;
show: boolean;
}
/**
* Alert Banner Component.
* Displays a promotional banner at the top of the page.
*/
export function AlertBanner({ message, button, link, show }: AlertBannerProps) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const bannerRef = React.useRef<HTMLAnchorElement>(null);
// Use null initial state to avoid hydration mismatch - server and client both render null initially
const [displayDate, setDisplayDate] = React.useState<string | null>(null);
React.useEffect(() => {
const calculateCountdown = () => {
const target = moment.tz('2025-06-11 08:00:00', 'Asia/Singapore');
const now = moment();
const daysUntil = target.diff(now, 'days');
let newDisplayDate = "JUNE 10-12";
if (daysUntil > 0) {
newDisplayDate = daysUntil === 1 ? 'IN 1 DAY' : `IN ${daysUntil} DAYS`;
} else if (daysUntil === 0) {
const hoursUntil = target.diff(now, 'hours');
newDisplayDate = hoursUntil > 0 ? 'TODAY' : "JUNE 10-12";
}
setDisplayDate(newDisplayDate);
};
calculateCountdown();
const interval = setInterval(calculateCountdown, 60 * 60 * 1000);
return () => clearInterval(interval);
}, []);
React.useEffect(() => {
const banner = bannerRef.current;
if (!banner) return;
const handleMouseEnter = () => {
banner.classList.add("has-hover");
};
banner.addEventListener("mouseenter", handleMouseEnter);
return () => {
banner.removeEventListener("mouseenter", handleMouseEnter);
};
}, []);
if (!show) return null;
return (
<a
href={link}
target="_blank"
ref={bannerRef}
className="top-banner fixed-top web-banner"
rel="noopener noreferrer"
aria-label={translate("Get Tickets for the APEX 2025 Event")}
>
<div className="banner-event-details">
<div className="event-info">{translate(message)}</div>
<div className="event-date">{displayDate ?? translate("JUNE 10-12")}</div>
</div>
<div className="banner-button">
<div className="button-text">{translate(button)}</div>
<img className="button-icon" src={arrowUpRight} alt="" />
</div>
</a>
);
}

View File

@@ -0,0 +1,114 @@
import * as React from "react";
import { useThemeHooks } from "@redocly/theme/core/hooks";
import { BdsLink } from "../../../../shared/components/Link/Link";
import { navItems } from "../constants/navigation";
interface NavItemsProps {
activeSubmenu: string | null;
onSubmenuEnter: (itemLabel: string) => void;
onSubmenuClose?: () => void;
}
/**
* Nav Items Component.
* Centered navigation links with submenu support.
* ARIA compliant with full keyboard navigation support.
*/
export function NavItems({ activeSubmenu, onSubmenuEnter, onSubmenuClose }: NavItemsProps) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const [activeItem, setActiveItem] = React.useState<string | null>(null);
const handleMouseEnter = (itemLabel: string, hasSubmenu: boolean) => {
setActiveItem(itemLabel);
if (hasSubmenu) {
onSubmenuEnter(itemLabel);
}
};
const handleMouseLeave = (hasSubmenu: boolean) => {
if (!hasSubmenu) {
setActiveItem(null);
}
// Don't close submenu on leave - let the parent Navbar handle that
};
const handleKeyDown = (event: React.KeyboardEvent, itemLabel: string) => {
switch (event.key) {
case 'Enter':
case ' ':
event.preventDefault();
// Toggle submenu on Enter/Space
if (activeSubmenu === itemLabel) {
onSubmenuClose?.();
} else {
onSubmenuEnter(itemLabel);
}
break;
case 'Escape':
event.preventDefault();
onSubmenuClose?.();
break;
case 'Tab':
// If submenu is open and Tab is pressed (without Shift), move focus into submenu
if (activeSubmenu === itemLabel && !event.shiftKey) {
event.preventDefault();
// Focus first focusable element in submenu
const submenu = document.querySelector('.bds-submenu--active');
const firstFocusable = submenu?.querySelector<HTMLElement>('a, button');
firstFocusable?.focus();
}
break;
case 'ArrowDown':
// If submenu is open, move focus into submenu
if (activeSubmenu === itemLabel) {
event.preventDefault();
// Focus first focusable element in submenu
const submenu = document.querySelector('.bds-submenu--active');
const firstFocusable = submenu?.querySelector<HTMLElement>('a, button');
firstFocusable?.focus();
}
break;
}
};
// Sync activeItem with activeSubmenu state
React.useEffect(() => {
if (!activeSubmenu) {
setActiveItem(null);
}
}, [activeSubmenu]);
return (
<nav className="bds-navbar__items" aria-label={translate("Main navigation")}>
{navItems.map((item) => (
item.hasSubmenu ? (
<button
key={item.label}
type="button"
className={`bds-navbar__item ${activeItem === item.label || activeSubmenu === item.label ? 'bds-navbar__item--active' : ''}`}
onMouseEnter={() => handleMouseEnter(item.label, true)}
onMouseLeave={() => handleMouseLeave(true)}
onKeyDown={(e) => handleKeyDown(e, item.label)}
aria-expanded={activeSubmenu === item.label}
aria-haspopup="menu"
>
{translate(item.labelTranslationKey, item.label)}
</button>
) : (
<BdsLink
key={item.label}
href={item.href}
className={`bds-navbar__item ${activeItem === item.label ? 'bds-navbar__item--active' : ''}`}
onMouseEnter={() => handleMouseEnter(item.label, false)}
onMouseLeave={() => handleMouseLeave(false)}
variant="inline"
>
{translate(item.labelTranslationKey, item.label)}
</BdsLink>
)
))}
</nav>
);
}

View File

@@ -0,0 +1,34 @@
import { useThemeHooks } from "@redocly/theme/core/hooks";
import { BdsLink } from "../../../../shared/components/Link/Link";
import { xrpSymbolBlack, xrpLogotypeBlack, xrpLedgerNav } from "../constants/icons";
/**
* Nav Logo Component.
* Shows symbol on desktop/mobile, full logotype on tablet.
* On desktop hover, the "XRP LEDGER" text animates out to the right.
*/
export function NavLogo() {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
return (
<BdsLink href="/" className="bds-navbar__logo" aria-label={translate("XRP Ledger Home")} variant="inline">
<img
src={xrpSymbolBlack}
alt={translate("XRP Ledger")}
className="bds-navbar__logo-symbol"
/>
<img
src={xrpLedgerNav}
alt=""
className="bds-navbar__logo-text"
/>
<img
src={xrpLogotypeBlack}
alt={translate("XRP Ledger")}
className="bds-navbar__logo-full"
/>
</BdsLink>
);
}

View File

@@ -0,0 +1,5 @@
// Re-export navbar components
export { AlertBanner } from './AlertBanner';
export { NavLogo } from './NavLogo';
export { NavItems } from './NavItems';

View File

@@ -0,0 +1,85 @@
// Navbar icon imports
// Main navbar icons
export { default as xrpSymbolBlack } from "../../../../static/img/navbar/xrp-symbol-black.svg";
export { default as xrpLogotypeBlack } from "../../../../static/img/navbar/xrp-logotype-black.svg";
export { default as xrpLedgerNav } from "../../../../static/img/navbar/xrp-ledger-nav.svg";
export { default as searchIcon } from "../../../../static/img/navbar/search-icon.svg";
export { default as modeToggleIcon } from "../../../../static/img/navbar/mode-toggle.svg";
export { default as globeIcon } from "../../../../static/img/navbar/globe-icon.svg";
export { default as chevronDown } from "../../../../static/img/navbar/chevron-down.svg";
export { default as hamburgerIcon } from "../../../../static/img/navbar/hamburger-icon.svg";
export { default as arrowUpRight } from "../../../../static/img/icons/arrow-up-right-custom.svg";
// Wallet icons for submenu
export { default as greenWallet } from "../../../../static/img/navbar/green-wallet.svg";
export { default as lilacWallet } from "../../../../static/img/navbar/lilac-wallet.svg";
export { default as yellowWallet } from "../../../../static/img/navbar/yellow-wallet.svg";
export { default as pinkWallet } from "../../../../static/img/navbar/pink-wallet.svg";
export { default as blueWallet } from "../../../../static/img/navbar/blue-wallet.svg";
// Develop submenu icons
export { default as devHomeIcon } from "../../../../static/img/navbar/dev_home.svg";
export { default as learnIcon } from "../../../../static/img/navbar/learn.svg";
export { default as codeSamplesIcon } from "../../../../static/img/navbar/code_samples.svg";
export { default as docsIcon } from "../../../../static/img/navbar/docs.svg";
export { default as clientLibIcon } from "../../../../static/img/navbar/client_lib.svg";
// Use Cases submenu icons
export { default as paymentsIcon } from "../../../../static/img/navbar/payments.svg";
export { default as tokenizationIcon } from "../../../../static/img/navbar/tokenization.svg";
export { default as creditIcon } from "../../../../static/img/navbar/credit.svg";
export { default as tradingIcon } from "../../../../static/img/navbar/trading.svg";
// Community submenu icons
export { default as communityIcon } from "../../../../static/img/navbar/community.svg";
// Network submenu icons
export { default as insightsIcon } from "../../../../static/img/navbar/insights.svg";
export { default as resourcesIcon } from "../../../../static/img/navbar/resources.svg";
// Network submenu pattern images
export { default as resourcesPurplePattern } from "../../../../static/img/navbar/resources-purple.svg";
export { default as insightsGreenPattern } from "../../../../static/img/navbar/insights-green.svg";
export { default as darkInsightsGreenPattern } from "../../../../static/img/navbar/dark-insights-green.svg";
export { default as darkLilacPattern } from "../../../../static/img/navbar/dark-lilac.svg";
// Wallet icon mapping for dynamic icon lookup
import greenWallet from "../../../../static/img/navbar/green-wallet.svg";
import lilacWallet from "../../../../static/img/navbar/lilac-wallet.svg";
import yellowWallet from "../../../../static/img/navbar/yellow-wallet.svg";
import pinkWallet from "../../../../static/img/navbar/pink-wallet.svg";
import blueWallet from "../../../../static/img/navbar/blue-wallet.svg";
import devHomeIcon from "../../../../static/img/navbar/dev_home.svg";
import learnIcon from "../../../../static/img/navbar/learn.svg";
import codeSamplesIcon from "../../../../static/img/navbar/code_samples.svg";
import docsIcon from "../../../../static/img/navbar/docs.svg";
import clientLibIcon from "../../../../static/img/navbar/client_lib.svg";
import paymentsIcon from "../../../../static/img/navbar/payments.svg";
import tokenizationIcon from "../../../../static/img/navbar/tokenization.svg";
import creditIcon from "../../../../static/img/navbar/credit.svg";
import tradingIcon from "../../../../static/img/navbar/trading.svg";
import communityIcon from "../../../../static/img/navbar/community.svg";
import insightsIcon from "../../../../static/img/navbar/insights.svg";
import resourcesIcon from "../../../../static/img/navbar/resources.svg";
export const walletIcons: Record<string, string> = {
green: greenWallet,
lilac: lilacWallet,
yellow: yellowWallet,
pink: pinkWallet,
blue: blueWallet,
dev_home: devHomeIcon,
learn: learnIcon,
code_samples: codeSamplesIcon,
docs: docsIcon,
client_lib: clientLibIcon,
payments: paymentsIcon,
tokenization: tokenizationIcon,
credit: creditIcon,
trading: tradingIcon,
community: communityIcon,
insights: insightsIcon,
resources: resourcesIcon,
};

View File

@@ -0,0 +1,4 @@
// Re-export all constants
export * from './icons';
export * from './navigation';

View File

@@ -0,0 +1,166 @@
import type { NavItem, SubmenuItemBase, SubmenuItemWithChildren, SubmenuItem, NetworkSubmenuSection } from '../types';
// Alert Banner Configuration
export const alertBanner = {
show: false,
message: "APEX 2025",
button: "REGISTER",
link: "https://www.xrpledgerapex.com/?utm_source=xrplwebsite&utm_medium=direct&utm_campaign=xrpl-event-ho-xrplapex-glb-2025-q1_xrplwebsite_ari_arp_bf_rsvp&utm_content=cta_btn_english_pencilbanner"
};
// Main navigation items
export const navItems: NavItem[] = [
{ label: "Develop", labelTranslationKey: "navbar.develop", href: "/docs", hasSubmenu: true },
{ label: "Use Cases", labelTranslationKey: "navbar.usecases", href: "/about/uses", hasSubmenu: true },
{ label: "Community", labelTranslationKey: "navbar.community", href: "/community", hasSubmenu: true },
{ label: "Network", labelTranslationKey: "navbar.network", href: "/docs/concepts/networks-and-servers", hasSubmenu: true },
];
// Develop submenu data structure
export const developSubmenuData: {
left: SubmenuItemBase[];
right: SubmenuItemWithChildren[];
} = {
left: [
{ label: "Developer's Home", href: "/docs", icon: "dev_home" },
{ label: "Learn", href: "/docs/tutorials", icon: "learn" },
{ label: "Code Samples", href: "/_code-samples", icon: "code_samples" },
],
right: [
{
label: "Docs",
href: "/docs",
icon: "docs",
children: [
{ label: "API Reference", href: "/docs/references" },
{ label: "Tutorials", href: "/docs/tutorials" },
{ label: "Concepts", href: "/docs/concepts" },
{ label: "Infrastructure", href: "/docs/infrastructure" },
],
},
{
label: "Client Libraries",
href: "/docs/references/client-libraries",
icon: "client_lib",
children: [
{ label: "JavaScript", href: "/docs/references/xrpljs" },
{ label: "Python", href: "/docs/references/xrpl-py" },
{ label: "PHP", href: "/docs/references/xrpl-php" },
{ label: "Go", href: "/docs/references/xrpl-go" },
],
},
],
};
// Use Cases submenu data structure
export const useCasesSubmenuData: {
left: SubmenuItemWithChildren[];
right: SubmenuItemWithChildren[];
} = {
left: [
{
label: "Payments",
href: "/about/uses/payments",
icon: "payments",
children: [
{ label: "Direct XRP Payments", href: "/about/uses/direct-xrp-payments" },
{ label: "Cross-currency Payments", href: "/about/uses/cross-currency-payments" },
{ label: "Escrow", href: "/about/uses/escrow" },
{ label: "Checks", href: "/about/uses/checks" },
],
},
{
label: "Tokenization",
href: "/about/uses/tokenization",
icon: "tokenization",
children: [
{ label: "Stablecoin", href: "/about/uses/stablecoin" },
{ label: "NFT", href: "/about/uses/nft" },
],
},
],
right: [
{
label: "Credit",
href: "/about/uses/credit",
icon: "credit",
children: [
{ label: "Lending", href: "/about/uses/lending" },
{ label: "Collateralization", href: "/about/uses/collateralization" },
{ label: "Sustainability", href: "/about/uses/sustainability" },
],
},
{
label: "Trading",
href: "/about/uses/trading",
icon: "trading",
children: [
{ label: "DEX", href: "/about/uses/dex" },
{ label: "Permissioned Trading", href: "/about/uses/permissioned-trading" },
{ label: "AMM", href: "/about/uses/amm" },
],
},
],
};
// Community submenu data structure
export const communitySubmenuData: {
left: SubmenuItem[];
right: SubmenuItem[];
} = {
left: [
{
label: "Community",
href: "/community",
icon: "community",
children: [
{ label: "Events", href: "/community/events" },
{ label: "News", href: "/blog", active: true },
{ label: "Blog", href: "/blog" },
{ label: "Marketplace", href: "/community/marketplace" },
{ label: "Partner Connect", href: "/community/partner-connect" },
],
},
{ label: "Funding", href: "/community/developer-funding", icon: "code_samples" },
],
right: [
{
label: "Contribute",
href: "/resources/contribute-documentation",
icon: "client_lib",
children: [
{ label: "Ecosystem Map", href: "/community/ecosystem-map" },
{ label: "Bug Bounty", href: "/community/bug-bounty" },
{ label: "Research", href: "/community/research" },
],
},
{ label: "Creators", href: "/community/ambassadors", icon: "learn" },
],
};
// Network submenu data
export const networkSubmenuData: NetworkSubmenuSection[] = [
{
label: "Resources",
href: "/docs/concepts/networks-and-servers",
icon: "resources",
children: [
{ label: "Validators", href: "/docs/concepts/networks-and-servers/validators" },
{ label: "Governance", href: "/docs/concepts/networks-and-servers/governance", active: true },
{ label: "XRPL Roadmap", href: "/docs/concepts/networks-and-servers/xrpl-roadmap" },
],
patternColor: 'lilac',
},
{
label: "Insights",
href: "/docs/concepts/networks-and-servers/insights",
icon: "insights",
children: [
{ label: "Explorer", href: "https://livenet.xrpl.org" },
{ label: "Data Dashboard", href: "/docs/concepts/networks-and-servers/data-dashboard" },
{ label: "Amendment Voting Status", href: "/docs/concepts/networks-and-servers/amendments" },
],
patternColor: 'green',
},
];

View File

@@ -0,0 +1,19 @@
import { useThemeHooks } from "@redocly/theme/core/hooks";
import { IconButton } from "./IconButton";
import { hamburgerIcon } from "../constants/icons";
interface HamburgerButtonProps {
onClick?: () => void;
}
/**
* Hamburger Menu Button Component.
* Mobile-only button that opens the mobile menu.
*/
export function HamburgerButton({ onClick }: HamburgerButtonProps) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
return <IconButton icon={hamburgerIcon} ariaLabel={translate("Open menu")} className="bds-navbar__hamburger" onClick={onClick} />;
}

View File

@@ -0,0 +1,28 @@
interface IconButtonProps {
/** The icon image source */
icon: string;
/** Accessible label for the button */
ariaLabel: string;
/** Optional click handler */
onClick?: () => void;
/** CSS class name for styling variants */
className?: string;
}
/**
* Unified Icon Button component.
* Used for search, mode toggle, hamburger menu, and other icon-only buttons.
*/
export function IconButton({ icon, ariaLabel, onClick, className = "bds-navbar__icon" }: IconButtonProps) {
return (
<button
type="button"
className={className}
aria-label={ariaLabel}
onClick={onClick}
>
<img src={icon} alt="" />
</button>
);
}

View File

@@ -0,0 +1,85 @@
import * as React from "react";
import { useLanguagePicker, useThemeHooks } from "@redocly/theme/core/hooks";
interface LanguageDropdownProps {
isOpen: boolean;
onClose: () => void;
}
/**
* Language Dropdown Component.
* Displays available language options in a dropdown menu.
* Based on Figma design: dark background with rounded corners.
*/
export function LanguageDropdown({ isOpen, onClose }: LanguageDropdownProps) {
const { currentLocale, locales, setLocale } = useLanguagePicker();
const { useL10n, useTranslate } = useThemeHooks();
const { changeLanguage } = useL10n();
const { translate } = useTranslate();
const dropdownRef = React.useRef<HTMLDivElement>(null);
// Handle clicking outside to close
React.useEffect(() => {
if (!isOpen) return;
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
// Check if click was on the language pill (parent trigger)
const target = event.target as HTMLElement;
if (!target.closest('.bds-navbar__lang-pill')) {
onClose();
}
}
};
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onClose();
}
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('keydown', handleEscape);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleEscape);
};
}, [isOpen, onClose]);
if (!isOpen || locales.length < 2) {
return null;
}
const handleLanguageSelect = (localeCode: string) => {
setLocale(localeCode);
changeLanguage(localeCode);
onClose();
};
return (
<div
ref={dropdownRef}
className="bds-lang-dropdown"
role="menu"
aria-label={translate("Language selection")}
>
{locales.map((locale) => {
const isActive = locale.code === currentLocale?.code;
return (
<button
key={locale.code}
type="button"
role="menuitem"
className={`bds-lang-dropdown__item ${isActive ? 'bds-lang-dropdown__item--active' : ''}`}
onClick={() => handleLanguageSelect(locale.code)}
aria-current={isActive ? 'true' : undefined}
>
{locale.name || locale.code}
</button>
);
})}
</div>
);
}

View File

@@ -0,0 +1,53 @@
import { useLanguagePicker, useThemeHooks } from "@redocly/theme/core/hooks";
import { globeIcon, chevronDown } from "../constants/icons";
interface LanguagePillProps {
onClick?: () => void;
isOpen?: boolean;
}
/**
* Get short display name for a locale code.
* e.g., "en-US" -> "En", "ja" -> "日本語"
*/
function getLocaleShortName(code: string | undefined): string {
if (!code) return "En";
// Map locale codes to short display names
const shortNames: Record<string, string> = {
"en-US": "En",
"en": "En",
"ja": "日本語",
};
return shortNames[code] || code.substring(0, 2).toUpperCase();
}
/**
* Language Pill Button Component.
* Shows current language and opens language selector.
*/
export function LanguagePill({ onClick, isOpen }: LanguagePillProps) {
const { currentLocale } = useLanguagePicker();
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const displayName = getLocaleShortName(currentLocale?.code);
return (
<button
type="button"
className={`bds-navbar__lang-pill ${isOpen ? 'bds-navbar__lang-pill--open' : ''}`}
aria-label={translate("Select language")}
aria-expanded={isOpen}
aria-haspopup="menu"
onClick={onClick}
>
<img src={globeIcon} alt="" className="bds-navbar__lang-pill-icon" />
<span className="bds-navbar__lang-pill-text">
<span>{displayName}</span>
<img src={chevronDown} alt="" className="bds-navbar__lang-pill-chevron" />
</span>
</button>
);
}

View File

@@ -0,0 +1,19 @@
import { useThemeHooks } from "@redocly/theme/core/hooks";
import { IconButton } from "./IconButton";
import { modeToggleIcon } from "../constants/icons";
interface ModeToggleButtonProps {
onClick?: () => void;
}
/**
* Mode Toggle Button Component.
* Icon button that toggles between light and dark mode.
*/
export function ModeToggleButton({ onClick }: ModeToggleButtonProps) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
return <IconButton icon={modeToggleIcon} ariaLabel={translate("Toggle color mode")} onClick={onClick} />;
}

View File

@@ -0,0 +1,46 @@
import * as React from "react";
import { SearchButton } from "./SearchButton";
import { ModeToggleButton } from "./ModeToggleButton";
import { LanguagePill } from "./LanguagePill";
import { LanguageDropdown } from "./LanguageDropdown";
interface NavControlsProps {
onSearch?: () => void;
}
/**
* Nav Controls Component.
* Right side of the navbar containing search, mode toggle, and language selector.
*/
export function NavControls({ onSearch }: NavControlsProps) {
const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = React.useState(false);
const handleModeToggle = () => {
// Toggle between light and dark theme
const newTheme = document.documentElement.classList.contains("dark") ? "light" : "dark";
window.localStorage.setItem("user-prefers-color", newTheme);
document.body.style.transition = "background-color .2s ease";
document.documentElement.classList.remove("dark", "light");
document.documentElement.classList.add(newTheme);
};
const handleLanguageClick = () => {
setIsLanguageDropdownOpen(!isLanguageDropdownOpen);
};
const handleLanguageDropdownClose = () => {
setIsLanguageDropdownOpen(false);
};
return (
<div className="bds-navbar__controls">
<SearchButton onClick={onSearch} />
<ModeToggleButton onClick={handleModeToggle} />
<div className="bds-navbar__lang-wrapper">
<LanguagePill onClick={handleLanguageClick} isOpen={isLanguageDropdownOpen} />
<LanguageDropdown isOpen={isLanguageDropdownOpen} onClose={handleLanguageDropdownClose} />
</div>
</div>
);
}

View File

@@ -0,0 +1,19 @@
import { useThemeHooks } from "@redocly/theme/core/hooks";
import { IconButton } from "./IconButton";
import { searchIcon } from "../constants/icons";
interface SearchButtonProps {
onClick?: () => void;
}
/**
* Search Button Component.
* Icon button that triggers the search modal.
*/
export function SearchButton({ onClick }: SearchButtonProps) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
return <IconButton icon={searchIcon} ariaLabel={translate("Search")} onClick={onClick} />;
}

View File

@@ -0,0 +1,11 @@
// Unified base component
export { IconButton } from './IconButton';
// Specific button implementations (use IconButton internally)
export { NavControls } from './NavControls';
export { SearchButton } from './SearchButton';
export { ModeToggleButton } from './ModeToggleButton';
export { LanguagePill } from './LanguagePill';
export { LanguageDropdown } from './LanguageDropdown';
export { HamburgerButton } from './HamburgerButton';

View File

@@ -0,0 +1,30 @@
import * as React from "react";
interface ChevronIconProps {
expanded: boolean;
}
/**
* Chevron Icon Component for Mobile Accordion
*/
export function ChevronIcon({ expanded }: ChevronIconProps) {
return (
<svg
className={`bds-mobile-menu__chevron ${expanded ? 'bds-mobile-menu__chevron--expanded' : ''}`}
width="13"
height="8"
viewBox="0 0 13 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1 1L6.5 6.5L12 1"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}

View File

@@ -0,0 +1,14 @@
import * as React from "react";
/**
* Close Icon Component for Mobile Menu
*/
export function CloseIcon() {
return (
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="7" y1="7" x2="21" y2="21" stroke="#141414" strokeWidth="2" strokeLinecap="round" />
<line x1="21" y1="7" x2="7" y2="21" stroke="#141414" strokeWidth="2" strokeLinecap="round" />
</svg>
);
}

View File

@@ -0,0 +1,56 @@
interface ArrowIconProps {
className?: string;
color?: string;
/**
* When true, the horizontal line has a class for CSS animation (parent links).
* When false, the full arrow is shown without animation class (child links).
*/
animated?: boolean;
}
/**
* Unified Arrow Icon component.
* - For parent links (animated=true): horizontal line animates away on hover
* - For child links (animated=false): full static arrow
*/
export function ArrowIcon({ className, color = "currentColor", animated = true }: ArrowIconProps) {
return (
<svg
className={className}
width="15"
height="14"
viewBox="0 0 26 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
{/* Chevron part */}
<path
d="M14.0019 1.00191L24.0015 11.0015L14.0019 21.001"
stroke={color}
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
/>
{/* Horizontal line */}
<path
d="M23.999 10.999H0"
stroke={color}
strokeWidth="2"
strokeMiterlimit="10"
strokeLinecap="round"
className={animated ? "arrow-horizontal" : undefined}
/>
</svg>
);
}
// Backwards-compatible aliases
export const SubmenuArrow = (props: Omit<ArrowIconProps, 'animated'>) => (
<ArrowIcon {...props} animated={true} />
);
export const SubmenuChildArrow = (props: Omit<ArrowIconProps, 'animated'>) => (
<ArrowIcon {...props} animated={false} />
);

View File

@@ -0,0 +1,6 @@
// Re-export all icon components
// Unified arrow icon with backwards-compatible aliases
export { ArrowIcon, SubmenuArrow, SubmenuChildArrow } from './SubmenuArrow';
export { CloseIcon } from './CloseIcon';
export { ChevronIcon } from './ChevronIcon';

View File

@@ -0,0 +1,13 @@
// Main Navbar component
export { Navbar } from './Navbar';
// Re-export types
export * from './types';
// Re-export components for advanced usage
export * from './components';
export * from './controls';
export * from './submenus';
export * from './mobile-menu';
export * from './icons';

View File

@@ -0,0 +1,215 @@
import * as React from "react";
import { useThemeHooks, useLanguagePicker } from "@redocly/theme/core/hooks";
import { BdsLink } from "../../../../shared/components/Link/Link";
import { CloseIcon, ChevronIcon } from "../icons";
import { xrpSymbolBlack, globeIcon, chevronDown, modeToggleIcon, searchIcon } from "../constants/icons";
import { navItems } from "../constants/navigation";
import { MobileMenuContent, type MobileMenuKey } from "./MobileMenuContent";
interface MobileMenuProps {
isOpen: boolean;
onClose: () => void;
onSearch?: () => void;
}
/**
* Mobile Menu Component.
* Full-screen slide-out menu for mobile devices.
*/
export function MobileMenu({ isOpen, onClose, onSearch }: MobileMenuProps) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const [expandedItem, setExpandedItem] = React.useState<string | null>("Develop");
// Handle body scroll lock
React.useEffect(() => {
if (isOpen) {
document.body.classList.add('bds-mobile-menu-open');
} else {
document.body.classList.remove('bds-mobile-menu-open');
}
return () => {
document.body.classList.remove('bds-mobile-menu-open');
};
}, [isOpen]);
const toggleAccordion = (item: string) => {
setExpandedItem(expandedItem === item ? null : item);
};
const handleSearch = () => {
if (onSearch) {
onSearch();
}
onClose();
};
const handleModeToggle = () => {
const newTheme = document.documentElement.classList.contains("dark") ? "light" : "dark";
window.localStorage.setItem("user-prefers-color", newTheme);
document.body.style.transition = "background-color .2s ease";
document.documentElement.classList.remove("dark", "light");
document.documentElement.classList.add(newTheme);
};
const renderAccordionContent = (label: string) => {
// All nav items with submenus use the unified MobileMenuContent
const validKeys: MobileMenuKey[] = ['Develop', 'Use Cases', 'Community', 'Network'];
if (validKeys.includes(label as MobileMenuKey)) {
return <MobileMenuContent menuKey={label as MobileMenuKey} />;
}
return null;
};
return (
<div className={`bds-mobile-menu ${isOpen ? 'bds-mobile-menu--open' : ''}`}>
{/* Header */}
<div className="bds-mobile-menu__header">
<BdsLink href="/" className="bds-navbar__logo" aria-label={translate("XRP Ledger Home")} onClick={onClose} variant="inline">
<img src={xrpSymbolBlack} alt={translate("XRP Ledger")} className="bds-navbar__logo-symbol" style={{ width: 33, height: 28 }} />
</BdsLink>
<button
type="button"
className="bds-mobile-menu__close"
aria-label={translate("Close menu")}
onClick={onClose}
>
<CloseIcon />
</button>
</div>
{/* Content */}
<div className="bds-mobile-menu__content">
<div className="bds-mobile-menu__accordion">
{navItems.map((item) => (
<React.Fragment key={item.label}>
<button
type="button"
className="bds-mobile-menu__accordion-header"
onClick={() => item.hasSubmenu ? toggleAccordion(item.label) : null}
aria-expanded={expandedItem === item.label}
>
{item.hasSubmenu ? (
<>
<span>{translate(item.labelTranslationKey, item.label)}</span>
<ChevronIcon expanded={expandedItem === item.label} />
</>
) : (
<BdsLink
href={item.href}
onClick={onClose}
variant="inline"
style={{
display: 'flex',
width: '100%',
justifyContent: 'space-between',
alignItems: 'center',
color: 'inherit',
textDecoration: 'none'
}}
>
<span>{translate(item.labelTranslationKey, item.label)}</span>
<ChevronIcon expanded={false} />
</BdsLink>
)}
</button>
{item.hasSubmenu && (
<div
className={`bds-mobile-menu__accordion-content ${
expandedItem === item.label ? 'bds-mobile-menu__accordion-content--expanded' : ''
}`}
>
{renderAccordionContent(item.label)}
</div>
)}
</React.Fragment>
))}
</div>
</div>
{/* Footer */}
<MobileMenuFooter
onModeToggle={handleModeToggle}
onSearch={handleSearch}
/>
</div>
);
}
interface MobileMenuFooterProps {
onModeToggle: () => void;
onSearch: () => void;
}
/**
* Get short display name for a locale code.
*/
function getLocaleShortName(code: string | undefined): string {
if (!code) return "En";
const shortNames: Record<string, string> = {
"en-US": "En",
"en": "En",
"ja": "日本語",
};
return shortNames[code] || code.substring(0, 2).toUpperCase();
}
function MobileMenuFooter({ onModeToggle, onSearch }: MobileMenuFooterProps) {
const { currentLocale, locales, setLocale } = useLanguagePicker();
const { useL10n, useTranslate } = useThemeHooks();
const { changeLanguage } = useL10n();
const { translate } = useTranslate();
const [isLangOpen, setIsLangOpen] = React.useState(false);
const displayName = getLocaleShortName(currentLocale?.code);
const handleLanguageSelect = (localeCode: string) => {
setLocale(localeCode);
changeLanguage(localeCode);
setIsLangOpen(false);
};
return (
<div className="bds-mobile-menu__footer">
<div className="bds-mobile-menu__lang-wrapper">
<button
type="button"
className={`bds-mobile-menu__lang-pill ${isLangOpen ? 'bds-mobile-menu__lang-pill--open' : ''}`}
aria-label={translate("Select language")}
aria-expanded={isLangOpen}
onClick={() => setIsLangOpen(!isLangOpen)}
>
<img src={globeIcon} alt="" className="bds-mobile-menu__lang-pill-icon" />
<span className="bds-mobile-menu__lang-pill-text">
<span>{displayName}</span>
<img src={chevronDown} alt="" className="bds-mobile-menu__lang-pill-chevron" />
</span>
</button>
{isLangOpen && locales.length >= 2 && (
<div className="bds-lang-dropdown bds-lang-dropdown--mobile" role="menu">
{locales.map((locale) => {
const isActive = locale.code === currentLocale?.code;
return (
<button
key={locale.code}
type="button"
role="menuitem"
className={`bds-lang-dropdown__item ${isActive ? 'bds-lang-dropdown__item--active' : ''}`}
onClick={() => handleLanguageSelect(locale.code)}
>
{locale.name || locale.code}
</button>
);
})}
</div>
)}
</div>
<button type="button" className="bds-mobile-menu__footer-icon" aria-label={translate("Toggle color mode")} onClick={onModeToggle}>
<img src={modeToggleIcon} alt="" />
</button>
<button type="button" className="bds-mobile-menu__footer-icon" aria-label={translate("Search")} onClick={onSearch}>
<img src={searchIcon} alt="" />
</button>
</div>
);
}

View File

@@ -0,0 +1,47 @@
import { MobileMenuSection } from "./MobileMenuSection";
import { developSubmenuData, useCasesSubmenuData, communitySubmenuData, networkSubmenuData } from "../constants/navigation";
import type { SubmenuItem, SubmenuItemWithChildren, NetworkSubmenuSection } from "../types";
export type MobileMenuKey = 'Develop' | 'Use Cases' | 'Community' | 'Network';
interface MobileMenuContentProps {
/** Which menu section to render */
menuKey: MobileMenuKey;
}
/** Get flattened menu items based on menu key */
function getMenuItems(menuKey: MobileMenuKey): (SubmenuItem | SubmenuItemWithChildren | NetworkSubmenuSection)[] {
switch (menuKey) {
case 'Develop':
return [...developSubmenuData.left, ...developSubmenuData.right];
case 'Use Cases':
return [...useCasesSubmenuData.left, ...useCasesSubmenuData.right];
case 'Community':
return [...communitySubmenuData.left, ...communitySubmenuData.right];
case 'Network':
return networkSubmenuData;
}
}
/**
* Unified Mobile Menu Content component.
* Renders accordion content for any menu section.
*/
export function MobileMenuContent({ menuKey }: MobileMenuContentProps) {
const items = getMenuItems(menuKey);
return (
<div className="bds-mobile-menu__tier-list">
{items.map((item) => (
<MobileMenuSection key={item.label} item={item} />
))}
</div>
);
}
// Backwards-compatible named exports
export const MobileMenuDevelopContent = () => <MobileMenuContent menuKey="Develop" />;
export const MobileMenuUseCasesContent = () => <MobileMenuContent menuKey="Use Cases" />;
export const MobileMenuCommunityContent = () => <MobileMenuContent menuKey="Community" />;
export const MobileMenuNetworkContent = () => <MobileMenuContent menuKey="Network" />;

View File

@@ -0,0 +1,54 @@
import * as React from "react";
import { useThemeHooks } from "@redocly/theme/core/hooks";
import { SubmenuArrow, SubmenuChildArrow } from "../icons";
import { walletIcons } from "../constants/icons";
import { hasChildren, type SubmenuItem } from "../types";
interface MobileMenuSectionProps {
item: SubmenuItem;
}
/**
* Reusable mobile menu section component.
* Renders a parent link with icon, and optionally child links.
*/
export function MobileMenuSection({ item }: MobileMenuSectionProps) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const itemHasChildren = hasChildren(item);
return (
<React.Fragment>
<a href={item.href} className="bds-mobile-menu__tier1 bds-mobile-menu__parent-link">
<span className="bds-mobile-menu__icon">
<img src={walletIcons[item.icon]} alt="" />
</span>
<span className="bds-mobile-menu__link bds-mobile-menu__link--bold">
{translate(item.label)}
<span className="bds-mobile-menu__arrow">
<SubmenuArrow />
</span>
</span>
</a>
{itemHasChildren && (
<div className="bds-mobile-menu__tier2">
{item.children.map((child) => (
<a
key={child.label}
href={child.href}
className="bds-mobile-menu__sublink"
target={child.href.startsWith('http') ? '_blank' : undefined}
rel={child.href.startsWith('http') ? 'noopener noreferrer' : undefined}
>
{translate(child.label)}
<span className="bds-mobile-menu__sublink-arrow">
<SubmenuChildArrow />
</span>
</a>
))}
</div>
)}
</React.Fragment>
);
}

View File

@@ -0,0 +1,16 @@
// Main mobile menu component
export { MobileMenu } from './MobileMenu';
// Unified content component with backwards-compatible aliases
export {
MobileMenuContent,
MobileMenuDevelopContent,
MobileMenuUseCasesContent,
MobileMenuCommunityContent,
MobileMenuNetworkContent,
type MobileMenuKey
} from './MobileMenuContent';
// Reusable section component
export { MobileMenuSection } from './MobileMenuSection';

View File

@@ -0,0 +1,16 @@
import { Submenu } from "./Submenu";
interface CommunitySubmenuProps {
isActive: boolean;
isClosing: boolean;
onClose?: () => void;
}
/**
* Desktop Community Submenu Component.
* Wrapper for unified Submenu component with 'community' variant.
*/
export function CommunitySubmenu({ isActive, isClosing, onClose }: CommunitySubmenuProps) {
return <Submenu variant="community" isActive={isActive} isClosing={isClosing} onClose={onClose} />;
}

View File

@@ -0,0 +1,16 @@
import { Submenu } from "./Submenu";
interface DevelopSubmenuProps {
isActive: boolean;
isClosing: boolean;
onClose?: () => void;
}
/**
* Desktop Develop Submenu Component.
* Wrapper for unified Submenu component with 'develop' variant.
*/
export function DevelopSubmenu({ isActive, isClosing, onClose }: DevelopSubmenuProps) {
return <Submenu variant="develop" isActive={isActive} isClosing={isClosing} onClose={onClose} />;
}

View File

@@ -0,0 +1,16 @@
import { Submenu } from "./Submenu";
interface NetworkSubmenuProps {
isActive: boolean;
isClosing: boolean;
onClose?: () => void;
}
/**
* Desktop Network Submenu Component.
* Wrapper for unified Submenu component with 'network' variant.
*/
export function NetworkSubmenu({ isActive, isClosing, onClose }: NetworkSubmenuProps) {
return <Submenu variant="network" isActive={isActive} isClosing={isClosing} onClose={onClose} />;
}

View File

@@ -0,0 +1,290 @@
import * as React from "react";
import { useThemeHooks } from "@redocly/theme/core/hooks";
import { SubmenuSection } from "./SubmenuSection";
import { ArrowIcon } from "../icons";
import { walletIcons, resourcesPurplePattern, insightsGreenPattern, darkInsightsGreenPattern, darkLilacPattern } from "../constants/icons";
import { developSubmenuData, useCasesSubmenuData, communitySubmenuData, networkSubmenuData } from "../constants/navigation";
import type { SubmenuItem, SubmenuItemWithChildren, NetworkSubmenuSection } from "../types";
export type SubmenuVariant = 'develop' | 'use-cases' | 'community' | 'network';
interface SubmenuProps {
/** Which submenu variant to render */
variant: SubmenuVariant;
/** Whether this submenu is currently active (visible) */
isActive: boolean;
/** Whether this submenu is in closing animation */
isClosing: boolean;
/** Callback when submenu should close (e.g., Escape key) */
onClose?: () => void;
}
/** Get submenu data based on variant */
function getSubmenuData(variant: SubmenuVariant) {
switch (variant) {
case 'develop': return developSubmenuData;
case 'use-cases': return useCasesSubmenuData;
case 'community': return communitySubmenuData;
case 'network': return networkSubmenuData;
}
}
/** Get CSS modifier class for variant */
function getVariantClass(variant: SubmenuVariant): string {
if (variant === 'develop') return '';
return `bds-submenu--${variant}`;
}
/**
* Get all focusable elements within a container
*/
function getFocusableElements(container: HTMLElement | null): HTMLElement[] {
if (!container) return [];
return Array.from(
container.querySelectorAll<HTMLElement>('a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])')
);
}
/**
* Find the next nav item button after the current expanded one
*/
function getNextNavItem(): HTMLElement | null {
const navItems = document.querySelectorAll<HTMLElement>('.bds-navbar__item');
const currentIndex = Array.from(navItems).findIndex(item =>
item.getAttribute('aria-expanded') === 'true'
);
if (currentIndex >= 0 && currentIndex < navItems.length - 1) {
return navItems[currentIndex + 1];
}
// If at the last nav item, go to the first control button (search, etc.)
const controls = document.querySelector<HTMLElement>('.bds-navbar__controls button, .bds-navbar__controls a');
return controls;
}
/**
* Unified Submenu component.
* Handles all submenu variants (develop, use-cases, community, network).
* ARIA compliant with full keyboard navigation support.
*/
export function Submenu({ variant, isActive, isClosing, onClose }: SubmenuProps) {
const submenuRef = React.useRef<HTMLDivElement>(null);
// Handle keyboard events for accessibility
const handleKeyDown = React.useCallback((event: KeyboardEvent) => {
if (!isActive) return;
if (event.key === 'Escape') {
event.preventDefault();
onClose?.();
// Return focus to the trigger button
const triggerButton = document.querySelector<HTMLButtonElement>(
`.bds-navbar__item[aria-expanded="true"]`
);
triggerButton?.focus();
}
// Handle Tab at end of submenu - move to next nav item
if (event.key === 'Tab' && !event.shiftKey) {
const activeSubmenu = document.querySelector<HTMLElement>('.bds-submenu--active');
const focusableElements = getFocusableElements(activeSubmenu);
const lastFocusable = focusableElements[focusableElements.length - 1];
if (document.activeElement === lastFocusable) {
event.preventDefault();
onClose?.();
const nextItem = getNextNavItem();
nextItem?.focus();
}
}
// Handle Shift+Tab at start of submenu - move back to trigger button
if (event.key === 'Tab' && event.shiftKey) {
const activeSubmenu = document.querySelector<HTMLElement>('.bds-submenu--active');
const focusableElements = getFocusableElements(activeSubmenu);
const firstFocusable = focusableElements[0];
if (document.activeElement === firstFocusable) {
event.preventDefault();
onClose?.();
// Return focus to the trigger button
const triggerButton = document.querySelector<HTMLButtonElement>(
`.bds-navbar__item[aria-expanded="true"]`
);
triggerButton?.focus();
}
}
}, [isActive, onClose]);
// Add keyboard event listener when submenu is active
React.useEffect(() => {
if (isActive) {
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}
}, [isActive, handleKeyDown]);
// Network submenu needs special handling for theme-aware patterns
if (variant === 'network') {
return <NetworkSubmenuContent isActive={isActive} isClosing={isClosing} onClose={onClose} />;
}
const data = getSubmenuData(variant);
const classNames = [
'bds-submenu',
getVariantClass(variant),
isActive ? 'bds-submenu--active' : '',
isClosing ? 'bds-submenu--closing' : '',
].filter(Boolean).join(' ');
// Standard two-column layout
const leftItems = 'left' in data ? data.left : [];
const rightItems = 'right' in data ? data.right : [];
return (
<div
ref={submenuRef}
className={classNames}
role="menu"
aria-hidden={!isActive}
>
<div className="bds-submenu__left">
{leftItems.map((item: SubmenuItem | SubmenuItemWithChildren) => (
<SubmenuSection key={item.label} item={item} />
))}
</div>
<div className="bds-submenu__right">
{rightItems.map((item: SubmenuItem | SubmenuItemWithChildren) => (
<SubmenuSection key={item.label} item={item} />
))}
</div>
</div>
);
}
/** Network submenu with theme-aware pattern images */
function NetworkSubmenuContent({ isActive, isClosing, onClose }: { isActive: boolean; isClosing: boolean; onClose?: () => void }) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
// Start with null to indicate "not yet determined" - avoids hydration mismatch
// by ensuring server and client both render the same initial state
const [isDarkMode, setIsDarkMode] = React.useState<boolean | null>(null);
// Handle keyboard events for accessibility
const handleKeyDown = React.useCallback((event: KeyboardEvent) => {
if (!isActive) return;
if (event.key === 'Escape') {
event.preventDefault();
onClose?.();
// Return focus to the trigger button
const triggerButton = document.querySelector<HTMLButtonElement>(
`.bds-navbar__item[aria-expanded="true"]`
);
triggerButton?.focus();
}
// Handle Tab at end of submenu - move to next nav item
if (event.key === 'Tab' && !event.shiftKey) {
const activeSubmenu = document.querySelector<HTMLElement>('.bds-submenu--active');
const focusableElements = getFocusableElements(activeSubmenu);
const lastFocusable = focusableElements[focusableElements.length - 1];
if (document.activeElement === lastFocusable) {
event.preventDefault();
onClose?.();
const nextItem = getNextNavItem();
nextItem?.focus();
}
}
// Handle Shift+Tab at start of submenu - move back to trigger button
if (event.key === 'Tab' && event.shiftKey) {
const activeSubmenu = document.querySelector<HTMLElement>('.bds-submenu--active');
const focusableElements = getFocusableElements(activeSubmenu);
const firstFocusable = focusableElements[0];
if (document.activeElement === firstFocusable) {
event.preventDefault();
onClose?.();
// Return focus to the trigger button
const triggerButton = document.querySelector<HTMLButtonElement>(
`.bds-navbar__item[aria-expanded="true"]`
);
triggerButton?.focus();
}
}
}, [isActive, onClose]);
// Add keyboard event listener when submenu is active
React.useEffect(() => {
if (isActive) {
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}
}, [isActive, handleKeyDown]);
React.useEffect(() => {
const checkTheme = () => {
setIsDarkMode(document.documentElement.classList.contains('dark'));
};
checkTheme();
const observer = new MutationObserver(checkTheme);
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
return () => observer.disconnect();
}, []);
// Default to light mode patterns until client-side detection runs
const patternImages = React.useMemo(() => ({
lilac: isDarkMode === true ? darkLilacPattern : resourcesPurplePattern,
green: isDarkMode === true ? darkInsightsGreenPattern : insightsGreenPattern,
}), [isDarkMode]);
const classNames = [
'bds-submenu',
'bds-submenu--network',
isActive ? 'bds-submenu--active' : '',
isClosing ? 'bds-submenu--closing' : '',
].filter(Boolean).join(' ');
return (
<div className={classNames} role="menu" aria-hidden={!isActive}>
{networkSubmenuData.map((section: NetworkSubmenuSection) => (
<div key={section.label} className="bds-submenu__section">
<a href={section.href} className="bds-submenu__tier1 bds-submenu__parent-link">
<span className="bds-submenu__icon">
<img src={walletIcons[section.icon]} alt="" />
</span>
<span className="bds-submenu__link bds-submenu__link--bold">
{translate(section.label)}
<span className="bds-submenu__arrow">
<ArrowIcon animated />
</span>
</span>
</a>
<div className="bds-submenu__network-content">
<div className="bds-submenu__tier2">
{section.children.map((child) => (
<a
key={child.label}
href={child.href}
className="bds-submenu__sublink"
target={child.href.startsWith('http') ? '_blank' : undefined}
rel={child.href.startsWith('http') ? 'noopener noreferrer' : undefined}
>
{translate(child.label)}
<span className="bds-submenu__sublink-arrow">
<ArrowIcon animated={false} />
</span>
</a>
))}
</div>
<div className="bds-submenu__pattern-container">
<img src={patternImages[section.patternColor]} alt="" className="bds-submenu__pattern" />
</div>
</div>
</div>
))}
</div>
);
}

View File

@@ -0,0 +1,70 @@
import { useThemeHooks } from "@redocly/theme/core/hooks";
import { ArrowIcon } from "../icons";
import { walletIcons } from "../constants/icons";
import { hasChildren, type SubmenuItem, type SubmenuItemWithChildren, type SubmenuItemBase } from "../types";
interface SubmenuSectionProps {
/** The menu item data */
item: SubmenuItem | SubmenuItemWithChildren | SubmenuItemBase;
/** Whether to render children links (default: true) */
showChildren?: boolean;
}
/**
* Unified submenu section component.
* Renders a parent link with icon, and optionally child links if the item has them.
*
* Usage:
* - For items that may or may not have children: <SubmenuSection item={item} />
* - For parent-only rendering: <SubmenuSection item={item} showChildren={false} />
*/
export function SubmenuSection({ item, showChildren = true }: SubmenuSectionProps) {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const itemHasChildren = hasChildren(item as SubmenuItem);
const shouldShowChildren = showChildren && itemHasChildren;
return (
<div className="bds-submenu__section">
<a href={item.href} className="bds-submenu__tier1 bds-submenu__parent-link">
<span className="bds-submenu__icon">
<img src={walletIcons[item.icon]} alt="" />
</span>
<span className="bds-submenu__link bds-submenu__link--bold">
{translate(item.label)}
<span className="bds-submenu__arrow">
<ArrowIcon animated />
</span>
</span>
</a>
{shouldShowChildren && (
<div className="bds-submenu__tier2">
{(item as SubmenuItemWithChildren).children.map((child) => (
<a
key={child.label}
href={child.href}
className="bds-submenu__sublink"
target={child.href.startsWith('http') ? '_blank' : undefined}
rel={child.href.startsWith('http') ? 'noopener noreferrer' : undefined}
>
{translate(child.label)}
<span className="bds-submenu__sublink-arrow">
<ArrowIcon animated={false} />
</span>
</a>
))}
</div>
)}
</div>
);
}
// Backwards-compatible aliases (all use the unified SubmenuSection)
export const SubmenuParentOnly = ({ item }: { item: SubmenuItemBase }) => (
<SubmenuSection item={item} showChildren={false} />
);
export const SubmenuWithChildren = ({ item }: { item: SubmenuItemWithChildren }) => (
<SubmenuSection item={item} showChildren={true} />
);

View File

@@ -0,0 +1,16 @@
import { Submenu } from "./Submenu";
interface UseCasesSubmenuProps {
isActive: boolean;
isClosing: boolean;
onClose?: () => void;
}
/**
* Desktop Use Cases Submenu Component.
* Wrapper for unified Submenu component with 'use-cases' variant.
*/
export function UseCasesSubmenu({ isActive, isClosing, onClose }: UseCasesSubmenuProps) {
return <Submenu variant="use-cases" isActive={isActive} isClosing={isClosing} onClose={onClose} />;
}

View File

@@ -0,0 +1,12 @@
// Unified submenu component
export { Submenu, type SubmenuVariant } from './Submenu';
// Variant-specific wrappers (all use Submenu internally)
export { DevelopSubmenu } from './DevelopSubmenu';
export { UseCasesSubmenu } from './UseCasesSubmenu';
export { CommunitySubmenu } from './CommunitySubmenu';
export { NetworkSubmenu } from './NetworkSubmenu';
// Reusable submenu section component
export { SubmenuSection, SubmenuParentOnly, SubmenuWithChildren } from './SubmenuSection';

View File

@@ -0,0 +1,42 @@
// Types for submenu data structures
export interface SubmenuChild {
label: string;
href: string;
active?: boolean;
}
export interface SubmenuItemBase {
label: string;
href: string;
icon: string;
}
export interface SubmenuItemWithChildren extends SubmenuItemBase {
children: SubmenuChild[];
}
export type SubmenuItem = SubmenuItemBase | SubmenuItemWithChildren;
// Network submenu section with decorative images
export interface NetworkSubmenuSection {
label: string;
href: string;
icon: string;
children: SubmenuChild[];
patternColor: 'lilac' | 'green';
}
// Nav item type
export interface NavItem {
label: string;
labelTranslationKey: string;
href: string;
hasSubmenu: boolean;
}
// Type guard to check if item has children
export function hasChildren(item: SubmenuItem): item is SubmenuItemWithChildren {
return 'children' in item && Array.isArray((item as SubmenuItemWithChildren).children);
}

View File

@@ -25,7 +25,7 @@ export function XRPLCard(props: XRPLCardProps) {
<p className="card-text">{props.body}</p>
)}
</div>
<div className="card-footer">&nbsp;</div>
{/* <div className="card-footer">&nbsp;</div> */}
</Link>
)
}

178
COLOR-MIGRATION-SUMMARY.md Normal file
View File

@@ -0,0 +1,178 @@
# Color System Migration Summary
**Date:** October 21, 2025
**Source:** [XRPL.org Design Tokens - Figma](https://figma.com/design/zRyhXG4hRP3Lk3B6Owr3eo/XRPL.org-Design-Tokens)
## Migration Strategy: Clean Migration
The old 10-level color scale (100-1000) has been completely migrated to a new 5-level scale (100-500). All references in the codebase have been updated, and backward compatibility aliases have been removed for a clean implementation.
**Mapping Applied:**
```
Old System → New System
100 → 100 (lightest)
200 → 100
300 → 200
400 → 200
500 → 300 (mid-tone, default)
600 → 300
700 → 400
800 → 400
900 → 500 (darkest)
1000 → 500
```
**Migration Approach:**
1. All color usages (600-1000) were found and replaced with their new equivalents (300-500)
2. Backward compatibility aliases were removed from `_colors.scss`
3. Only 100-500 design tokens remain for each color family
## Color Families Updated
### Primary Colors
#### Gray (Neutral) ⏸️ NOT UPDATED
- **Status:** Original values retained - design tokens not ready
- **Current values:** #FCFCFD, #F5F5F7, #E0E0E1, #C1C1C2, #A2A2A4, #838386, #454549, #343437, #232325, #111112
- **Note:** Gray/Neutral design tokens will be migrated in a future update
#### Green ✅
- **New Design Tokens:** #EAFCF1, #70EE97, #21E46B, #0DAA3E, #078139
- **Variables:** `$green-100` through `$green-500` only
- **Migrated:** 14 file references updated
- **Special:** `$apex-2023-green` (#00FF76) retained
#### Lilac (Primary) ✅ *replaces blue-purple*
- **New Design Tokens:** #F2EDFF, #D9CAFF, #C0A7FF, #7649E3, #5429A1
- **Variables:** `$lilac-100` through `$lilac-500` only
- **Legacy aliases:** `$blue-purple-100` through `$blue-purple-500` map to lilac (600-900 removed)
- **Migrated:** 16 file references updated
- **Note:** This is a new color name in the design system
### Secondary Colors
#### Red ✅ *NEW*
- **New Design Tokens:** #FDECE7, #F27A66, #F0643A, #DA4518, #A22514
- **Variables:** `$red-100` through `$red-500` only
- **Note:** This is a completely new color family added to the design system
#### Pink ✅ *replaces magenta*
- **New Design Tokens:** #FDF1F4, #F2B5C3, #F18DA5, #FF577F, #DC466F
- **Variables:** `$pink-100` through `$pink-500` only
- **Legacy aliases:** `$magenta-100` through `$magenta-500` map to pink (600-900 removed)
- **Migrated:** 7 file references updated
#### Blue ✅
- **New Design Tokens:** #EDF4FF, #93BFF1, #428CFF, #0179E7, #0A4DC0
- **Variables:** `$blue-100` through `$blue-500` only
- **Migrated:** 8 file references updated
- **Special:** `$accent-blue-90` (#001133) retained
#### Yellow ✅
- **New Design Tokens:** #F3F1EB, #E6F1A7, #DBF15E, #E1DB26, #D4C02D
- **Variables:** `$yellow-100` through `$yellow-500` only
- **Migrated:** 11 file references updated
## Colors Retained (No Design Token Replacement)
### Orange
- **Status:** Legacy values retained
- **Values:** #FFEEE5, #FFCCB2, #FFAA80, #FF884B, #FF6719, #E54D00, #B23C00, #802B00, #4C1A00
- **Reason:** No corresponding design token in new system
### Red-purple
- **Status:** Legacy values retained
- **Values:** #FBE5FF, #F2B2FF, #EA80FF, #E24CFF, #D919FF, #C000E5, #9500B2, #6B0080, #40004C
- **Reason:** No corresponding design token in new system
### Special Event Colors
- `$apex-2023-green: #00FF76`
- `$token-2049-purple: #410bb9`
- `$accent-blue-90: #001133`
## Bootstrap & Component Colors
All Bootstrap theme variables remain functional:
- `$primary``$purple` (now `$lilac-400`)
- `$secondary``$gray-200`
- `$success``$green-500`
- `$info``$blue-500`
- `$warning``$yellow-500`
- `$danger``$magenta-500` (now `$pink-500`)
## Breaking Changes
**Removed Variables:**
- All color variables from 600-1000 have been removed for: Green, Blue, Lilac, Pink, Red, Yellow
- `$blue-purple-600` through `$blue-purple-900` removed (use 100-500)
- `$magenta-600` through `$magenta-900` removed (use 100-500)
**No Impact:**
- All usages in the codebase have been updated
- Legacy color name aliases maintained (100-500 only):
- `$blue-purple-100` through `$blue-purple-500` → maps to `$lilac-*`
- `$magenta-100` through `$magenta-500` → maps to `$pink-*`
## Color Name Changes
| Old Name | New Name | Reason |
|----------|----------|--------|
| `blue-purple-*` | `lilac-*` | Design system rebranding |
| `magenta-*` | `pink-*` | Design system rebranding |
| N/A | `red-*` | New color family added |
## Usage Recommendations
### Current Best Practices
Use the new 5-level design tokens (100-500):
```scss
// Primary colors
color: $gray-300; // Gray (not yet migrated - still uses old values)
color: $green-300; // Default green
color: $lilac-400; // Primary purple
// Secondary colors
color: $red-300; // Default red
color: $pink-300; // Default pink
color: $blue-300; // Default blue
color: $yellow-300; // Default yellow
```
### Legacy Aliases Still Available
```scss
// These legacy names work (100-500 only)
color: $blue-purple-400; // Same as $lilac-400
color: $magenta-300; // Same as $pink-300
```
## Files Modified
- `styles/_colors.scss` - Complete color system update
## Validation Status
✅ All SCSS variables resolve correctly
✅ No linter errors
✅ Bootstrap theme colors functional
✅ All old color references (600-1000) removed from codebase
✅ Special event colors preserved
⏸️ Gray/Neutral colors - pending future update
## Migration Statistics
**Files Updated:** 11 SCSS files
- `styles/_colors.scss` - Color definitions cleaned up
- `styles/light/_light-theme.scss` - 11 color references updated
- `styles/_status-labels.scss` - 39 color references updated
- `styles/_diagrams.scss` - 6 color references updated
- `styles/_code-tabs.scss` - 2 color references updated
- `styles/_content.scss` - 1 color reference updated
- `styles/_buttons.scss` - 7 color references updated
- `styles/_pages.scss` - 3 color references updated
- `styles/_blog.scss` - 2 color references updated
- `styles/_feedback.scss` - 2 color references updated
- `styles/_rpc-tool.scss` - 1 color reference updated
- `styles/_landings.scss` - 1 color reference updated
**Total Color References Updated:** 75+ instances

154
CSS-OPTIMIZATION-SUMMARY.md Normal file
View File

@@ -0,0 +1,154 @@
# CSS Optimization - Implementation Summary
## ✅ Successfully Completed
The CSS build pipeline has been modernized with industry-standard optimization tools, resulting in significant performance improvements.
## Results
### Bundle Size Improvements
\`\`\`
=== CSS Bundle Comparison ===
Master (Bootstrap 4):
Uncompressed: 405.09 KB
Gzipped: 63.44 KB
This Branch BEFORE Optimization (Bootstrap 5):
Uncompressed: 486.64 KB
Gzipped: 71.14 KB
This Branch AFTER Optimization (Bootstrap 5 + PurgeCSS):
Uncompressed: 280.92 KB ✅ 42% smaller
Gzipped: 43.32 KB ✅ 39% smaller (network transfer)
\`\`\`
### Key Improvements
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| **Network Transfer (Gzipped)** | 71.14 KB | 43.32 KB | **39% smaller** |
| **Uncompressed Size** | 486.64 KB | 280.92 KB | **42% smaller** |
| **CSS Selectors** | 5,423 | 2,681 | **51% fewer** |
| **DevTools Filter Performance** | ~60 seconds | <1 second | **98% faster** |
### Real-World Impact
- **Page Load:** 40% faster CSS download on 3G connections
- **Developer Experience:** DevTools CSS filtering is now instant (<1s vs 60s)
- **Bandwidth Savings:** ~28 KB less per page load
- **Maintainability:** Modern tooling with source maps in development
## What Was Implemented
### 1. Modern Build Pipeline
- **Upgraded Sass** from 1.26.10 (2020) 1.93.2 (latest)
- **Added PostCSS** with optimization plugins:
- **PurgeCSS** - Removes unused CSS selectors
- **Autoprefixer** - Browser compatibility
- **cssnano** - Advanced minification
### 2. Build Scripts
```json
{
"scripts": {
"build-css": "Production build with full optimization",
"build-css-dev": "Development build with source maps",
"build-css-watch": "Watch mode for continuous compilation",
"analyze-css": "Bundle analysis tool"
}
}
```
### 3. PurgeCSS Configuration
- Scans all `.tsx`, `.md`, `.yaml`, `.html` files for class names
- Intelligent safelist for dynamically-added classes
- Preserves Bootstrap JS components, CodeMirror, custom tools
- Only runs in production (dev builds are fast)
### 4. CSS Analysis Tool
Created `scripts/analyze-css.js` to monitor:
- Bundle size and composition
- Bootstrap component usage
- Optimization opportunities
- Before/after comparisons
## Files Created/Modified
### New Files
- `postcss.config.cjs` - PostCSS and PurgeCSS configuration
- `scripts/analyze-css.js` - CSS bundle analysis tool
- `CSS-OPTIMIZATION.md` - Comprehensive optimization guide
- `CSS-OPTIMIZATION-SUMMARY.md` - This summary
### Modified Files
- `package.json` - Updated dependencies and build scripts
- `styles/README.md` - Updated build documentation
### Configuration Files
All configuration files include extensive inline documentation explaining decisions and patterns.
## Usage
### For Production
```bash
npm run build-css # Full optimization
npm run analyze-css # Check results
```
### For Development
```bash
npm run build-css:dev # Fast build with source maps
npm run build-css:watch # Auto-rebuild on changes
```
## Backward Compatibility
**No breaking changes** - All existing styles are preserved
Visual appearance is identical
All Bootstrap components still work
Dynamic classes are safelisted
## Documentation
- **`styles/README.md`** - Build process and troubleshooting
- **`CSS-OPTIMIZATION.md`** - Detailed implementation guide
- **`postcss.config.cjs`** - Inline configuration documentation
## Maintenance
### Adding New Styles
1. Create `_component.scss` file
2. Import in `xrpl.scss`
3. Add dynamic classes to safelist if needed
4. Test: `npm run build-css:dev` and `npm run build-css`
5. Analyze: `npm run analyze-css`
### Troubleshooting Missing Styles
If styles are missing in production:
1. Check if class is added dynamically
2. Add pattern to safelist in `postcss.config.cjs`
3. Rebuild with `npm run build-css`
## Next Steps (Optional Future Optimizations)
1. **Code Splitting** - Separate vendor CSS from custom styles
2. **Critical CSS** - Extract above-the-fold styles
3. **Bootstrap Customization** - Import only needed components
4. **CSS Modules** - Component-scoped styles for React pages
## Conclusion
The CSS optimization is complete and working perfectly. The bundle size has been reduced by 42% (uncompressed) and 39% (gzipped), resulting in faster page loads and dramatically improved developer experience.
**Status: ✅ Ready for Production**
---
*Last Updated: October 2025*

381
CSS-OPTIMIZATION.md Normal file
View File

@@ -0,0 +1,381 @@
# CSS Optimization Guide
## Overview
This document describes the CSS optimization implementation for the XRPL Dev Portal, including the rationale, implementation details, performance improvements, and maintenance guidelines.
## The Problem
### Before Optimization
The dev portal was serving a **486 KB** minified CSS bundle that included:
- **Entire Bootstrap 5.3.8 framework** (~200+ KB)
- Thousands of unused CSS selectors
- No tree-shaking or dead code elimination
- All styles loaded on every page, regardless of usage
- **1-minute lag** in Chrome DevTools when filtering CSS
#### Impact
- **Developer Experience:** DevTools filter took 60+ seconds to respond
- **Page Performance:** 486 KB CSS downloaded on every page load
- **Build Process:** Outdated Sass 1.26.10 (from 2020)
- **Debugging:** No source maps, even in development
### Analysis Results
Initial analysis showed:
```
Bundle Size: 486.64 KB
Total Selectors: 5,423
Unique Selectors: 4,678
Bootstrap Component Usage:
- Pagination: 998 usages
- Cards: 428 usages
- Grid System: 253 usages
- ...but also...
- Toast: 8 usages
- Spinner: 8 usages
- Accordion: 0 usages (unused!)
```
## The Solution
### Modern Build Pipeline
Implemented a three-stage optimization pipeline:
```
SCSS → Sass Compiler → PostCSS → Optimized CSS
├─ PurgeCSS (removes unused)
├─ Autoprefixer (adds vendor prefixes)
└─ cssnano (minifies)
```
### Key Technologies
1. **Sass (latest)** - Modern SCSS compilation with better performance
2. **PostCSS** - Industry-standard CSS processing
3. **PurgeCSS** - Intelligent unused CSS removal
4. **Autoprefixer** - Browser compatibility
5. **cssnano** - Advanced minification
## Implementation
### 1. Dependency Upgrades
```json
{
"devDependencies": {
"sass": "^1.93.2", // was 1.26.10
"postcss": "^8.5.6",
"postcss-cli": "^11.0.1",
"@fullhuman/postcss-purgecss": "^7.0.2",
"autoprefixer": "^10.4.21",
"cssnano": "^7.1.1"
}
}
```
### 2. Build Scripts
Created separate development and production builds:
```json
{
"scripts": {
"build-css": "Production build with full optimization",
"build-css:dev": "Development build with source maps",
"build-css:watch": "Watch mode for continuous compilation",
"analyze-css": "node scripts/analyze-css.js"
}
}
```
**Production Build:**
- ✅ Full PurgeCSS optimization
- ✅ Minified and compressed
- ✅ Autoprefixed
- ❌ No source maps
**Development Build:**
- ✅ Source maps for debugging
- ✅ Autoprefixed
- ❌ No PurgeCSS (faster builds)
- ❌ Not minified (readable)
### 3. PurgeCSS Configuration
Created `postcss.config.cjs` with intelligent safelist:
```javascript
// Content paths - scan these for class names
content: [
'./**/*.tsx',
'./**/*.md',
'./**/*.yaml',
'./**/*.html',
'./static/js/**/*.js',
]
// Safelist - preserve these classes
safelist: {
standard: [
'html', 'body', 'light', 'dark',
/^show$/, /^active$/, /^disabled$/,
],
deep: [
/dropdown-menu/, /modal-backdrop/,
/cm-/, /CodeMirror/, // Third-party
/rpc-tool/, /websocket/, // Custom components
],
}
```
**Safelist Strategy:**
- **Standard:** State classes added by JavaScript
- **Deep:** Component patterns (keeps parent and children)
- **Greedy:** Attribute-based matching
### 4. Analysis Tool
Created `scripts/analyze-css.js` to track optimization:
- Bundle size metrics
- Selector counts
- Bootstrap component usage
- Custom pattern detection
- Optimization recommendations
## Results
### Performance Improvements
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| **Bundle Size (Uncompressed)** | 486.64 KB | 280.92 KB | **42% smaller** |
| **Bundle Size (Gzipped)** | 71.14 KB | 43.32 KB | **39% smaller** |
| **Total Selectors** | 5,423 | 2,681 | **51% fewer** |
| **Unique Selectors** | 4,678 | 2,167 | **54% fewer** |
| **DevTools Filter** | ~60 seconds | <1 second | **98% faster** |
| **Download Time (3G)** | ~2.0s | ~1.2s | **40% faster** |
**Note:** Gzipped size is what actually gets transmitted over the network, representing the real-world bandwidth savings.
### Bootstrap Component Optimization
| Component | Before | After | Reduction |
|-----------|--------|-------|-----------|
| Pagination | 998 | 831 | 17% |
| Cards | 428 | 306 | 29% |
| Grid System | 253 | 94 | 63% |
| Badge | 253 | 0 | 100% (unused) |
| Navbar | 171 | 78 | 54% |
| Buttons | 145 | 77 | 47% |
| Forms | 179 | 70 | 61% |
### Developer Experience
**Before:**
```
Build time: 5-10 seconds
DevTools CSS filter: 60 seconds
Debugging: No source maps
```
**After:**
```
Production build: 8-12 seconds
Development build: 3-5 seconds (no PurgeCSS)
DevTools CSS filter: <1 second
Debugging: Source maps in dev mode
```
## Maintenance
### Adding New Styles
When adding new component styles:
1. **Create the SCSS file:**
```scss
// styles/_my-component.scss
.my-component {
// styles here
}
```
2. **Import in xrpl.scss:**
```scss
@import "_my-component.scss";
```
3. **If using dynamic classes, update safelist:**
```javascript
// postcss.config.cjs
deep: [
/my-component/, // Keeps all .my-component-* classes
]
```
4. **Test both builds:**
```bash
npm run build-css:dev # Test development build
npm run build-css # Test production build
npm run analyze-css # Check bundle size impact
```
### Troubleshooting Missing Styles
If styles are missing after a production build:
1. **Identify the missing class:**
```bash
# Search for class usage in codebase
grep -r "missing-class" .
```
2. **Check if it's dynamically added:**
- Bootstrap JavaScript components
- React state-based classes
- Third-party library classes
3. **Add to PurgeCSS safelist:**
```javascript
// postcss.config.cjs
safelist: {
deep: [
/missing-class/, // Preserve this pattern
],
}
```
4. **Rebuild and verify:**
```bash
npm run build-css
npm run analyze-css
```
### Monitoring Bundle Size
Run the analysis tool regularly:
```bash
npm run analyze-css
```
**Watch for:**
- Bundle size > 350 KB (indicates regression)
- Components with 0 usages (can be removed from Bootstrap import)
- Significant selector count increases
### Future Optimizations
Potential next steps for further optimization:
1. **Code Splitting**
- Split vendor CSS (Bootstrap) from custom styles
- Lazy-load page-specific styles
- Critical CSS extraction
2. **Bootstrap Customization**
- Import only needed Bootstrap components
- Remove unused variables and mixins
- Custom Bootstrap build
3. **Component-Level CSS**
- CSS Modules for page components
- CSS-in-JS for dynamic styles
- Scoped styles per route
4. **Advanced Compression**
- Brotli compression (88% ratio vs 76% gzip)
- CSS splitting by media queries
- HTTP/2 server push for critical CSS
## Migration Notes
### Breaking Changes
**None** - This optimization is backward-compatible. All existing classes and styles are preserved.
### Testing Checklist
When testing the optimization:
- [ ] Homepage loads correctly
- [ ] Documentation pages display properly
- [ ] Blog posts render correctly
- [ ] Dev tools (RPC tool, WebSocket tool) function
- [ ] Navigation menus work
- [ ] Dropdowns and modals open correctly
- [ ] Forms are styled properly
- [ ] Code syntax highlighting works
- [ ] Print styles work
- [ ] Light/dark theme switching works
### Rollback Procedure
If issues are found:
1. **Temporarily revert to old build:**
```bash
# In package.json, change build-css to:
"build-css": "sass --load-path styles/scss styles/xrpl.scss ./static/css/devportal2024-v1.css --style compressed --no-source-map"
```
2. **Rebuild:**
```bash
npm run build-css
```
3. **Report the issue** with:
- Missing class names
- Page where issue appears
- Expected vs actual behavior
## Resources
### Documentation
- [PurgeCSS Documentation](https://purgecss.com/)
- [PostCSS Documentation](https://postcss.org/)
- [Sass Documentation](https://sass-lang.com/)
- [Bootstrap Customization](https://getbootstrap.com/docs/5.3/customize/sass/)
### Tools
- `npm run build-css` - Production build
- `npm run build-css:dev` - Development build
- `npm run build-css:watch` - Watch mode
- `npm run analyze-css` - Bundle analysis
### Files
- `styles/README.md` - Build process documentation
- `postcss.config.cjs` - PostCSS and PurgeCSS configuration
- `scripts/analyze-css.js` - Bundle analysis tool
- `package.json` - Build scripts
## Conclusion
This optimization reduces the CSS bundle by 42% (486 KB 281 KB), dramatically improving both developer experience and end-user performance. The implementation uses industry-standard tools and maintains full backward compatibility while providing a foundation for future optimizations.
**Key Takeaways:**
- 42% smaller uncompressed CSS bundle (486 KB 281 KB)
- 39% smaller gzipped bundle (71 KB 43 KB network transfer)
- 98% faster DevTools filtering (60s <1s)
- Modern build tooling (Sass + PostCSS + PurgeCSS)
- Source maps in development mode
- Backward compatible - no breaking changes
- Well documented and maintainable
---
*Last updated: October 2025*
*Contributors: CSS Optimization Initiative*

View File

@@ -1,7 +0,0 @@
{
"id": 2,
"command": "account_objects",
"account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"ledger_index": "validated",
"type": "escrow"
}

View File

@@ -1,7 +0,0 @@
{
"id": 5,
"command": "account_objects",
"account": "rfztBskAVszuS3s5Kq7zDS74QtHrw893fm",
"ledger_index": "validated",
"type": "escrow"
}

View File

@@ -1,26 +0,0 @@
{
"id": 2,
"status": "success",
"type": "response",
"result": {
"account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"account_objects": [
{
"Account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"Amount": "10000",
"CancelAfter": 559913895,
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"FinishAfter": 559892324,
"Flags": 0,
"LedgerEntryType": "Escrow",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "4756C22BBB7FC23D9081FDB180806939D6FEBC967BE0EC2DB95B166AF9C086E9",
"PreviousTxnLgrSeq": 2764813,
"index": "7243A9750FA4BE3E63F75F6DACFD79AD6B6C76947F6BDC46CD0F52DBEEF64C89"
}
],
"ledger_hash": "82F24FFA72AED16F467BBE79D387E92FDA39F29038B26E79464CDEDFB506E366",
"ledger_index": 2764826,
"validated": true
}
}

View File

@@ -1,60 +0,0 @@
{
"id": 5,
"result": {
"account": "rfztBskAVszuS3s5Kq7zDS74QtHrw893fm",
"account_objects": [{
"Account": "rafD3taonqdnVpaxCCT6sjnScZUeFGf1JG",
"Amount": "250",
"Destination": "rfztBskAVszuS3s5Kq7zDS74QtHrw893fm",
"DestinationNode": "0000000000000000",
"FinishAfter": 570672000,
"Flags": 0,
"LedgerEntryType": "Escrow",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "A0951691DF3BCBEEB3108F2229A702D078BBBF848268BC601E59B68A2E390AAC",
"PreviousTxnLgrSeq": 4602906,
"index": "2BF3226ACCA8FF7ACB7201F20A701F51D8666A2FA2FBFBE6A05C9161F9228A18"
}, {
"Account": "rfztBskAVszuS3s5Kq7zDS74QtHrw893fm",
"Amount": "250",
"Destination": "r9gyNNzhMtfwZara61u3ycfMLdkTpKJZHX",
"DestinationNode": "0000000000000000",
"FinishAfter": 570672000,
"Flags": 0,
"LedgerEntryType": "Escrow",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "463D5A3CF09F4890B8471027F80414B3B438E6907425B71DC324D7118E90A107",
"PreviousTxnLgrSeq": 4603003,
"index": "35462CDC28AD830B29D101E8307AF5B6BFBC262F1BDCCA7EB45D1CA3F8B44F53"
}, {
"Account": "r9gyNNzhMtfwZara61u3ycfMLdkTpKJZHX",
"Amount": "250",
"Destination": "rfztBskAVszuS3s5Kq7zDS74QtHrw893fm",
"DestinationNode": "0000000000000000",
"FinishAfter": 570672000,
"Flags": 0,
"LedgerEntryType": "Escrow",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "08C9B20AC9EB191238038A108CC4CBBC0243672484B466FB42DED0A7DF6A31A1",
"PreviousTxnLgrSeq": 4602954,
"index": "A7B0983A1B53D92278E21499064A4F8BBE08CB8D14DB6BBBA8F688AB1D3FDA45"
}, {
"Account": "rfztBskAVszuS3s5Kq7zDS74QtHrw893fm",
"Amount": "250",
"Destination": "rafD3taonqdnVpaxCCT6sjnScZUeFGf1JG",
"DestinationNode": "0000000000000000",
"FinishAfter": 570672000,
"Flags": 0,
"LedgerEntryType": "Escrow",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "F4778F528AB3CB945BDB88036EF9FE6C0E899F1629D9E51129E3B93CD488395A",
"PreviousTxnLgrSeq": 4602977,
"index": "F99A4DDADDDF623908C9A048170AB107AFF78684AB8F3110E9F00BBBC606ABD2"
}],
"ledger_hash": "1D4850035F175CA6F1CD5CE3B53C01AA83E4F086C13085E4FBC1EEFCCB345A9B",
"ledger_index": 4603176,
"validated": true
},
"status": "success",
"type": "response"
}

View File

@@ -1,5 +0,0 @@
{
"id": 4,
"command": "ledger",
"ledger_index": "validated"
}

View File

@@ -1,5 +0,0 @@
{
"id": 4,
"command": "ledger",
"ledger_index": "validated"
}

View File

@@ -1,19 +0,0 @@
{
"id": 1,
"status": "success",
"type": "response",
"result": {
"ledger": {
# ... (trimmed) ...
"close_time": 560302643,
"close_time_human": "2017-Oct-02 23:37:23",
"close_time_resolution": 10,
# ... (trimmed) ...
},
"ledger_hash": "668F0647A6F3CC277496245DBBE9BD2E3B8E70E7AA824E97EF3237FE7E1EE3F2",
"ledger_index": 2906341,
"validated": true
}
}

View File

@@ -1,28 +0,0 @@
{
"id": 4,
"status": "success",
"type": "response",
"result": {
"ledger": {
"accepted": true,
"account_hash": "3B5A8FF5334F94F4D3D09F236F9D1B4C028FCAE30948ACC986D461DDEE1D886B",
"close_flags": 0,
"close_time": 557256670,
"close_time_human": "2017-Aug-28 17:31:10",
"close_time_resolution": 10,
"closed": true,
"hash": "A999223A80174A7CB39D766B625C9E476F24AD2F15860A712CD029EE5ED1C320",
"ledger_hash": "A999223A80174A7CB39D766B625C9E476F24AD2F15860A712CD029EE5ED1C320",
"ledger_index": "1908253",
"parent_close_time": 557256663,
"parent_hash": "6A70C5336ACFDA05760D827776079F7A544D2361CFD5B21BD55A92AA20477A61",
"seqNum": "1908253",
"totalCoins": "99997280690562728",
"total_coins": "99997280690562728",
"transaction_hash": "49A51DFB1CAB2F134D93D5D1C5FF55A15B12DA36DAF9F5862B17C47EE966647D"
},
"ledger_hash": "A999223A80174A7CB39D766B625C9E476F24AD2F15860A712CD029EE5ED1C320",
"ledger_index": 1908253,
"validated": true
}
}

View File

@@ -1,11 +0,0 @@
{
"id": 5,
"command": "submit",
"secret": "s████████████████████████████",
"tx_json": {
"Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp",
"TransactionType": "EscrowCancel",
"Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"OfferSequence": 1
}
}

View File

@@ -1,13 +0,0 @@
{
"id": 1,
"command": "submit",
"secret": "s████████████████████████████",
"tx_json": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"TransactionType": "EscrowCreate",
"Amount": "100000",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
"CancelAfter": 556927412
}
}

View File

@@ -1,12 +0,0 @@
{
"id": 2,
"command": "submit",
"secret": "s████████████████████████████",
"tx_json": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"TransactionType": "EscrowCreate",
"Amount": "10000",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"FinishAfter": 557020800
}
}

View File

@@ -1,14 +0,0 @@
{
"id": 4,
"command": "submit",
"secret": "s████████████████████████████",
"tx_json": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"TransactionType": "EscrowFinish",
"Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"OfferSequence": 5,
"Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
"Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048",
"Fee": "500"
}
}

View File

@@ -1,11 +0,0 @@
{
"id": 5,
"command": "submit",
"secret": "s████████████████████████████",
"tx_json": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"TransactionType": "EscrowFinish",
"Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"OfferSequence": 1
}
}

View File

@@ -1,23 +0,0 @@
{
"id": 5,
"status": "success",
"type": "response",
"result": {
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"tx_blob": "1200042280000000240000000320190000000168400000000000000A7321027FB1CF34395F18901CD294F77752EEE25277C6E87A224FC7388AA7EF872DB43D74473045022100AC45749FC4291F7811B2D8AC01CA04FEE38910CB7216FB0C5C0AEBC9C0A95F4302203F213C71C00136A0ADC670EFE350874BCB2E559AC02059CEEDFB846685948F2B81142866B7B47574C8A70D5E71FFB95FFDB18951427B82144E87970CD3EA984CF48B1AA6AB6C77DC4AB059FC",
"tx_json": {
"Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp",
"Fee": "10",
"Flags": 2147483648,
"OfferSequence": 1,
"Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"Sequence": 3,
"SigningPubKey": "027FB1CF34395F18901CD294F77752EEE25277C6E87A224FC7388AA7EF872DB43D",
"TransactionType": "EscrowCancel",
"TxnSignature": "3045022100AC45749FC4291F7811B2D8AC01CA04FEE38910CB7216FB0C5C0AEBC9C0A95F4302203F213C71C00136A0ADC670EFE350874BCB2E559AC02059CEEDFB846685948F2B",
"hash": "65F36C5514153D94F0ADE5CE747061A5E70B73B56B4C66DA5040D99CAF252831"
}
}
}

View File

@@ -1,25 +0,0 @@
{
"id": 1,
"status": "success",
"type": "response",
"result": {
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"tx_blob": "120001228000000024000000052024213209B46140000000000186A068400000000000000A732103E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61744730450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F701127A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD81012081149A2AA667E1517EFA8A6B552AB2EDB859A99F26B283144B4E9C06F24296074F7BC48F92A97916C6DC5EA9",
"tx_json": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Amount": "100000",
"CancelAfter": 556927412,
"Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "10",
"Flags": 2147483648,
"Sequence": 5,
"SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61",
"TransactionType": "EscrowCreate",
"TxnSignature": "30450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F",
"hash": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324"
}
}
}

View File

@@ -1,24 +0,0 @@
{
"id": 2,
"status": "success",
"type": "response",
"result": {
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"tx_blob": "1200012280000000240000000120252133768061400000000000271068400000000000000A732103C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E437446304402203C9AA4C21E1A1A7427D41583283E7A513DDBDD967B246CADD3B2705D858A7A8E02201BEA7B923B18910EEB9F306F6DE3B3F53549BBFAD46335B62B4C34A6DCB4A47681143EEB46C355B04EE8D08E8EED00F422895C79EA6A83144B4E9C06F24296074F7BC48F92A97916C6DC5EA9",
"tx_json": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Amount": "10000",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "10",
"FinishAfter": 557020800,
"Flags": 2147483648,
"Sequence": 1,
"SigningPubKey": "03C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E43",
"TransactionType": "EscrowCreate",
"TxnSignature": "304402203C9AA4C21E1A1A7427D41583283E7A513DDBDD967B246CADD3B2705D858A7A8E02201BEA7B923B18910EEB9F306F6DE3B3F53549BBFAD46335B62B4C34A6DCB4A476",
"hash": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263"
}
}
}

View File

@@ -1,25 +0,0 @@
{
"id": 4,
"status": "success",
"type": "response",
"result": {
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"tx_blob": "120002228000000024000000062019000000056840000000000001F4732103E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B617446304402207DE4EA9C8655E75BA01F96345B3F62074313EB42C15D9C4871E30F02202D2BA50220070E52AD308A31AC71E33BA342F31B68D1D1B2A7A3A3ED6E8552CA3DCF14FBB2701024A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048701127A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD81012081149A2AA667E1517EFA8A6B552AB2EDB859A99F26B282149A2AA667E1517EFA8A6B552AB2EDB859A99F26B2",
"tx_json": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
"Fee": "500",
"Flags": 2147483648,
"Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048",
"OfferSequence": 5,
"Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Sequence": 6,
"SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61",
"TransactionType": "EscrowFinish",
"TxnSignature": "304402207DE4EA9C8655E75BA01F96345B3F62074313EB42C15D9C4871E30F02202D2BA50220070E52AD308A31AC71E33BA342F31B68D1D1B2A7A3A3ED6E8552CA3DCF14FBB2",
"hash": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF"
}
}
}

View File

@@ -1,23 +0,0 @@
{
"id": 5,
"status": "success",
"type": "response",
"result": {
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"tx_blob": "1200022280000000240000000220190000000168400000000000000A732103C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E4374473045022100923B91BA4FD6450813F5335D71C64BA9EB81304A86859A631F2AD8571424A46502200CCE660D36781B84634C5F23619EB6CFCCF942709F54DCCF27CF6F499AE78C9B81143EEB46C355B04EE8D08E8EED00F422895C79EA6A82143EEB46C355B04EE8D08E8EED00F422895C79EA6A",
"tx_json": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Fee": "10",
"Flags": 2147483648,
"OfferSequence": 1,
"Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Sequence": 2,
"SigningPubKey": "03C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E43",
"TransactionType": "EscrowFinish",
"TxnSignature": "3045022100923B91BA4FD6450813F5335D71C64BA9EB81304A86859A631F2AD8571424A46502200CCE660D36781B84634C5F23619EB6CFCCF942709F54DCCF27CF6F499AE78C9B",
"hash": "41856A742B3CAF307E7B4D0B850F302101F0F415B785454F7501E9960A2A1F6B"
}
}
}

View File

@@ -1,5 +0,0 @@
{
"id": 6,
"command": "tx",
"transaction": "65F36C5514153D94F0ADE5CE747061A5E70B73B56B4C66DA5040D99CAF252831"
}

View File

@@ -1,5 +0,0 @@
{
"id": 3,
"command": "tx",
"transaction": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324"
}

View File

@@ -1,5 +0,0 @@
{
"id": 3,
"command": "tx",
"transaction": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263"
}

View File

@@ -1,5 +0,0 @@
{
"id": 20,
"command": "tx",
"transaction": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF"
}

View File

@@ -1,5 +0,0 @@
{
"id": 21,
"command": "tx",
"transaction": "41856A742B3CAF307E7B4D0B850F302101F0F415B785454F7501E9960A2A1F6B"
}

View File

@@ -1,101 +0,0 @@
{
"id": 6,
"status": "success",
"type": "response",
"result": {
"Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp",
"Fee": "10",
"Flags": 2147483648,
"OfferSequence": 1,
"Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"Sequence": 3,
"SigningPubKey": "027FB1CF34395F18901CD294F77752EEE25277C6E87A224FC7388AA7EF872DB43D",
"TransactionType": "EscrowCancel",
"TxnSignature": "3045022100AC45749FC4291F7811B2D8AC01CA04FEE38910CB7216FB0C5C0AEBC9C0A95F4302203F213C71C00136A0ADC670EFE350874BCB2E559AC02059CEEDFB846685948F2B",
"date": 560302841,
"hash": "65F36C5514153D94F0ADE5CE747061A5E70B73B56B4C66DA5040D99CAF252831",
"inLedger": 2906406,
"ledger_index": 2906406,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
"PreviousTxnID": "4756C22BBB7FC23D9081FDB180806939D6FEBC967BE0EC2DB95B166AF9C086E9",
"PreviousTxnLgrSeq": 2764813
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rhgdnc82FwHFUKXp9ZcpgwXWRAxKf5Buqp",
"Balance": "9999999970",
"Flags": 0,
"OwnerCount": 0,
"Sequence": 4
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "3430FA3A160FA8F9842FA4A8B5549ECDCB3783E585D0F9796A1736DEAE35F6FE",
"PreviousFields": {
"Balance": "9999999980",
"Sequence": 3
},
"PreviousTxnID": "DA6F5CA8CE13A03B8BC58515E085F2FEF90B3C08230B5AEC8DE4FAF39F79010B",
"PreviousTxnLgrSeq": 2906391
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"Amount": "10000",
"CancelAfter": 559913895,
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"FinishAfter": 559892324,
"Flags": 0,
"OwnerNode": "0000000000000000",
"PreviousTxnID": "4756C22BBB7FC23D9081FDB180806939D6FEBC967BE0EC2DB95B166AF9C086E9",
"PreviousTxnLgrSeq": 2764813
},
"LedgerEntryType": "Escrow",
"LedgerIndex": "7243A9750FA4BE3E63F75F6DACFD79AD6B6C76947F6BDC46CD0F52DBEEF64C89"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"Owner": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"RootIndex": "DACDBEBD31D14EAC4207A45DB88734AD14D26D908507F41D2FC623BDD91C582F"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "DACDBEBD31D14EAC4207A45DB88734AD14D26D908507F41D2FC623BDD91C582F"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "r3wN3v2vTUkr5qd6daqDc2xE4LSysdVjkT",
"Balance": "9999999990",
"Flags": 0,
"OwnerCount": 0,
"Sequence": 2
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "F5F1834B80A8B5DA878270AB4DE4EA444281181349375F1D21E46D5F3F0ABAC8",
"PreviousFields": {
"Balance": "9999989990",
"OwnerCount": 1
},
"PreviousTxnID": "4756C22BBB7FC23D9081FDB180806939D6FEBC967BE0EC2DB95B166AF9C086E9",
"PreviousTxnLgrSeq": 2764813
}
}
],
"TransactionIndex": 2,
"TransactionResult": "tesSUCCESS"
},
"validated": true
}
}

View File

@@ -1,81 +0,0 @@
{
"id": 3,
"status": "success",
"type": "response",
"result": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Amount": "100000",
"CancelAfter": 556927412,
"Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "10",
"Flags": 2147483648,
"Sequence": 5,
"SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61",
"TransactionType": "EscrowCreate",
"TxnSignature": "30450221008AC8BDC2151D5EF956197F0E6E89A4F49DEADC1AC38367870E444B1EA8D88D97022075E31427B455DFF87F0F22B849C71FC3987A91C19D63B6D0242E808347EC8A8F",
"date": 556841101,
"hash": "E22D1F6EB006CAD35E0DBD3B4F3748427055E4C143EBE95AA6603823AEEAD324",
"inLedger": 1772019,
"ledger_index": 1772019,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
"PreviousTxnID": "52C4F626FE6F33699B6BE8ADF362836DDCE9B0B1294BFAA15D65D61501350BE6",
"PreviousTxnLgrSeq": 1771204
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"RootIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Balance": "9999798970",
"Flags": 0,
"OwnerCount": 1,
"Sequence": 6
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "5F3B7107F4B524367A173A2B0EAB66E8CC4D2178C1B0C0528CB2F73A8B6BF254",
"PreviousFields": {
"Balance": "9999898980",
"OwnerCount": 0,
"Sequence": 5
},
"PreviousTxnID": "52C4F626FE6F33699B6BE8ADF362836DDCE9B0B1294BFAA15D65D61501350BE6",
"PreviousTxnLgrSeq": 1771204
}
},
{
"CreatedNode": {
"LedgerEntryType": "Escrow",
"LedgerIndex": "E2CF730A31FD419382350C9DBD8DB7CD775BA5AA9B97A9BE9AB07304AA217A75",
"NewFields": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Amount": "100000",
"CancelAfter": 556927412,
"Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
}
}
}
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS"
},
"validated": true
}
}

View File

@@ -1,78 +0,0 @@
{
"id": 3,
"status": "success",
"type": "response",
"result": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Amount": "10000",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "10",
"FinishAfter": 557020800,
"Flags": 2147483648,
"Sequence": 1,
"SigningPubKey": "03C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E43",
"TransactionType": "EscrowCreate",
"TxnSignature": "304402203C9AA4C21E1A1A7427D41583283E7A513DDBDD967B246CADD3B2705D858A7A8E02201BEA7B923B18910EEB9F306F6DE3B3F53549BBFAD46335B62B4C34A6DCB4A476",
"date": 557014081,
"hash": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263",
"inLedger": 1828796,
"ledger_index": 1828796,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
"PreviousTxnID": "613B28E0890FC975F2CBA3D700F75116F623B1E3FE48CB7CB2EB216EAD6F097D",
"PreviousTxnLgrSeq": 1799920
}
},
{
"CreatedNode": {
"LedgerEntryType": "Escrow",
"LedgerIndex": "2B9845CB9DF686B9615BF04F3EC66095A334D985E03E71B893B90FCF6D4DC9E6",
"NewFields": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Amount": "10000",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"FinishAfter": 557020800
}
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Balance": "9999989990",
"Flags": 0,
"OwnerCount": 1,
"Sequence": 2
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "AE5AB6584A76C37C7382B6880609FC7792D90CDA36FF362AF412EB914C1715D3",
"PreviousFields": {
"Balance": "10000000000",
"OwnerCount": 0,
"Sequence": 1
},
"PreviousTxnID": "F181D45FD094A7417926F791D9DF958B84CE4B7B3D92CC9DDCACB1D5EC59AAAA",
"PreviousTxnLgrSeq": 1828732
}
},
{
"CreatedNode": {
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "D623EBEEEE701D4323D0ADA5320AF35EA8CC6520EBBEF69343354CD593DABC88",
"NewFields": {
"Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"RootIndex": "D623EBEEEE701D4323D0ADA5320AF35EA8CC6520EBBEF69343354CD593DABC88"
}
}
}
],
"TransactionIndex": 3,
"TransactionResult": "tesSUCCESS"
},
"validated": true
}
}

View File

@@ -1,95 +0,0 @@
{
"id": 20,
"status": "success",
"type": "response",
"result": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
"Fee": "500",
"Flags": 2147483648,
"Fulfillment": "A0228020D280D1A02BAD0D2EBC0528B92E9BF37AC3E2530832C2C52620307135156F1048",
"OfferSequence": 2,
"Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Sequence": 4,
"SigningPubKey": "03E498E35BC1E109C5995BD3AB0A6D4FFAB61B853C8F6010FABC5DABAF34478B61",
"TransactionType": "EscrowFinish",
"TxnSignature": "3045022100925FEBE21C2E57F81C472A4E5869CAB1D0164C472A46532F39F6F9F7ED6846D002202CF9D9063ADC4CC0ADF4C4692B7EE165C5D124CAA855649389E245D993F41D4D",
"date": 556838610,
"hash": "0E88368CAFC69A722ED829FAE6E2DD3575AE9C192691E60B5ACDF706E219B2BF",
"inLedger": 1771204,
"ledger_index": 1771204,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Balance": "400100000",
"Flags": 0,
"OwnerCount": 0,
"Sequence": 1
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
"PreviousFields": {
"Balance": "400000000"
},
"PreviousTxnID": "795CBC8AFAAB9DC7BD9944C7FAEABF9BB0802A84520BC649213AD6A2C3256C95",
"PreviousTxnLgrSeq": 1770775
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"Owner": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"RootIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "4B4EBB6D8563075813D47491CC325865DFD3DC2E94889F0F39D59D9C059DD81F"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Balance": "9999898980",
"Flags": 0,
"OwnerCount": 0,
"Sequence": 5
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "5F3B7107F4B524367A173A2B0EAB66E8CC4D2178C1B0C0528CB2F73A8B6BF254",
"PreviousFields": {
"Balance": "9999899480",
"OwnerCount": 1,
"Sequence": 4
},
"PreviousTxnID": "5C2A1E7B209A7404D3722A010D331A8C1C853109A47DDF620DE5E3D59F026581",
"PreviousTxnLgrSeq": 1771042
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "rEhw9vD98ZrkY4tZPvkZst5H18RysqFdaB",
"Amount": "100000",
"Condition": "A0258020E24D9E1473D4DF774F6D8E089067282034E4FA7ECACA2AD2E547953B2C113CBD810120",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"FinishAfter": 556838185,
"Flags": 0,
"OwnerNode": "0000000000000000",
"PreviousTxnID": "795CBC8AFAAB9DC7BD9944C7FAEABF9BB0802A84520BC649213AD6A2C3256C95",
"PreviousTxnLgrSeq": 1770775
},
"LedgerEntryType": "Escrow",
"LedgerIndex": "DC524D17B3F650E7A215B332F418E54AE59B0DFC5392E74958B0037AFDFE8C8D"
}
}
],
"TransactionIndex": 1,
"TransactionResult": "tesSUCCESS"
},
"validated": true
}
}

View File

@@ -1,92 +0,0 @@
{
"id": 21,
"status": "success",
"type": "response",
"result": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Fee": "10",
"Flags": 2147483648,
"OfferSequence": 1,
"Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Sequence": 2,
"SigningPubKey": "03C3555B7339FFDDB43495A8371A3A87B4C66B67D49D06CB9BA1FDBFEEB57B6E43",
"TransactionType": "EscrowFinish",
"TxnSignature": "3045022100923B91BA4FD6450813F5335D71C64BA9EB81304A86859A631F2AD8571424A46502200CCE660D36781B84634C5F23619EB6CFCCF942709F54DCCF27CF6F499AE78C9B",
"date": 557256681,
"hash": "41856A742B3CAF307E7B4D0B850F302101F0F415B785454F7501E9960A2A1F6B",
"inLedger": 1908257,
"ledger_index": 1908257,
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Balance": "400210000",
"Flags": 0,
"OwnerCount": 0,
"Sequence": 1
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8",
"PreviousFields": {
"Balance": "400200000"
},
"PreviousTxnID": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263",
"PreviousTxnLgrSeq": 1828796
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Amount": "10000",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"FinishAfter": 557020800,
"Flags": 0,
"OwnerNode": "0000000000000000",
"PreviousTxnID": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263",
"PreviousTxnLgrSeq": 1828796
},
"LedgerEntryType": "Escrow",
"LedgerIndex": "2B9845CB9DF686B9615BF04F3EC66095A334D985E03E71B893B90FCF6D4DC9E6"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"Balance": "9999989980",
"Flags": 0,
"OwnerCount": 0,
"Sequence": 3
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "AE5AB6584A76C37C7382B6880609FC7792D90CDA36FF362AF412EB914C1715D3",
"PreviousFields": {
"Balance": "9999989990",
"OwnerCount": 1,
"Sequence": 2
},
"PreviousTxnID": "55B2057332F8999208C43BA1E7091B423A16E5ED2736C06300B4076085205263",
"PreviousTxnLgrSeq": 1828796
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"Owner": "rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG",
"RootIndex": "D623EBEEEE701D4323D0ADA5320AF35EA8CC6520EBBEF69343354CD593DABC88"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "D623EBEEEE701D4323D0ADA5320AF35EA8CC6520EBBEF69343354CD593DABC88"
}
}
],
"TransactionIndex": 2,
"TransactionResult": "tesSUCCESS"
},
"validated": true
}
}

View File

@@ -1,80 +1,87 @@
{
"result": {
"ledger_hash": "0C445F6F348AA5FF25A631C904F7277980F7FD2A6BACBB3A74FCF95F671D4884",
"ledger_index": 82681558,
"ledger_hash": "3787026448652A36491493C1202A443B2A6CC6022599BB0B25DDB0802DD7F1E7",
"ledger_index": 82681623,
"validated": true,
"ledger": {
"account_hash": "60BF81E9BCA5CEDB629B8D19DE0791F13318B4C6B6886E35A211824F9EB04DE5",
"account_hash": "39D34D858A0FD652143ED84B67A09193772DE0CCEBD2D63619E679293B7A3388",
"close_flags": 0,
"close_time": 748569330,
"close_time_human": "2023-Sep-20 23:55:30.000000000 UTC",
"close_time": 748569571,
"close_time_human": "2023-Sep-20 23:59:31.000000000 UTC",
"close_time_resolution": 10,
"close_time_iso": "2023-09-20T23:59:31Z",
"ledger_hash": "3787026448652A36491493C1202A443B2A6CC6022599BB0B25DDB0802DD7F1E7",
"parent_close_time": 748569570,
"parent_hash": "674FF6C68956E06CB9628833677C3DD71824C87C0AEFB487984CF98C3964DAEE",
"total_coins": "99988406188847858",
"transaction_hash": "11EE9C448D6B07B88A80B4FC7935B485E513816B3B47D0976CE9F51E7CF10A85",
"ledger_index": 82681623,
"closed": true,
"ledger_hash": "0C445F6F348AA5FF25A631C904F7277980F7FD2A6BACBB3A74FCF95F671D4884",
"ledger_index": "82681558",
"parent_close_time": 748569321,
"parent_hash": "817E4F1791BE34C1214E78E02CAB794C54615F69E765D140D0BD820EA81BF0E9",
"total_coins": "99988406204421588",
"transaction_hash": "2D7808600F9CF57E263EC1EC4AA7357586AE949908EA7DBF023D241812CDC9B5",
"diff": [
{
"object_id": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F0E155AF07E5400",
"object_id": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F0E134815E74400",
"object": {
"ExchangeRate": "4f0e155af07e5400",
"ExchangeRate": "4f0e134815e74400",
"Flags": 0,
"Indexes": [
"4CF31E76F470F4CBE7E7EDD1973CDFA564A59672D14C577C51517A1E3469E53A"
"AA0B31FB7FEF4A2546DA2BD0C44E9EC0D3A173EDE92DA1D50F78E61024BAFE4F"
],
"LedgerEntryType": "DirectoryNode",
"RootIndex": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F0E155AF07E5400",
"RootIndex": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F0E134815E74400",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "000000000000000000000000434E590000000000",
"TakerPaysIssuer": "CED6E99370D5C00EF4EBF72567DA99F5661BFB3A",
"index": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F0E155AF07E5400"
"index": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F0E134815E74400"
}
},
# ... (trimmed for length) ...
{
"object_id": "F0B9A528CE25FE77C51C38040A7FEC016C2C841E74C1418D5B0A3845AE4FF3FC",
"object_id": "F65CCB13C33A1739BE63CBF6C77636429B0F4F506766D5E427A7CCC6C102037D",
"object": {
"ExchangeRate": "5b0a3845ae4ff3fc",
"Flags": 0,
"Indexes": [
"7C085618D0A2BC3A8919A032699A2219C08D112CC4020E615CE37C4ABE31A13C"
],
"LedgerEntryType": "DirectoryNode",
"RootIndex": "F0B9A528CE25FE77C51C38040A7FEC016C2C841E74C1418D5B0A3845AE4FF3FC",
"TakerGetsCurrency": "0000000000000000000000005553440000000000",
"TakerGetsIssuer": "2ADB0B3959D60A6E6991F729E1918B7163925230",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000",
"index": "F0B9A528CE25FE77C51C38040A7FEC016C2C841E74C1418D5B0A3845AE4FF3FC"
"Balance": {
"currency": "MAG",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-0.0157136905"
},
"Flags": 2228224,
"HighLimit": {
"currency": "MAG",
"issuer": "rwED3Kn7qu2og2nhCiDun5gKfWBDZ1YzfM",
"value": "45999776.589682"
},
"HighNode": "0",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "MAG",
"issuer": "rXmagwMmnFtVet3uL26Q2iwk287SRvVMJ",
"value": "0"
},
"LowNode": "8d",
"PreviousTxnID": "82783FBAE34A01C1AFA580F105FD716FA083B572BCE91254FD1B93B580E6DD3C",
"PreviousTxnLgrSeq": 82681623,
"index": "F65CCB13C33A1739BE63CBF6C77636429B0F4F506766D5E427A7CCC6C102037D"
}
},
{
"object_id": "F0B9A528CE25FE77C51C38040A7FEC016C2C841E74C1418D5B0A395385B74F22",
"object_id": "F7F1E123DC7B155F93DA6E122C4ED86B7032DFB6C399A5514A422635A8459821",
"object": ""
},
{
"object_id": "F97B88D103742E8C7CBDB982FF8843DC9E128E968668837B9468B1DB47EABBDC",
"object_id": "FA295451E29A37C8BDEC209DB56B2E7D35F714492A1CBD20258FD8E806906E1F",
"object": {
"Account": "r4AZpDKVoBxVcYUJCWMcqZzyWsHTteC4ZE",
"BookDirectory": "623C4C4AD65873DA787AC85A0A1385FE6233B6DE100799474F12ED4BF0EFEAD9",
"BookNode": "0",
"Account": "rff9KstNpP3eFWDp81uv3vizh5dreQRVZv",
"Balance": "11720167",
"Domain": "6D61726B6574696E67",
"EmailHash": "A5EC2C95531D608F0DB6369F8097A16E",
"Flags": 0,
"LedgerEntryType": "Offer",
"OwnerNode": "0",
"PreviousTxnID": "15D26953F0CE5216DC8BC7123E3DB8C8EC210D6127A7F34E182318206E0F2DFE",
"PreviousTxnLgrSeq": 82681558,
"Sequence": 132450778,
"TakerGets": "3677818937",
"TakerPays": {
"currency": "CNY",
"issuer": "rJ1adrpGS3xsnQMb9Cw54tWJVFPuSdZHK",
"value": "19593.43327917432"
},
"index": "F97B88D103742E8C7CBDB982FF8843DC9E128E968668837B9468B1DB47EABBDC"
"LedgerEntryType": "AccountRoot",
"OwnerCount": 1,
"PreviousTxnID": "295A3AE6C90436F80EABB833B716DB52EC234C3D148622C2E5A449539C9FB0EE",
"PreviousTxnLgrSeq": 82681623,
"Sequence": 68507056,
"index": "FA295451E29A37C8BDEC209DB56B2E7D35F714492A1CBD20258FD8E806906E1F",
"urlgravatar": "http://www.gravatar.com/avatar/a5ec2c95531d608f0db6369f8097a16e"
}
}
]
@@ -87,5 +94,4 @@
"message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request"
}
]
}
}

View File

@@ -6,7 +6,8 @@
"transactions": false,
"expand": false,
"owner_funds": false,
"diff": false
"diff": false,
"api_version": 2
}
]
}
}

View File

@@ -1,21 +1,22 @@
{
"result": {
"ledger_hash": "4E2F27F997943EAF522FB0D6AE4B25B1F54FDDE531E0469EF436B18391CFC7D9",
"ledger_index": 82681548,
"ledger_hash": "3787026448652A36491493C1202A443B2A6CC6022599BB0B25DDB0802DD7F1E7",
"ledger_index": 82681623,
"validated": true,
"ledger": {
"account_hash": "608FCCDB3261FEF57B6EB76C89E3FE11B7C8D198DF443831BABF33D08FC8C12A",
"account_hash": "39D34D858A0FD652143ED84B67A09193772DE0CCEBD2D63619E679293B7A3388",
"close_flags": 0,
"close_time": 748569290,
"close_time_human": "2023-Sep-20 23:54:50.000000000 UTC",
"close_time": 748569571,
"close_time_human": "2023-Sep-20 23:59:31.000000000 UTC",
"close_time_resolution": 10,
"closed": true,
"ledger_hash": "4E2F27F997943EAF522FB0D6AE4B25B1F54FDDE531E0469EF436B18391CFC7D9",
"ledger_index": "82681548",
"parent_close_time": 748569282,
"parent_hash": "DCECE701AE72CD9E3C1161EC6C98048DFF5797045CD49AD6038BE6D4610EED93",
"total_coins": "99988406204467365",
"transaction_hash": "95491C538DC25D4980AADC5E2ABAB90E1D4E20A02E772EA6A1C514BFC19987E4"
"close_time_iso": "2023-09-20T23:59:31Z",
"ledger_hash": "3787026448652A36491493C1202A443B2A6CC6022599BB0B25DDB0802DD7F1E7",
"parent_close_time": 748569570,
"parent_hash": "674FF6C68956E06CB9628833677C3DD71824C87C0AEFB487984CF98C3964DAEE",
"total_coins": "99988406188847858",
"transaction_hash": "11EE9C448D6B07B88A80B4FC7935B485E513816B3B47D0976CE9F51E7CF10A85",
"ledger_index": 82681623,
"closed": true
},
"status": "success"
},
@@ -25,4 +26,4 @@
"message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request"
}
]
}
}

View File

@@ -1,9 +1,9 @@
{
"id": 14,
"id": "example_ledger_req",
"command": "ledger",
"ledger_index": "validated",
"transactions": false,
"expand": false,
"owner_funds": false,
"diff": false
}
"api_version": 2
}

View File

@@ -9,15 +9,18 @@
"close_time": 748569571,
"close_time_human": "2023-Sep-20 23:59:31.000000000 UTC",
"close_time_resolution": 10,
"closed": true,
"close_time_iso": "2023-09-20T23:59:31Z",
"ledger_hash": "3787026448652A36491493C1202A443B2A6CC6022599BB0B25DDB0802DD7F1E7",
"ledger_index": "82681623",
"parent_close_time": 748569570,
"parent_hash": "674FF6C68956E06CB9628833677C3DD71824C87C0AEFB487984CF98C3964DAEE",
"total_coins": "99988406188847858",
"transaction_hash": "11EE9C448D6B07B88A80B4FC7935B485E513816B3B47D0976CE9F51E7CF10A85"
"transaction_hash": "11EE9C448D6B07B88A80B4FC7935B485E513816B3B47D0976CE9F51E7CF10A85",
"ledger_index": 82681623,
"closed": true
}
},
"id": "example_ledger_req",
"api_version": 2,
"status": "success",
"type": "response",
"warnings": [
@@ -26,4 +29,4 @@
"message": "This is a clio server. clio only serves validated data. If you want to talk to rippled, include 'ledger_index':'current' in your request"
}
]
}
}

View File

@@ -1,22 +1,23 @@
{
"result": {
"ledger": {
"account_hash": "23C1C8F8ACCEFACBDD9A1804CC25E652A324F9EABD7D0BEF103DA56D6E0306E7",
"close_flags": 0,
"close_time": 752188801,
"close_time_human": "2023-Nov-01 21:20:01.000000000 UTC",
"close_time_resolution": 10,
"closed": true,
"ledger_hash": "140B769E9ED61FCD675A6EEC1F005084614314C1D675C2CFDD11A1024BBD2C96",
"ledger_index": "83626952",
"parent_close_time": 752188800,
"parent_hash": "7D169A530960AFA8A0E38D036D8EF960BC2C2E02C4A0CE848A4200B9376AC99C",
"total_coins": "99988256304478252",
"transaction_hash": "77226182F58D9B5C798262F0E9D8C575D174E434F0C3C7119FB658BA70004CE9"
},
"ledger_hash": "140B769E9ED61FCD675A6EEC1F005084614314C1D675C2CFDD11A1024BBD2C96",
"ledger_index": 83626952,
"status": "success",
"validated": true
}
}
"result": {
"ledger_hash": "9DAAAE85FC0D64E95506608FDB48E8B77706EF64FF144F18EEBC2FC4366D9B20",
"ledger_index": 100972465,
"validated": true,
"ledger": {
"account_hash": "6FD2916DDD574886EBCBAA1CE0048D94E5A57E13EA5DD7B0283A7AB63EBF0131",
"close_flags": 0,
"close_time": 819429360,
"close_time_human": "2025-Dec-19 03:16:00.000000000 UTC",
"close_time_resolution": 10,
"close_time_iso": "2025-12-19T03:16:00Z",
"ledger_hash": "9DAAAE85FC0D64E95506608FDB48E8B77706EF64FF144F18EEBC2FC4366D9B20",
"parent_close_time": 819429352,
"parent_hash": "1FB3618846E1201E38FED328BD13A9D62012469193C0B5BA4ECE6C8BDAA2BFC9",
"total_coins": "99985738467416092",
"transaction_hash": "B61C7B94F583DBFEEDA58DE348518D6FC2F397053EA0E3AC4B8F2BD440629F19",
"ledger_index": 100972465,
"closed": true
},
"status": "success"
}
}

View File

@@ -1,24 +1,26 @@
{
"id": "example_ledger_req",
"result": {
"ledger": {
"account_hash": "B8B2C0C3F9E75E3AEE31D467B2544AB56244E618890BA58679707D6BFC0AF41D",
"close_flags": 0,
"close_time": 752188602,
"close_time_human": "2023-Nov-01 21:16:42.000000000 UTC",
"close_time_resolution": 10,
"closed": true,
"ledger_hash": "1BEECD5D21592EABDEF98D8E4BC038AD10B5700FF7E98011870DF5D6C2A2F39B",
"ledger_index": "83626901",
"parent_close_time": 752188601,
"parent_hash": "6B32CFC42B32C5FB90019AE17F701D96B499A4C8E148A002E18135A434A19D98",
"total_coins": "99988256314388830",
"transaction_hash": "21586C664DC47E12AF34F22EBF1DB55D23F8C98972542BAC0C39B1009CAC84D4"
},
"ledger_hash": "1BEECD5D21592EABDEF98D8E4BC038AD10B5700FF7E98011870DF5D6C2A2F39B",
"ledger_index": 83626901,
"validated": true
},
"status": "success",
"type": "response"
}
"result": {
"ledger_hash": "9D346B0C050C6C5C5172BD731063C33A50C4E1D89EB51F47BBE73E4DA340A684",
"ledger_index": 100972403,
"validated": true,
"ledger": {
"account_hash": "3227B0AE820CA0C7B96A761942912785A27CFC5F3407A39AF03DA0BE9C6A4298",
"close_flags": 0,
"close_time": 819429112,
"close_time_human": "2025-Dec-19 03:11:52.000000000 UTC",
"close_time_resolution": 10,
"close_time_iso": "2025-12-19T03:11:52Z",
"ledger_hash": "9D346B0C050C6C5C5172BD731063C33A50C4E1D89EB51F47BBE73E4DA340A684",
"parent_close_time": 819429111,
"parent_hash": "667CE88E45EC7E9C71436A93970437F2F1C14D5A367F24BE3B571A27846C1EF3",
"total_coins": "99985738468528946",
"transaction_hash": "BC2E8BC91AF126D924CD4C43F0A28DF0DF5DE271A11A781516697C56488F03F7",
"ledger_index": 100972403,
"closed": true
}
},
"id": "example_ledger_req",
"api_version": 2,
"status": "success",
"type": "response"
}

View File

@@ -1,115 +0,0 @@
{
"result": {
"tx_json": {
"Account": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"DeliverMax": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "1"
},
"Destination": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"Fee": "10",
"Flags": 0,
"Paths": [
[{
"account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"currency": "USD",
"issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"type": 49
}],
[{
"account": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x",
"currency": "USD",
"issuer": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x",
"type": 49
}, {
"account": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY",
"currency": "USD",
"issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY",
"type": 49
}, {
"account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"currency": "USD",
"issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"type": 49
}]
],
"SendMax": {
"currency": "USD",
"issuer": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"value": "1.01"
},
"Sequence": 88,
"SigningPubKey": "02EAE5DAB54DD8E1C49641D848D5B97D1B29149106174322EDF98A1B2CCE5D7F8E",
"TransactionType": "Payment",
"TxnSignature": "30440220791B6A3E036ECEFFE99E8D4957564E8C84D1548C8C3E80A87ED1AA646ECCFB16022037C5CAC97E34E3021EBB426479F2ACF3ACA75DB91DCC48D1BCFB4CF547CFEAA0",
"date": 416445410,
"ledger_index": 348734
},
"ctid": "C005523E00000000",
"hash": "E08D6E9754025BA2534A78707605E0601F03ACE063687A0CA1BDDACFCD1698C7",
"meta": {
"AffectedNodes": [{
"ModifiedNode": {
"FinalFields": {
"Account": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"Balance": "59328999119",
"Flags": 0,
"OwnerCount": 11,
"Sequence": 89
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "E0D7BDE68B468FF0B8D948FD865576517DA987569833A05374ADB9A72E870A06",
"PreviousFields": {
"Balance": "59328999129",
"Sequence": 88
},
"PreviousTxnID": "C26AA6B4F7C3B9F55E17CD0D11F12032A1C7AD2757229FFD277C9447A8815E6E",
"PreviousTxnLgrSeq": 348700
}
}, {
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "100"
},
"HighNode": "0",
"LowLimit": {
"currency": "USD",
"issuer": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"value": "0"
},
"LowNode": "0"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0"
}
},
"PreviousTxnID": "53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8",
"PreviousTxnLgrSeq": 343570
}
}],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS",
"delivered_amount": "unavailable"
},
"validated": true,
"ledger_index": 348734,
"ledger_hash": "195F62F34EB2CCFA4C5888BA20387E82EB353DDB4508BAE6A835AF19FB8B0C09",
"close_time_iso": "2013-03-12T23:16:50Z",
"status": "success"
}
}

View File

@@ -1,116 +0,0 @@
{
"result": {
"tx_json": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"Fee": "12",
"Flags": 0,
"LastLedgerSequence": 56865248,
"OfferSequence": 5037708,
"Sequence": 5037710,
"SigningPubKey": "03B51A3EDF70E4098DA7FB053A01C5A6A0A163A30ED1445F14F87C7C3295FCB3BE",
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
},
"TransactionType": "OfferCreate",
"TxnSignature": "3045022100A5023A0E64923616FCDB6D664F569644C7C9D1895772F986CD6B981B515B02A00220530C973E9A8395BC6FE2484948D2751F6B030FC7FB8575D1BFB406368AD554D9",
"date": 648248020,
"ledger_index": 56865245
},
"ctid": "C363B1DD00000000",
"hash": "C53ECF838647FA5A4C780377025FEC7999AB4182590510CA461444B207AB74A9",
"meta": {
"AffectedNodes": [{
"ModifiedNode": {
"FinalFields": {
"ExchangeRate": "4f04c66806cf7400",
"Flags": 0,
"RootIndex": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "000000000000000000000000434E590000000000",
"TakerPaysIssuer": "CED6E99370D5C00EF4EBF72567DA99F5661BFB3A"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400"
}
}, {
"ModifiedNode": {
"FinalFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"Balance": "10404767991",
"Flags": 0,
"OwnerCount": 3,
"Sequence": 5037711
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "1DECD9844E95FFBA273F1B94BA0BF2564DDF69F2804497A6D7837B52050174A2",
"PreviousFields": {
"Balance": "10404768003",
"Sequence": 5037710
},
"PreviousTxnID": "4DC47B246B5EB9CCE92ABA8C482479E3BF1F946CABBEF74CA4DE36521D5F9008",
"PreviousTxnLgrSeq": 56865244
}
}, {
"DeletedNode": {
"FinalFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"BookDirectory": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"BookNode": "0",
"Flags": 0,
"OwnerNode": "0",
"PreviousTxnID": "8F5FF57B404827F12BDA7561876A13C3E3B3095CBF75334DBFB5F227391A660C",
"PreviousTxnLgrSeq": 56865244,
"Sequence": 5037708,
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "26AAE6CA8D29E28A47C92ADF22D5D96A0216F0551E16936856DDC8CB1AAEE93B"
}
}, {
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"IndexNext": "0",
"IndexPrevious": "0",
"Owner": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"RootIndex": "47FAF5D102D8CE655574F440CDB97AC67C5A11068BB3759E87C2B9745EE94548"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "47FAF5D102D8CE655574F440CDB97AC67C5A11068BB3759E87C2B9745EE94548"
}
}, {
"CreatedNode": {
"LedgerEntryType": "Offer",
"LedgerIndex": "8BAEE3C7DE04A568E96007420FA11ABD0BC9AE44D35932BB5640E9C3FB46BC9B",
"NewFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"BookDirectory": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"Sequence": 5037710,
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
}
}
}
}],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS"
},
"validated": true,
"ledger_index": 56865245,
"ledger_hash": "793E56131D8D4ABFB27FA383BFC44F2978B046E023FF46C588D7E0C874C2472A",
"close_time_iso": "2020-07-16T20:53:40Z",
"status": "success"
}
}

View File

@@ -0,0 +1,123 @@
{
"result": {
"tx_json": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"Fee": "12",
"Flags": 0,
"LastLedgerSequence": 56865248,
"OfferSequence": 5037708,
"Sequence": 5037710,
"SigningPubKey": "03B51A3EDF70E4098DA7FB053A01C5A6A0A163A30ED1445F14F87C7C3295FCB3BE",
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
},
"TransactionType": "OfferCreate",
"TxnSignature": "3045022100A5023A0E64923616FCDB6D664F569644C7C9D1895772F986CD6B981B515B02A00220530C973E9A8395BC6FE2484948D2751F6B030FC7FB8575D1BFB406368AD554D9",
"ledger_index": 56865245,
"ctid": "C363B1DD00000000",
"date": 648248020
},
"hash": "C53ECF838647FA5A4C780377025FEC7999AB4182590510CA461444B207AB74A9",
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"ExchangeRate": "4f04c66806cf7400",
"Flags": 0,
"RootIndex": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "000000000000000000000000434E590000000000",
"TakerPaysIssuer": "CED6E99370D5C00EF4EBF72567DA99F5661BFB3A"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"Balance": "10404767991",
"Flags": 0,
"OwnerCount": 3,
"Sequence": 5037711
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "1DECD9844E95FFBA273F1B94BA0BF2564DDF69F2804497A6D7837B52050174A2",
"PreviousFields": {
"Balance": "10404768003",
"Sequence": 5037710
},
"PreviousTxnID": "4DC47B246B5EB9CCE92ABA8C482479E3BF1F946CABBEF74CA4DE36521D5F9008",
"PreviousTxnLgrSeq": 56865244
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"BookDirectory": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"BookNode": "0",
"Flags": 0,
"OwnerNode": "0",
"PreviousTxnID": "8F5FF57B404827F12BDA7561876A13C3E3B3095CBF75334DBFB5F227391A660C",
"PreviousTxnLgrSeq": 56865244,
"Sequence": 5037708,
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "26AAE6CA8D29E28A47C92ADF22D5D96A0216F0551E16936856DDC8CB1AAEE93B"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"IndexNext": "0",
"IndexPrevious": "0",
"Owner": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"RootIndex": "47FAF5D102D8CE655574F440CDB97AC67C5A11068BB3759E87C2B9745EE94548"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "47FAF5D102D8CE655574F440CDB97AC67C5A11068BB3759E87C2B9745EE94548"
}
},
{
"CreatedNode": {
"LedgerEntryType": "Offer",
"LedgerIndex": "8BAEE3C7DE04A568E96007420FA11ABD0BC9AE44D35932BB5640E9C3FB46BC9B",
"NewFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"BookDirectory": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"Sequence": 5037710,
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
}
}
}
}
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS"
},
"validated": true,
"ledger_index": 56865245,
"ledger_hash": "793E56131D8D4ABFB27FA383BFC44F2978B046E023FF46C588D7E0C874C2472A",
"close_time_iso": "2020-07-16T20:53:40Z",
"ctid": "C363B1DD00000000",
"status": "success"
}
}

View File

@@ -1,127 +0,0 @@
{
"result": {
"tx_json": {
"Account": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"DeliverMax": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "1"
},
"Destination": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"Fee": "10",
"Flags": 0,
"Paths": [
[
{
"account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"currency": "USD",
"issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"type": 49
}
],
[
{
"account": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x",
"currency": "USD",
"issuer": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x",
"type": 49
},
{
"account": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY",
"currency": "USD",
"issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY",
"type": 49
},
{
"account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"currency": "USD",
"issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"type": 49
}
]
],
"SendMax": {
"currency": "USD",
"issuer": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"value": "1.01"
},
"Sequence": 88,
"SigningPubKey": "02EAE5DAB54DD8E1C49641D848D5B97D1B29149106174322EDF98A1B2CCE5D7F8E",
"TransactionType": "Payment",
"TxnSignature": "30440220791B6A3E036ECEFFE99E8D4957564E8C84D1548C8C3E80A87ED1AA646ECCFB16022037C5CAC97E34E3021EBB426479F2ACF3ACA75DB91DCC48D1BCFB4CF547CFEAA0",
"date": 416445410,
"ledger_index": 348734
},
"ctid": "C005523E00000000",
"hash": "E08D6E9754025BA2534A78707605E0601F03ACE063687A0CA1BDDACFCD1698C7",
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"Balance": "59328999119",
"Flags": 0,
"OwnerCount": 11,
"Sequence": 89
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "E0D7BDE68B468FF0B8D948FD865576517DA987569833A05374ADB9A72E870A06",
"PreviousFields": {
"Balance": "59328999129",
"Sequence": 88
},
"PreviousTxnID": "C26AA6B4F7C3B9F55E17CD0D11F12032A1C7AD2757229FFD277C9447A8815E6E",
"PreviousTxnLgrSeq": 348700
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "100"
},
"HighNode": "0",
"LowLimit": {
"currency": "USD",
"issuer": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"value": "0"
},
"LowNode": "0"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0"
}
},
"PreviousTxnID": "53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8",
"PreviousTxnLgrSeq": 343570
}
}
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS",
"delivered_amount": "unavailable"
},
"validated": true,
"ledger_index": 348734,
"ledger_hash": "195F62F34EB2CCFA4C5888BA20387E82EB353DDB4508BAE6A835AF19FB8B0C09",
"close_time_iso": "2013-03-12T23:16:50Z"
},
"id": "CTID example",
"api_version": 2,
"status": "success",
"type": "response"
}

View File

@@ -1,126 +0,0 @@
{
"result": {
"tx_json": {
"Account": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"DeliverMax": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "1"
},
"Destination": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"Fee": "10",
"Flags": 0,
"Paths": [
[
{
"account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"currency": "USD",
"issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"type": 49
}
],
[
{
"account": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x",
"currency": "USD",
"issuer": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x",
"type": 49
},
{
"account": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY",
"currency": "USD",
"issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY",
"type": 49
},
{
"account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"currency": "USD",
"issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
"type": 49
}
]
],
"SendMax": {
"currency": "USD",
"issuer": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"value": "1.01"
},
"Sequence": 88,
"SigningPubKey": "02EAE5DAB54DD8E1C49641D848D5B97D1B29149106174322EDF98A1B2CCE5D7F8E",
"TransactionType": "Payment",
"TxnSignature": "30440220791B6A3E036ECEFFE99E8D4957564E8C84D1548C8C3E80A87ED1AA646ECCFB16022037C5CAC97E34E3021EBB426479F2ACF3ACA75DB91DCC48D1BCFB4CF547CFEAA0",
"date": 416445410,
"ledger_index": 348734
},
"ctid": "C005523E00000000",
"hash": "E08D6E9754025BA2534A78707605E0601F03ACE063687A0CA1BDDACFCD1698C7",
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"Account": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"Balance": "59328999119",
"Flags": 0,
"OwnerCount": 11,
"Sequence": 89
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "E0D7BDE68B468FF0B8D948FD865576517DA987569833A05374ADB9A72E870A06",
"PreviousFields": {
"Balance": "59328999129",
"Sequence": 88
},
"PreviousTxnID": "C26AA6B4F7C3B9F55E17CD0D11F12032A1C7AD2757229FFD277C9447A8815E6E",
"PreviousTxnLgrSeq": 348700
}
},
{
"ModifiedNode": {
"FinalFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"value": "100"
},
"HighNode": "0",
"LowLimit": {
"currency": "USD",
"issuer": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH",
"value": "0"
},
"LowNode": "0"
},
"LedgerEntryType": "RippleState",
"LedgerIndex": "EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959",
"PreviousFields": {
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0"
}
},
"PreviousTxnID": "53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8",
"PreviousTxnLgrSeq": 343570
}
}
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS",
"delivered_amount": "unavailable"
},
"validated": true,
"ledger_index": 348734,
"ledger_hash": "195F62F34EB2CCFA4C5888BA20387E82EB353DDB4508BAE6A835AF19FB8B0C09",
"close_time_iso": "2013-03-12T23:16:50Z"
},
"api_version": 2,
"status": "success",
"type": "response"
}

View File

@@ -0,0 +1,126 @@
{
"result": {
"tx_json": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"Fee": "12",
"Flags": 0,
"LastLedgerSequence": 56865248,
"OfferSequence": 5037708,
"Sequence": 5037710,
"SigningPubKey": "03B51A3EDF70E4098DA7FB053A01C5A6A0A163A30ED1445F14F87C7C3295FCB3BE",
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
},
"TransactionType": "OfferCreate",
"TxnSignature": "3045022100A5023A0E64923616FCDB6D664F569644C7C9D1895772F986CD6B981B515B02A00220530C973E9A8395BC6FE2484948D2751F6B030FC7FB8575D1BFB406368AD554D9",
"ledger_index": 56865245,
"ctid": "C363B1DD00000000",
"date": 648248020
},
"hash": "C53ECF838647FA5A4C780377025FEC7999AB4182590510CA461444B207AB74A9",
"meta": {
"AffectedNodes": [
{
"ModifiedNode": {
"FinalFields": {
"ExchangeRate": "4f04c66806cf7400",
"Flags": 0,
"RootIndex": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "000000000000000000000000434E590000000000",
"TakerPaysIssuer": "CED6E99370D5C00EF4EBF72567DA99F5661BFB3A"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"Balance": "10404767991",
"Flags": 0,
"OwnerCount": 3,
"Sequence": 5037711
},
"LedgerEntryType": "AccountRoot",
"LedgerIndex": "1DECD9844E95FFBA273F1B94BA0BF2564DDF69F2804497A6D7837B52050174A2",
"PreviousFields": {
"Balance": "10404768003",
"Sequence": 5037710
},
"PreviousTxnID": "4DC47B246B5EB9CCE92ABA8C482479E3BF1F946CABBEF74CA4DE36521D5F9008",
"PreviousTxnLgrSeq": 56865244
}
},
{
"DeletedNode": {
"FinalFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"BookDirectory": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"BookNode": "0",
"Flags": 0,
"OwnerNode": "0",
"PreviousTxnID": "8F5FF57B404827F12BDA7561876A13C3E3B3095CBF75334DBFB5F227391A660C",
"PreviousTxnLgrSeq": 56865244,
"Sequence": 5037708,
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
}
},
"LedgerEntryType": "Offer",
"LedgerIndex": "26AAE6CA8D29E28A47C92ADF22D5D96A0216F0551E16936856DDC8CB1AAEE93B"
}
},
{
"ModifiedNode": {
"FinalFields": {
"Flags": 0,
"IndexNext": "0",
"IndexPrevious": "0",
"Owner": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"RootIndex": "47FAF5D102D8CE655574F440CDB97AC67C5A11068BB3759E87C2B9745EE94548"
},
"LedgerEntryType": "DirectoryNode",
"LedgerIndex": "47FAF5D102D8CE655574F440CDB97AC67C5A11068BB3759E87C2B9745EE94548"
}
},
{
"CreatedNode": {
"LedgerEntryType": "Offer",
"LedgerIndex": "8BAEE3C7DE04A568E96007420FA11ABD0BC9AE44D35932BB5640E9C3FB46BC9B",
"NewFields": {
"Account": "rhhh49pFH96roGyuC4E5P4CHaNjS1k8gzM",
"BookDirectory": "02BAAC1E67C1CE0E96F0FA2E8061020536CEDD043FEB0FF54F04C66806CF7400",
"Sequence": 5037710,
"TakerGets": "15000000000",
"TakerPays": {
"currency": "CNY",
"issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y",
"value": "20160.75"
}
}
}
}
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS"
},
"validated": true,
"ledger_index": 56865245,
"ledger_hash": "793E56131D8D4ABFB27FA383BFC44F2978B046E023FF46C588D7E0C874C2472A",
"close_time_iso": "2020-07-16T20:53:40Z",
"ctid": "C363B1DD00000000"
},
"id": "example_tx_hash",
"api_version": 2,
"status": "success",
"type": "response"
}

View File

@@ -92,7 +92,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="send-xrp-modal-label">Send XRP</h1>
<h1 class="modal-title subhead-sm-r" id="send-xrp-modal-label">Send XRP</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">

View File

@@ -92,7 +92,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="send-xrp-modal-label">Send XRP</h1>
<h1 class="modal-title subhead-sm-r" id="send-xrp-modal-label">Send XRP</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">

View File

@@ -1,51 +1,174 @@
'use strict'
const xrpl = require('xrpl');
import { Client, isoTimeToRippleTime, rippleTimeToISOTime, validate, getBalanceChanges } from 'xrpl'
// Preqrequisites:
// 1. Create an escrow using the create-escrow.js snippet
// 2. Replace the OfferSequence with the sequence number of the escrow you created
// 3. Paste the seed of the account that created the escrow
// 4. Run this snippet
const client = new Client('wss://s.altnet.rippletest.net:51233')
await client.connect()
const seed = "sEd7jfWyNG6J71dEojB3W9YdHp2KCjy"; // replace with your seed
const sequenceNumber = 0; // replace with the sequence number of your escrow
console.log('Funding new wallet from faucet...')
const { wallet } = await client.fundWallet()
// const destinationAddress = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet
// Alternative: Get another account to send the escrow to. Use this if you get
// a tecDIR_FULL error trying to create escrows to the Testnet faucet.
const destinationAddress = (await client.fundWallet()).wallet.address
async function main() {
try {
// Connect -------------------------------------------------------------------
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
// Create an escrow that won't be finished -------------------------------------
const cancelDelay = 30
const cancelAfter = new Date()
cancelAfter.setSeconds(cancelAfter.getSeconds() + cancelDelay)
console.log('This escrow will expire after:', cancelAfter)
// Convert cancelAfter to seconds since the Ripple Epoch:
const cancelAfterRippleTime = isoTimeToRippleTime(cancelAfter.toISOString())
const conditionHex = 'A02580200000000000000000000000000000000000000000000000000000000000000000810120'
// Prepare wallet to sign the transaction -------------------------------------
const wallet = await xrpl.Wallet.fromSeed(seed);
console.log("Wallet Address: ", wallet.address);
console.log("Seed: ", seed);
const escrowCreate = {
TransactionType: 'EscrowCreate',
Account: wallet.address,
Destination: destinationAddress,
Amount: '123456',
Condition: conditionHex,
CancelAfter: cancelAfterRippleTime
}
validate(escrowCreate)
// Construct the escrow cancel transaction ------------------------------------
console.log('Signing and submitting the EscrowCreate transaction.')
const response = await client.submitAndWait(escrowCreate, {
wallet,
autofill: true // Note: fee is higher based on condition size in bytes
})
console.log(JSON.stringify(response.result, null, 2))
const escrowCreateResultCode = response.result.meta.TransactionResult
if (escrowCreateResultCode !== 'tesSUCCESS') {
console.error(`EscrowCreate failed with code ${escrowCreateResultCode}.`)
client.disconnect()
process.exit(1)
}
if(!sequenceNumber){
throw new Error("Please specify the sequence number of the escrow you created");
};
// Wait for the escrow to expire -----------------------------------------------
// Since ledger close times can be rounded by up to 10 seconds, wait an extra
// 10 seconds to make sure the escrow has officially expired.
console.log(`Waiting ${cancelDelay + 10} seconds for the escrow to expire...`)
await sleep(cancelDelay + 10)
const escrowCancelTransaction = {
"Account": wallet.address,
"TransactionType": "EscrowCancel",
"Owner": wallet.address,
"OfferSequence": sequenceNumber, // Sequence number
};
/* Sleep function that can be used with await */
function sleep (delayInSeconds) {
const delayInMs = delayInSeconds * 1000
return new Promise((resolve) => setTimeout(resolve, delayInMs))
}
xrpl.validate(escrowCancelTransaction);
// Look up the official close time of the validated ledger ---------------------
const ledger = await client.request({
command: 'ledger',
ledger_index: 'validated'
})
if (ledger.error) {
console.error(`Error looking up validated ledger: ${ledger.error}`)
client.disconnect()
process.exit(1)
}
const closeTime = ledger.result.ledger.close_time
console.log('Latest validated ledger closed at',
rippleTimeToISOTime(closeTime)
)
const ledgerHash = ledger.result.ledger.ledger_hash
// Sign and submit the transaction --------------------------------------------
console.log('Signing and submitting the transaction: ', JSON.stringify(escrowCancelTransaction, null, "\t"));
const response = await client.submitAndWait(escrowCancelTransaction, { wallet });
console.log(`Finished submitting! \n${JSON.stringify(response.result, null, "\t")}`);
// Look up escrows connected to the account, handling pagination ---------------
let marker
let expiredEscrow
while (true) {
console.log(`Requesting page of account_objects with marker ${marker}`)
const resp = await client.request({
command: 'account_objects',
account: wallet.address,
ledger_hash: ledgerHash,
type: 'escrow',
marker
})
if (resp.error) {
console.error('account_objects failed with error', resp)
client.disconnect()
process.exit(1)
}
await client.disconnect();
// Add new escrows to the full list
for (const escrow of resp.result.account_objects) {
if (!escrow.hasOwnProperty('CancelAfter')) {
console.log('This escrow does not have an expiration.')
} else if (escrow.CancelAfter < closeTime) {
console.log('This escrow has expired.')
expiredEscrow = escrow
break
} else {
const expirationTime = rippleTimeToISOTime(escrow.CancelAfter)
console.log('This escrow expires at', expirationTime)
}
}
} catch (error) {
console.log(error);
if (expiredEscrow) {
// Found an expired escrow, stop paginating
break
}
// If there's a marker, loop and fetch the next page of results
if (resp.result.marker) {
marker = resp.result.marker
} else {
break
}
}
main()
if (!expiredEscrow) {
console.error('Did not find any expired escrows.')
process.exit(1)
}
// Find the sequence number of the expired escrow ------------------------------
let escrow_seq
const txResp = await client.request({
command: 'tx',
transaction: expiredEscrow.PreviousTxnID
})
if (txResp.error) {
console.error("Couldn't get transaction. Maybe this server doesn't have",
'enough transaction history available?')
client.disconnect()
process.exit(1)
}
if (txResp.result.tx_json.TransactionType === 'EscrowCreate') {
// Save this sequence number for canceling the escrow
escrow_seq = txResp.result.tx_json.Sequence
if (escrow_seq === 0) {
// This transaction used a Ticket, so use TicketSequence instead.
escrow_seq = response.result.tx_json.TicketSequence
}
} else {
console.error("This escrow's previous transaction wasn't EscrowCreate!")
client.disconnect()
process.exit(1)
}
// Send EscrowCancel transaction -----------------------------------------------
const escrowCancel = {
TransactionType: 'EscrowCancel',
Account: wallet.address,
Owner: expiredEscrow.Account,
OfferSequence: escrow_seq
}
validate(escrowCancel)
console.log('Signing and submitting the EscrowCancel transaction.')
const cancelResponse = await client.submitAndWait(escrowCancel, {
wallet,
autofill: true
})
console.log(JSON.stringify(cancelResponse.result, null, 2))
const cancelResultCode = cancelResponse.result.meta.TransactionResult
if (cancelResultCode !== 'tesSUCCESS') {
console.error(`EscrowCancel failed with result code ${cancelResultCode}`)
client.disconnect()
process.exit(1)
}
console.log('Escrow canceled. Balance changes:')
console.log(JSON.stringify(getBalanceChanges(cancelResponse.result.meta), null, 2))
client.disconnect()

View File

@@ -1,69 +0,0 @@
'use strict'
const xrpl = require('xrpl');
const cc = require('five-bells-condition');
const crypto = require('crypto');
// Useful Documentation:-
// 1. five-bells-condition: https://www.npmjs.com/package/five-bells-condition
// 2. Crypto module: https://nodejs.org/api/crypto.html
// Your seed value, for testing purposes you can make one with the faucet:
// https://xrpl.org/resources/dev-tools/xrp-faucets
const seed = "sEd7jfWyNG6J71dEojB3W9YdHp2KCjy";
async function main() {
try {
// Connect ----------------------------------------------------------------
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
// Prepare wallet to sign the transaction ---------------------------------
const wallet = await xrpl.Wallet.fromSeed(seed);
console.log("Wallet Address: ", wallet.address);
console.log("Seed: ", seed);
// Set the escrow finish time ---------------------------------------------
let finishAfter = new Date((new Date().getTime() / 1000) + 120); // 2 minutes from now
finishAfter = new Date(finishAfter * 1000);
console.log("This escrow will finish after: ", finishAfter);
// Construct condition and fulfillment ------------------------------------
const preimageData = crypto.randomBytes(32);
const myFulfillment = new cc.PreimageSha256();
myFulfillment.setPreimage(preimageData);
const conditionHex = myFulfillment.getConditionBinary().toString('hex').toUpperCase();
console.log('Condition:', conditionHex);
console.log('Fulfillment:', myFulfillment.serializeBinary().toString('hex').toUpperCase());
// Prepare EscrowCreate transaction ------------------------------------
const escrowCreateTransaction = {
"TransactionType": "EscrowCreate",
"Account": wallet.address,
"Destination": wallet.address,
"Amount": "6000000", //drops XRP
"DestinationTag": 2023,
"Condition": conditionHex, // Omit this for time-held escrows
"Fee": "12",
"FinishAfter": xrpl.isoTimeToRippleTime(finishAfter.toISOString()),
};
xrpl.validate(escrowCreateTransaction);
// Sign and submit the transaction ----------------------------------------
console.log('Signing and submitting the transaction:',
JSON.stringify(escrowCreateTransaction, null, "\t"), "\n"
);
const response = await client.submitAndWait(escrowCreateTransaction, { wallet });
console.log(`Sequence number: ${response.result.tx_json.Sequence}`);
console.log(`Finished submitting! ${JSON.stringify(response.result, null, "\t")}`);
await client.disconnect();
} catch (error) {
console.log(error);
}
}
main()

View File

@@ -1,60 +0,0 @@
'use strict'
const xrpl = require('xrpl')
// Preqrequisites:
// 1. Create an escrow using the create-escrow.js snippet
// 2. Replace the OfferSequence with the sequence number of the escrow you created
// 3. Replace the Condition and Fulfillment with the values from the escrow you created
// 4. Paste the seed of the account that created the escrow
// 5. Run the snippet
const seed = "sEd7jfWyNG6J71dEojB3W9YdHp2KCjy"; // Test seed. Don't use
const offerSequence = null;
const condition = "";
const fulfillment = "";
const main = async () => {
try {
// Connect ----------------------------------------------------------------
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
// Prepare wallet to sign the transaction ---------------------------------
const wallet = await xrpl.Wallet.fromSeed(seed);
console.log("Wallet Address: ", wallet.address);
console.log("Seed: ", seed);
if((!offerSequence)|| (condition === "" || fulfillment === "")){
throw new Error("Please specify the sequence number, condition and fulfillment of the escrow you created");
};
// Prepare EscrowFinish transaction ---------------------------------
const escrowFinishTransaction = {
"Account": wallet.address,
"TransactionType": "EscrowFinish",
"Owner": wallet.address,
// This should equal the sequence number of the escrow transaction
"OfferSequence": offerSequence,
// Crypto condition that must be met before escrow can be completed, passed on escrow creation.
// Omit this for time-held escrows.
"Condition": condition,
// Fulfillment of the condition, passed on escrow creation.
// Omit this for time-held escrows.
"Fulfillment": fulfillment,
};
xrpl.validate(escrowFinishTransaction);
// Sign and submit the transaction ----------------------------------------
console.log('Signing and submitting the transaction:', JSON.stringify(escrowFinishTransaction, null, "\t"));
const response = await client.submitAndWait(escrowFinishTransaction, { wallet });
console.log(`Finished submitting! ${JSON.stringify(response.result, null, "\t")}`);
await client.disconnect();
} catch (error) {
console.log(error);
}
}
main()

View File

@@ -1,54 +1,114 @@
const xrpl = require('xrpl')
import { Client, dropsToXrp, rippleTimeToISOTime } from 'xrpl'
// List the Escrows on an existing account filtered by source and destination
// https://xrpl.org/escrow.html#escrow
// https://xrpl.org/account_objects.html#account_objects
async function main() {
// Testnet example: rPRKeXbcFMcn69nR2bovp4bEcP8kZx7x5i
account = "rPRKeXbcFMcn69nR2bovp4bEcP8kZx7x5i"
// Connect to a testnet node
console.log("Connecting to Testnet...")
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233/')
// Set up client and address
const address = 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn'
console.log('Connecting to Mainnet...')
const client = new Client('wss://xrplcluster.com/')
await client.connect()
const response = await client.request({
"command": "account_objects",
"account": account,
"ledger_index": "validated",
"type": "escrow"
// Look up the official close time of the validated ledger ---------------------
const ledger = await client.request({
command: 'ledger',
ledger_index: 'validated'
})
if (ledger.error) {
console.error(`Error looking up validated ledger: ${ledger.error}`)
client.disconnect()
process.exit(1)
}
const close_time = ledger.result.ledger.close_time
const ledger_hash = ledger.result.ledger.ledger_hash
var incoming = []
var outgoing = []
// Look up objects filtered to escrows, handling pagination --------------------
let marker
const escrows = []
while (true) {
console.log(`Requesting page of account_objects with marker ${marker}`)
const resp = await client.request({
command: 'account_objects',
account: address,
ledger_hash, // Caution: if you use a shortcut
// such as "validated", the ledger may
// change during iteration, leading to
// inconsistent results.
type: 'escrow',
marker
})
if (resp.error) {
console.error('account_objects failed with error', resp)
client.disconnect()
process.exit(1)
}
for (var i = 0; i < response.result.account_objects.length; i++) {
if (response.result.account_objects[i].Account == account) {
outgoing.push(response.result.account_objects[i])
// Add new escrows to the full list
for (const escrow of resp.result.account_objects) {
escrows.push(escrow)
}
// If there's a marker, loop and fetch the next page of results
if (resp.result.marker) {
marker = resp.result.marker
} else {
break
}
}
// Define helper function for displaying amounts -------------------------------
function display_amount (amount) {
if (typeof amount === 'string') {
// amount is drops of XRP.
const decimal_xrp = dropsToXrp(amount)
return `${decimal_xrp} XRP`
} else if (amount.hasOwnProperty('mpt_issuance_id')) {
// amount is an MPT.
// More info may be available, but that would require looking it up.
return `${amount.value} units of MPT ${amount.mpt_issuance_id}`
} else if (amount.hasOwnProperty('issuer')) {
// amount is a trust line token.
// Currency may be 3 chars or hex. For guidelines parsing hex codes,
// see "Normalize Currency Codes" code sample.
return `${amount.value} ${amount.currency} issued by ${amount.issuer}`
}
console.error(`Unexpected type of amount: ${amount}`)
client.disconnect()
process.exit(1)
}
// Summarize results -----------------------------------------------------------
console.log(`Found ${escrows.length} escrow(s).`)
for (const escrow of escrows) {
if (escrow.Account === address) {
console.log(`Outgoing escrow to ${escrow.Destination}`)
} else if (escrow.Destination === address) {
console.log(`Incoming escrow from ${escrow.Account}`)
} else {
console.log('Neither incoming nor outgoing? This is unexexpected.')
}
console.log(` Amount: ${display_amount(escrow.Amount)}`)
if (escrow.hasOwnProperty('Condition')) {
console.log(` Condition: ${escrow.Condition}`)
}
if (escrow.FinishAfter) {
const mature_time_display = rippleTimeToISOTime(escrow.FinishAfter)
if (escrow.FinishAfter < close_time) {
console.log(` Matured at ${mature_time_display}`)
} else {
incoming.push(response.result.account_objects[i])
console.log(` Will mature at ${mature_time_display}`)
}
}
}
console.log("\nIncoming/Received escrow(s):")
for (var i = 0; i < incoming.length; i++) {
console.log(`\n${i+1}. Index (ObjectID/keylet): ${incoming[i].index}`)
console.log(` - Account: ${incoming[i].Account})`)
console.log(` - Destination: ${incoming[i].Destination}`)
console.log(` - Amount: ${incoming[i].Amount} drops`)
}
console.log("\nOutgoing/Sent escrow(s):")
for (var i = 0; i < outgoing.length; i++) {
console.log(`\n${i+1}. Index (ObjectID/keylet): ${outgoing[i].index}`)
console.log(` - Account: ${outgoing[i].Account})`)
console.log(` - Destination: ${outgoing[i].Destination}`)
console.log(` - Amount: ${outgoing[i].Amount} drops`)
if (escrow.hasOwnProperty('CancelAfter')) {
const cancel_time_display = rippleTimeToISOTime(escrow.CancelAfter)
if (escrow.CancelAfter < close_time) {
console.log(` EXPIRED at ${cancel_time_display}`)
} else {
console.log(` Expires at ${cancel_time_display}`)
}
}
}
client.disconnect()
// End main()
}
main()

View File

@@ -1,9 +0,0 @@
const cc = require('five-bells-condition')
const crypto = require('crypto')
const preimageData = crypto.randomBytes(32)
const myFulfillment = new cc.PreimageSha256()
myFulfillment.setPreimage(preimageData)
console.log('Condition:', myFulfillment.getConditionBinary().toString('hex').toUpperCase())
console.log('Fulfillment:', myFulfillment.serializeBinary().toString('hex').toUpperCase())

View File

@@ -1,9 +1,10 @@
{
"name": "escrow-examples",
"version": "0.0.3",
"version": "2.0.0",
"license": "MIT",
"dependencies": {
"five-bells-condition": "*",
"xrpl": "^4.0.0"
}
"xrpl": "^4.4.0"
},
"type": "module"
}

View File

@@ -0,0 +1,89 @@
import xrpl from 'xrpl'
import { PreimageSha256 } from 'five-bells-condition'
import { randomBytes } from 'crypto'
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
await client.connect()
console.log('Funding new wallet from faucet...')
const { wallet } = await client.fundWallet()
// const destination_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet
// Alternative: Get another account to send the escrow to. Use this if you get
// a tecDIR_FULL error trying to create escrows to the Testnet faucet.
const destination_address = (await client.fundWallet()).wallet.address
// Create the crypto-condition for release ----------------------------------
const preimage = randomBytes(32)
const fulfillment = new PreimageSha256()
fulfillment.setPreimage(preimage)
const fulfillmentHex = fulfillment.serializeBinary().toString('hex').toUpperCase()
const conditionHex = fulfillment.getConditionBinary().toString('hex').toUpperCase()
console.log('Condition:', conditionHex)
console.log('Fulfillment:', fulfillmentHex)
// Set the escrow expiration ------------------------------------------------
const cancelDelay = 300 // Seconds in the future when the escrow should expire
const cancelAfter = new Date() // Current time
cancelAfter.setSeconds(cancelAfter.getSeconds() + cancelDelay)
console.log('This escrow will expire after:', cancelAfter)
// Convert cancelAfter to seconds since the Ripple Epoch:
const cancelAfterRippleTime = xrpl.isoTimeToRippleTime(cancelAfter.toISOString())
// Send EscrowCreate transaction --------------------------------------------
const escrowCreate = {
TransactionType: 'EscrowCreate',
Account: wallet.address,
Destination: destination_address,
Amount: '123456', // drops of XRP
Condition: conditionHex,
CancelAfter: cancelAfterRippleTime
}
xrpl.validate(escrowCreate)
console.log('Signing and submitting the transaction:',
JSON.stringify(escrowCreate, null, 2))
const response = await client.submitAndWait(escrowCreate, {
wallet,
autofill: true // Note: fee is higher based on condition size in bytes
})
// Check result of submitting -----------------------------------------------
console.log(JSON.stringify(response.result, null, 2))
const escrowCreateResultCode = response.result.meta.TransactionResult
if (escrowCreateResultCode === 'tesSUCCESS') {
console.log('Escrow created successfully.')
} else {
console.error(`EscrowCreate failed with code ${escrowCreateResultCode}.`)
client.disconnect()
process.exit(1)
}
// Save the sequence number so you can identify the escrow later.
const escrowSeq = response.result.tx_json.Sequence
console.log(`Escrow sequence is ${escrowSeq}.`)
// Send EscrowFinish transaction --------------------------------------------
const escrowFinish = {
TransactionType: 'EscrowFinish',
Account: wallet.address,
Owner: wallet.address,
OfferSequence: escrowSeq,
Condition: conditionHex,
Fulfillment: fulfillmentHex
}
xrpl.validate(escrowFinish)
console.log('Signing and submitting the transaction:',
JSON.stringify(escrowFinish, null, 2))
const response2 = await client.submitAndWait(escrowFinish, {
wallet,
autofill: true // Note: fee is higher based on fulfillment size in bytes
})
console.log(JSON.stringify(response2.result, null, 2))
if (response2.result.meta.TransactionResult === 'tesSUCCESS') {
console.log('Escrow finished successfully.')
} else {
console.log(`Failed with result code ${response2.result.meta.TransactionResult}`)
}
client.disconnect()

View File

@@ -0,0 +1,108 @@
import xrpl from 'xrpl'
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
await client.connect()
console.log('Funding new wallet from faucet...')
const { wallet } = await client.fundWallet()
// const destination_address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' // Testnet faucet
// Alternative: Get another account to send the escrow to. Use this if you get
// a tecDIR_FULL error trying to create escrows to the Testnet faucet.
const destination_address = (await client.fundWallet()).wallet.address
// Set the escrow finish time -----------------------------------------------
const delay = 30 // Seconds in the future when the escrow should mature
const finishAfter = new Date() // Current time
finishAfter.setSeconds(finishAfter.getSeconds() + delay)
console.log('This escrow will finish after:', finishAfter)
// Convert finishAfter to seconds since the Ripple Epoch:
const finishAfterRippleTime = xrpl.isoTimeToRippleTime(finishAfter.toISOString())
// Send EscrowCreate transaction --------------------------------------------
const escrowCreate = {
TransactionType: 'EscrowCreate',
Account: wallet.address,
Destination: destination_address,
Amount: '123456', // drops of XRP
FinishAfter: finishAfterRippleTime
}
xrpl.validate(escrowCreate)
console.log('Signing and submitting the transaction:',
JSON.stringify(escrowCreate, null, 2))
const response = await client.submitAndWait(escrowCreate, {
wallet,
autofill: true
})
console.log(JSON.stringify(response.result, null, 2))
const escrowCreateResultCode = response.result.meta.TransactionResult
if (escrowCreateResultCode === 'tesSUCCESS') {
console.log('Escrow created successfully.')
} else {
console.error(`EscrowCreate failed with code ${escrowCreateResultCode}.`)
client.disconnect()
process.exit(1)
}
// Save the sequence number so you can identify the escrow later.
const escrowSeq = response.result.tx_json.Sequence
console.log(`Escrow sequence is ${escrowSeq}.`)
// Wait for the escrow to be finishable -------------------------------------
console.log(`Waiting ${delay} seconds for the escrow to mature...`)
await sleep(delay)
/* Sleep function that can be used with await */
function sleep (delayInSeconds) {
const delayInMs = delayInSeconds * 1000
return new Promise((resolve) => setTimeout(resolve, delayInMs))
}
// Check if escrow can be finished -------------------------------------------
let escrowReady = false
while (!escrowReady) {
// Check the close time of the latest validated ledger.
// Close times are rounded by about 10 seconds, so the exact time the escrow
// is ready to finish may vary by +/- 10 seconds.
const validatedLedger = await client.request({
command: 'ledger',
ledger_index: 'validated'
})
const ledgerCloseTime = validatedLedger.result.ledger.close_time
console.log('Latest validated ledger closed at',
xrpl.rippleTimeToISOTime(ledgerCloseTime))
if (ledgerCloseTime > finishAfterRippleTime) {
escrowReady = true
console.log('Escrow is mature.')
} else {
let timeDifference = finishAfterRippleTime - ledgerCloseTime
if (timeDifference === 0) { timeDifference = 1 }
console.log(`Waiting another ${timeDifference} second(s).`)
await sleep(timeDifference)
}
}
// Send EscrowFinish transaction --------------------------------------------
const escrowFinish = {
TransactionType: 'EscrowFinish',
Account: wallet.address,
Owner: wallet.address,
OfferSequence: escrowSeq
}
xrpl.validate(escrowFinish)
console.log('Signing and submitting the transaction:',
JSON.stringify(escrowFinish, null, 2))
const response2 = await client.submitAndWait(escrowFinish, {
wallet,
autofill: true
})
console.log(JSON.stringify(response2.result, null, 2))
if (response2.result.meta.TransactionResult === 'tesSUCCESS') {
console.log('Escrow finished successfully. Balance changes:')
console.log(
JSON.stringify(xrpl.getBalanceChanges(response2.result.meta), null, 2)
)
}
client.disconnect()

View File

@@ -1,31 +1,136 @@
import json
from datetime import datetime, timedelta, UTC
from time import sleep
from xrpl.clients import JsonRpcClient
from xrpl.models import EscrowCancel
from xrpl.models import EscrowCreate, EscrowCancel
from xrpl.models.requests import AccountObjects, Ledger, Tx
from xrpl.transaction import submit_and_wait
from xrpl.utils import datetime_to_ripple_time, ripple_time_to_datetime, get_balance_changes
from xrpl.wallet import generate_faucet_wallet
client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to the testnetwork
# Set up client and get a wallet
client = JsonRpcClient("https://s.altnet.rippletest.net:51234")
print("Funding new wallet from faucet...")
wallet = generate_faucet_wallet(client, debug=True)
# destination_address = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Testnet faucet
# Alternative: Get another account to send the escrow to. Use this if you get
# a tecDIR_FULL error trying to create escrows to the Testnet faucet.
destination_address = generate_faucet_wallet(client, debug=True).address
# Cancel an escrow
# An Escrow can only be canceled if it was created with a CancelAfter time
escrow_sequence = 30215126
# Sender wallet object
sender_wallet = generate_faucet_wallet(client=client)
# Build escrow cancel transaction
cancel_txn = EscrowCancel(
account=sender_wallet.address,
owner=sender_wallet.address,
offer_sequence=escrow_sequence
# Create an escrow that won't be finished --------------------------------------
cancel_delay = 30
cancel_after = datetime.now(tz=UTC) + timedelta(seconds=cancel_delay)
print("This escrow will expire after", cancel_after)
cancel_after_rippletime = datetime_to_ripple_time(cancel_after)
# Use a crypto-condition that nobody knows the fulfillment for
condition_hex = "A02580200000000000000000000000000000000000000000000000000000000000000000810120"
escrow_create = EscrowCreate(
account=wallet.address,
destination=destination_address,
amount="123456", # drops of XRP
condition=condition_hex,
cancel_after=cancel_after_rippletime
)
print("Signing and submitting the EscrowCreate transaction.")
response = submit_and_wait(escrow_create, client, wallet, autofill=True)
print(json.dumps(response.result, indent=2))
# Autofill, sign, then submit transaction and wait for result
stxn_response = submit_and_wait(cancel_txn, client, sender_wallet)
result_code = response.result["meta"]["TransactionResult"]
if result_code != "tesSUCCESS":
print(f"EscrowCreate failed with result code {result_code}")
exit(1)
# Parse response and return result
stxn_result = stxn_response.result
# Wait for the escrow to expire ------------------------------------------------
# Since ledger close times can be rounded by up to 10 seconds, wait an extra
# 10 seconds to make sure the escrow has officially expired.
print(f"Waiting {cancel_delay + 10} seconds for the escrow to expire.")
sleep(cancel_delay + 10)
# Parse result and print out the transaction result and transaction hash
print(stxn_result["meta"]["TransactionResult"])
print(stxn_result["hash"])
# Look up the official close time of the validated ledger ----------------------
validated_ledger = client.request(Ledger(ledger_index="validated"))
close_time = validated_ledger.result["ledger"]["close_time"]
print("Latest validated ledger closed at",
ripple_time_to_datetime(close_time)
)
ledger_hash = validated_ledger.result["ledger"]["ledger_hash"]
# Look up escrows connected to the account, handling pagination ----------------
expired_escrow = None
marker = None
while True:
try:
response = client.request(AccountObjects(
account=wallet.address,
ledger_hash=ledger_hash,
type="escrow",
marker=marker
))
except Exception as e:
print(f"Error: account_objects failed: {e}")
exit(1)
for escrow in response.result["account_objects"]:
if "CancelAfter" not in escrow:
print("This escrow does not have an expiration")
elif escrow["CancelAfter"] < close_time:
print("This escrow has expired.")
expired_escrow = escrow
break
else:
expiration_time = ripple_time_to_datetime(escrow["CancelAfter"])
print(f"This escrow expires at {expiration_time}.")
if expired_escrow:
# Found an expired escrow, stop paginating
break
if "marker" in response.result.keys():
marker=marker
else:
# This is the last page of results
break
if not expired_escrow:
print("Did not find any expired escrows.")
exit(1)
# Find the sequence number of the expired escrow -------------------------------
response = client.request(Tx(transaction=expired_escrow["PreviousTxnID"]))
if not response.is_successful():
print("Couldn't get transaction. Maybe this server doesn't have enough "
"transaction history available?")
exit(1)
if response.result["tx_json"]["TransactionType"] == "EscrowCreate":
# Save this sequence number for canceling the escrow
escrow_seq = response.result["tx_json"]["Sequence"]
if escrow_seq == 0:
# This transaction used a Ticket, so use the TicketSequence instead.
escrow_seq = response.result["tx_json"]["TicketSequence"]
else:
# Currently, this is impossible since no current transaction can update
# an escrow without finishing or canceling it. But in the future, if
# that becomes possible, you would have to look at the transaction
# metadata to find the previous transaction and repeat until you found
# the transaction that created the escrow.
print("The escrow's previous transaction wasn't EscrowCreate!")
exit(1)
# Send EscrowCancel transaction ------------------------------------------------
escrow_cancel = EscrowCancel(
account=wallet.address,
owner=expired_escrow["Account"],
offer_sequence=escrow_seq
)
print("Signing and submitting the EscrowCancel transaction.")
response2 = submit_and_wait(escrow_cancel, client, wallet, autofill=True)
print(json.dumps(response2.result, indent=2))
result_code = response2.result["meta"]["TransactionResult"]
if result_code != "tesSUCCESS":
print(f"EscrowCancel failed with result code {result_code}")
exit(1)
print("Escrow canceled. Balance changes:")
print(json.dumps(get_balance_changes(response2.result["meta"]), indent=2))

View File

@@ -1,52 +0,0 @@
from datetime import datetime, timedelta
from xrpl.clients import JsonRpcClient
from xrpl.models import EscrowCreate
from xrpl.transaction import submit_and_wait
from xrpl.utils import datetime_to_ripple_time, xrp_to_drops
from xrpl.wallet import generate_faucet_wallet
# Create Escrow
client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to client
amount_to_escrow = 10.000
receiver_addr = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Example: send back to Testnet Faucet
# Escrow will be available to claim after 3 days
claim_date = datetime_to_ripple_time(datetime.now() + timedelta(days=3))
# Escrow will expire after 5 days
expiry_date = datetime_to_ripple_time(datetime.now() + timedelta(days=5))
# Optional field
# You can optionally use a Crypto Condition to allow for dynamic release of funds. For example:
condition = "A02580205A0E9E4018BE1A6E0F51D39B483122EFDF1DDEF3A4BE83BE71522F9E8CDAB179810120" # do not use in production
# sender wallet object
sender_wallet = generate_faucet_wallet(client=client)
# Build escrow create transaction
create_txn = EscrowCreate(
account=sender_wallet.address,
amount=xrp_to_drops(amount_to_escrow),
destination=receiver_addr,
finish_after=claim_date,
cancel_after=expiry_date,
condition=condition # Omit this for time-held escrows
)
# Autofill, sign, then submit transaction and wait for result
stxn_response = submit_and_wait(create_txn, client, sender_wallet)
# Return result of transaction
stxn_result = stxn_response.result
# Parse result and print out the neccesary info
print(stxn_result["tx_json"]["Account"])
print(stxn_result["tx_json"]["Sequence"])
print(stxn_result["meta"]["TransactionResult"])
print(stxn_result["hash"])

View File

@@ -1,44 +0,0 @@
from xrpl.clients import JsonRpcClient
from xrpl.models import EscrowFinish
from xrpl.transaction import submit_and_wait
from xrpl.wallet import generate_faucet_wallet
client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to the testnetwork
# Complete an escrow
# Cannot be called until the finish time is reached
# Required fields (modify to match an escrow you create)
escrow_creator = generate_faucet_wallet(client=client).address
escrow_sequence = 27641268
# Optional fields
# Crypto condition that must be met before escrow can be completed, passed on escrow creation
condition = "A02580203882E2EB9B44130530541C4CC360D079F265792C4A7ED3840968897CB7DF2DA1810120"
# Crypto fulfillment of the condtion
fulfillment = "A0228020AED2C5FE4D147D310D3CFEBD9BFA81AD0F63CE1ADD92E00379DDDAF8E090E24C"
# Sender wallet object
sender_wallet = generate_faucet_wallet(client=client)
# Build escrow finish transaction
finish_txn = EscrowFinish(
account=sender_wallet.address,
owner=escrow_creator,
offer_sequence=escrow_sequence, # The sequence number of the escrow transaction
condition=condition, # Omit this for time-held escrows
fulfillment=fulfillment # Omit this for time-held escrows
)
# Autofill, sign, then submit transaction and wait for result
stxn_response = submit_and_wait(finish_txn, client, sender_wallet)
# Parse response and return result
stxn_result = stxn_response.result
# Parse result and print out the transaction result and transaction hash
print(stxn_result["meta"]["TransactionResult"])
print(stxn_result["hash"])

View File

@@ -1,19 +0,0 @@
import random
from os import urandom
from cryptoconditions import PreimageSha256
# """Generate a condition and fulfillment for escrows"""
# Generate a random preimage with at least 32 bytes of cryptographically-secure randomness.
secret = urandom(32)
# Generate cryptic image from secret
fufill = PreimageSha256(preimage=secret)
# Parse image and return the condition and fulfillment
condition = str.upper(fufill.condition_binary.hex()) # conditon
fulfillment = str.upper(fufill.serialize_binary().hex()) # fulfillment
# Print condition and fulfillment
print(f"condition: {condition} \n fulfillment {fulfillment}")

Some files were not shown because too many files have changed in this diff Show More