mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2026-02-23 07:12:25 +00:00
Compare commits
1 Commits
tutorials-
...
new-delete
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5bf019608 |
@@ -577,16 +577,6 @@ Stay ahead of the curve with the latest developments in RWA tokenization on the
|
||||
Join the Developer Discord: 開発者ディスコードに参加
|
||||
Sign up for the Newsletter: ニュースレターに登録
|
||||
|
||||
# docs/tutorials/index.page.tsx
|
||||
Crypto Wallet and Blockchain Development Tutorials: 暗号資産ウォレットやブロックチェーン開発のチュートリアル
|
||||
These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.: これらのチュートリアルでは、お好きなプログラミング言語を使って、XRP Ledgerに接続するアプリケーションを構築するための基礎について説明します。
|
||||
Get Started with SDKs: SDKを使って始める
|
||||
Using the xrpl.js client library.: クライアントライブラリxrpl.jsを使用
|
||||
Using xrpl.py, a pure Python library.: Pythonライブラリxrpl.pyを使用
|
||||
Using xrpl4j, a pure Java library.: Javaライブラリを使用
|
||||
Using the XRPL_PHP client library.: PHPライブラリXRPL_PHPを使用
|
||||
Access the XRP Ledger directly through the APIs of its core server.: コアサーバのAPIを通じてXRP Ledgerに直接アクセス
|
||||
|
||||
# community/index.page.tsx
|
||||
XRPL Community: XRPLコミュニティ
|
||||
community.index.h1part1: 開発者とイノベーターによる
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { indexPages } from './plugins/index-pages.js';
|
||||
import { codeSamples } from './plugins/code-samples.js';
|
||||
import { blogPosts } from './plugins/blog-posts.js';
|
||||
import { tutorialLanguages } from './plugins/tutorial-languages.js';
|
||||
|
||||
export default function customPlugin() {
|
||||
const indexPagesInst = indexPages();
|
||||
const codeSamplesInst = codeSamples();
|
||||
const blogPostsInst = blogPosts();
|
||||
const tutorialLanguagesInst = tutorialLanguages();
|
||||
|
||||
|
||||
|
||||
/** @type {import("@redocly/realm/dist/server/plugins/types").PluginInstance } */
|
||||
const pluginInstance = {
|
||||
@@ -16,7 +16,6 @@ export default function customPlugin() {
|
||||
await indexPagesInst.processContent?.(content, actions);
|
||||
await codeSamplesInst.processContent?.(content, actions);
|
||||
await blogPostsInst.processContent?.(content, actions);
|
||||
await tutorialLanguagesInst.processContent?.(content, actions);
|
||||
},
|
||||
afterRoutesCreated: async (content, actions) => {
|
||||
await indexPagesInst.afterRoutesCreated?.(content, actions);
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* Plugin to detect languages supported in tutorial pages by scanning for tab labels.
|
||||
* This creates shared data that maps tutorial paths to their supported languages.
|
||||
*/
|
||||
export function tutorialLanguages() {
|
||||
/** @type {import("@redocly/realm/dist/server/plugins/types").PluginInstance } */
|
||||
const instance = {
|
||||
processContent: async (actions, { fs, cache }) => {
|
||||
try {
|
||||
/** @type {Record<string, string[]>} */
|
||||
const tutorialLanguagesMap = {};
|
||||
const allFiles = await fs.scan();
|
||||
|
||||
// Find all markdown files in tutorials directory
|
||||
const tutorialFiles = allFiles.filter((file) =>
|
||||
file.relativePath.match(/^docs[\/\\]tutorials[\/\\].*\.md$/)
|
||||
);
|
||||
|
||||
for (const { relativePath } of tutorialFiles) {
|
||||
try {
|
||||
const { data } = await cache.load(relativePath, 'markdown-ast');
|
||||
const languages = extractLanguagesFromAst(data.ast);
|
||||
|
||||
if (languages.length > 0) {
|
||||
// Convert file path to URL path
|
||||
const urlPath = '/' + relativePath
|
||||
.replace(/\.md$/, '/')
|
||||
.replace(/\\/g, '/');
|
||||
tutorialLanguagesMap[urlPath] = languages;
|
||||
}
|
||||
} catch {
|
||||
continue; // Skip files that can't be parsed.
|
||||
}
|
||||
}
|
||||
|
||||
actions.createSharedData('tutorial-languages', tutorialLanguagesMap);
|
||||
actions.addRouteSharedData('/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
|
||||
actions.addRouteSharedData('/ja/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
|
||||
actions.addRouteSharedData('/es-es/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract language names from tab labels in the markdown AST
|
||||
*/
|
||||
function extractLanguagesFromAst(ast) {
|
||||
const languages = new Set();
|
||||
|
||||
visit(ast, (node) => {
|
||||
// Look for tab nodes with a label attribute
|
||||
if (isNode(node) && node.type === 'tag' && node.tag === 'tab') {
|
||||
const label = node.attributes?.label;
|
||||
if (label) {
|
||||
const normalizedLang = normalizeLanguage(label);
|
||||
if (normalizedLang) {
|
||||
languages.add(normalizedLang);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(languages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize language label to match our icon keys
|
||||
*/
|
||||
function normalizeLanguage(label) {
|
||||
const labelLower = label.toLowerCase();
|
||||
|
||||
if (labelLower.includes('javascript') || labelLower === 'js') {
|
||||
return 'javascript';
|
||||
}
|
||||
if (labelLower.includes('python') || labelLower === 'py') {
|
||||
return 'python';
|
||||
}
|
||||
if (labelLower.includes('java') && !labelLower.includes('javascript')) {
|
||||
return 'java';
|
||||
}
|
||||
if (labelLower.includes('php')) {
|
||||
return 'php';
|
||||
}
|
||||
if (labelLower.includes('go') || labelLower === 'golang') {
|
||||
return 'go';
|
||||
}
|
||||
if (labelLower.includes('http') || labelLower.includes('websocket')) {
|
||||
return 'http';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function isNode(value) {
|
||||
return !!(value?.$$mdtype === 'Node');
|
||||
}
|
||||
|
||||
function visit(node, visitor) {
|
||||
if (!node) return;
|
||||
|
||||
visitor(node);
|
||||
|
||||
if (node.children) {
|
||||
for (const child of node.children) {
|
||||
if (!child || typeof child === 'string') {
|
||||
continue;
|
||||
}
|
||||
visit(child, visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
_code-samples/delete-account/README.md
Normal file
3
_code-samples/delete-account/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Delete Account
|
||||
|
||||
Delete an account from the XRP Ledger, removing its data and sending its XRP to another account.
|
||||
3
_code-samples/delete-account/js/README.md
Normal file
3
_code-samples/delete-account/js/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Delete Account (JavaScript)
|
||||
|
||||
JavaScript sample code showing how to delete an account from the XRP Ledger.
|
||||
187
_code-samples/delete-account/js/delete-account.js
Normal file
187
_code-samples/delete-account/js/delete-account.js
Normal file
@@ -0,0 +1,187 @@
|
||||
import { Client, getBalanceChanges, validate } from "xrpl"
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
await client.connect()
|
||||
|
||||
// Where to send the deleted account's remaining XRP:
|
||||
const DESTINATION_ACCOUNT = 'rJjHYTCPpNA3qAM8ZpCDtip3a8xg7B8PFo' // Testnet faucet
|
||||
|
||||
/* Alternative to load an existing account from a seed in a .env file ----------
|
||||
// To use: do `npm install dotenv` and uncomment this section, but comment out
|
||||
// the "Get an account from the faucet..." section below.
|
||||
// Also edit example.env with your account seed and rename it to .env
|
||||
// Change the algorithm to secp256k1 if the seed is that type.
|
||||
// (If the generated address isn't what you expect, this is probably the case.)
|
||||
|
||||
import 'dotenv/config'
|
||||
import { Wallet } from "xrpl"
|
||||
const wallet = Wallet.fromSeed(process.env.ACCOUNT_SEED, { algorithm: 'ed25519' })
|
||||
console.log(`Using existing wallet:
|
||||
Address: ${wallet.address}
|
||||
Seed: ${wallet.seed}
|
||||
`)
|
||||
*/
|
||||
|
||||
// Get an account from the faucet and wait for it to be old enough to delete ---
|
||||
const { wallet } = await client.fundWallet()
|
||||
console.log(`Got new account from faucet:
|
||||
Address: ${wallet.address}
|
||||
Seed: ${wallet.seed}
|
||||
`)
|
||||
|
||||
// Wait ~20 minutes for the current ledger index to get 256+ larger than the
|
||||
// account's sequence number.
|
||||
const delay = 60 * 20
|
||||
console.log(`Waiting ${delay} seconds for the account to be old enough to delete.`)
|
||||
await sleep(delay)
|
||||
|
||||
// Sleep function that can be used with await
|
||||
function sleep (delayInSeconds) {
|
||||
const delayInMs = delayInSeconds * 1000
|
||||
return new Promise((resolve) => setTimeout(resolve, delayInMs))
|
||||
}
|
||||
|
||||
// Check account info to see if account can be deleted -------------------------
|
||||
let acctInfoResp
|
||||
try {
|
||||
acctInfoResp = await client.request({
|
||||
command: 'account_info',
|
||||
account: wallet.address,
|
||||
ledger_index: 'validated'
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('account_info failed with error:', err)
|
||||
client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
let numProblems = 0
|
||||
|
||||
// Check if sequence number is too high
|
||||
const acctSeq = acctInfoResp.result.account_data.Sequence
|
||||
const lastValidatedLedgerIndex = acctInfoResp.result.ledger_index
|
||||
if (acctSeq + 256 >= lastValidatedLedgerIndex) {
|
||||
console.error(`Account is too new to be deleted.
|
||||
Account sequence: ${acctSeq}
|
||||
Validated ledger index: ${lastValidatedLedgerIndex}
|
||||
(Sequence + 256 must be less than ledger index)`)
|
||||
numProblems += 1
|
||||
} else {
|
||||
console.log(`OK: Account sequence number (${acctSeq}) is low enough.`)
|
||||
}
|
||||
|
||||
// Check if owner count is too high
|
||||
const ownerCount = acctInfoResp.result.account_data.OwnerCount
|
||||
if (ownerCount > 1000) {
|
||||
console.error(`Account owns too many objects in the ledger.
|
||||
Owner count: ${ownerCount}
|
||||
(Must be less than 1000)`)
|
||||
numProblems += 1
|
||||
} else {
|
||||
console.log(`OK: Account owner count (${ownerCount}) is low enough.`)
|
||||
}
|
||||
|
||||
// Check if XRP balance is high enough
|
||||
// Look up current incremental owner reserve to compare vs account's XRP balance
|
||||
// using server_state so that both are in drops
|
||||
let serverStateResp
|
||||
try {
|
||||
serverStateResp = await client.request({
|
||||
command: 'server_state',
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('server_state failed with error:', err)
|
||||
client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
const deletionCost = serverStateResp.result.state.validated_ledger?.reserve_inc
|
||||
if (!deletionCost) {
|
||||
console.error("Couldn't get reserve values from server. "+
|
||||
"Maybe it's not synced to the network?")
|
||||
console.log(JSON.stringify(serverStateResp.result, null, 2))//TODO:remove
|
||||
client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const acctBalance = acctInfoResp.result.account_data.Balance
|
||||
if (acctBalance < deletionCost) {
|
||||
console.error(`Account does not have enough XRP to pay the cost of deletion.
|
||||
Balance: ${acctBalance}
|
||||
Cost of account deletion: ${deletionCost}`)
|
||||
numProblems += 1
|
||||
} else {
|
||||
console.log(`OK: Account balance (${acctBalance} drops) is high enough.`)
|
||||
}
|
||||
|
||||
if (numProblems) {
|
||||
console.error(`A total of ${numProblems} problem(s) prevent the account from being deleted.`)
|
||||
client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Check for deletion blockers -------------------------------------------------
|
||||
let blockers = []
|
||||
let marker
|
||||
let ledger_index = 'validated'
|
||||
while (true) {
|
||||
let accountObjResp
|
||||
try {
|
||||
accountObjResp = await client.request({
|
||||
command: 'account_objects',
|
||||
account: wallet.address,
|
||||
deletion_blockers_only: true,
|
||||
ledger_index,
|
||||
marker
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('account_objects failed with error:', err)
|
||||
client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
for (const obj of accountObjResp.result.account_objects) {
|
||||
blockers.push(obj)
|
||||
}
|
||||
if (accountObjResp.result.marker) {
|
||||
marker = accountObjResp.result.marker
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!blockers.length) {
|
||||
console.log('OK: Account has no deletion blockers.')
|
||||
} else {
|
||||
console.log(`Account cannot be deleted until ${blockers.length} blocker(s) are removed:`)
|
||||
for (const blocker of blockers) {
|
||||
console.log(JSON.stringify(blocker, null, 2))
|
||||
}
|
||||
client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Delete the account ----------------------------------------------------------
|
||||
const accountDeleteTx = {
|
||||
TransactionType: 'AccountDelete',
|
||||
Account: wallet.address,
|
||||
Destination: DESTINATION_ACCOUNT
|
||||
}
|
||||
validate(accountDeleteTx)
|
||||
|
||||
console.log('Signing and submitting the AccountDelete transaction:',
|
||||
JSON.stringify(accountDeleteTx, null, 2))
|
||||
const deleteTxResponse = await client.submitAndWait(accountDeleteTx, { wallet, autofill: true, failHard: true })
|
||||
|
||||
// Check result of the AccountDelete transaction -------------------------------
|
||||
console.log(JSON.stringify(deleteTxResponse.result, null, 2))
|
||||
const resultCode = deleteTxResponse.result.meta.TransactionResult
|
||||
if (resultCode !== 'tesSUCCESS') {
|
||||
console.error(`AccountDelete failed with code ${resultCode}.`)
|
||||
client.disconnect()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log('Account deleted successfully.')
|
||||
const balanceChanges = getBalanceChanges(deleteTxResponse.result.meta)
|
||||
console.log('Balance changes:', JSON.stringify(balanceChanges, null, 2))
|
||||
|
||||
client.disconnect()
|
||||
4
_code-samples/delete-account/js/example.env
Normal file
4
_code-samples/delete-account/js/example.env
Normal file
@@ -0,0 +1,4 @@
|
||||
# Example env file used when loading an existing account. Ignore if getting
|
||||
# a new account to delete from the faucet.
|
||||
# Replace the seed with an appropriate value, then rename to .env
|
||||
ACCOUNT_SEED=s████████████████████████████
|
||||
10
_code-samples/delete-account/js/package.json
Normal file
10
_code-samples/delete-account/js/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "delete-account",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dotenv": "^17.3.1",
|
||||
"xrpl": "^4.6.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
29
docs/tutorials/index.md
Normal file
29
docs/tutorials/index.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
seo:
|
||||
description: Learn how to get started building on the XRP Ledger with these helpful crypto wallet and blockchain tutorials for developers.
|
||||
---
|
||||
# Crypto Wallet and Blockchain Development Tutorials
|
||||
|
||||
The XRP Ledger tutorials walk you through the steps to learn and get started with the XRP Ledger and to use the ledger for advanced use cases.
|
||||
|
||||
## Get Started with SDKs
|
||||
|
||||
These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.
|
||||
|
||||
{% card-grid %}
|
||||
|
||||
{% xrpl-card title="Javascript" body="Using the xrpl.js client library." href="/docs/tutorials/get-started/get-started-javascript/" image="/img/logos/javascript.svg" imageAlt="Javascript logo" /%}
|
||||
|
||||
{% xrpl-card title="Python" body="Using xrpl.py, a pure Python library." href="/docs/tutorials/get-started/get-started-python/" image="/img/logos/python.svg" imageAlt="Python logo" /%}
|
||||
|
||||
{% xrpl-card title="Go" body="Using xrpl-go, a pure Go library." href="/docs/tutorials/get-started/get-started-go/" image="/img/logos/golang.svg" imageAlt="Go logo" /%}
|
||||
|
||||
{% xrpl-card title="Java" body="Using xrpl4j, a pure Java library." href="/docs/tutorials/get-started/get-started-java/" image="/img/logos/java.svg" imageAlt="Java logo" /%}
|
||||
|
||||
{% xrpl-card title="PHP" body="Using the XRPL_PHP client library." href="/docs/tutorials/get-started/get-started-php/" image="/img/logos/php.svg" imageAlt="PHP logo" /%}
|
||||
|
||||
{% xrpl-card title="HTTP & WebSocket APIs" body="Access the XRP Ledger directly through the APIs of its core server." href="/docs/tutorials/get-started/get-started-http-websocket-apis/" image="/img/logos/globe.svg" imageAlt="globe icon" /%}
|
||||
|
||||
{% /card-grid %}
|
||||
|
||||
<!-- TODO: Add new sections for the new tutorial categories -->
|
||||
@@ -1,353 +0,0 @@
|
||||
import { useThemeHooks } from "@redocly/theme/core/hooks";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: "Tutorials",
|
||||
description:
|
||||
"Learn how to get started building on the XRP Ledger with these helpful crypto wallet and blockchain tutorials for developers.",
|
||||
},
|
||||
};
|
||||
|
||||
const langIcons: Record<string, { src: string; alt: string }> = {
|
||||
javascript: { src: "/img/logos/javascript.svg", alt: "JavaScript" },
|
||||
python: { src: "/img/logos/python.svg", alt: "Python" },
|
||||
java: { src: "/img/logos/java.svg", alt: "Java" },
|
||||
go: { src: "/img/logos/golang.svg", alt: "Go" },
|
||||
php: { src: "/img/logos/php.svg", alt: "PHP" },
|
||||
http: { src: "/img/logos/globe.svg", alt: "HTTP / WebSocket" },
|
||||
xrpl: { src: "/img/logos/xrp-mark.svg", alt: "XRP Ledger" },
|
||||
};
|
||||
|
||||
// Type for the tutorial languages map from the plugin
|
||||
type TutorialLanguagesMap = Record<string, string[]>;
|
||||
|
||||
interface Tutorial {
|
||||
title: string;
|
||||
body?: string;
|
||||
path: string;
|
||||
icon?: string; // Single language icon (for single-language tutorials)
|
||||
}
|
||||
|
||||
interface TutorialSection {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
tutorials: Tutorial[];
|
||||
}
|
||||
|
||||
// Get Started tutorials -----------------
|
||||
const getStartedTutorials: Tutorial[] = [
|
||||
{
|
||||
title: "JavaScript",
|
||||
body: "Using the xrpl.js client library.",
|
||||
path: "/docs/tutorials/get-started/get-started-javascript/",
|
||||
icon: "javascript",
|
||||
},
|
||||
{
|
||||
title: "Python",
|
||||
body: "Using xrpl.py, a pure Python library.",
|
||||
path: "/docs/tutorials/get-started/get-started-python/",
|
||||
icon: "python",
|
||||
},
|
||||
{
|
||||
title: "Go",
|
||||
body: "Using xrpl-go, a pure Go library.",
|
||||
path: "/docs/tutorials/get-started/get-started-go/",
|
||||
icon: "go",
|
||||
},
|
||||
{
|
||||
title: "Java",
|
||||
body: "Using xrpl4j, a pure Java library.",
|
||||
path: "/docs/tutorials/get-started/get-started-java/",
|
||||
icon: "java",
|
||||
},
|
||||
{
|
||||
title: "PHP",
|
||||
body: "Using the XRPL_PHP client library.",
|
||||
path: "/docs/tutorials/get-started/get-started-php/",
|
||||
icon: "php",
|
||||
},
|
||||
{
|
||||
title: "HTTP & WebSocket APIs",
|
||||
body: "Access the XRP Ledger directly through the APIs of its core server.",
|
||||
path: "/docs/tutorials/get-started/get-started-http-websocket-apis/",
|
||||
icon: "http",
|
||||
},
|
||||
];
|
||||
|
||||
// Other tutorial sections -----------------
|
||||
// Languages are auto-detected from the markdown files by the tutorial-languages plugin.
|
||||
// Only specify `icon` for single-language tutorials without tabs.
|
||||
const sections: TutorialSection[] = [
|
||||
{
|
||||
id: "tokens",
|
||||
title: "Tokens",
|
||||
description: "Create and manage tokens on the XRP Ledger.",
|
||||
tutorials: [
|
||||
{
|
||||
title: "Issue a Multi-Purpose Token",
|
||||
body: "Issue new tokens using the v2 fungible token standard.",
|
||||
path: "/docs/tutorials/tokens/mpts/issue-a-multi-purpose-token/",
|
||||
},
|
||||
{
|
||||
title: "Issue a Fungible Token",
|
||||
body: "Issue new tokens using the v1 fungible token standard.",
|
||||
path: "/docs/tutorials/tokens/fungible-tokens/issue-a-fungible-token/",
|
||||
},
|
||||
{
|
||||
title: "Mint and Burn NFTs Using JavaScript",
|
||||
body: "Create new NFTs, retrieve existing tokens, and burn the ones you no longer need.",
|
||||
path: "/docs/tutorials/tokens/nfts/mint-and-burn-nfts-js/",
|
||||
icon: "javascript",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "payments",
|
||||
title: "Payments",
|
||||
description: "Transfer XRP and issued currencies using various payment types.",
|
||||
tutorials: [
|
||||
{
|
||||
title: "Send XRP",
|
||||
body: "Send a direct XRP payment to another account.",
|
||||
path: "/docs/tutorials/payments/send-xrp/",
|
||||
},
|
||||
{
|
||||
title: "Sending MPTs in JavaScript",
|
||||
body: "Send a Multi-Purpose Token (MPT) to another account with the JavaScript SDK.",
|
||||
path: "/docs/tutorials/tokens/mpts/sending-mpts-in-javascript/",
|
||||
icon: "javascript",
|
||||
},
|
||||
{
|
||||
title: "Create Trust Line and Send Currency in JavaScript",
|
||||
body: "Set up trust lines and send issued currencies with the JavaScript SDK.",
|
||||
path: "/docs/tutorials/payments/create-trust-line-send-currency-in-javascript/",
|
||||
icon: "javascript",
|
||||
},
|
||||
{
|
||||
title: "Create Trust Line and Send Currency in Python",
|
||||
body: "Set up trust lines and send issued currencies with the Python SDK.",
|
||||
path: "/docs/tutorials/payments/create-trust-line-send-currency-in-python/",
|
||||
icon: "python",
|
||||
},
|
||||
{
|
||||
title: "Send a Conditional Escrow",
|
||||
body: "Send an escrow that can be released when a specific crypto-condition is fulfilled.",
|
||||
path: "/docs/tutorials/payments/send-a-conditional-escrow/",
|
||||
},
|
||||
{
|
||||
title: "Send a Timed Escrow",
|
||||
body: "Send an escrow whose only condition for release is that a specific time has passed.",
|
||||
path: "/docs/tutorials/payments/send-a-timed-escrow/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "defi",
|
||||
title: "DeFi",
|
||||
description: "Trade, provide liquidity, and lend using native XRP Ledger DeFi features.",
|
||||
tutorials: [
|
||||
{
|
||||
title: "Create an Automated Market Maker",
|
||||
body: "Set up an AMM for a token pair and provide liquidity.",
|
||||
path: "/docs/tutorials/defi/dex/create-an-automated-market-maker/",
|
||||
},
|
||||
{
|
||||
title: "Trade in the Decentralized Exchange",
|
||||
body: "Buy and sell tokens in the Decentralized Exchange (DEX).",
|
||||
path: "/docs/tutorials/defi/dex/trade-in-the-decentralized-exchange/",
|
||||
},
|
||||
{
|
||||
title: "Create a Loan Broker",
|
||||
body: "Set up a loan broker to create and manage loans.",
|
||||
path: "/docs/tutorials/defi/lending/use-the-lending-protocol/create-a-loan-broker/",
|
||||
},
|
||||
{
|
||||
title: "Create a Loan",
|
||||
body: "Create a loan on the XRP Ledger.",
|
||||
path: "/docs/tutorials/defi/lending/use-the-lending-protocol/create-a-loan/",
|
||||
},
|
||||
{
|
||||
title: "Create a Single Asset Vault",
|
||||
body: "Create a single asset vault on the XRP Ledger.",
|
||||
path: "/docs/tutorials/defi/lending/use-single-asset-vaults/create-a-single-asset-vault/",
|
||||
},
|
||||
{
|
||||
title: "Deposit into a Vault",
|
||||
body: "Deposit assets into a vault and receive shares.",
|
||||
path: "/docs/tutorials/defi/lending/use-single-asset-vaults/deposit-into-a-vault/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "best-practices",
|
||||
title: "Best Practices",
|
||||
description: "Learn recommended patterns for building reliable, secure applications on the XRP Ledger.",
|
||||
tutorials: [
|
||||
{
|
||||
title: "API Usage",
|
||||
body: "Best practices for using XRP Ledger APIs.",
|
||||
path: "/docs/tutorials/best-practices/api-usage/",
|
||||
},
|
||||
{
|
||||
title: "Use Tickets",
|
||||
body: "Use tickets to send transactions out of the normal order.",
|
||||
path: "/docs/tutorials/best-practices/transaction-sending/use-tickets/",
|
||||
},
|
||||
{
|
||||
title: "Send a Single Account Batch Transaction",
|
||||
body: "Group multiple transactions together and execute them as a single atomic operation.",
|
||||
path: "/docs/tutorials/best-practices/transaction-sending/send-a-single-account-batch-transaction/",
|
||||
},
|
||||
{
|
||||
title: "Assign a Regular Key Pair",
|
||||
body: "Assign a regular key pair for signing transactions.",
|
||||
path: "/docs/tutorials/best-practices/key-management/assign-a-regular-key-pair/",
|
||||
},
|
||||
{
|
||||
title: "Set Up Multi-Signing",
|
||||
body: "Configure multi-signing for enhanced security.",
|
||||
path: "/docs/tutorials/best-practices/key-management/set-up-multi-signing/",
|
||||
},
|
||||
{
|
||||
title: "Send a Multi-Signed Transaction",
|
||||
body: "Send a transaction with multiple signatures.",
|
||||
path: "/docs/tutorials/best-practices/key-management/send-a-multi-signed-transaction/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "sample-apps",
|
||||
title: "Sample Apps",
|
||||
description: "Build complete, end-to-end applications like wallets and credential services.",
|
||||
tutorials: [
|
||||
{
|
||||
title: "Build a Browser Wallet in JavaScript",
|
||||
body: "Build a browser wallet for the XRP Ledger using JavaScript and various libraries.",
|
||||
path: "/docs/tutorials/sample-apps/build-a-browser-wallet-in-javascript/",
|
||||
icon: "javascript",
|
||||
},
|
||||
{
|
||||
title: "Build a Desktop Wallet in JavaScript",
|
||||
body: "Build a desktop wallet for the XRP Ledger using JavaScript, the Electron Framework, and various libraries.",
|
||||
path: "/docs/tutorials/sample-apps/build-a-desktop-wallet-in-javascript/",
|
||||
icon: "javascript",
|
||||
},
|
||||
{
|
||||
title: "Build a Desktop Wallet in Python",
|
||||
body: "Build a desktop wallet for the XRP Ledger using Python and various libraries.",
|
||||
path: "/docs/tutorials/sample-apps/build-a-desktop-wallet-in-python/",
|
||||
icon: "python",
|
||||
},
|
||||
{
|
||||
title: "Credential Issuing Service in JavaScript",
|
||||
body: "Build a credential issuing service using the JavaScript SDK.",
|
||||
path: "/docs/tutorials/sample-apps/credential-issuing-service-in-javascript/",
|
||||
icon: "javascript",
|
||||
},
|
||||
{
|
||||
title: "Credential Issuing Service in Python",
|
||||
body: "Build a credential issuing service using the Python SDK.",
|
||||
path: "/docs/tutorials/sample-apps/credential-issuing-service-in-python/",
|
||||
icon: "python",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function TutorialCard({
|
||||
tutorial,
|
||||
detectedLanguages,
|
||||
showFooter = false,
|
||||
translate,
|
||||
}: {
|
||||
tutorial: Tutorial;
|
||||
detectedLanguages?: string[];
|
||||
showFooter?: boolean;
|
||||
translate: (text: string) => string;
|
||||
}) {
|
||||
// Get icons: auto-detected languages take priority, then manual icon, then XRPL fallback
|
||||
const icons = detectedLanguages && detectedLanguages.length > 0
|
||||
? detectedLanguages.map((lang) => langIcons[lang]).filter(Boolean)
|
||||
: tutorial.icon && langIcons[tutorial.icon]
|
||||
? [langIcons[tutorial.icon]]
|
||||
: [langIcons.xrpl];
|
||||
|
||||
return (
|
||||
<a href={tutorial.path} className="card">
|
||||
<div className="card-header d-flex align-items-center flex-wrap">
|
||||
{icons.map((icon, idx) => (
|
||||
<span className="circled-logo" key={idx}>
|
||||
<img src={icon.src} alt={icon.alt} />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<h4 className="card-title h5">{translate(tutorial.title)}</h4>
|
||||
{tutorial.body && <p className="card-text">{translate(tutorial.body)}</p>}
|
||||
</div>
|
||||
{showFooter && <div className="card-footer"></div>}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TutorialsIndex() {
|
||||
const { useTranslate, usePageSharedData } = useThemeHooks();
|
||||
const { translate } = useTranslate();
|
||||
|
||||
// Get auto-detected languages from the plugin (maps tutorial paths to language arrays).
|
||||
const tutorialLanguages = usePageSharedData<TutorialLanguagesMap>("tutorial-languages") || {};
|
||||
|
||||
return (
|
||||
<main className="landing page-tutorials landing-builtin-bg">
|
||||
<section className="container-new py-26">
|
||||
<div className="col-lg-8 mx-auto text-lg-center">
|
||||
<div className="d-flex flex-column-reverse">
|
||||
<h1 className="mb-0">
|
||||
{translate("Crypto Wallet and Blockchain Development Tutorials")}
|
||||
</h1>
|
||||
<h6 className="eyebrow mb-3">{translate("Tutorials")}</h6>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Get Started */}
|
||||
<section className="container-new pt-10 pb-20">
|
||||
<div className="col-12 col-xl-8 p-0">
|
||||
<h3 className="h4 mb-3">{translate("Get Started with SDKs")}</h3>
|
||||
<p className="mb-4">
|
||||
{translate("These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="row tutorial-cards">
|
||||
{getStartedTutorials.map((tutorial, idx) => (
|
||||
<div key={idx} className="col-lg-4 col-md-6 mb-5">
|
||||
<TutorialCard tutorial={tutorial} showFooter translate={translate} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Other Tutorials */}
|
||||
{sections.map((section) => (
|
||||
<section className="container-new pt-10 pb-10" key={section.id}>
|
||||
<div className="col-12 col-xl-8 p-0">
|
||||
<h3 className="h4 mb-3">{translate(section.title)}</h3>
|
||||
<p className="mb-4">{translate(section.description)}</p>
|
||||
</div>
|
||||
<div className="row tutorial-cards">
|
||||
{section.tutorials.slice(0, 6).map((tutorial, idx) => (
|
||||
<div key={idx} className="col-lg-4 col-md-6 mb-5">
|
||||
<TutorialCard
|
||||
tutorial={tutorial}
|
||||
detectedLanguages={tutorialLanguages[tutorial.path]}
|
||||
translate={translate}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -127,7 +127,7 @@ footer:
|
||||
- page: docs/concepts/index.md
|
||||
label: Concepts
|
||||
labelTranslationKey: footer.docs.concepts
|
||||
- page: docs/tutorials/index.page.tsx
|
||||
- page: docs/tutorials/index.md
|
||||
label: Tutorials
|
||||
labelTranslationKey: footer.docs.tutorials
|
||||
- page: docs/references/index.md
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
- page: docs/concepts/decentralized-storage/decentralized-identifiers.md
|
||||
- page: docs/concepts/decentralized-storage/price-oracles.md
|
||||
|
||||
- page: docs/tutorials/index.page.tsx
|
||||
- page: docs/tutorials/index.md
|
||||
label: Tutorials
|
||||
labelTranslationKey: sidebar.docs.tutorials
|
||||
expanded: false
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -138,9 +138,6 @@ a.card:hover h3 {
|
||||
padding: .65rem;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
@@ -323,81 +320,3 @@ main article .card-grid {
|
||||
margin-bottom: 0.25rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* Tutorial cards */
|
||||
.page-tutorials .tutorial-cards {
|
||||
> div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 280px;
|
||||
transition: all 0.35s ease-out;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-16px);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
border-bottom: none;
|
||||
background: transparent;
|
||||
padding: 1.5rem 1.5rem 0 2rem;
|
||||
}
|
||||
|
||||
.circled-logo {
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1rem 1.5rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
background-color: transparent;
|
||||
border-top: none;
|
||||
padding: 0;
|
||||
height: 40px;
|
||||
background-size: cover;
|
||||
background-position: bottom;
|
||||
background-repeat: no-repeat;
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply colored footers to each card
|
||||
> div:nth-child(1) .card .card-footer {
|
||||
background-image: url("../img/cards/3-col-pink.svg");
|
||||
}
|
||||
> div:nth-child(2) .card .card-footer {
|
||||
background-image: url("../img/cards/3col-blue-light-blue.svg");
|
||||
}
|
||||
> div:nth-child(3) .card .card-footer {
|
||||
background-image: url("../img/cards/3-col-light-blue.svg");
|
||||
}
|
||||
> div:nth-child(4) .card .card-footer {
|
||||
background-image: url("../img/cards/3col-blue-green.svg");
|
||||
}
|
||||
> div:nth-child(5) .card .card-footer {
|
||||
background-image: url("../img/cards/3col-magenta.svg");
|
||||
}
|
||||
> div:nth-child(6) .card .card-footer {
|
||||
background-image: url("../img/cards/3-col-orange.svg");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -424,7 +424,7 @@ aside .card {
|
||||
.circled-logo {
|
||||
background-color: $gray-200;
|
||||
|
||||
img[src="/img/logos/globe.svg"] {
|
||||
img[src="assets/img/logos/globe.svg"] {
|
||||
filter: invert(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
- page: ./docs/concepts/index.md
|
||||
label: Concepts
|
||||
labelTranslationKey: topnav.docs.concepts
|
||||
- page: ./docs/tutorials/index.page.tsx
|
||||
- page: ./docs/tutorials/index.md
|
||||
label: Tutorials
|
||||
labelTranslationKey: topnav.docs.tutorials
|
||||
- page: ./docs/references/index.md
|
||||
|
||||
Reference in New Issue
Block a user