Compare commits

...

31 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
6a9bd7adcb feat: add GitHub project management infrastructure (RACI, issue templates, workflows)
Co-authored-by: amarantha-k <13425257+amarantha-k@users.noreply.github.com>
2026-03-02 21:44:33 +00:00
copilot-swe-agent[bot]
2680344850 Initial plan 2026-03-02 21:38:00 +00:00
oeggert
16364ea2d5 Merge pull request #3527 from XRPLF/update-lending-spec
Add DestinationTag and update links to lending spec
2026-02-27 15:55:25 -08:00
Oliver Eggert
27c25dbb12 add DestinationTag and update links to lending spec 2026-02-27 15:43:40 -08:00
Amarantha Kulkarni
61fc160523 Merge pull request #3521 from XRPLF/vulnerability-disclosure-report-feb2026
Vulnerability disclosure report February 2026
2026-02-26 14:13:20 -08:00
Amarantha Kulkarni
dc3db2c4f2 Apply suggestions from code review
Co-authored-by: oeggert <117319296+oeggert@users.noreply.github.com>
2026-02-26 14:06:37 -08:00
Amarantha Kulkarni
3ca8de9646 Apply suggestions from code review
Co-authored-by: oeggert <117319296+oeggert@users.noreply.github.com>
2026-02-26 13:54:57 -08:00
Maria Shodunke
2ca8706e41 Merge pull request #3519 from XRPLF/remove-batch-sav-setup
Don't use Batch for SAV tutorials setup
2026-02-26 18:05:43 +00:00
oeggert
dc0778b666 Merge pull request #3522 from XRPLF/update-lending-setup
Update lending tutorial setup scripts
2026-02-26 09:50:00 -08:00
Oliver Eggert
7268a0e52e add warnings for obsolete amendments 2026-02-26 09:45:35 -08:00
Amarantha Kulkarni
025c262f3e Merge pull request #3489 from XRPLF/add-bitget-wallet
Add Bitget wallet to XRP and Uses pages
2026-02-26 09:43:02 -08:00
Maria Shodunke
f26c5921b8 Apply review comments 2026-02-26 15:11:26 +00:00
amarantha-k
4094d1f76a Fix syntax for links 2026-02-25 13:33:53 -08:00
Oliver Eggert
62288d6c3b mark batch and fixbatchinnersigs as obsolete 2026-02-25 13:29:39 -08:00
amarantha-k
f5f08d8690 Incorporate review feedback 2026-02-25 13:14:46 -08:00
Oliver Eggert
88c28dfd05 update lending_setup.py to use tickets instead of batch 2026-02-25 12:56:32 -08:00
amarantha-k
c297fc5960 Fix typo in attribution 2026-02-25 12:33:58 -08:00
amarantha-k
fa9ddb8d67 Add attribution to post 2026-02-25 12:32:14 -08:00
amarantha-k
3c14520ad0 Add disclosure report for Batch 2026-02-25 12:08:39 -08:00
Oliver Eggert
a83e6e3a1a update lendingSetup.js to use tickets instead of batch 2026-02-25 11:40:52 -08:00
Maria Shodunke
79e800baae Add amendment badge to Batch concept doc 2026-02-25 18:00:51 +00:00
Maria Shodunke
646d4a7381 Don't use Batch for SAV tutorials setup 2026-02-25 17:29:21 +00:00
oeggert
62238e2ba8 Merge pull request #3511 from XRPLF/blog-3.1.1
Add 3.1.1 blog
2026-02-23 16:50:50 -08:00
Oliver Eggert
f211351f4c add reviewer suggestions 2026-02-23 16:47:53 -08:00
Oliver Eggert
7c41d016db add 3.1.1 blog 2026-02-23 15:49:01 -08:00
amarantha-k
9af8e7849e Update logo image for dark mode 2026-02-12 11:18:09 -08:00
mDuo13
c62d5090d0 Fix typo causing Bitget logo to not apear 2026-02-11 04:03:45 -08:00
amarantha-k
19ec38e7d8 Update number of wallets to 7 2026-02-10 20:34:49 -08:00
amarantha-k
f119214cea replace corrupted image files 2026-02-10 20:24:23 -08:00
amarantha-k
69d4113536 Add missing image files 2026-02-10 16:37:14 -08:00
amarantha-k
f5988554db Add Bitget wallet to XRP and Uses pages 2026-02-10 16:23:43 -08:00
37 changed files with 1844 additions and 388 deletions

38
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,38 @@
# CODEOWNERS
# Defines code ownership for automatic review assignment.
# The last matching pattern takes precedence.
# See: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# Global default — core maintainers review everything
* @XRPLF/docs-maintainers
# Concepts and narrative docs
/docs/concepts/ @XRPLF/docs-maintainers
# Tutorials
/docs/tutorials/ @XRPLF/docs-maintainers
# API reference
/docs/references/ @XRPLF/docs-maintainers
# Infrastructure / node docs
/docs/infrastructure/ @XRPLF/docs-maintainers
# Code samples — require technical review
/_code-samples/ @XRPLF/docs-maintainers
# API examples
/_api-examples/ @XRPLF/docs-maintainers
# Site theme and web tooling
/@theme/ @XRPLF/docs-maintainers
/styles/ @XRPLF/docs-maintainers
# Localization files
/@l10n/ @XRPLF/docs-maintainers
# CI/CD and project configuration — maintainer-only
/.github/ @XRPLF/docs-maintainers
/redocly.yaml @XRPLF/docs-maintainers
/package.json @XRPLF/docs-maintainers
/package-lock.json @XRPLF/docs-maintainers

67
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: "Bug Report"
description: Report a typo, broken link, incorrect information, or other documentation error
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for helping improve the XRP Ledger documentation! Please fill out this form as completely as possible.
- type: input
id: page-url
attributes:
label: Affected Page URL
description: The URL of the page with the issue (e.g. https://xrpl.org/docs/...)
placeholder: "https://xrpl.org/docs/..."
validations:
required: true
- type: dropdown
id: bug-type
attributes:
label: Type of Issue
description: What kind of problem did you find?
options:
- Typo or grammar error
- Broken or incorrect link
- Incorrect technical information
- Outdated content
- Missing content
- Formatting or display issue
- Other
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: Describe the problem clearly. Include what is wrong and what it should say instead.
placeholder: "The page states X, but it should say Y because..."
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Content
description: What should the correct content say?
validations:
required: false
- type: input
id: source-reference
attributes:
label: Source Reference (optional)
description: Link to source code, specification, or other reference that confirms the correct information
placeholder: "https://github.com/XRPLF/rippled/..."
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched existing issues to confirm this has not already been reported
required: true
- label: I have verified this issue exists on the live site (not just locally)

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: XRP Ledger Developer Discord
url: https://discord.gg/xrpl
about: Ask questions and discuss XRP Ledger development with the community
- name: XRPL.org Feedback
url: https://xrpl.org/about/contact
about: General feedback about the XRP Ledger portal

View File

@@ -0,0 +1,86 @@
name: "Content Update"
description: Request an update, addition, or improvement to existing documentation
title: "[Content]: "
labels: ["content updates"]
body:
- type: markdown
attributes:
value: |
Use this template to request changes to existing documentation content — for example, adding new details, updating outdated information, or improving clarity.
- type: input
id: page-url
attributes:
label: Affected Page URL
description: URL of the page that needs updating (if applicable)
placeholder: "https://xrpl.org/docs/..."
- type: dropdown
id: update-type
attributes:
label: Type of Update
options:
- Add missing information
- Update outdated content
- Improve clarity or readability
- Reorganize or restructure content
- Add examples or code samples
- Other
validations:
required: true
- type: dropdown
id: content-area
attributes:
label: Content Area
description: Which area of the documentation does this affect?
options:
- Concepts
- Tutorials
- References / API
- Infrastructure / Nodes
- Tokenization / NFTs
- DEX / AMM
- Payment Channels / Escrow
- Accounts / Wallets
- Localization / Translation
- Site tooling
- Other
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: Describe what needs to be changed and why. Be as specific as possible.
placeholder: "The documentation for X needs to be updated because..."
validations:
required: true
- type: input
id: source-reference
attributes:
label: Source / Reference
description: Link to the rippled source, amendment details, XLS proposal, or other authoritative reference
placeholder: "https://github.com/XRPLF/rippled/..."
- type: dropdown
id: amendment-status
attributes:
label: Amendment Status (if applicable)
description: If this relates to a protocol amendment, what is its status?
options:
- "N/A"
- In development (not yet proposed)
- Proposed (in open voting)
- Enabled on Mainnet
- Enabled on Devnet/Testnet only
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched existing issues to avoid duplicates
required: true

View File

@@ -0,0 +1,66 @@
name: "Feature Request"
description: Suggest a new page, section, tool, or site feature
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Use this template to suggest a new documentation page, site feature, or tooling improvement for the XRP Ledger Developer Portal.
- type: dropdown
id: feature-type
attributes:
label: Feature Type
options:
- New documentation page or section
- New interactive tool or code sample
- Site navigation or UX improvement
- Developer tooling or CI/CD improvement
- Localization support
- Other
validations:
required: true
- type: textarea
id: problem
attributes:
label: Problem to Solve
description: Describe the problem or gap this feature would address. Who would benefit?
placeholder: "As a developer building on the XRP Ledger, I need..."
validations:
required: true
- type: textarea
id: solution
attributes:
label: Proposed Solution
description: Describe what you'd like to see added or changed.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Have you considered any alternative approaches?
- type: dropdown
id: priority
attributes:
label: Priority (your assessment)
options:
- Low nice to have
- Medium would significantly improve the docs
- High blocks common developer workflows
validations:
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched existing issues to avoid duplicates
required: true
- label: I am willing to contribute a PR for this feature

52
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,52 @@
## Description
<!-- Provide a clear, concise description of what this PR does. -->
Fixes #<!-- issue number -->
## Type of Change
<!-- Check all that apply -->
- [ ] Bug fix (documentation typo, broken link, incorrect information)
- [ ] Content update (new information, updated existing docs)
- [ ] New page or section
- [ ] Site feature / tooling change
- [ ] Localization / translation
- [ ] Refactoring (no functional change)
## RACI Confirmation
This project follows a RACI model for contributions. Please confirm the following:
| Role | Description | Confirmed |
|------|-------------|-----------|
| **R Responsible** | I am the author of these changes and have completed the work described above | - [ ] |
| **A Accountable** | I understand that a maintainer must approve this PR before it can be merged | - [ ] |
| **C Consulted** | I have tagged relevant subject-matter experts as reviewers (if applicable) | - [ ] |
| **I Informed** | The linked issue keeps stakeholders informed of this change | - [ ] |
> See [RACI Model](.github/project-management/RACI.md) for full role definitions.
## Review Checklist
**Author (Responsible)**
- [ ] PR is linked to an open issue
- [ ] Changes are accurate and technically correct
- [ ] Content follows the [style guide](https://xrpl.org/resources/contribute-documentation/)
- [ ] All links work and point to correct targets
- [ ] Code samples (if any) have been tested
- [ ] Localization impact considered (does this need translation updates?)
**Reviewer (Accountable / Consulted)**
- [ ] Technical accuracy verified
- [ ] Writing is clear and follows style guidelines
- [ ] No broken links or missing references
- [ ] No security-sensitive information inadvertently exposed
## Screenshots (if applicable)
<!-- Add before/after screenshots for UI or formatting changes -->
## Additional Notes
<!-- Any context, decisions made, or follow-up items -->

80
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
# Auto-labeler configuration for pull_request_target events.
# Labels are applied based on which files are changed in the PR.
# See: https://github.com/actions/labeler
"content updates":
- changed-files:
- any-glob-to-any-file:
- "docs/**/*.md"
- "docs/**/*.mdx"
- "docs/**/*.page.tsx"
"web dev":
- changed-files:
- any-glob-to-any-file:
- "@theme/**"
- "styles/**"
- "*.tsx"
- "*.ts"
- "shared/**"
"docs-tooling":
- changed-files:
- any-glob-to-any-file:
- ".github/**"
- "redocly.yaml"
- "package.json"
- "package-lock.json"
- "tsconfig.json"
- "sidebars.yaml"
- "redirects.yaml"
"localization":
- changed-files:
- any-glob-to-any-file:
- "@l10n/**"
- "translations.yaml"
- "**/*.ja.md"
- "**/*.es-ES.md"
"javascript":
- changed-files:
- any-glob-to-any-file:
- "_code-samples/**/*.js"
- "_code-samples/**/*.mjs"
- "_code-samples/**/*.cjs"
- "docs/tutorials/**/*javascript*"
- "docs/tutorials/**/*js*"
"python":
- changed-files:
- any-glob-to-any-file:
- "_code-samples/**/*.py"
- "docs/tutorials/**/*python*"
"java":
- changed-files:
- any-glob-to-any-file:
- "_code-samples/**/*.java"
- "docs/tutorials/**/*java*"
"tokenization":
- changed-files:
- any-glob-to-any-file:
- "docs/**/*nft*"
- "docs/**/*token*"
- "docs/**/*fungible*"
- "docs/tutorials/nfts/**"
- "docs/concepts/tokens/**"
"infra":
- changed-files:
- any-glob-to-any-file:
- "docs/infrastructure/**"
"organization":
- changed-files:
- any-glob-to-any-file:
- "sidebars.yaml"
- "top-nav.yaml"
- "redirects.yaml"

179
.github/project-management/RACI.md vendored Normal file
View File

@@ -0,0 +1,179 @@
# RACI Model — XRP Ledger Developer Portal
This document defines the **RACI model** used to manage contributions to the XRP Ledger Developer Portal. RACI ensures every task has clear ownership, appropriate oversight, and proper communication for all stakeholders.
---
## What is RACI?
| Letter | Role | Description |
|--------|------|-------------|
| **R** | **Responsible** | The person who does the work. Every issue and PR must have exactly one responsible party. |
| **A** | **Accountable** | The person ultimately answerable for the outcome. Must approve work before it is merged. There is always exactly one accountable person per change. |
| **C** | **Consulted** | Subject-matter experts whose input is needed before or during the work. Two-way communication. |
| **I** | **Informed** | Stakeholders kept up to date on decisions and outcomes. One-way communication. |
---
## Role Mapping for This Project
### R Responsible (Contributors)
- **Community contributors** — anyone opening issues or submitting PRs
- **Assigned issue owners** — maintainers or contributors assigned to an issue
- Responsible parties create PRs, write content, and address review feedback
- A single contributor should be assigned `Responsible` per issue/PR
### A Accountable (Maintainers / CODEOWNERS)
- The **`@XRPLF/docs-maintainers`** team is accountable for all merged changes
- Enforced via [CODEOWNERS](../CODEOWNERS): every PR requires at least **one approval** from a code owner before it can be merged
- Maintainers may delegate review to subject-matter experts (Consulted) but remain accountable for the final merge decision
- Branch protection rules require:
- ✅ At least **1 approving review** from a CODEOWNER
- ✅ All **status checks pass** (CI workflows)
-**No unresolved conversations**
### C Consulted (Subject-Matter Experts)
Depending on the content area, the relevant experts should be tagged as reviewers:
| Content Area | Who to Consult |
|---|---|
| Protocol / core XRP Ledger mechanics | Protocol engineers (`@XRPLF/rippled` team members) |
| Smart contracts / Hooks | Hooks developers |
| NFT / tokenization | Tokenization working group |
| AMM / DeFi | DeFi working group |
| Client libraries (xrpl.js, xrpl-py, xrpl4j) | Respective library maintainers |
| Localization | Translation contributors for the target language |
| Site tooling / Redocly | Redocly configuration owners |
Tag SMEs using `@username` in PR descriptions or inline review comments.
### I Informed (Stakeholders)
The following parties are automatically informed via GitHub notifications:
- **Issue reporters** — notified of comments and resolution
- **PR watchers** — subscribed to PR updates
- **Repository watchers** — receive notifications for all activity
- **Linked issues** — cross-referenced via `Fixes #<number>` in PR descriptions
---
## RACI by Activity Type
| Activity | R (Does the Work) | A (Approves) | C (Consulted) | I (Informed) |
|---|---|---|---|---|
| Report a bug or issue | Community contributor | Maintainer (triage) | — | Repo watchers |
| Fix a documentation bug | Community contributor / Maintainer | CODEOWNER | SME if technical | Issue reporter |
| Write a new doc page | Assigned contributor | CODEOWNER | Protocol SME + Tech writer | Community |
| Update API reference | Assigned contributor | CODEOWNER | Protocol engineer | Developers |
| Add/update code sample | Contributor | CODEOWNER | Library maintainer | Developers |
| Merge a PR | — | CODEOWNER | — | PR author, watchers |
| Triage issues | Maintainer | Lead maintainer | — | Issue reporter |
| Release / deploy | CI/CD (automated) | Lead maintainer | — | Stakeholders |
| Update site tooling | Maintainer | CODEOWNER | Redocly config owner | All contributors |
---
## Workflow Summary
```
Community Contributor (R)
├─ Opens Issue ──────────────────────► Auto-triage labels applied
│ Maintainer triages & prioritizes
└─ Opens Pull Request ────────────────► PR Validation check runs
│ Auto-labels applied
│ Greeting for new contributors
├─ Tags SME reviewers (C) ───────► SMEs leave review comments
└─ Maintainer reviews (A) ───────► Approves or requests changes
└─ All checks pass ─────────► PR merged by Accountable maintainer
```
---
## Project Board
The XRP Ledger Developer Portal uses a **GitHub Project board** to track issue and PR status. The board has the following columns:
| Column | Description |
|---|---|
| **📋 Backlog** | Issues that are accepted but not yet scheduled |
| **🔍 Needs Triage** | New issues awaiting maintainer review |
| **📅 Planned** | Issues scheduled for an upcoming milestone |
| **🚧 In Progress** | Issues with a linked open PR or active assignee |
| **👀 In Review** | PRs awaiting review or with pending change requests |
| **✅ Done** | Merged PRs and closed issues |
> **Setup**: Maintainers can create and configure the board at:
> [organization-level project](https://github.com/orgs/XRPLF/projects)
> or
> [repository-level project](https://github.com/XRPLF/xrpl-dev-portal/projects)
### Automation Rules (configure in GitHub Projects)
- **Issue opened** → Move to _Needs Triage_
- **Label `stale` added** → Move to _Backlog_
- **PR opened / linked to issue** → Move issue to _In Progress_
- **PR review requested** → Move PR to _In Review_
- **PR merged** → Move to _Done_ and close linked issue
- **Issue closed** → Move to _Done_
---
## Branch Protection Requirements
To enforce the "at least one human review" requirement, configure branch protection on `master`:
1. Go to **Settings → Branches → Branch protection rules**
2. Add a rule for `master` (or your default branch)
3. Enable:
-**Require a pull request before merging**
-**Require approvals** — set to **1** (minimum)
-**Require review from Code Owners** (enforces CODEOWNERS file)
-**Require status checks to pass before merging**
- Add: `validate-pr` (from `pr-check.yml`)
-**Require conversation resolution before merging**
-**Do not allow bypassing the above settings** (for non-admins)
---
## Labels Reference
| Label | Description | Who Applies |
|---|---|---|
| `bug` | Typo, broken link, or incorrect info | Auto-triage / contributor |
| `content updates` | Updates to existing documentation | Auto-triage / maintainer |
| `enhancement` | New pages or features | Auto-triage / maintainer |
| `good first issue` | Suitable for new contributors | Maintainer |
| `help wanted` | Community contributions welcome | Maintainer |
| `needs triage` | Awaiting maintainer review | Auto-triage |
| `stale` | No activity for 90+ days | Stale bot |
| `wip` | Work in progress, not ready for review | Contributor |
| `tokenization` | NFT/token-related content | Auto-triage |
| `infra` | Validator/node/server content | Auto-triage |
| `web dev` | Site tooling, styles, templates | Auto-triage |
| `organization` | Navigation, structure, architecture | Auto-triage |
| `docs-tooling` | CI/CD, build tooling | Auto-triage |
| `localization` | Translation and i18n | Auto-triage |
| `javascript` | JavaScript code samples/tutorials | Auto-triage |
| `python` | Python code samples/tutorials | Auto-triage |
---
## Contributing Quick Reference
1. **Find an issue** — Browse the [project board](https://github.com/XRPLF/xrpl-dev-portal/issues) or open a new one using a template
2. **Assign yourself** — Comment "I'll work on this" or ask a maintainer to assign you
3. **Fork & branch** — Create a branch from `master`
4. **Make changes** — Follow the [style guide](https://xrpl.org/resources/contribute-documentation/)
5. **Open a PR** — Fill out the PR template completely, including RACI confirmation
6. **Tag reviewers** — Add SMEs as reviewers for technical accuracy
7. **Address feedback** — Respond to review comments and push updates
8. **Merge** — A maintainer (Accountable) will merge once all checks pass

19
.github/workflows/auto-label.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Auto-Label Pull Requests
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
sync-labels: true

92
.github/workflows/greet-contributor.yml vendored Normal file
View File

@@ -0,0 +1,92 @@
name: Greet New Contributors
on:
issues:
types: [opened]
pull_request_target:
types: [opened]
permissions:
issues: write
pull-requests: write
jobs:
greet:
runs-on: ubuntu-latest
steps:
- name: Greet first-time issue reporter
if: github.event_name == 'issues'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const creator = context.payload.issue.user.login;
// Check if this is their first issue in the repo
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
creator: creator,
state: 'all',
});
// If only 1 issue exists, it's the one they just opened
if (issues.length <= 1) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
body: [
`👋 Welcome to the XRP Ledger Developer Portal, @${creator}! Thanks for opening your first issue.`,
'',
'Here are a few things to know:',
'- A maintainer will triage this issue and add appropriate labels',
'- If you\'d like to fix this yourself, check out our [Contributing Guide](https://xrpl.org/resources/contribute-documentation/)',
'- Join the conversation on [XRPL Discord](https://discord.gg/xrpl) if you have questions',
'',
'We appreciate your contribution to the XRP Ledger community! 🚀',
].join('\n'),
});
}
- name: Greet first-time PR contributor
if: github.event_name == 'pull_request_target'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const creator = context.payload.pull_request.user.login;
// Check previous merged PRs from this user
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'all',
});
const userPRs = prs.filter(pr => pr.user.login === creator);
// If this is their first PR
if (userPRs.length <= 1) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: [
`🎉 Welcome, @${creator}! Thanks for submitting your first pull request to the XRP Ledger Developer Portal.`,
'',
'Here\'s what happens next:',
'1. 🔍 Automated checks will run on your PR',
'2. 👀 A maintainer will review your changes (this may take a few days)',
'3. 💬 You may receive feedback or requests for changes',
'4. ✅ Once approved by a maintainer, your PR will be merged',
'',
'Please make sure your PR:',
'- Has a clear description of what was changed and why',
'- Is linked to an open issue (e.g. `Fixes #123`)',
'- Follows our [style guide](https://xrpl.org/resources/contribute-documentation/)',
'',
'Thank you for contributing to the XRP Ledger community! 🙏',
].join('\n'),
});
}

97
.github/workflows/issue-triage.yml vendored Normal file
View File

@@ -0,0 +1,97 @@
name: Issue Triage
on:
issues:
types: [opened, reopened]
permissions:
issues: write
jobs:
triage:
runs-on: ubuntu-latest
steps:
- name: Auto-label based on issue title and body
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issue = context.payload.issue;
const title = (issue.title || '').toLowerCase();
const body = (issue.body || '').toLowerCase();
const text = title + ' ' + body;
const labelsToAdd = new Set();
// --- keyword → label mapping ---
const rules = [
// Content areas
{ patterns: ['nft', 'nonfungible', 'non-fungible', 'token', 'mpt', 'fungible'], label: 'tokenization' },
{ patterns: ['amm', 'dex', 'offer', 'liquidity', 'pool'], label: 'defi' },
{ patterns: ['escrow', 'payment channel', 'check'], label: 'content updates' },
{ patterns: ['tutorial', 'how to', 'how-to', 'walkthrough', 'guide'], label: 'content updates' },
{ patterns: ['api', 'rpc', 'websocket', 'method', 'command', 'response field'], label: 'content updates' },
{ patterns: ['amendment', 'feature flag'], label: 'content updates' },
{ patterns: ['rippled', 'validator', 'node', 'server', 'peer'], label: 'infra' },
{ patterns: ['javascript', 'js ', 'node.js', 'typescript'], label: 'javascript' },
{ patterns: ['python'], label: 'python' },
{ patterns: ['java', 'xrpl4j'], label: 'java' },
{ patterns: ['navigate', 'menu', 'sidebar', 'landing page', 'homepage'], label: 'organization' },
{ patterns: ['style', 'css', 'layout', 'theme', 'mobile', 'responsive'], label: 'web dev' },
{ patterns: ['translate', 'translation', 'locali', 'japanese', 'spanish'], label: 'localization' },
{ patterns: ['broken link', 'dead link', '404 error', 'page not found', 'typo', 'grammar', 'spelling'], label: 'bug' },
{ patterns: ['redocly', 'build', 'ci ', 'ci/', 'workflow', 'lint', 'tooling'], label: 'docs-tooling' },
];
for (const rule of rules) {
if (rule.patterns.some(p => text.includes(p))) {
labelsToAdd.add(rule.label);
}
}
// If no labels matched, add 'needs triage' so maintainers notice it
if (labelsToAdd.size === 0) {
labelsToAdd.add('needs triage');
}
const existingLabels = (issue.labels || []).map(l => l.name);
const newLabels = [...labelsToAdd].filter(l => !existingLabels.includes(l));
if (newLabels.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: newLabels,
});
console.log(`Added labels: ${newLabels.join(', ')}`);
} else {
console.log('No new labels to add.');
}
- name: Request triage for unlabeled issues
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issue = context.payload.issue;
const labels = (issue.labels || []).map(l => l.name);
// If the only label is 'needs triage', add a comment directing maintainers
if (labels.length === 1 && labels[0] === 'needs triage') {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: [
'👋 Thanks for opening this issue!',
'',
'This issue has been flagged for **maintainer triage** because it could not be automatically categorized.',
'A maintainer will review it shortly to add labels and assess priority.',
'',
'In the meantime, you can help by:',
'- Making sure the issue title clearly describes the problem',
'- Adding a link to the affected page (for documentation issues)',
'- Providing a reference to source code or specs (for technical accuracy issues)',
].join('\n'),
});
}

95
.github/workflows/pr-check.yml vendored Normal file
View File

@@ -0,0 +1,95 @@
name: PR Validation
on:
pull_request_target:
types: [opened, edited, synchronize, reopened]
permissions:
pull-requests: write
issues: read
jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
- name: Validate PR description and linked issue
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const pr = context.payload.pull_request;
const body = pr.body || '';
const title = pr.title || '';
const warnings = [];
const errors = [];
// 1. PR must have a non-trivial description
const descriptionSection = body.replace(/<!--[\s\S]*?-->/g, '').trim();
if (descriptionSection.length < 30) {
errors.push('❌ **Missing description**: Please provide a clear description of what this PR changes and why.');
}
// 2. PR should reference an issue (Fixes/Closes/Resolves #N or mention #N)
const issueRef = /(?:fix(?:es|ed)?|close[sd]?|resolve[sd]?)\s+#\d+|#\d+/i.test(body);
if (!issueRef) {
warnings.push('⚠️ **No linked issue detected**: Consider linking this PR to an issue using `Fixes #<number>` so progress is tracked on the project board.');
}
// 3. RACI table must have at least Responsible checked
// Simple check: if the RACI section exists, at least one checkbox should be filled
if (body.includes('RACI') && !/- \[x\]/i.test(body)) {
warnings.push('⚠️ **RACI checklist incomplete**: Please tick the checkboxes in the RACI Confirmation table.');
}
// 4. Draft PRs skip hard errors
const isDraft = pr.draft;
// Post a review comment only if there are issues
if (errors.length > 0 || warnings.length > 0) {
const lines = [
'## 🤖 PR Validation Report',
'',
...errors,
...warnings,
'',
isDraft ? '_This is a draft PR — errors above will need to be resolved before requesting review._'
: '_Please address the items above before requesting a maintainer review._',
'',
'---',
'_This comment is generated automatically. See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines._',
];
// Check if we already posted a validation comment so we update rather than duplicate
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('PR Validation Report')
);
const commentBody = lines.join('\n');
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: commentBody,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: commentBody,
});
}
// Fail the check if there are hard errors and this is not a draft
if (errors.length > 0 && !isDraft) {
core.setFailed(`PR validation failed: ${errors.join(' | ')}`);
}
}

73
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,73 @@
name: Stale Issue & PR Management
on:
schedule:
# Runs daily at 01:00 UTC
- cron: "0 1 * * *"
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# ── Issues ────────────────────────────────────────────────────────
days-before-issue-stale: 90
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: >
This issue has been open for 90 days with no recent activity.
It will be closed in 14 days unless someone comments or removes
the `stale` label.
If this issue is still relevant, please:
- Leave a comment with an update
- Remove the `stale` label if you plan to work on it
- Link it to an open PR if work is already in progress
close-issue-message: >
This issue was closed due to inactivity (90 + 14 days).
If you believe it is still relevant, please open a new issue
and reference this one.
# Issues with these labels are never marked stale
exempt-issue-labels: >
pinned,
security,
good first issue,
help wanted
# ── Pull Requests ─────────────────────────────────────────────────
days-before-pr-stale: 30
days-before-pr-close: 7
stale-pr-label: "stale"
stale-pr-message: >
This pull request has been open for 30 days with no recent activity.
It will be closed in 7 days unless there is new activity.
If this PR is still in progress:
- Push an update or leave a comment
- Remove the `stale` label to reset the timer
close-pr-message: >
This pull request was closed due to inactivity (30 + 7 days).
The branch has been preserved. Please open a new PR if you
wish to continue this work.
exempt-pr-labels: >
pinned,
security,
work in progress,
wip
# Keep the stale label when the issue/PR becomes active again
remove-stale-when-updated: true
# Only count non-bot comments as activity
operations-per-run: 100

View File

@@ -1,3 +1,32 @@
# Contributing
For information about how to contribute to this repository, see [Contribute Documentation (XRPL.org)](https://xrpl.org/resources/contribute-documentation/).
For information about how to contribute to this repository, see [Contribute Documentation (XRPL.org)](https://xrpl.org/resources/contribute-documentation/).
## Quick Start
1. **Report an issue** — Use one of the [issue templates](.github/ISSUE_TEMPLATE/) to report bugs, request content updates, or suggest new features.
2. **Work on an issue** — Browse [open issues](https://github.com/XRPLF/xrpl-dev-portal/issues) with the `good first issue` or `help wanted` labels.
3. **Submit a pull request** — Fill out the [PR template](.github/PULL_REQUEST_TEMPLATE.md) completely, including linking to the issue you are addressing.
## Review Process (RACI Model)
This project follows a [RACI model](.github/project-management/RACI.md) to manage contributions:
- **R Responsible**: The contributor who authors the change
- **A Accountable**: A maintainer from `@XRPLF/docs-maintainers` who approves and merges
- **C Consulted**: Subject-matter experts tagged as reviewers for technical accuracy
- **I Informed**: Stakeholders notified via linked issues and PR comments
**At least one maintainer approval is required before any PR can be merged.**
## Automated Workflows
The following GitHub Actions workflows run automatically:
| Workflow | Trigger | Purpose |
|---|---|---|
| Issue Triage | New issue opened | Applies labels based on content |
| PR Validation | PR opened/updated | Validates description and linked issue |
| Auto-Label | PR opened/updated | Labels PRs based on changed file paths |
| Stale Management | Daily | Marks and closes inactive issues/PRs |
| Greet Contributor | First issue/PR | Welcomes new contributors |

View File

@@ -3,7 +3,7 @@
import xrpl from 'xrpl'
import fs from 'fs'
process.stdout.write('Setting up tutorial: 0/6\r')
process.stdout.write('Setting up tutorial: 0/7\r')
const client = new xrpl.Client('wss://s.devnet.rippletest.net:51233')
await client.connect()
@@ -21,10 +21,45 @@ const [
client.fundWallet()
])
process.stdout.write('Setting up tutorial: 1/6\r')
process.stdout.write('Setting up tutorial: 1/7\r')
// Create tickets for parallel transactions
const extractTickets = (response) =>
response.result.meta.AffectedNodes
.filter(node => node.CreatedNode?.LedgerEntryType === 'Ticket')
.map(node => node.CreatedNode.NewFields.TicketSequence)
const [ciTicketResponse, lbTicketResponse, brTicketResponse, dpTicketResponse] = await Promise.all([
client.submitAndWait({
TransactionType: 'TicketCreate',
Account: credentialIssuer.address,
TicketCount: 4
}, { wallet: credentialIssuer, autofill: true }),
client.submitAndWait({
TransactionType: 'TicketCreate',
Account: loanBroker.address,
TicketCount: 4
}, { wallet: loanBroker, autofill: true }),
client.submitAndWait({
TransactionType: 'TicketCreate',
Account: borrower.address,
TicketCount: 2
}, { wallet: borrower, autofill: true }),
client.submitAndWait({
TransactionType: 'TicketCreate',
Account: depositor.address,
TicketCount: 2
}, { wallet: depositor, autofill: true })
])
const ciTickets = extractTickets(ciTicketResponse)
const lbTickets = extractTickets(lbTicketResponse)
const brTickets = extractTickets(brTicketResponse)
const dpTickets = extractTickets(dpTicketResponse)
process.stdout.write('Setting up tutorial: 2/7\r')
// Issue MPT with depositor
// Create tickets for later use with loanBroker
// Set up credentials and domain with credentialIssuer
const credentialType = xrpl.convertStringToHex('KYC-Verified')
const mptData = {
@@ -56,12 +91,7 @@ const mptData = {
}
}
const [ticketCreateResponse, mptIssuanceResponse] = await Promise.all([
client.submitAndWait({
TransactionType: 'TicketCreate',
Account: loanBroker.address,
TicketCount: 2
}, { wallet: loanBroker, autofill: true }),
const [mptIssuanceResponse, domainSetResponse] = await Promise.all([
client.submitAndWait({
TransactionType: 'MPTokenIssuanceCreate',
Account: depositor.address,
@@ -74,104 +104,87 @@ const [ticketCreateResponse, mptIssuanceResponse] = await Promise.all([
MPTokenMetadata: xrpl.encodeMPTokenMetadata(mptData)
}, { wallet: depositor, autofill: true }),
client.submitAndWait({
TransactionType: 'Batch',
TransactionType: 'PermissionedDomainSet',
Account: credentialIssuer.address,
Flags: xrpl.BatchFlags.tfAllOrNothing,
RawTransactions: [
AcceptedCredentials: [
{
RawTransaction: {
TransactionType: 'CredentialCreate',
Account: credentialIssuer.address,
Subject: loanBroker.address,
CredentialType: credentialType,
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
},
{
RawTransaction: {
TransactionType: 'CredentialCreate',
Account: credentialIssuer.address,
Subject: borrower.address,
CredentialType: credentialType,
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
},
{
RawTransaction: {
TransactionType: 'CredentialCreate',
Account: credentialIssuer.address,
Subject: depositor.address,
CredentialType: credentialType,
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
},
{
RawTransaction: {
TransactionType: 'PermissionedDomainSet',
Account: credentialIssuer.address,
AcceptedCredentials: [
{
Credential: {
Issuer: credentialIssuer.address,
CredentialType: credentialType
}
}
],
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
Credential: {
Issuer: credentialIssuer.address,
CredentialType: credentialType
}
}
]
],
Sequence: 0,
TicketSequence: ciTickets[0]
}, { wallet: credentialIssuer, autofill: true }),
client.submitAndWait({
TransactionType: 'CredentialCreate',
Account: credentialIssuer.address,
Subject: loanBroker.address,
CredentialType: credentialType,
Sequence: 0,
TicketSequence: ciTickets[1]
}, { wallet: credentialIssuer, autofill: true }),
client.submitAndWait({
TransactionType: 'CredentialCreate',
Account: credentialIssuer.address,
Subject: borrower.address,
CredentialType: credentialType,
Sequence: 0,
TicketSequence: ciTickets[2]
}, { wallet: credentialIssuer, autofill: true }),
client.submitAndWait({
TransactionType: 'CredentialCreate',
Account: credentialIssuer.address,
Subject: depositor.address,
CredentialType: credentialType,
Sequence: 0,
TicketSequence: ciTickets[3]
}, { wallet: credentialIssuer, autofill: true })
])
// Extract ticket sequence numbers
const tickets = ticketCreateResponse.result.meta.AffectedNodes
.filter(node => node.CreatedNode?.LedgerEntryType === 'Ticket')
.map(node => node.CreatedNode.NewFields.TicketSequence)
// Extract MPT issuance ID
const mptID = mptIssuanceResponse.result.meta.mpt_issuance_id
// Get domain ID
const credentialIssuerObjects = await client.request({
command: 'account_objects',
account: credentialIssuer.address,
ledger_index: 'validated'
})
const domainID = credentialIssuerObjects.result.account_objects.find(node =>
node.LedgerEntryType === 'PermissionedDomain'
).index
// Extract domain ID
const domainID = domainSetResponse.result.meta.AffectedNodes.find(node =>
node.CreatedNode?.LedgerEntryType === 'PermissionedDomain'
).CreatedNode.LedgerIndex
process.stdout.write('Setting up tutorial: 2/6\r')
process.stdout.write('Setting up tutorial: 3/7\r')
// Accept credentials and authorize MPT for each account
await Promise.all([
...([loanBroker, borrower].map(wallet =>
client.submitAndWait({
TransactionType: 'Batch',
Account: wallet.address,
Flags: xrpl.BatchFlags.tfAllOrNothing,
RawTransactions: [
{
RawTransaction: {
TransactionType: 'CredentialAccept',
Account: wallet.address,
Issuer: credentialIssuer.address,
CredentialType: credentialType,
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
},
{
RawTransaction: {
TransactionType: 'MPTokenAuthorize',
Account: wallet.address,
MPTokenIssuanceID: mptID,
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
}
]
}, { wallet, autofill: true })
)),
client.submitAndWait({
TransactionType: 'CredentialAccept',
Account: loanBroker.address,
Issuer: credentialIssuer.address,
CredentialType: credentialType,
Sequence: 0,
TicketSequence: lbTickets[0]
}, { wallet: loanBroker, autofill: true }),
client.submitAndWait({
TransactionType: 'MPTokenAuthorize',
Account: loanBroker.address,
MPTokenIssuanceID: mptID,
Sequence: 0,
TicketSequence: lbTickets[1]
}, { wallet: loanBroker, autofill: true }),
client.submitAndWait({
TransactionType: 'CredentialAccept',
Account: borrower.address,
Issuer: credentialIssuer.address,
CredentialType: credentialType,
Sequence: 0,
TicketSequence: brTickets[0]
}, { wallet: borrower, autofill: true }),
client.submitAndWait({
TransactionType: 'MPTokenAuthorize',
Account: borrower.address,
MPTokenIssuanceID: mptID,
Sequence: 0,
TicketSequence: brTickets[1]
}, { wallet: borrower, autofill: true }),
// Depositor only needs to accept credentials
client.submitAndWait({
TransactionType: 'CredentialAccept',
@@ -181,7 +194,7 @@ await Promise.all([
}, { wallet: depositor, autofill: true })
])
process.stdout.write('Setting up tutorial: 3/6\r')
process.stdout.write('Setting up tutorial: 4/7\r')
// Create private vault and distribute MPT to accounts
const [vaultCreateResponse] = await Promise.all([
@@ -195,35 +208,26 @@ const [vaultCreateResponse] = await Promise.all([
DomainID: domainID
}, { wallet: loanBroker, autofill: true }),
client.submitAndWait({
TransactionType: 'Batch',
TransactionType: 'Payment',
Account: depositor.address,
Flags: xrpl.BatchFlags.tfAllOrNothing,
RawTransactions: [
{
RawTransaction: {
TransactionType: 'Payment',
Account: depositor.address,
Destination: loanBroker.address,
Amount: {
mpt_issuance_id: mptID,
value: '5000'
},
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
},
{
RawTransaction: {
TransactionType: 'Payment',
Account: depositor.address,
Destination: borrower.address,
Amount: {
mpt_issuance_id: mptID,
value: '2500'
},
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
}
]
Destination: loanBroker.address,
Amount: {
mpt_issuance_id: mptID,
value: '5000'
},
Sequence: 0,
TicketSequence: dpTickets[0]
}, { wallet: depositor, autofill: true }),
client.submitAndWait({
TransactionType: 'Payment',
Account: depositor.address,
Destination: borrower.address,
Amount: {
mpt_issuance_id: mptID,
value: '2500'
},
Sequence: 0,
TicketSequence: dpTickets[1]
}, { wallet: depositor, autofill: true })
])
@@ -231,7 +235,7 @@ const vaultID = vaultCreateResponse.result.meta.AffectedNodes.find(node =>
node.CreatedNode?.LedgerEntryType === 'Vault'
).CreatedNode.LedgerIndex
process.stdout.write('Setting up tutorial: 4/6\r')
process.stdout.write('Setting up tutorial: 5/7\r')
// Create LoanBroker and deposit MPT into vault
const [loanBrokerSetResponse] = await Promise.all([
@@ -255,7 +259,7 @@ const loanBrokerID = loanBrokerSetResponse.result.meta.AffectedNodes.find(node =
node.CreatedNode?.LedgerEntryType === 'LoanBroker'
).CreatedNode.LedgerIndex
process.stdout.write('Setting up tutorial: 5/6\r')
process.stdout.write('Setting up tutorial: 6/7\r')
// Create 2 identical loans with complete repayment due in 30 days
@@ -289,8 +293,8 @@ async function createLoan (ticketSequence) {
}
const [submitResponse1, submitResponse2] = await Promise.all([
createLoan(tickets[0]),
createLoan(tickets[1])
createLoan(lbTickets[2]),
createLoan(lbTickets[3])
])
const loanID1 = submitResponse1.result.meta.AffectedNodes.find(node =>
@@ -301,7 +305,7 @@ const loanID2 = submitResponse2.result.meta.AffectedNodes.find(node =>
node.CreatedNode?.LedgerEntryType === 'Loan'
).CreatedNode.LedgerIndex
process.stdout.write('Setting up tutorial: 6/6\r')
process.stdout.write('Setting up tutorial: 7/7\r')
// Write setup data to JSON file
const setupData = {

View File

@@ -8,9 +8,6 @@ from xrpl.asyncio.wallet import generate_faucet_wallet
from xrpl.asyncio.transaction import submit_and_wait, autofill, sign
from xrpl.transaction import sign_loan_set_by_counterparty
from xrpl.models import (
AccountObjects,
Batch,
BatchFlag,
CredentialAccept,
CredentialCreate,
LoanBrokerSet,
@@ -34,7 +31,7 @@ from xrpl.utils import encode_mptoken_metadata, str_to_hex
async def main():
async with AsyncWebsocketClient("wss://s.devnet.rippletest.net:51233") as client:
print("Setting up tutorial: 0/6", end="\r")
print("Setting up tutorial: 0/7", end="\r")
# Create and fund wallets
loan_broker, borrower, depositor, credential_issuer = await asyncio.gather(
@@ -44,10 +41,61 @@ async def main():
generate_faucet_wallet(client),
)
print("Setting up tutorial: 1/6", end="\r")
print("Setting up tutorial: 1/7", end="\r")
# Create tickets for parallel transactions
def extract_tickets(response):
return [
node["CreatedNode"]["NewFields"]["TicketSequence"]
for node in response.result["meta"]["AffectedNodes"]
if node.get("CreatedNode", {}).get("LedgerEntryType") == "Ticket"
]
ci_ticket_response, lb_ticket_response, br_ticket_response, dp_ticket_response = (
await asyncio.gather(
submit_and_wait(
TicketCreate(
account=credential_issuer.address,
ticket_count=4,
),
client,
credential_issuer,
),
submit_and_wait(
TicketCreate(
account=loan_broker.address,
ticket_count=4,
),
client,
loan_broker,
),
submit_and_wait(
TicketCreate(
account=borrower.address,
ticket_count=2,
),
client,
borrower,
),
submit_and_wait(
TicketCreate(
account=depositor.address,
ticket_count=2,
),
client,
depositor,
),
)
)
ci_tickets = extract_tickets(ci_ticket_response)
lb_tickets = extract_tickets(lb_ticket_response)
br_tickets = extract_tickets(br_ticket_response)
dp_tickets = extract_tickets(dp_ticket_response)
print("Setting up tutorial: 2/7", end="\r")
# Issue MPT with depositor
# Create tickets for later use with loan_broker
# Set up credentials and domain with credential_issuer
credential_type = str_to_hex("KYC-Verified")
@@ -80,15 +128,7 @@ async def main():
},
}
ticket_create_response, mpt_issuance_response, _ = await asyncio.gather(
submit_and_wait(
TicketCreate(
account=loan_broker.address,
ticket_count=2,
),
client,
loan_broker,
),
mpt_issuance_response, domain_set_response, *_ = await asyncio.gather(
submit_and_wait(
MPTokenIssuanceCreate(
account=depositor.address,
@@ -105,104 +145,112 @@ async def main():
depositor,
),
submit_and_wait(
Batch(
PermissionedDomainSet(
account=credential_issuer.address,
flags=BatchFlag.TF_ALL_OR_NOTHING,
raw_transactions=[
CredentialCreate(
account=credential_issuer.address,
subject=loan_broker.address,
accepted_credentials=[
Credential(
issuer=credential_issuer.address,
credential_type=credential_type,
),
CredentialCreate(
account=credential_issuer.address,
subject=borrower.address,
credential_type=credential_type,
),
CredentialCreate(
account=credential_issuer.address,
subject=depositor.address,
credential_type=credential_type,
),
PermissionedDomainSet(
account=credential_issuer.address,
accepted_credentials=[
Credential(
issuer=credential_issuer.address,
credential_type=credential_type,
),
],
),
],
sequence=0,
ticket_sequence=ci_tickets[0],
),
client,
credential_issuer,
),
submit_and_wait(
CredentialCreate(
account=credential_issuer.address,
subject=loan_broker.address,
credential_type=credential_type,
sequence=0,
ticket_sequence=ci_tickets[1],
),
client,
credential_issuer,
),
submit_and_wait(
CredentialCreate(
account=credential_issuer.address,
subject=borrower.address,
credential_type=credential_type,
sequence=0,
ticket_sequence=ci_tickets[2],
),
client,
credential_issuer,
),
submit_and_wait(
CredentialCreate(
account=credential_issuer.address,
subject=depositor.address,
credential_type=credential_type,
sequence=0,
ticket_sequence=ci_tickets[3],
),
client,
credential_issuer,
),
)
# Extract ticket sequence numbers
tickets = [
node["CreatedNode"]["NewFields"]["TicketSequence"]
for node in ticket_create_response.result["meta"]["AffectedNodes"]
if node.get("CreatedNode", {}).get("LedgerEntryType") == "Ticket"
]
# Extract MPT issuance ID
mpt_id = mpt_issuance_response.result["meta"]["mpt_issuance_id"]
# Get domain ID
credential_issuer_objects = await client.request(AccountObjects(
account=credential_issuer.address,
ledger_index="validated",
))
# Extract domain ID
domain_id = next(
node["index"]
for node in credential_issuer_objects.result["account_objects"]
if node["LedgerEntryType"] == "PermissionedDomain"
node["CreatedNode"]["LedgerIndex"]
for node in domain_set_response.result["meta"]["AffectedNodes"]
if node.get("CreatedNode", {}).get("LedgerEntryType") == "PermissionedDomain"
)
print("Setting up tutorial: 2/6", end="\r")
print("Setting up tutorial: 3/7", end="\r")
# Accept credentials and authorize MPT for each account
await asyncio.gather(
submit_and_wait(
Batch(
CredentialAccept(
account=loan_broker.address,
flags=BatchFlag.TF_ALL_OR_NOTHING,
raw_transactions=[
CredentialAccept(
account=loan_broker.address,
issuer=credential_issuer.address,
credential_type=credential_type,
),
MPTokenAuthorize(
account=loan_broker.address,
mptoken_issuance_id=mpt_id,
),
],
issuer=credential_issuer.address,
credential_type=credential_type,
sequence=0,
ticket_sequence=lb_tickets[0],
),
client,
loan_broker,
),
submit_and_wait(
Batch(
MPTokenAuthorize(
account=loan_broker.address,
mptoken_issuance_id=mpt_id,
sequence=0,
ticket_sequence=lb_tickets[1],
),
client,
loan_broker,
),
submit_and_wait(
CredentialAccept(
account=borrower.address,
flags=BatchFlag.TF_ALL_OR_NOTHING,
raw_transactions=[
CredentialAccept(
account=borrower.address,
issuer=credential_issuer.address,
credential_type=credential_type,
),
MPTokenAuthorize(
account=borrower.address,
mptoken_issuance_id=mpt_id,
),
],
issuer=credential_issuer.address,
credential_type=credential_type,
sequence=0,
ticket_sequence=br_tickets[0],
),
client,
borrower,
),
submit_and_wait(
MPTokenAuthorize(
account=borrower.address,
mptoken_issuance_id=mpt_id,
sequence=0,
ticket_sequence=br_tickets[1],
),
client,
borrower,
),
# Depositor only needs to accept credentials
submit_and_wait(
CredentialAccept(
account=depositor.address,
@@ -214,10 +262,10 @@ async def main():
),
)
print("Setting up tutorial: 3/6", end="\r")
print("Setting up tutorial: 4/7", end="\r")
# Create private vault and distribute MPT to accounts
vault_create_response, _ = await asyncio.gather(
vault_create_response, *_ = await asyncio.gather(
submit_and_wait(
VaultCreate(
account=loan_broker.address,
@@ -229,21 +277,23 @@ async def main():
loan_broker,
),
submit_and_wait(
Batch(
Payment(
account=depositor.address,
flags=BatchFlag.TF_ALL_OR_NOTHING,
raw_transactions=[
Payment(
account=depositor.address,
destination=loan_broker.address,
amount=MPTAmount(mpt_issuance_id=mpt_id, value="5000"),
),
Payment(
account=depositor.address,
destination=borrower.address,
amount=MPTAmount(mpt_issuance_id=mpt_id, value="2500"),
),
],
destination=loan_broker.address,
amount=MPTAmount(mpt_issuance_id=mpt_id, value="5000"),
sequence=0,
ticket_sequence=dp_tickets[0],
),
client,
depositor,
),
submit_and_wait(
Payment(
account=depositor.address,
destination=borrower.address,
amount=MPTAmount(mpt_issuance_id=mpt_id, value="2500"),
sequence=0,
ticket_sequence=dp_tickets[1],
),
client,
depositor,
@@ -256,7 +306,7 @@ async def main():
if node.get("CreatedNode", {}).get("LedgerEntryType") == "Vault"
)
print("Setting up tutorial: 4/6", end="\r")
print("Setting up tutorial: 5/7", end="\r")
# Create LoanBroker and deposit MPT into vault
loan_broker_set_response, _ = await asyncio.gather(
@@ -285,10 +335,10 @@ async def main():
if node.get("CreatedNode", {}).get("LedgerEntryType") == "LoanBroker"
)
print("Setting up tutorial: 5/6", end="\r")
print("Setting up tutorial: 6/7", end="\r")
# Create 2 identical loans with complete repayment due in 30 days
# Helper function to create, sign, and submit a LoanSet transaction
async def create_loan(ticket_sequence):
loan_set_tx = await autofill(LoanSet(
@@ -312,8 +362,8 @@ async def main():
return submit_response
submit_response_1, submit_response_2 = await asyncio.gather(
create_loan(tickets[0]),
create_loan(tickets[1]),
create_loan(lb_tickets[2]),
create_loan(lb_tickets[3]),
)
loan_id_1 = next(
@@ -328,7 +378,7 @@ async def main():
if node.get("CreatedNode", {}).get("LedgerEntryType") == "Loan"
)
print("Setting up tutorial: 6/6", end="\r")
print("Setting up tutorial: 7/7", end="\r")
# Write setup data to JSON file
setup_data = {

View File

@@ -1,9 +1,16 @@
import xrpl from 'xrpl'
import fs from 'fs'
// Helper function to extract ticket sequences from a TicketCreate transaction result
function getTicketSequences(ticketCreateResult) {
return ticketCreateResult.result.meta.AffectedNodes
.filter(node => node.CreatedNode?.LedgerEntryType === 'Ticket')
.map(node => node.CreatedNode.NewFields.TicketSequence)
}
// Setup script for vault tutorials
process.stdout.write('Setting up tutorial: 0/5\r')
process.stdout.write('Setting up tutorial: 0/6\r')
const client = new xrpl.Client('wss://s.devnet.rippletest.net:51233')
await client.connect()
@@ -21,11 +28,37 @@ const [
client.fundWallet()
])
// Step 1: Create MPT issuance, permissioned domain, and credentials in parallel
process.stdout.write('Setting up tutorial: 1/5\r')
// Step 1: Create tickets for domain owner and depositor to submit transactions concurrently
process.stdout.write('Setting up tutorial: 1/6\r')
const credType = 'VaultAccess'
const [mptCreateResult] = await Promise.all([
const [domainOwnerTicketCreateResult, depositorTicketCreateResult] = await Promise.all([
client.submitAndWait(
{
TransactionType: 'TicketCreate',
Account: domainOwner.address,
TicketCount: 2
},
{ wallet: domainOwner, autofill: true }
),
client.submitAndWait(
{
TransactionType: 'TicketCreate',
Account: depositor.address,
TicketCount: 2
},
{ wallet: depositor, autofill: true }
)
])
// Get the ticket sequences from transaction results
const domainOwnerTicketSequences = getTicketSequences(domainOwnerTicketCreateResult)
const depositorTicketSequences = getTicketSequences(depositorTicketCreateResult)
// Step 2: Create MPT issuance, permissioned domain, and credentials in parallel
process.stdout.write('Setting up tutorial: 2/6\r')
const [mptCreateResult, domainSetResult] = await Promise.all([
client.submitAndWait(
{
TransactionType: "MPTokenIssuanceCreate",
@@ -71,35 +104,29 @@ const [mptCreateResult] = await Promise.all([
),
client.submitAndWait(
{
TransactionType: "Batch",
TransactionType: "PermissionedDomainSet",
Account: domainOwner.address,
Flags: xrpl.BatchFlags.tfAllOrNothing,
RawTransactions: [
AcceptedCredentials: [
{
RawTransaction: {
TransactionType: "PermissionedDomainSet",
Account: domainOwner.address,
AcceptedCredentials: [
{
Credential: {
Issuer: domainOwner.address,
CredentialType: xrpl.convertStringToHex(credType),
},
},
],
Flags: xrpl.GlobalFlags.tfInnerBatchTxn,
},
},
{
RawTransaction: {
TransactionType: "CredentialCreate",
Account: domainOwner.address,
Subject: depositor.address,
Credential: {
Issuer: domainOwner.address,
CredentialType: xrpl.convertStringToHex(credType),
Flags: xrpl.GlobalFlags.tfInnerBatchTxn,
},
},
],
TicketSequence: domainOwnerTicketSequences[0],
Sequence: 0
},
{ wallet: domainOwner, autofill: true },
),
client.submitAndWait(
{
TransactionType: "CredentialCreate",
Account: domainOwner.address,
Subject: depositor.address,
CredentialType: xrpl.convertStringToHex(credType),
TicketSequence: domainOwnerTicketSequences[1],
Sequence: 0
},
{ wallet: domainOwner, autofill: true },
),
@@ -107,44 +134,34 @@ const [mptCreateResult] = await Promise.all([
const mptIssuanceId = mptCreateResult.result.meta.mpt_issuance_id
// Get domain ID
const domainOwnerObjects = await client.request({
command: 'account_objects',
account: domainOwner.address,
ledger_index: 'validated'
})
const domainId = domainOwnerObjects.result.account_objects.find(
(node) => node.LedgerEntryType === 'PermissionedDomain'
).index
// Get domain ID from transaction result
const domainNode = domainSetResult.result.meta.AffectedNodes.find(
(node) => node.CreatedNode?.LedgerEntryType === 'PermissionedDomain'
)
const domainId = domainNode.CreatedNode.LedgerIndex
// Step 2: Depositor accepts credential, authorizes MPT, and creates vault in parallel
process.stdout.write('Setting up tutorial: 2/5\r')
// Step 3: Depositor accepts credential, authorizes MPT, and creates vault in parallel
process.stdout.write('Setting up tutorial: 3/6\r')
const [, vaultCreateResult] = await Promise.all([
const [, , vaultCreateResult] = await Promise.all([
client.submitAndWait(
{
TransactionType: 'Batch',
TransactionType: 'CredentialAccept',
Account: depositor.address,
Flags: xrpl.BatchFlags.tfAllOrNothing,
RawTransactions: [
{
RawTransaction: {
TransactionType: 'CredentialAccept',
Account: depositor.address,
Issuer: domainOwner.address,
CredentialType: xrpl.convertStringToHex(credType),
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
},
{
RawTransaction: {
TransactionType: 'MPTokenAuthorize',
Account: depositor.address,
MPTokenIssuanceID: mptIssuanceId,
Flags: xrpl.GlobalFlags.tfInnerBatchTxn
}
}
]
Issuer: domainOwner.address,
CredentialType: xrpl.convertStringToHex(credType),
TicketSequence: depositorTicketSequences[0],
Sequence: 0
},
{ wallet: depositor, autofill: true }
),
client.submitAndWait(
{
TransactionType: 'MPTokenAuthorize',
Account: depositor.address,
MPTokenIssuanceID: mptIssuanceId,
TicketSequence: depositorTicketSequences[1],
Sequence: 0
},
{ wallet: depositor, autofill: true }
),
@@ -196,8 +213,8 @@ const vaultNode = vaultCreateResult.result.meta.AffectedNodes.find(
const vaultID = vaultNode.CreatedNode.LedgerIndex
const vaultShareMPTIssuanceId = vaultNode.CreatedNode.NewFields.ShareMPTID
// Step 3: Issuer sends payment to depositor
process.stdout.write('Setting up tutorial: 3/5\r')
// Step 4: Issuer sends payment to depositor
process.stdout.write('Setting up tutorial: 4/6\r')
const paymentResult = await client.submitAndWait(
{
@@ -218,8 +235,8 @@ if (paymentResult.result.meta.TransactionResult !== 'tesSUCCESS') {
process.exit(1)
}
// Step 4: Make an initial deposit so withdraw example has shares to work with
process.stdout.write('Setting up tutorial: 4/5\r')
// Step 5: Make an initial deposit so withdraw example has shares to work with
process.stdout.write('Setting up tutorial: 5/6\r')
const initialDepositResult = await client.submitAndWait(
{
@@ -240,8 +257,8 @@ if (initialDepositResult.result.meta.TransactionResult !== 'tesSUCCESS') {
process.exit(1)
}
// Step 5: Save setup data to file
process.stdout.write('Setting up tutorial: 5/5\r')
// Step 6: Save setup data to file
process.stdout.write('Setting up tutorial: 6/6\r')
const setupData = {
mptIssuer: {

View File

@@ -6,11 +6,10 @@ from xrpl.asyncio.clients import AsyncWebsocketClient
from xrpl.asyncio.transaction import submit_and_wait
from xrpl.asyncio.wallet import generate_faucet_wallet
from xrpl.models import (
Batch, BatchFlag, CredentialAccept, CredentialCreate, MPTokenAuthorize,
CredentialAccept, CredentialCreate, MPTokenAuthorize,
MPTokenIssuanceCreate, MPTokenIssuanceCreateFlag, Payment,
PermissionedDomainSet, VaultDeposit
PermissionedDomainSet, TicketCreate, VaultDeposit
)
from xrpl.models.requests import AccountObjects
from xrpl.models.transactions.deposit_preauth import Credential
from xrpl.models.transactions.vault_create import (
VaultCreate, VaultCreateFlag, WithdrawalPolicy
@@ -18,9 +17,17 @@ from xrpl.models.transactions.vault_create import (
from xrpl.utils import encode_mptoken_metadata, str_to_hex
def get_ticket_sequences(ticket_create_result):
"""Extract ticket sequences from a TicketCreate transaction result."""
return [
node["CreatedNode"]["NewFields"]["TicketSequence"]
for node in ticket_create_result.result["meta"]["AffectedNodes"]
if "CreatedNode" in node and node["CreatedNode"].get("LedgerEntryType") == "Ticket"
]
async def main():
# Setup script for vault tutorials
print("Setting up tutorial: 0/5", end="\r")
print("Setting up tutorial: 0/6", end="\r")
async with AsyncWebsocketClient("wss://s.devnet.rippletest.net:51233") as client:
# Create and fund all wallets concurrently
@@ -31,11 +38,39 @@ async def main():
generate_faucet_wallet(client),
)
# Step 1: Create MPT issuance, permissioned domain, and credentials in parallel
print("Setting up tutorial: 1/5", end="\r")
# Step 1: Create tickets for domain owner and depositor to submit transactions concurrently
print("Setting up tutorial: 1/6", end="\r")
cred_type = "VaultAccess"
mpt_create_result, _ = await asyncio.gather(
domain_owner_ticket_create_result, depositor_ticket_create_result = await asyncio.gather(
submit_and_wait(
TicketCreate(
account=domain_owner.address,
ticket_count=2
),
client,
domain_owner,
autofill=True
),
submit_and_wait(
TicketCreate(
account=depositor.address,
ticket_count=2
),
client,
depositor,
autofill=True
)
)
# Get the ticket sequences from transaction results
domain_owner_ticket_sequences = get_ticket_sequences(domain_owner_ticket_create_result)
depositor_ticket_sequences = get_ticket_sequences(depositor_ticket_create_result)
# Step 2: Create MPT issuance, permissioned domain, and credentials in parallel
print("Setting up tutorial: 2/6", end="\r")
mpt_create_result, domain_set_result, _ = await asyncio.gather(
submit_and_wait(
MPTokenIssuanceCreate(
account=mpt_issuer.address,
@@ -82,25 +117,28 @@ async def main():
autofill=True
),
submit_and_wait(
Batch(
PermissionedDomainSet(
account=domain_owner.address,
flags=BatchFlag.TF_ALL_OR_NOTHING,
raw_transactions=[
PermissionedDomainSet(
account=domain_owner.address,
accepted_credentials=[
Credential(
issuer=domain_owner.address,
credential_type=str_to_hex(cred_type)
)
],
),
CredentialCreate(
account=domain_owner.address,
subject=depositor.address,
credential_type=str_to_hex(cred_type),
),
accepted_credentials=[
Credential(
issuer=domain_owner.address,
credential_type=str_to_hex(cred_type)
)
],
ticket_sequence=domain_owner_ticket_sequences[0],
sequence=0
),
client,
domain_owner,
autofill=True
),
submit_and_wait(
CredentialCreate(
account=domain_owner.address,
subject=depositor.address,
credential_type=str_to_hex(cred_type),
ticket_sequence=domain_owner_ticket_sequences[1],
sequence=0
),
client,
domain_owner,
@@ -110,36 +148,35 @@ async def main():
mpt_issuance_id = mpt_create_result.result["meta"]["mpt_issuance_id"]
# Get domain ID
domain_owner_objects = await client.request(AccountObjects(
account=domain_owner.address,
ledger_index="validated",
))
domain_id = next(
node["index"]
for node in domain_owner_objects.result["account_objects"]
if node["LedgerEntryType"] == "PermissionedDomain"
# Get domain ID from transaction result
domain_node = next(
node for node in domain_set_result.result["meta"]["AffectedNodes"]
if "CreatedNode" in node and node["CreatedNode"].get("LedgerEntryType") == "PermissionedDomain"
)
domain_id = domain_node["CreatedNode"]["LedgerIndex"]
# Step 2: Depositor accepts credential, authorizes MPT, and creates vault in parallel
print("Setting up tutorial: 2/5", end="\r")
# Step 3: Depositor accepts credential, authorizes MPT, and creates vault in parallel
print("Setting up tutorial: 3/6", end="\r")
_, vault_create_result = await asyncio.gather(
_, _, vault_create_result = await asyncio.gather(
submit_and_wait(
Batch(
CredentialAccept(
account=depositor.address,
flags=BatchFlag.TF_ALL_OR_NOTHING,
raw_transactions=[
CredentialAccept(
account=depositor.address,
issuer=domain_owner.address,
credential_type=str_to_hex(cred_type),
),
MPTokenAuthorize(
account=depositor.address,
mptoken_issuance_id=mpt_issuance_id,
),
],
issuer=domain_owner.address,
credential_type=str_to_hex(cred_type),
ticket_sequence=depositor_ticket_sequences[0],
sequence=0
),
client,
depositor,
autofill=True
),
submit_and_wait(
MPTokenAuthorize(
account=depositor.address,
mptoken_issuance_id=mpt_issuance_id,
ticket_sequence=depositor_ticket_sequences[1],
sequence=0
),
client,
depositor,
@@ -194,8 +231,8 @@ async def main():
vault_id = vault_node["CreatedNode"]["LedgerIndex"]
vault_share_mpt_issuance_id = vault_node["CreatedNode"]["NewFields"]["ShareMPTID"]
# Step 3: Issuer sends payment to depositor
print("Setting up tutorial: 3/5", end="\r")
# Step 4: Issuer sends payment to depositor
print("Setting up tutorial: 4/6", end="\r")
payment_result = await submit_and_wait(
Payment(
@@ -215,8 +252,8 @@ async def main():
print(f"\nPayment failed: {payment_result.result['meta']['TransactionResult']}", file=sys.stderr)
sys.exit(1)
# Step 4: Make an initial deposit so withdraw example has shares to work with
print("Setting up tutorial: 4/5", end="\r")
# Step 5: Make an initial deposit so withdraw example has shares to work with
print("Setting up tutorial: 5/6", end="\r")
initial_deposit_result = await submit_and_wait(
VaultDeposit(
@@ -236,8 +273,8 @@ async def main():
print(f"\nInitial deposit failed: {initial_deposit_result.result['meta']['TransactionResult']}", file=sys.stderr)
sys.exit(1)
# Step 5: Save setup data to file
print("Setting up tutorial: 5/5", end="\r")
# Step 6: Save setup data to file
print("Setting up tutorial: 6/6", end="\r")
setup_data = {
"mpt_issuer": {

View File

@@ -26,7 +26,7 @@ const logos = {
],
developer_tooling: ["cryptum", "evernode", "threezy", "tokenize"],
interoperability: ["multichain"],
wallet: ["crossmark", "edge", "gem-wallet", "xumm", "joey-wallet", "bifrost-wallet"],
wallet: ["crossmark", "edge", "gem-wallet", "xumm", "joey-wallet", "bifrost-wallet", "bitget-wallet"],
nfts: [
"aesthetes",
"audiotarky",
@@ -422,6 +422,15 @@ const cardsData = [
category_name: "Wallet",
link: "https://bifrostwallet.com/",
},
{
id: "bitget-wallet",
title: "Bitget Wallet",
description:
"Bitget Wallet is a non-custodial wallet designed to make crypto simple and secure for everyone.",
category_id: "wallet",
category_name: "Wallet",
link: "https://web3.bitget.com/",
},
];
const featured_categories = {
@@ -468,7 +477,7 @@ const uses = [
{
id: "wallet",
title: "Wallet",
number: 6,
number: 7,
description:
"Build digital wallets to store passwords and interact with various blockchains to send and receive digital assets, including XRP."
},

View File

@@ -19,18 +19,14 @@ const links = [
const softwallets = [
{ href: "https://bifrostwallet.com/", id: "wallet-bifrost", alt: "Bifrost Wallet" },
{ href: "https://xaman.app/", id: "wallet-xumm", alt: "Xaman" },
{ href: "https://trustwallet.com/", id: "wallet-trust", alt: "Trust Wallet" },
{
href: "https://gatehub.net/",
id: "wallet-gatehub",
alt: "Gatehub",
imgclasses: "invertible-img",
},
{ href: "https://gemwallet.app/", id: "wallet-gem", alt: "Gem Wallet" },
{ href: "https://web3.bitget.com/", id: "wallet-bitget", alt: "Bitget Wallet" },
{ href: "https://coin.space/", id: "wallet-coin", alt: "Coin Space" },
{ href: "https://crossmark.io/", id: "wallet-crossmark", alt: "Crossmark Wallet" },
{ href: "https://gatehub.net/", id: "wallet-gatehub", alt: "Gatehub", imgclasses: "invertible-img" },
{ href: "https://gemwallet.app/", id: "wallet-gem", alt: "Gem Wallet" },
{ href: "https://joeywallet.xyz/", id: "wallet-joey", alt: "Joey Wallet" },
{ href: "https://trustwallet.com/", id: "wallet-trust", alt: "Trust Wallet" },
{ href: "https://xaman.app/", id: "wallet-xumm", alt: "Xaman" }
];
const hardwallets = [
@@ -61,16 +57,8 @@ const exchanges = [
{ href: "https://www.liquid.com/", id: "exch-liquid", alt: "Liquid" },
{ href: "https://www.lmax.com/", id: "exch-lmax", alt: "LMAX" },
{ href: "https://www.bitfinex.com/", id: "exch-bitfinex", alt: "Bitfinex" },
{
href: "https://www.etoro.com/crypto/exchange/",
id: "exch-etoro",
alt: "eToro",
},
{
href: "https://currency.com",
id: "exch-currency-com",
alt: "Currency.com",
},
{ href: "https://www.etoro.com/crypto/exchange/", id: "exch-etoro", alt: "eToro"},
{ href: "https://currency.com", id: "exch-currency-com", alt: "Currency.com"},
{ href: "https://bittrex.com/", id: "exch-bittrex", alt: "Bittrex" },
];

142
blog/2026/rippled-3.1.1.md Normal file
View File

@@ -0,0 +1,142 @@
---
category: 2026
date: "2026-02-23"
template: '../../@theme/templates/blogpost'
seo:
title: Introducing XRP Ledger version 3.1.1 and upcoming Devnet reset
description: rippled version 3.1.1 is now available. This version disables the Batch and fixBatchInnerSigs amendments. Devnet is also scheduled to reset on Tuesday, March 3, 2026 to prevent validators from becoming amendment blocked.
labels:
- rippled Release Notes
markdown:
editPage:
hide: true
---
# Introducing XRP Ledger version 3.1.1 and Upcoming Devnet Reset
Version 3.1.1 of `rippled`, the reference server implementation of the XRP Ledger protocol, is now available. This release supersedes version 3.1.0, disabling the `Batch` and `fixBatchInnerSigs` amendments due to a severe bug.
## Upcoming Devnet Reset
Devnet is scheduled for a reset on **Tuesday, March 3, 2026**. The `Batch` amendment requires more development and is now set to unsupported in version 3.1.1. To prevent validators that upgrade to this version from becoming amendment blocked, Devnet must be reset.
### Impact
This reset affects Devnet only. Other networks will continue to operate as usual, including XRPL Mainnet, XRPL Testnet, Xahau, and the Hooks Testnet.
The reset will delete all ledger data in Devnet, including all accounts, transactions, balances, settings, offers, AMMs, escrows, and other data. This means all balances will be reset to zero and the block number will start at one again. No changes are anticipated to services such as Devnet APIs, faucets, Explorers, access rights, and wallet integrations; these services usually manage resets without issues.
Any existing accounts or other data will need new test XRP from the faucet and will need to be re-created.
If code relies on specific addresses, a request to the faucet can fund the same address again. However, any AMMs or Vaults that are re-created after the reset will have different pseudo-account addresses. As a reminder, it's best not to use the same addresses or key pairs on Mainnet and any developer networks.
## Action Required
If you run an XRP Ledger server, upgrade to version 3.1.1 as soon as possible to ensure service continuity.
### Add New GPG Key
As a reminder, [Ripple rotated the GPG key](./gpg-key-rotation.md) used to sign `rippled` packages. You must download and trust the new key before upgrading to version 3.1.1.
{% tabs %}
{% tab label="Red Hat / CentOS" %}
```bash
sudo rpm --import https://repos.ripple.com/repos/rippled-rpm/stable/repodata/repomd.xml.key
rpm -qi gpg-pubkey-ab06faa6 | gpg --show-keys --fingerprint
```
{% /tab %}
{% tab label="Ubuntu / Debian" %}
```bash
sudo install -d -m 0755 /etc/apt/keyrings && \
curl -fsSL https://repos.ripple.com/repos/api/gpg/key/public \
| gpg --dearmor \
| sudo tee /etc/apt/keyrings/ripple.gpg > /dev/null
gpg --show-keys --fingerprint /etc/apt/keyrings/ripple.gpg
```
Ensure the `signed-by` path in your Ripple source list refers to the location the key was downloaded. For example, on an Ubuntu 22.04 Jammy installation, `/etc/apt/sources.list.d/ripple.list` would contain:
```
deb [signed-by=/etc/apt/keyrings/ripple.gpg] https://repos.ripple.com/repos/rippled-deb jammy stable
```
{% /tab %}
{% /tabs %}
The output should include an entry for Ripple such as the following:
```
pub ed25519 2026-02-16 [SC] [expires: 2033-02-14]
E057 C1CF 72B0 DF1A 4559 E857 7DEE 9236 AB06 FAA6
uid TechOps Team at Ripple <techops+rippled@ripple.com>
sub ed25519 2026-02-16 [S] [expires: 2029-02-15]
```
{% admonition type="danger" name="Warning" %}
Only trust this key if its fingerprint exactly matches: `E057 C1CF 72B0 DF1A 4559 E857 7DEE 9236 AB06 FAA6`.
{% /admonition %}
### Install / Upgrade
On supported platforms, see the [instructions on installing or updating `rippled`](../../docs/infrastructure/installation/index.md).
| Package | SHA-256 |
|:--------|:--------|
| [RPM for Red Hat / CentOS (x86-64)](https://repos.ripple.com/repos/rippled-rpm/stable/rippled-3.1.1-1.el9.x86_64.rpm) | `c6d028db1e2a4da898df68e5a92a893bebf1d167a0539d15ae27435f2155ccb2` |
| [DEB for Ubuntu / Debian (x86-64)](https://repos.ripple.com/repos/rippled-deb/pool/stable/rippled_3.1.1-1_amd64.deb) | `cc30c33012bd83ed793b38738870cf931a96ae106fde60b71685c766da1d22e3` |
For other platforms, please [build from source](https://github.com/XRPLF/rippled/blob/master/BUILD.md). The most recent commit in the git log should be the change setting the version:
```text
commit c5988233d05bedddac28866ed37607f4869855f9
Author: Ed Hennis <ed@ripple.com>
Date: Mon Feb 23 16:47:09 2026 -0400
Set version to 3.1.1 (#6410)
```
### Delete Devnet Database
If you run a `rippled` server that is connected to Devnet, after the reset you should delete your database data and restart the server. Database files and folders are defined in your config file in the `[database_path]` and `[node_db]` stanzas. If you use the default config, you can run the following commands:
```sh
rm -r /var/lib/rippled/db/*
systemctl restart rippled.service
```
## Full Changelog
### Amendments
- **Batch** - A bug was discovered in `Batch`, and the amendment was disabled. The fix for this feature will be included in a future release as `BatchV1_1`. ([#6402](https://github.com/XRPLF/rippled/pull/6402))
- **fixBatchInnerSigs** - A bug was discovered in `Batch`, so this amendment was also disabled. This fix will be included in a future release as part of `BatchV1_1`. ([#6402](https://github.com/XRPLF/rippled/pull/6402))
### CI/Build
- CI: Update `prepare-runner` action to fix macOS build environment. ([#6402](https://github.com/XRPLF/rippled/pull/6402))
## Credits
Thanks to Pranamya Keshkamat and Cantina AI for discovering and responsibly disclosing the `Batch` issue.
The following RippleX teams contributed to this release:
- RippleX Engineering
- RippleX Docs
- RippleX Product
## Bug Bounties and Responsible Disclosures
We welcome reviews of the `rippled` code and urge researchers to responsibly disclose any issues they may find.
For more information, see:
- [Ripple's Bug Bounty Program](https://ripple.com/legal/bug-bounty/)
- [rippled Security Policy](https://github.com/XRPLF/rippled/blob/develop/SECURITY.md)

View File

@@ -0,0 +1,113 @@
---
category: 2026
date: "2026-02-26"
template: '../../@theme/templates/blogpost'
seo:
description: This vulnerability disclosure report contains technical details of the XRP Ledger bug reported on February 19, 2026.
labels:
- Advisories
markdown:
editPage:
hide: true
---
# Vulnerability Disclosure Report: XRPL Batch Amendment Unauthorized Inner Transaction Execution
_By XRPL Labs_
This vulnerability disclosure report contains technical details of the XRPL `Batch` amendment bug reported on February 19, 2026.
**Date Reported:** February 19, 2026
**Affected Version(s):** rippled 3.1.0 (with the `Batch` amendment enabled)
## Summary of Vulnerability
On February 19, 2026, **Pranamya Keshkamat** and **Cantina AI** identified a critical logic flaw in the signature-validation logic of the XRPL `Batch` amendment. The bug allowed an attacker to execute inner transactions on behalf of arbitrary victim accounts without their private keys, enabling unauthorized fund transfers and ledger state changes. The amendment was in its voting phase and had not been activated on mainnet; no funds were at risk. Note that even before this bug was reported, the `Batch` amendment was disabled due to extra caution around ensuring the `fixBatchInnerSigs` amendment activated first.
UNL validators were immediately advised to vote "No" on the amendment. The emergency release **rippled 3.1.1** marks both `Batch` and `fixBatchInnerSigs` as unsupported, preventing activation. A corrected replacement, `BatchV1_1`, has been implemented and is currently under review; no release date has been set.
## Impact
If the `Batch` amendment had activated before this bug was caught, an attacker could have:
- **Stolen funds:** executed inner `Payment` transactions draining victim accounts down to the reserve, without access to victim private keys.
- **Modified ledger state:** submitted `AccountSet`, `TrustSet`, and potentially `AccountDelete` transactions from victim accounts without authorization.
- **Destabilized the ecosystem:** a successful large-scale exploit could have caused substantial loss of confidence in XRPL, with potentially significant disruption for the broader ecosystem.
## Technical Details
### Discovery
**Pranamya Keshkamat** and the autonomous AI security tool **Apex** (developed by **Cantina AI**) identified the vulnerability via static analysis of the rippled codebase and submitted a responsible disclosure report. Ripple engineering teams promptly validated the report with an independent proof-of-concept and a full unit-test reproduction. Remediation began the same evening.
### Root Cause
When the `Batch` amendment is enabled, inner transactions in a batch are intentionally unsigned; authorization is delegated entirely to the outer batch's list of batch signers. The function responsible for validating those signers contained a critical loop error: when it encountered a signer whose account did not yet exist on the ledger and whose signing key matched their own account (the normal case for a brand-new account), it immediately declared success and exited, skipping validation of all remaining signers entirely.
### Exploit Path
1. The attacker constructs a batch transaction containing three inner transactions: one that creates a new account they control (account B), one simple transaction from that new account (making it a required signer), and one payment from the victim account to the attacker.
2. The attacker provides two batch signer entries: a legitimate one for account B signed with B's own key, and a forged one claiming to authorize the victim account but signed with the attacker's own key.
3. Because account B does not yet exist at validation time, the signer check exits successfully after the first entry and never validates the second.
4. The victim's payment executes without the victim's keys ever being involved.
## Remediation
- UNL validators were contacted and advised to vote "No" on the `Batch` amendment.
- **rippled 3.1.1** was published on February 23, 2026. This release marks `Batch` and `fixBatchInnerSigs` as unsupported, preventing them from receiving validator votes or being activated on the network. This is the immediate remediation and does not contain the underlying logic fix.
- The full logic fix removing the early-exit, adding additional authorization guards, and tightening the scope of the signing check has been implemented and is undergoing a thorough review process prior to release under the `BatchV1_1` amendment. Given the sensitivity of the changes, no timeline has been set.
## Security Enhancements Roadmap
- Adding AI-assisted code audit pipelines as a standard step in the review process.
- Extending static analysis coverage to flag premature success returns inside signer-iteration loops.
- Adding explicit comments and invariant assertions documenting expected behavior for uncreated accounts at validation time.
- Reviewing all other locations in the codebase where early success returns occur inside loops to confirm no similar patterns exist.
## Steps to Reproduce
1. Fund two attacker-controlled accounts and leave a third unfunded. Construct a batch transaction containing a payment that creates the unfunded account, a simple transaction from that account, and a payment from the victim account to the attacker.
2. Sign the batch message with the unfunded account's master key for its signer entry, and with the attacker's own key for a second signer entry that falsely claims to authorize the victim account.
3. Submit the batch transaction to the network.
**Pre-fix behavior:**
- The batch transaction succeeds.
- The victim account's balance is reduced by the payment amount without the victim's keys being used.
- The attacker's balance increases by the net amount minus the batch fee.
**Expected post-fix behavior:**
- The batch transaction is rejected with an authorization error.
- No account balances change.
## Fixes / Patches Available
[**rippled 3.1.1**](https://xrpl.org/blog/2026/rippled-3.1.1) is available now. The corrected `BatchV1_1` amendment will be included in a future release following completion of its review; no date has been set.
## Acknowledgements
Thanks to **Pranamya Keshkamat** and **Cantina AI** (whose autonomous security tool *Apex* identified the vulnerability) for responsible and thorough disclosure. Their detailed bug report, proof-of-concept, and constructive collaboration throughout the remediation process were invaluable.
Thanks also to the UNL validators who moved swiftly to vote against the affected amendment, the Ripple and XRPL Foundation engineering teams for the expedited review and emergency release, and the broader community of validators, developers, and contributors who keep the XRP Ledger safe and secure.
## References
- [rippled 3.1.1 release](https://xrpl.org/blog/2026/rippled-3.1.1)
- [Community post on X](https://x.com/hrkrshnn/status/2025336360010387613)
## Contact
To report security issues, contact [security@ripple.com](mailto:security@ripple.com).
## Incident Response Timeline
| Key Action | Timestamp | Description |
|---|---|---|
| Initial Discovery | February 19, 2026 | Bug reported by Pranamya Keshkamat & Cantina AI. Engineering teams validated the report and opened an emergency response channel. |
| Validator Notification | February 19, 2026 | UNL validators advised to vote "No" on `Batch`. Several validators applied vetoes the same evening. |
| Unit Test Reproduction | February 19, 2026 | Independent unit-test reproduction confirmed. Proof-of-concept script validated by multiple engineers. |
| Patch In Review | February 21, 2026 | Fix created in a private repository and currently under review. |
| Emergency Release | February 23, 2026 | rippled 3.1.1 published. Operators advised to upgrade. |
| Report Published | February 25, 2026 | Public vulnerability disclosure report published. |

View File

@@ -10,6 +10,8 @@
- group: '2026'
expanded: false
items:
- page: 2026/vulnerabilitydisclosurereport-bug-feb2026.md
- page: 2026/rippled-3.1.1.md
- page: 2026/gpg-key-rotation.md
- page: 2026/rippled-3.1.0.md
- page: 2026/clio-2.7.0.md

View File

@@ -10,6 +10,8 @@ status: not_enabled
XRPL Batch Transactions let you package multiple [transactions](/docs/concepts/transactions) together and execute them as a single unit. It eliminates the risk of partial completion and unexpected outcomes, giving you a more reliable and predictable experience for complex operations. Up to eight transactions can be submitted in a single batch.
{% amendment-disclaimer name="Batch" /%}
## XRPL Batch Use Cases
Some potential uses for `Batch` include the following.

View File

@@ -40,11 +40,12 @@ Only the owner of the associated `LoanBroker` entry can initiate this transactio
In addition to the [common fields][], {% code-page-name /%} transactions use the following fields:
| Field Name | JSON Type | Internal Type | Required? | Description |
|:-------------- |:--------------------|:--------------|:----------|:------------|
| `LoanBrokerID` | String | Hash256 | Yes | The ID of the `LoanBroker` ledger entry to withdraw from. |
| `Amount` | [Currency Amount][] | Amount | Yes | The amount of first-loss capital to withdraw. |
| `Destination` | String | AccountID | No | An account to receive the assets. |
| Field Name | JSON Type | Internal Type | Required? | Description |
|:---------------- |:--------------------|:--------------|:----------|:------------|
| `LoanBrokerID` | String | Hash256 | Yes | The ID of the `LoanBroker` ledger entry to withdraw from. |
| `Amount` | [Currency Amount][] | Amount | Yes | The amount of first-loss capital to withdraw. |
| `Destination` | String | AccountID | No | An account to receive the assets. |
| `DestinationTag` | Number | UInt32 | No | An arbitrary tag identifying the reason for the transaction to the destination. |
## Error Cases

View File

@@ -20,7 +20,7 @@ A loan payment has four types, depending on the amount and timing of the payment
- **Early Full Payment**: A payment that covers the outstanding principal of the loan. A `CloseInterestRate` is charged on the outstanding principal.
- **Overpayment**: A payment that exceeds the required minimum payment amount.
To see how loan payment transactions are calculated, see [transaction pseudo-code](https://github.com/Tapanito/XRPL-Standards/tree/xls-66-lending-protocol/XLS-0066-lending-protocol#a-3-loanpay-implementation-reference).
To see how loan payment transactions are calculated, see [LoanPay Implementation Reference](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0066-lending-protocol#a-3-loanpay-implementation-reference).
## Example {% $frontmatter.seo.title %} JSON

View File

@@ -35,6 +35,8 @@ The following is a list of known [amendments](../docs/concepts/networks-and-serv
| Name | Introduced | Status |
|:----------------------------------|:-----------|:------------------------------|
| [fixBatchInnerSigs] | v3.1.0 | {% badge href="https://xrpl.org/blog/2026/rippled-3.1.1" %}Obsolete: Removed in v3.1.1{% /badge %} |
| [Batch] | v2.5.0 | {% badge href="https://xrpl.org/blog/2026/rippled-3.1.1" %}Obsolete: Removed in v3.1.1{% /badge %} |
| [PermissionDelegation] | v2.5.0 | {% badge href="https://xrpl.org/blog/2025/rippled-2.6.1" %}Obsolete: Removed in v2.6.1{% /badge %} |
| [fixNFTokenNegOffer][] | v1.9.2 | {% badge %}Obsolete: To Be Removed{% /badge %} |
| [fixNFTokenDirV1][] | v1.9.1 | {% badge %}Obsolete: To Be Removed{% /badge %} |
@@ -105,12 +107,16 @@ For details, see the [XLS-73: AMMClawback specification](https://github.com/XRPL
| Amendment | Batch |
|:-------------|:------|
| Amendment ID | 894646DD5284E97DECFE6674A6D6152686791C4A95F8C132CCA9BAF9E5812FB6 |
| Status | Open for Voting |
| Status | Obsolete |
| Default Vote (Latest stable release) | No |
| Pre-amendment functionality retired? | No |
Allows multiple transactions to be bundled into a batch that's processed all together. Standard: [XLS-56](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0056-batch)
{% admonition type="danger" name="Warning" %}
This amendment was disabled in v3.1.1 due to a bug. It will be replaced by `BatchV1_1` in a future release.
{% /admonition %}
### CheckCashMakesTrustLine
[CheckCashMakesTrustLine]: #checkcashmakestrustline
@@ -746,12 +752,16 @@ Adds several fixes to Automated Market Maker code, specifically:
| Amendment | fixBatchInnerSigs |
|:-------------|:----------------|
| Amendment ID | 267624F8F744C4A4F1B5821A7D54410BCEBABE987F0172EE89E5FC4B6EDBC18A |
| Status | Open for Voting |
| Status | Obsolete |
| Default Vote (Latest stable release) | No |
| Pre-amendment functionality retired? | No |
This amendment fixes an issue where inner transactions of a `Batch` transaction would be flagged as having valid signatures. Since inner transactions aren't signed directly, they should never have valid signatures.
{% admonition type="danger" name="Warning" %}
This amendment was disabled in v3.1.1 due to a bug. It will be replaced by `BatchV1_1` in a future release.
{% /admonition %}
### fixCheckThreading
[fixCheckThreading]: #fixcheckthreading
@@ -1487,7 +1497,7 @@ This amendment adds several new invariants to protect the ledger against bugs in
The Lending Protocol enables on-chain, fixed-term, uncollateralized loans using pooled funds from a Single Asset Vault. This implementation relies on off-chain underwriting and risk management to assess the creditworthiness of borrowers, but offers configurable, peer-to-peer loans.
Specification: [XLS-66](https://github.com/Tapanito/XRPL-Standards/tree/xls-66-lending-protocol/XLS-0066-lending-protocol).
Specification: [XLS-66](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0066-lending-protocol).
### MPTokensV1
@@ -1699,9 +1709,11 @@ For more information, see the [Payment Channels Tutorial](../docs/tutorials/how-
| Default Vote (Latest stable release) | No |
| Pre-amendment functionality retired? | No |
Allows accounts to delegate some permissions to other accounts. This amendment was disabled in v2.6.1 due to a bug. It will be replaced by `PermissionDelegationV1_1` in a future release.
Allows accounts to delegate some permissions to other accounts. Specification: [XLS-75](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0075-permission-delegation).
Specification: [XLS-75](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0075-permission-delegation).
{% admonition type="danger" name="Warning" %}
This amendment was disabled in v2.6.1 due to a bug. It will be replaced by `PermissionDelegationV1_1` in a future release.
{% /admonition %}
### PermissionedDEX

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
<svg width="1623" height="256" viewBox="0 0 1623 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1542_6419)">
<rect width="256" height="256" rx="60" fill="white"/>
<path d="M106.241 37.6137C95.9065 37.6084 85.5894 37.6031 75.2185 37.619C68.9476 37.619 66.5955 45.4918 71.2593 50.1551L141.857 120.753C144.06 122.768 145.434 124.959 145.485 127.846C145.434 130.733 144.06 132.923 141.857 134.939L71.2593 205.537C66.5955 210.2 68.9476 218.073 75.2185 218.073C85.5894 218.089 95.9065 218.083 106.241 218.078C111.416 218.075 116.594 218.073 121.787 218.073C128.579 218.073 132.62 214.444 136.249 210.815L199.919 147.145C205 142.063 209.547 135.316 209.485 127.846C209.547 120.375 205 113.628 199.919 108.547L136.249 44.8768C132.62 41.2479 128.579 37.619 121.787 37.619C116.594 37.619 111.416 37.6163 106.241 37.6137Z" fill="#001F29"/>
<path d="M1601.73 208.139C1579.27 208.139 1568.77 196.418 1568.77 175.662V119.011H1559.98C1556.31 119.011 1554.12 116.814 1554.12 113.151V101.674C1554.12 98.0115 1556.31 95.8138 1559.98 95.8138H1568.77V67.2442C1568.77 63.5815 1570.96 61.3838 1574.63 61.3838H1593.43C1597.09 61.3838 1599.29 63.5815 1599.29 67.2442V95.8138H1617.36C1621.02 95.8138 1623.22 98.0115 1623.22 101.674V113.151C1623.22 116.814 1621.02 119.011 1617.36 119.011H1599.29V170.779C1599.29 183.965 1607.35 184.453 1614.43 184.453H1616.14C1619.8 184.453 1622 186.651 1622 190.313V202.278C1622 205.941 1619.8 208.139 1616.14 208.139H1601.73Z" fill="white"/>
<path d="M1493.17 210.58C1458.75 210.58 1436.04 185.429 1436.04 151.976C1436.04 118.523 1458.01 93.6157 1491.95 93.6157C1525.4 93.6157 1545.91 118.523 1545.91 151.976V154.418C1545.91 158.08 1543.72 160.278 1540.05 160.278H1465.34C1468.27 175.418 1479.26 184.452 1493.17 184.452C1504.41 184.452 1511.49 180.546 1516.13 176.394C1518.81 173.953 1521.25 173.464 1524.18 175.662L1535.66 184.208C1538.83 186.65 1539.57 189.092 1537.37 191.778C1527.36 203.499 1511.73 210.58 1493.17 210.58ZM1465.83 140.988H1517.35C1514.66 127.802 1506.36 117.79 1491.95 117.79C1478.04 117.79 1468.76 126.825 1465.83 140.988Z" fill="white"/>
<path d="M1393.23 208.139C1389.56 208.139 1387.37 205.941 1387.37 202.279V55.2794C1387.37 51.6166 1389.56 49.4189 1393.23 49.4189H1412.27C1415.93 49.4189 1418.13 51.6166 1418.13 55.2794L1417.89 202.279C1417.89 205.941 1415.69 208.139 1412.03 208.139H1393.23Z" fill="white"/>
<path d="M1340.4 208.139C1336.74 208.139 1334.54 205.941 1334.54 202.279V55.2794C1334.54 51.6166 1336.74 49.4189 1340.4 49.4189H1359.45C1363.11 49.4189 1365.31 51.6166 1365.31 55.2794L1365.07 202.279C1365.07 205.941 1362.87 208.139 1359.21 208.139H1340.4Z" fill="white"/>
<path d="M1249.73 210.093C1218.96 210.093 1196.5 185.43 1196.5 151.977C1196.5 118.035 1218.96 94.105 1249.73 94.105C1263.89 94.105 1274.39 99.2329 1281.96 107.779V101.675C1281.96 98.0119 1284.16 95.8143 1287.82 95.8143H1306.62C1310.28 95.8143 1312.48 98.0119 1312.48 101.675V202.279C1312.48 205.942 1310.28 208.139 1306.62 208.139H1287.82C1284.16 208.139 1281.96 205.942 1281.96 202.279V196.418C1274.39 204.965 1263.89 210.093 1249.73 210.093ZM1254.12 181.279C1270.97 181.279 1281.96 168.826 1281.96 151.977C1281.96 135.128 1270.97 122.675 1254.12 122.675C1237.28 122.675 1226.04 135.128 1226.04 151.977C1226.04 168.826 1237.28 181.279 1254.12 181.279Z" fill="white"/>
<path d="M1037.19 208.139C1033.77 208.139 1031.33 206.43 1030.35 203.255L981.274 56.0119C980.053 52.105 982.006 49.4189 986.157 49.4189H1007.4C1010.82 49.4189 1013.26 51.1282 1014.24 54.3026L1046.22 150.511L1077.97 54.3026C1078.94 51.1282 1081.14 49.4189 1084.56 49.4189H1097.5C1100.92 49.4189 1103.36 51.1282 1104.34 54.3026L1135.35 146.605L1166.11 54.3026C1167.09 51.1282 1169.53 49.4189 1172.95 49.4189H1194.19C1198.34 49.4189 1200.29 52.105 1199.07 56.0119L1150 203.255C1149.02 206.43 1146.58 208.139 1143.16 208.139H1126.31C1122.89 208.139 1120.45 206.43 1119.47 203.255L1090.17 114.616L1060.87 203.255C1059.9 206.43 1057.45 208.139 1054.04 208.139H1037.19Z" fill="white"/>
<path d="M904.354 208.139C881.89 208.139 871.39 196.418 871.39 175.662V119.011H862.6C858.938 119.011 856.74 116.814 856.74 113.151V101.674C856.74 98.0115 858.938 95.8138 862.6 95.8138H871.39V67.2442C871.39 63.5815 873.588 61.3838 877.251 61.3838H896.052C899.714 61.3838 901.912 63.5815 901.912 67.2442V95.8138H919.98C923.643 95.8138 925.841 98.0115 925.841 101.674V113.151C925.841 116.814 923.643 119.011 919.98 119.011H901.912V170.779C901.912 183.965 909.969 184.453 917.05 184.453H918.76C922.422 184.453 924.62 186.651 924.62 190.313V202.278C924.62 205.941 922.422 208.139 918.76 208.139H904.354Z" fill="white"/>
<path d="M795.801 210.58C761.373 210.58 738.665 185.429 738.665 151.976C738.665 118.523 760.64 93.6157 794.58 93.6157C828.032 93.6157 848.542 118.523 848.542 151.976V154.418C848.542 158.08 846.344 160.278 842.682 160.278H767.966C770.896 175.418 781.883 184.452 795.801 184.452C807.033 184.452 814.114 180.546 818.753 176.394C821.439 173.953 823.881 173.464 826.811 175.662L838.287 184.208C841.461 186.65 842.193 189.092 839.996 191.778C829.985 203.499 814.358 210.58 795.801 210.58ZM768.454 140.988H819.974C817.288 127.802 808.986 117.79 794.58 117.79C780.662 117.79 771.384 126.825 768.454 140.988Z" fill="white"/>
<path d="M660.68 256C642.855 256 627.717 249.895 617.217 240.127C614.287 237.441 614.776 234.511 617.706 231.825L628.693 222.546C631.379 220.104 634.065 220.349 636.995 222.546C644.809 228.895 653.355 230.36 660.68 230.36C671.179 230.36 690.224 224.5 690.224 200.325V196.174C682.655 204.721 671.912 210.093 657.75 210.093C626.984 210.093 604.765 185.43 604.765 151.977C604.765 118.523 626.984 94.105 657.75 94.105C671.912 94.105 682.655 99.2329 690.224 108.024V101.675C690.224 98.0119 692.422 95.8143 696.084 95.8143H714.886C718.548 95.8143 720.746 98.0119 720.746 101.675V200.325C720.746 232.558 699.503 256 660.68 256ZM662.145 181.279C678.993 181.279 690.224 168.826 690.224 151.977C690.224 135.128 678.993 122.675 662.145 122.675C645.297 122.675 634.309 135.128 634.309 151.977C634.309 168.826 645.297 181.279 662.145 181.279Z" fill="white"/>
<path d="M574.449 208.139C551.986 208.139 541.486 196.418 541.486 175.662V119.011H532.696C529.033 119.011 526.836 116.814 526.836 113.151V101.674C526.836 98.0115 529.033 95.8138 532.696 95.8138H541.486V67.2442C541.486 63.5815 543.684 61.3838 547.346 61.3838H566.147C569.81 61.3838 572.008 63.5815 572.008 67.2442V95.8138H590.076C593.739 95.8138 595.936 98.0115 595.936 101.674V113.151C595.936 116.814 593.739 119.011 590.076 119.011H572.008V170.779C572.008 183.965 580.065 184.453 587.146 184.453H588.855C592.518 184.453 594.715 186.651 594.715 190.313V202.278C594.715 205.941 592.518 208.139 588.855 208.139H574.449Z" fill="white"/>
<path d="M495.323 79.2091C487.022 79.2091 478.476 72.8603 478.476 62.1162C478.476 51.6162 487.022 46 495.323 46C503.381 46 512.171 51.6162 512.171 62.1162C512.171 72.8603 503.381 79.2091 495.323 79.2091ZM485.801 208.139C482.138 208.139 479.941 205.941 479.941 202.278V101.674C479.941 98.0114 482.138 95.8137 485.801 95.8137H504.846C508.509 95.8137 510.706 98.0114 510.706 101.674L510.462 202.278C510.462 205.941 508.264 208.139 504.602 208.139H485.801Z" fill="white"/>
<path d="M341.86 208.139C338.198 208.139 336 205.941 336 202.279V55.2794C336 51.6166 338.198 49.4189 341.86 49.4189H409.984C434.401 49.4189 456.132 65.291 456.132 94.1048C456.132 109.244 450.272 119.5 441.238 125.849C453.446 132.686 461.992 144.651 461.992 162.232C461.992 191.046 438.796 208.139 414.379 208.139H341.86ZM367.498 113.64H404.856C416.332 113.64 423.657 106.802 423.657 95.8141C423.657 84.8258 416.332 78.2328 404.856 78.2328H367.498V113.64ZM367.498 179.325H409.251C421.948 179.325 429.517 170.535 429.517 159.546C429.517 148.558 421.46 139.767 409.251 139.767H367.498V179.325Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_1542_6419">
<rect width="1623" height="256" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1,23 @@
<svg width="1623" height="256" viewBox="0 0 1623 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1542_2219)">
<rect width="256" height="256" rx="60" fill="#001F29"/>
<path d="M106.241 37.6137C95.9065 37.6084 85.5894 37.6031 75.2185 37.619C68.9476 37.619 66.5955 45.4918 71.2593 50.1551L141.857 120.753C144.06 122.768 145.434 124.959 145.485 127.846C145.434 130.733 144.06 132.923 141.857 134.939L71.2593 205.537C66.5955 210.2 68.9476 218.073 75.2185 218.073C85.5894 218.089 95.9065 218.083 106.241 218.078C111.416 218.075 116.594 218.073 121.787 218.073C128.579 218.073 132.62 214.444 136.249 210.815L199.919 147.145C205 142.063 209.547 135.316 209.485 127.846C209.547 120.375 205 113.628 199.919 108.547L136.249 44.8768C132.62 41.2479 128.579 37.619 121.787 37.619C116.594 37.619 111.416 37.6163 106.241 37.6137Z" fill="#00F0FF"/>
<path d="M1601.73 208.139C1579.27 208.139 1568.77 196.418 1568.77 175.662V119.011H1559.98C1556.31 119.011 1554.12 116.814 1554.12 113.151V101.674C1554.12 98.0115 1556.31 95.8138 1559.98 95.8138H1568.77V67.2442C1568.77 63.5815 1570.96 61.3838 1574.63 61.3838H1593.43C1597.09 61.3838 1599.29 63.5815 1599.29 67.2442V95.8138H1617.36C1621.02 95.8138 1623.22 98.0115 1623.22 101.674V113.151C1623.22 116.814 1621.02 119.011 1617.36 119.011H1599.29V170.779C1599.29 183.965 1607.35 184.453 1614.43 184.453H1616.14C1619.8 184.453 1622 186.651 1622 190.313V202.278C1622 205.941 1619.8 208.139 1616.14 208.139H1601.73Z" fill="#001F29"/>
<path d="M1493.17 210.58C1458.75 210.58 1436.04 185.429 1436.04 151.976C1436.04 118.523 1458.01 93.6157 1491.95 93.6157C1525.4 93.6157 1545.91 118.523 1545.91 151.976V154.418C1545.91 158.08 1543.72 160.278 1540.05 160.278H1465.34C1468.27 175.418 1479.26 184.452 1493.17 184.452C1504.41 184.452 1511.49 180.546 1516.13 176.394C1518.81 173.953 1521.25 173.464 1524.18 175.662L1535.66 184.208C1538.83 186.65 1539.57 189.092 1537.37 191.778C1527.36 203.499 1511.73 210.58 1493.17 210.58ZM1465.83 140.988H1517.35C1514.66 127.802 1506.36 117.79 1491.95 117.79C1478.04 117.79 1468.76 126.825 1465.83 140.988Z" fill="#001F29"/>
<path d="M1393.23 208.139C1389.56 208.139 1387.37 205.941 1387.37 202.279V55.2794C1387.37 51.6166 1389.56 49.4189 1393.23 49.4189H1412.27C1415.93 49.4189 1418.13 51.6166 1418.13 55.2794L1417.89 202.279C1417.89 205.941 1415.69 208.139 1412.03 208.139H1393.23Z" fill="#001F29"/>
<path d="M1340.4 208.139C1336.74 208.139 1334.54 205.941 1334.54 202.279V55.2794C1334.54 51.6166 1336.74 49.4189 1340.4 49.4189H1359.45C1363.11 49.4189 1365.31 51.6166 1365.31 55.2794L1365.07 202.279C1365.07 205.941 1362.87 208.139 1359.21 208.139H1340.4Z" fill="#001F29"/>
<path d="M1249.73 210.093C1218.96 210.093 1196.5 185.43 1196.5 151.977C1196.5 118.035 1218.96 94.105 1249.73 94.105C1263.89 94.105 1274.39 99.2329 1281.96 107.779V101.675C1281.96 98.0119 1284.16 95.8143 1287.82 95.8143H1306.62C1310.28 95.8143 1312.48 98.0119 1312.48 101.675V202.279C1312.48 205.942 1310.28 208.139 1306.62 208.139H1287.82C1284.16 208.139 1281.96 205.942 1281.96 202.279V196.418C1274.39 204.965 1263.89 210.093 1249.73 210.093ZM1254.12 181.279C1270.97 181.279 1281.96 168.826 1281.96 151.977C1281.96 135.128 1270.97 122.675 1254.12 122.675C1237.28 122.675 1226.04 135.128 1226.04 151.977C1226.04 168.826 1237.28 181.279 1254.12 181.279Z" fill="#001F29"/>
<path d="M1037.19 208.139C1033.77 208.139 1031.33 206.43 1030.35 203.255L981.274 56.0119C980.053 52.105 982.006 49.4189 986.157 49.4189H1007.4C1010.82 49.4189 1013.26 51.1282 1014.24 54.3026L1046.22 150.511L1077.97 54.3026C1078.94 51.1282 1081.14 49.4189 1084.56 49.4189H1097.5C1100.92 49.4189 1103.36 51.1282 1104.34 54.3026L1135.35 146.605L1166.11 54.3026C1167.09 51.1282 1169.53 49.4189 1172.95 49.4189H1194.19C1198.34 49.4189 1200.29 52.105 1199.07 56.0119L1150 203.255C1149.02 206.43 1146.58 208.139 1143.16 208.139H1126.31C1122.89 208.139 1120.45 206.43 1119.47 203.255L1090.17 114.616L1060.87 203.255C1059.9 206.43 1057.45 208.139 1054.04 208.139H1037.19Z" fill="#001F29"/>
<path d="M904.354 208.139C881.89 208.139 871.39 196.418 871.39 175.662V119.011H862.6C858.938 119.011 856.74 116.814 856.74 113.151V101.674C856.74 98.0115 858.938 95.8138 862.6 95.8138H871.39V67.2442C871.39 63.5815 873.588 61.3838 877.251 61.3838H896.052C899.714 61.3838 901.912 63.5815 901.912 67.2442V95.8138H919.98C923.643 95.8138 925.841 98.0115 925.841 101.674V113.151C925.841 116.814 923.643 119.011 919.98 119.011H901.912V170.779C901.912 183.965 909.969 184.453 917.05 184.453H918.76C922.422 184.453 924.62 186.651 924.62 190.313V202.278C924.62 205.941 922.422 208.139 918.76 208.139H904.354Z" fill="#001F29"/>
<path d="M795.801 210.58C761.373 210.58 738.665 185.429 738.665 151.976C738.665 118.523 760.64 93.6157 794.58 93.6157C828.032 93.6157 848.542 118.523 848.542 151.976V154.418C848.542 158.08 846.344 160.278 842.682 160.278H767.966C770.896 175.418 781.883 184.452 795.801 184.452C807.033 184.452 814.114 180.546 818.753 176.394C821.439 173.953 823.881 173.464 826.811 175.662L838.287 184.208C841.461 186.65 842.193 189.092 839.996 191.778C829.985 203.499 814.358 210.58 795.801 210.58ZM768.454 140.988H819.974C817.288 127.802 808.986 117.79 794.58 117.79C780.662 117.79 771.384 126.825 768.454 140.988Z" fill="#001F29"/>
<path d="M660.68 256C642.855 256 627.717 249.895 617.217 240.127C614.287 237.441 614.776 234.511 617.706 231.825L628.693 222.546C631.379 220.104 634.065 220.349 636.995 222.546C644.809 228.895 653.355 230.36 660.68 230.36C671.179 230.36 690.224 224.5 690.224 200.325V196.174C682.655 204.721 671.912 210.093 657.75 210.093C626.984 210.093 604.765 185.43 604.765 151.977C604.765 118.523 626.984 94.105 657.75 94.105C671.912 94.105 682.655 99.2329 690.224 108.024V101.675C690.224 98.0119 692.422 95.8143 696.084 95.8143H714.886C718.548 95.8143 720.746 98.0119 720.746 101.675V200.325C720.746 232.558 699.503 256 660.68 256ZM662.145 181.279C678.993 181.279 690.224 168.826 690.224 151.977C690.224 135.128 678.993 122.675 662.145 122.675C645.297 122.675 634.309 135.128 634.309 151.977C634.309 168.826 645.297 181.279 662.145 181.279Z" fill="#001F29"/>
<path d="M574.449 208.139C551.986 208.139 541.486 196.418 541.486 175.662V119.011H532.696C529.033 119.011 526.836 116.814 526.836 113.151V101.674C526.836 98.0115 529.033 95.8138 532.696 95.8138H541.486V67.2442C541.486 63.5815 543.684 61.3838 547.346 61.3838H566.147C569.81 61.3838 572.008 63.5815 572.008 67.2442V95.8138H590.076C593.739 95.8138 595.936 98.0115 595.936 101.674V113.151C595.936 116.814 593.739 119.011 590.076 119.011H572.008V170.779C572.008 183.965 580.065 184.453 587.146 184.453H588.855C592.518 184.453 594.715 186.651 594.715 190.313V202.278C594.715 205.941 592.518 208.139 588.855 208.139H574.449Z" fill="#001F29"/>
<path d="M495.323 79.2091C487.022 79.2091 478.476 72.8603 478.476 62.1162C478.476 51.6162 487.022 46 495.323 46C503.381 46 512.171 51.6162 512.171 62.1162C512.171 72.8603 503.381 79.2091 495.323 79.2091ZM485.801 208.139C482.138 208.139 479.941 205.941 479.941 202.278V101.674C479.941 98.0114 482.138 95.8137 485.801 95.8137H504.846C508.509 95.8137 510.706 98.0114 510.706 101.674L510.462 202.278C510.462 205.941 508.264 208.139 504.602 208.139H485.801Z" fill="#001F29"/>
<path d="M341.86 208.139C338.198 208.139 336 205.941 336 202.279V55.2794C336 51.6166 338.198 49.4189 341.86 49.4189H409.984C434.401 49.4189 456.132 65.291 456.132 94.1048C456.132 109.244 450.272 119.5 441.238 125.849C453.446 132.686 461.992 144.651 461.992 162.232C461.992 191.046 438.796 208.139 414.379 208.139H341.86ZM367.498 113.64H404.856C416.332 113.64 423.657 106.802 423.657 95.8141C423.657 84.8258 416.332 78.2328 404.856 78.2328H367.498V113.64ZM367.498 179.325H409.251C421.948 179.325 429.517 170.535 429.517 159.546C429.517 148.558 421.46 139.767 409.251 139.767H367.498V179.325Z" fill="#001F29"/>
</g>
<defs>
<clipPath id="clip0_1542_2219">
<rect width="1623" height="256" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,23 @@
<svg width="1623" height="256" viewBox="0 0 1623 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1542_6419)">
<rect width="256" height="256" rx="60" fill="white"/>
<path d="M106.241 37.6137C95.9065 37.6084 85.5894 37.6031 75.2185 37.619C68.9476 37.619 66.5955 45.4918 71.2593 50.1551L141.857 120.753C144.06 122.768 145.434 124.959 145.485 127.846C145.434 130.733 144.06 132.923 141.857 134.939L71.2593 205.537C66.5955 210.2 68.9476 218.073 75.2185 218.073C85.5894 218.089 95.9065 218.083 106.241 218.078C111.416 218.075 116.594 218.073 121.787 218.073C128.579 218.073 132.62 214.444 136.249 210.815L199.919 147.145C205 142.063 209.547 135.316 209.485 127.846C209.547 120.375 205 113.628 199.919 108.547L136.249 44.8768C132.62 41.2479 128.579 37.619 121.787 37.619C116.594 37.619 111.416 37.6163 106.241 37.6137Z" fill="#001F29"/>
<path d="M1601.73 208.139C1579.27 208.139 1568.77 196.418 1568.77 175.662V119.011H1559.98C1556.31 119.011 1554.12 116.814 1554.12 113.151V101.674C1554.12 98.0115 1556.31 95.8138 1559.98 95.8138H1568.77V67.2442C1568.77 63.5815 1570.96 61.3838 1574.63 61.3838H1593.43C1597.09 61.3838 1599.29 63.5815 1599.29 67.2442V95.8138H1617.36C1621.02 95.8138 1623.22 98.0115 1623.22 101.674V113.151C1623.22 116.814 1621.02 119.011 1617.36 119.011H1599.29V170.779C1599.29 183.965 1607.35 184.453 1614.43 184.453H1616.14C1619.8 184.453 1622 186.651 1622 190.313V202.278C1622 205.941 1619.8 208.139 1616.14 208.139H1601.73Z" fill="white"/>
<path d="M1493.17 210.58C1458.75 210.58 1436.04 185.429 1436.04 151.976C1436.04 118.523 1458.01 93.6157 1491.95 93.6157C1525.4 93.6157 1545.91 118.523 1545.91 151.976V154.418C1545.91 158.08 1543.72 160.278 1540.05 160.278H1465.34C1468.27 175.418 1479.26 184.452 1493.17 184.452C1504.41 184.452 1511.49 180.546 1516.13 176.394C1518.81 173.953 1521.25 173.464 1524.18 175.662L1535.66 184.208C1538.83 186.65 1539.57 189.092 1537.37 191.778C1527.36 203.499 1511.73 210.58 1493.17 210.58ZM1465.83 140.988H1517.35C1514.66 127.802 1506.36 117.79 1491.95 117.79C1478.04 117.79 1468.76 126.825 1465.83 140.988Z" fill="white"/>
<path d="M1393.23 208.139C1389.56 208.139 1387.37 205.941 1387.37 202.279V55.2794C1387.37 51.6166 1389.56 49.4189 1393.23 49.4189H1412.27C1415.93 49.4189 1418.13 51.6166 1418.13 55.2794L1417.89 202.279C1417.89 205.941 1415.69 208.139 1412.03 208.139H1393.23Z" fill="white"/>
<path d="M1340.4 208.139C1336.74 208.139 1334.54 205.941 1334.54 202.279V55.2794C1334.54 51.6166 1336.74 49.4189 1340.4 49.4189H1359.45C1363.11 49.4189 1365.31 51.6166 1365.31 55.2794L1365.07 202.279C1365.07 205.941 1362.87 208.139 1359.21 208.139H1340.4Z" fill="white"/>
<path d="M1249.73 210.093C1218.96 210.093 1196.5 185.43 1196.5 151.977C1196.5 118.035 1218.96 94.105 1249.73 94.105C1263.89 94.105 1274.39 99.2329 1281.96 107.779V101.675C1281.96 98.0119 1284.16 95.8143 1287.82 95.8143H1306.62C1310.28 95.8143 1312.48 98.0119 1312.48 101.675V202.279C1312.48 205.942 1310.28 208.139 1306.62 208.139H1287.82C1284.16 208.139 1281.96 205.942 1281.96 202.279V196.418C1274.39 204.965 1263.89 210.093 1249.73 210.093ZM1254.12 181.279C1270.97 181.279 1281.96 168.826 1281.96 151.977C1281.96 135.128 1270.97 122.675 1254.12 122.675C1237.28 122.675 1226.04 135.128 1226.04 151.977C1226.04 168.826 1237.28 181.279 1254.12 181.279Z" fill="white"/>
<path d="M1037.19 208.139C1033.77 208.139 1031.33 206.43 1030.35 203.255L981.274 56.0119C980.053 52.105 982.006 49.4189 986.157 49.4189H1007.4C1010.82 49.4189 1013.26 51.1282 1014.24 54.3026L1046.22 150.511L1077.97 54.3026C1078.94 51.1282 1081.14 49.4189 1084.56 49.4189H1097.5C1100.92 49.4189 1103.36 51.1282 1104.34 54.3026L1135.35 146.605L1166.11 54.3026C1167.09 51.1282 1169.53 49.4189 1172.95 49.4189H1194.19C1198.34 49.4189 1200.29 52.105 1199.07 56.0119L1150 203.255C1149.02 206.43 1146.58 208.139 1143.16 208.139H1126.31C1122.89 208.139 1120.45 206.43 1119.47 203.255L1090.17 114.616L1060.87 203.255C1059.9 206.43 1057.45 208.139 1054.04 208.139H1037.19Z" fill="white"/>
<path d="M904.354 208.139C881.89 208.139 871.39 196.418 871.39 175.662V119.011H862.6C858.938 119.011 856.74 116.814 856.74 113.151V101.674C856.74 98.0115 858.938 95.8138 862.6 95.8138H871.39V67.2442C871.39 63.5815 873.588 61.3838 877.251 61.3838H896.052C899.714 61.3838 901.912 63.5815 901.912 67.2442V95.8138H919.98C923.643 95.8138 925.841 98.0115 925.841 101.674V113.151C925.841 116.814 923.643 119.011 919.98 119.011H901.912V170.779C901.912 183.965 909.969 184.453 917.05 184.453H918.76C922.422 184.453 924.62 186.651 924.62 190.313V202.278C924.62 205.941 922.422 208.139 918.76 208.139H904.354Z" fill="white"/>
<path d="M795.801 210.58C761.373 210.58 738.665 185.429 738.665 151.976C738.665 118.523 760.64 93.6157 794.58 93.6157C828.032 93.6157 848.542 118.523 848.542 151.976V154.418C848.542 158.08 846.344 160.278 842.682 160.278H767.966C770.896 175.418 781.883 184.452 795.801 184.452C807.033 184.452 814.114 180.546 818.753 176.394C821.439 173.953 823.881 173.464 826.811 175.662L838.287 184.208C841.461 186.65 842.193 189.092 839.996 191.778C829.985 203.499 814.358 210.58 795.801 210.58ZM768.454 140.988H819.974C817.288 127.802 808.986 117.79 794.58 117.79C780.662 117.79 771.384 126.825 768.454 140.988Z" fill="white"/>
<path d="M660.68 256C642.855 256 627.717 249.895 617.217 240.127C614.287 237.441 614.776 234.511 617.706 231.825L628.693 222.546C631.379 220.104 634.065 220.349 636.995 222.546C644.809 228.895 653.355 230.36 660.68 230.36C671.179 230.36 690.224 224.5 690.224 200.325V196.174C682.655 204.721 671.912 210.093 657.75 210.093C626.984 210.093 604.765 185.43 604.765 151.977C604.765 118.523 626.984 94.105 657.75 94.105C671.912 94.105 682.655 99.2329 690.224 108.024V101.675C690.224 98.0119 692.422 95.8143 696.084 95.8143H714.886C718.548 95.8143 720.746 98.0119 720.746 101.675V200.325C720.746 232.558 699.503 256 660.68 256ZM662.145 181.279C678.993 181.279 690.224 168.826 690.224 151.977C690.224 135.128 678.993 122.675 662.145 122.675C645.297 122.675 634.309 135.128 634.309 151.977C634.309 168.826 645.297 181.279 662.145 181.279Z" fill="white"/>
<path d="M574.449 208.139C551.986 208.139 541.486 196.418 541.486 175.662V119.011H532.696C529.033 119.011 526.836 116.814 526.836 113.151V101.674C526.836 98.0115 529.033 95.8138 532.696 95.8138H541.486V67.2442C541.486 63.5815 543.684 61.3838 547.346 61.3838H566.147C569.81 61.3838 572.008 63.5815 572.008 67.2442V95.8138H590.076C593.739 95.8138 595.936 98.0115 595.936 101.674V113.151C595.936 116.814 593.739 119.011 590.076 119.011H572.008V170.779C572.008 183.965 580.065 184.453 587.146 184.453H588.855C592.518 184.453 594.715 186.651 594.715 190.313V202.278C594.715 205.941 592.518 208.139 588.855 208.139H574.449Z" fill="white"/>
<path d="M495.323 79.2091C487.022 79.2091 478.476 72.8603 478.476 62.1162C478.476 51.6162 487.022 46 495.323 46C503.381 46 512.171 51.6162 512.171 62.1162C512.171 72.8603 503.381 79.2091 495.323 79.2091ZM485.801 208.139C482.138 208.139 479.941 205.941 479.941 202.278V101.674C479.941 98.0114 482.138 95.8137 485.801 95.8137H504.846C508.509 95.8137 510.706 98.0114 510.706 101.674L510.462 202.278C510.462 205.941 508.264 208.139 504.602 208.139H485.801Z" fill="white"/>
<path d="M341.86 208.139C338.198 208.139 336 205.941 336 202.279V55.2794C336 51.6166 338.198 49.4189 341.86 49.4189H409.984C434.401 49.4189 456.132 65.291 456.132 94.1048C456.132 109.244 450.272 119.5 441.238 125.849C453.446 132.686 461.992 144.651 461.992 162.232C461.992 191.046 438.796 208.139 414.379 208.139H341.86ZM367.498 113.64H404.856C416.332 113.64 423.657 106.802 423.657 95.8141C423.657 84.8258 416.332 78.2328 404.856 78.2328H367.498V113.64ZM367.498 179.325H409.251C421.948 179.325 429.517 170.535 429.517 159.546C429.517 148.558 421.46 139.767 409.251 139.767H367.498V179.325Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_1542_6419">
<rect width="1623" height="256" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -0,0 +1,23 @@
<svg width="1623" height="256" viewBox="0 0 1623 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1542_2219)">
<rect width="256" height="256" rx="60" fill="#001F29"/>
<path d="M106.241 37.6137C95.9065 37.6084 85.5894 37.6031 75.2185 37.619C68.9476 37.619 66.5955 45.4918 71.2593 50.1551L141.857 120.753C144.06 122.768 145.434 124.959 145.485 127.846C145.434 130.733 144.06 132.923 141.857 134.939L71.2593 205.537C66.5955 210.2 68.9476 218.073 75.2185 218.073C85.5894 218.089 95.9065 218.083 106.241 218.078C111.416 218.075 116.594 218.073 121.787 218.073C128.579 218.073 132.62 214.444 136.249 210.815L199.919 147.145C205 142.063 209.547 135.316 209.485 127.846C209.547 120.375 205 113.628 199.919 108.547L136.249 44.8768C132.62 41.2479 128.579 37.619 121.787 37.619C116.594 37.619 111.416 37.6163 106.241 37.6137Z" fill="#00F0FF"/>
<path d="M1601.73 208.139C1579.27 208.139 1568.77 196.418 1568.77 175.662V119.011H1559.98C1556.31 119.011 1554.12 116.814 1554.12 113.151V101.674C1554.12 98.0115 1556.31 95.8138 1559.98 95.8138H1568.77V67.2442C1568.77 63.5815 1570.96 61.3838 1574.63 61.3838H1593.43C1597.09 61.3838 1599.29 63.5815 1599.29 67.2442V95.8138H1617.36C1621.02 95.8138 1623.22 98.0115 1623.22 101.674V113.151C1623.22 116.814 1621.02 119.011 1617.36 119.011H1599.29V170.779C1599.29 183.965 1607.35 184.453 1614.43 184.453H1616.14C1619.8 184.453 1622 186.651 1622 190.313V202.278C1622 205.941 1619.8 208.139 1616.14 208.139H1601.73Z" fill="#001F29"/>
<path d="M1493.17 210.58C1458.75 210.58 1436.04 185.429 1436.04 151.976C1436.04 118.523 1458.01 93.6157 1491.95 93.6157C1525.4 93.6157 1545.91 118.523 1545.91 151.976V154.418C1545.91 158.08 1543.72 160.278 1540.05 160.278H1465.34C1468.27 175.418 1479.26 184.452 1493.17 184.452C1504.41 184.452 1511.49 180.546 1516.13 176.394C1518.81 173.953 1521.25 173.464 1524.18 175.662L1535.66 184.208C1538.83 186.65 1539.57 189.092 1537.37 191.778C1527.36 203.499 1511.73 210.58 1493.17 210.58ZM1465.83 140.988H1517.35C1514.66 127.802 1506.36 117.79 1491.95 117.79C1478.04 117.79 1468.76 126.825 1465.83 140.988Z" fill="#001F29"/>
<path d="M1393.23 208.139C1389.56 208.139 1387.37 205.941 1387.37 202.279V55.2794C1387.37 51.6166 1389.56 49.4189 1393.23 49.4189H1412.27C1415.93 49.4189 1418.13 51.6166 1418.13 55.2794L1417.89 202.279C1417.89 205.941 1415.69 208.139 1412.03 208.139H1393.23Z" fill="#001F29"/>
<path d="M1340.4 208.139C1336.74 208.139 1334.54 205.941 1334.54 202.279V55.2794C1334.54 51.6166 1336.74 49.4189 1340.4 49.4189H1359.45C1363.11 49.4189 1365.31 51.6166 1365.31 55.2794L1365.07 202.279C1365.07 205.941 1362.87 208.139 1359.21 208.139H1340.4Z" fill="#001F29"/>
<path d="M1249.73 210.093C1218.96 210.093 1196.5 185.43 1196.5 151.977C1196.5 118.035 1218.96 94.105 1249.73 94.105C1263.89 94.105 1274.39 99.2329 1281.96 107.779V101.675C1281.96 98.0119 1284.16 95.8143 1287.82 95.8143H1306.62C1310.28 95.8143 1312.48 98.0119 1312.48 101.675V202.279C1312.48 205.942 1310.28 208.139 1306.62 208.139H1287.82C1284.16 208.139 1281.96 205.942 1281.96 202.279V196.418C1274.39 204.965 1263.89 210.093 1249.73 210.093ZM1254.12 181.279C1270.97 181.279 1281.96 168.826 1281.96 151.977C1281.96 135.128 1270.97 122.675 1254.12 122.675C1237.28 122.675 1226.04 135.128 1226.04 151.977C1226.04 168.826 1237.28 181.279 1254.12 181.279Z" fill="#001F29"/>
<path d="M1037.19 208.139C1033.77 208.139 1031.33 206.43 1030.35 203.255L981.274 56.0119C980.053 52.105 982.006 49.4189 986.157 49.4189H1007.4C1010.82 49.4189 1013.26 51.1282 1014.24 54.3026L1046.22 150.511L1077.97 54.3026C1078.94 51.1282 1081.14 49.4189 1084.56 49.4189H1097.5C1100.92 49.4189 1103.36 51.1282 1104.34 54.3026L1135.35 146.605L1166.11 54.3026C1167.09 51.1282 1169.53 49.4189 1172.95 49.4189H1194.19C1198.34 49.4189 1200.29 52.105 1199.07 56.0119L1150 203.255C1149.02 206.43 1146.58 208.139 1143.16 208.139H1126.31C1122.89 208.139 1120.45 206.43 1119.47 203.255L1090.17 114.616L1060.87 203.255C1059.9 206.43 1057.45 208.139 1054.04 208.139H1037.19Z" fill="#001F29"/>
<path d="M904.354 208.139C881.89 208.139 871.39 196.418 871.39 175.662V119.011H862.6C858.938 119.011 856.74 116.814 856.74 113.151V101.674C856.74 98.0115 858.938 95.8138 862.6 95.8138H871.39V67.2442C871.39 63.5815 873.588 61.3838 877.251 61.3838H896.052C899.714 61.3838 901.912 63.5815 901.912 67.2442V95.8138H919.98C923.643 95.8138 925.841 98.0115 925.841 101.674V113.151C925.841 116.814 923.643 119.011 919.98 119.011H901.912V170.779C901.912 183.965 909.969 184.453 917.05 184.453H918.76C922.422 184.453 924.62 186.651 924.62 190.313V202.278C924.62 205.941 922.422 208.139 918.76 208.139H904.354Z" fill="#001F29"/>
<path d="M795.801 210.58C761.373 210.58 738.665 185.429 738.665 151.976C738.665 118.523 760.64 93.6157 794.58 93.6157C828.032 93.6157 848.542 118.523 848.542 151.976V154.418C848.542 158.08 846.344 160.278 842.682 160.278H767.966C770.896 175.418 781.883 184.452 795.801 184.452C807.033 184.452 814.114 180.546 818.753 176.394C821.439 173.953 823.881 173.464 826.811 175.662L838.287 184.208C841.461 186.65 842.193 189.092 839.996 191.778C829.985 203.499 814.358 210.58 795.801 210.58ZM768.454 140.988H819.974C817.288 127.802 808.986 117.79 794.58 117.79C780.662 117.79 771.384 126.825 768.454 140.988Z" fill="#001F29"/>
<path d="M660.68 256C642.855 256 627.717 249.895 617.217 240.127C614.287 237.441 614.776 234.511 617.706 231.825L628.693 222.546C631.379 220.104 634.065 220.349 636.995 222.546C644.809 228.895 653.355 230.36 660.68 230.36C671.179 230.36 690.224 224.5 690.224 200.325V196.174C682.655 204.721 671.912 210.093 657.75 210.093C626.984 210.093 604.765 185.43 604.765 151.977C604.765 118.523 626.984 94.105 657.75 94.105C671.912 94.105 682.655 99.2329 690.224 108.024V101.675C690.224 98.0119 692.422 95.8143 696.084 95.8143H714.886C718.548 95.8143 720.746 98.0119 720.746 101.675V200.325C720.746 232.558 699.503 256 660.68 256ZM662.145 181.279C678.993 181.279 690.224 168.826 690.224 151.977C690.224 135.128 678.993 122.675 662.145 122.675C645.297 122.675 634.309 135.128 634.309 151.977C634.309 168.826 645.297 181.279 662.145 181.279Z" fill="#001F29"/>
<path d="M574.449 208.139C551.986 208.139 541.486 196.418 541.486 175.662V119.011H532.696C529.033 119.011 526.836 116.814 526.836 113.151V101.674C526.836 98.0115 529.033 95.8138 532.696 95.8138H541.486V67.2442C541.486 63.5815 543.684 61.3838 547.346 61.3838H566.147C569.81 61.3838 572.008 63.5815 572.008 67.2442V95.8138H590.076C593.739 95.8138 595.936 98.0115 595.936 101.674V113.151C595.936 116.814 593.739 119.011 590.076 119.011H572.008V170.779C572.008 183.965 580.065 184.453 587.146 184.453H588.855C592.518 184.453 594.715 186.651 594.715 190.313V202.278C594.715 205.941 592.518 208.139 588.855 208.139H574.449Z" fill="#001F29"/>
<path d="M495.323 79.2091C487.022 79.2091 478.476 72.8603 478.476 62.1162C478.476 51.6162 487.022 46 495.323 46C503.381 46 512.171 51.6162 512.171 62.1162C512.171 72.8603 503.381 79.2091 495.323 79.2091ZM485.801 208.139C482.138 208.139 479.941 205.941 479.941 202.278V101.674C479.941 98.0114 482.138 95.8137 485.801 95.8137H504.846C508.509 95.8137 510.706 98.0114 510.706 101.674L510.462 202.278C510.462 205.941 508.264 208.139 504.602 208.139H485.801Z" fill="#001F29"/>
<path d="M341.86 208.139C338.198 208.139 336 205.941 336 202.279V55.2794C336 51.6166 338.198 49.4189 341.86 49.4189H409.984C434.401 49.4189 456.132 65.291 456.132 94.1048C456.132 109.244 450.272 119.5 441.238 125.849C453.446 132.686 461.992 144.651 461.992 162.232C461.992 191.046 438.796 208.139 414.379 208.139H341.86ZM367.498 113.64H404.856C416.332 113.64 423.657 106.802 423.657 95.8141C423.657 84.8258 416.332 78.2328 404.856 78.2328H367.498V113.64ZM367.498 179.325H409.251C421.948 179.325 429.517 170.535 429.517 159.546C429.517 148.558 421.46 139.767 409.251 139.767H367.498V179.325Z" fill="#001F29"/>
</g>
<defs>
<clipPath id="clip0_1542_2219">
<rect width="1623" height="256" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -172,7 +172,7 @@
}
#wallets {
@each $wallet in "ledger", "secalot", "trezor", "xumm", "trust", "gatehub", "bifrost", "keystone", "dcent", "coin", "gem", "joey" {
@each $wallet in "ledger", "secalot", "trezor", "xumm", "trust", "gatehub", "bifrost", "bitget", "keystone", "dcent", "coin", "gem", "joey" {
#wallet-#{$wallet} {
content: url("../img/wallets/#{$wallet}.svg");
}
@@ -405,6 +405,7 @@
"multichain": "svg",
"xumm-wallet": "svg",
"bifrost-wallet": "svg",
"bitget-wallet": "svg",
"gem-wallet": "svg",
"aesthetes": "svg",
"audiotarky": "svg",

View File

@@ -4,7 +4,7 @@
$infrastructure-logos: "xrp-ledger", "gatehub", "towolabs", "xrpscan", "xrp-toolkit", "bithomp", "onthedex";
$developer-tooling-logos: "cryptum", "evernode", "threezy", "tokenize";
$interoperability-logos: "multichain";
$wallet-logos: "crossmark", "edge", "gem-wallet", "xumm", "joey-wallet", "bifrost-wallet";
$wallet-logos: "crossmark", "edge", "gem-wallet", "xumm", "joey-wallet", "bifrost-wallet", "bitget-wallet";
$nfts-logos: "aesthetes", "audiotarky", "nftmaster", "peerkat", "sologenic_dex", "xrp-cafe", "xrp-oval";
$exchanges-logos: "sologenic_dex", "xpmarket", "orchestra-finance", "moai-finance", "first-ledger-bot";
$gaming-logos: "forte", "ledger-city", "futureverse", "zerpmon";

View File

@@ -573,6 +573,10 @@ pre {
#wallet-joey {
content: url("../img/wallets/lightmode/joey.svg");
}
#wallet-bitget {
content: url("../img/wallets/lightmode/bitget.svg");
}
}
@@ -669,6 +673,7 @@ pre {
'ripples-on-demand-liquidity': 'svg',
'xumm-wallet': 'svg',
'bifrost-wallet': 'svg',
'bitget-wallet': 'svg',
'sologenic-dex': 'svg',
'joey-wallet': 'svg',
'Crossmark': 'png'