Merge branch 'master' of https://github.com/JakeatRipple/xrpl-dev-portal into feat-learningportal-banner
# Conflicts: # assets/css/devportal2022-v9.css
BIN
assets/img/community/community-blog@2x.png
Normal file
|
After Width: | Height: | Size: 479 KiB |
BIN
assets/img/community/lightmode/community-blog@2x.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
94
content/_code-samples/checks/py/account_checks.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
from xrpl.clients import JsonRpcClient
|
||||||
|
from xrpl.models import AccountObjects
|
||||||
|
from xrpl.utils import drops_to_xrp, hex_to_str, ripple_time_to_datetime
|
||||||
|
|
||||||
|
client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to the testnetwork
|
||||||
|
|
||||||
|
|
||||||
|
# Query the ledger for all xrp checks an account has created or received
|
||||||
|
|
||||||
|
|
||||||
|
wallet_addr_to_query = "rPKcw5cXUtREMgsQZqSLkxJTfpwMGg7WcP"
|
||||||
|
|
||||||
|
checks_dict = {}
|
||||||
|
|
||||||
|
sent_checks = []
|
||||||
|
|
||||||
|
received_checks = []
|
||||||
|
|
||||||
|
# Build request
|
||||||
|
req = AccountObjects(account=wallet_addr, ledger_index="validated", type="check")
|
||||||
|
|
||||||
|
# Make request and return result
|
||||||
|
response = client.request(req)
|
||||||
|
result = response.result
|
||||||
|
|
||||||
|
# Parse result
|
||||||
|
if "account_objects" in result:
|
||||||
|
account_checks = result["account_objects"]
|
||||||
|
for check in account_checks:
|
||||||
|
if isinstance(check["SendMax"], str):
|
||||||
|
check_data = {}
|
||||||
|
check_data["sender"] = check["Account"]
|
||||||
|
check_data["receiver"] = check["Destination"]
|
||||||
|
if "Expiration" in check:
|
||||||
|
check_data["expiry_date"] = str(ripple_time_to_datetime(check["Expiration"]))
|
||||||
|
check_data["amount"] = str(drops_to_xrp(check["SendMax"]))
|
||||||
|
check_data["check_id"] = check["index"]
|
||||||
|
if check_data["sender"] == wallet_addr:
|
||||||
|
sent.append(check_data)
|
||||||
|
elif check_data["sender"] != wallet_addr:
|
||||||
|
receive.append(check_data)
|
||||||
|
|
||||||
|
# Sort checks
|
||||||
|
checks_dict["sent"] = sent
|
||||||
|
checks_dict["receive"] = receive
|
||||||
|
print(checks_dict)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############################# Query for token checks #############################
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Query the ledger for all token checks an account has created or received
|
||||||
|
|
||||||
|
|
||||||
|
wallet_addr_to_query = "rPKcw5cXUtREMgsQZqSLkxJTfpwMGg7WcP"
|
||||||
|
|
||||||
|
checks_dict = {}
|
||||||
|
|
||||||
|
sent_dict = []
|
||||||
|
|
||||||
|
received_dict = []
|
||||||
|
|
||||||
|
# Build request
|
||||||
|
req = AccountObjects(account=wallet_addr, ledger_index="validated", type="check")
|
||||||
|
|
||||||
|
# Make request and return result
|
||||||
|
response = client.request(req)
|
||||||
|
result = response.result
|
||||||
|
|
||||||
|
# Parse result
|
||||||
|
if "account_objects" in result:
|
||||||
|
account_checks = result["account_objects"]
|
||||||
|
for check in account_checks:
|
||||||
|
if isinstance(check["SendMax"], dict):
|
||||||
|
check_data = {}
|
||||||
|
check_data["sender"] = check["Account"]
|
||||||
|
check_data["receiver"] = check["Destination"]
|
||||||
|
if "Expiration" in check:
|
||||||
|
check_data["expiry_date"] = str(ripple_time_to_datetime(check["Expiration"]))
|
||||||
|
check_data["token"] = hex_to_str(check["SendMax"]["currency"])
|
||||||
|
check_data["issuer"] = check["SendMax"]["issuer"]
|
||||||
|
check_data["amount"] = check["SendMax"]["value"]
|
||||||
|
check_data["check_id"] = check["index"]
|
||||||
|
if check_data["sender"] == wallet_addr:
|
||||||
|
sent.append(check_data)
|
||||||
|
elif check_data["sender"] != wallet_addr:
|
||||||
|
receive.append(check_data)
|
||||||
|
|
||||||
|
# Sort checks
|
||||||
|
checks_dict["sent"] = sent
|
||||||
|
checks_dict["receive"] = receive
|
||||||
|
print(checks_dict)
|
||||||
32
content/_code-samples/checks/py/cancel_check.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
from xrpl.wallet import Wallet, generate_faucet_wallet
|
||||||
|
from xrpl.clients import JsonRpcClient
|
||||||
|
from xrpl.models import CheckCancel
|
||||||
|
from xrpl.transaction import (safe_sign_and_autofill_transaction,
|
||||||
|
send_reliable_submission)
|
||||||
|
|
||||||
|
client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to the testnetwork
|
||||||
|
|
||||||
|
"""Cancel a check"""
|
||||||
|
# Sender is the check creator or recipient
|
||||||
|
# If the Check has expired, any address can cancel it
|
||||||
|
|
||||||
|
# Check id
|
||||||
|
check_id = "F944CB379DEE18EFDA7A58A4F81AF1A98C46E54A8B9F2D268F1E26610BC0EB03"
|
||||||
|
|
||||||
|
# Create wallet object
|
||||||
|
sender_wallet = generate_faucet_wallet(client=client)
|
||||||
|
|
||||||
|
# Build check cancel transaction
|
||||||
|
check_txn = CheckCancel(account=sender_wallet.classic_address, check_id=check_id)
|
||||||
|
|
||||||
|
# Sign and submit transaction
|
||||||
|
stxn = safe_sign_and_autofill_transaction(check_txn, sender_wallet, client)
|
||||||
|
stxn_response = send_reliable_submission(stxn, client)
|
||||||
|
|
||||||
|
# Parse response for result
|
||||||
|
stxn_result = stxn_response.result
|
||||||
|
|
||||||
|
# Print result and transaction hash
|
||||||
|
print(stxn_result["meta"]["TransactionResult"])
|
||||||
|
print(stxn_result["hash"])
|
||||||
|
|
||||||
74
content/_code-samples/checks/py/cash_check.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
from xrpl.clients import JsonRpcClient
|
||||||
|
from xrpl.models import CheckCash, IssuedCurrencyAmount
|
||||||
|
from xrpl.transaction import (safe_sign_and_autofill_transaction,
|
||||||
|
send_reliable_submission)
|
||||||
|
from xrpl.utils import str_to_hex, xrp_to_drops
|
||||||
|
from xrpl.wallet import generate_faucet_wallet
|
||||||
|
|
||||||
|
# Connect to a network
|
||||||
|
client = JsonRpcClient("https://s.altnet.rippletest.net:51234")
|
||||||
|
|
||||||
|
|
||||||
|
# Cash an xrp check
|
||||||
|
|
||||||
|
# Check id
|
||||||
|
check_id = "F944CB379DEE18EFDA7A58A4F81AF1A98C46E54A8B9F2D268F1E26610BC0EB03"
|
||||||
|
|
||||||
|
# Amount to cash
|
||||||
|
amount = 10.00
|
||||||
|
|
||||||
|
# Generate wallet
|
||||||
|
sender_wallet = generate_faucet_wallet(client=client)
|
||||||
|
|
||||||
|
# Build check cash transaction
|
||||||
|
check_txn = CheckCash(account=sender_wallet.classic_address, check_id=check_id, amount=xrp_to_drops(amount))
|
||||||
|
|
||||||
|
# Sign transaction
|
||||||
|
stxn = safe_sign_and_autofill_transaction(check_txn, sender_wallet, client)
|
||||||
|
|
||||||
|
# Submit transaction and wait for result
|
||||||
|
stxn_response = send_reliable_submission(stxn, client)
|
||||||
|
|
||||||
|
# Parse response for result
|
||||||
|
stxn_result = stxn_response.result
|
||||||
|
|
||||||
|
# Print result and transaction hash
|
||||||
|
print(stxn_result["meta"]["TransactionResult"])
|
||||||
|
print(stxn_result["hash"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#################### cash token check #############################
|
||||||
|
|
||||||
|
# Cash token check
|
||||||
|
|
||||||
|
# Token name
|
||||||
|
token = "USD"
|
||||||
|
|
||||||
|
# Amount of token to deliver
|
||||||
|
amount = 10.00
|
||||||
|
|
||||||
|
# Token issuer address
|
||||||
|
issuer = generate_faucet_wallet(client=client).classic_address
|
||||||
|
|
||||||
|
# Create sender wallet object
|
||||||
|
sender_wallet = generate_faucet_wallet(client=client)
|
||||||
|
|
||||||
|
# Build check cash transaction
|
||||||
|
check_txn = CheckCash(account=sender_wallet.classic_address, check_id=check_id, amount=IssuedCurrencyAmount(
|
||||||
|
currency=str_to_hex(token),
|
||||||
|
issuer=issuer,
|
||||||
|
value=amount))
|
||||||
|
|
||||||
|
# Sign transaction
|
||||||
|
stxn = safe_sign_and_autofill_transaction(check_txn, sender_wallet, client)
|
||||||
|
|
||||||
|
# Submit transaction and wait for result
|
||||||
|
stxn_response = send_reliable_submission(stxn, client)
|
||||||
|
|
||||||
|
# Parse response for result
|
||||||
|
stxn_result = stxn_response.result
|
||||||
|
|
||||||
|
# Print result and transaction hash
|
||||||
|
print(stxn_result["meta"]["TransactionResult"])
|
||||||
|
print(stxn_result["hash"])
|
||||||
78
content/_code-samples/checks/py/create_check.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from xrpl.clients import JsonRpcClient
|
||||||
|
from xrpl.models import CheckCreate, IssuedCurrencyAmount
|
||||||
|
from xrpl.transaction import (safe_sign_and_autofill_transaction,
|
||||||
|
send_reliable_submission)
|
||||||
|
from xrpl.utils import datetime_to_ripple_time, str_to_hex, xrp_to_drops
|
||||||
|
from xrpl.wallet import generate_faucet_wallet
|
||||||
|
|
||||||
|
client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Connect to the testnetwork
|
||||||
|
|
||||||
|
"""Create a token check"""
|
||||||
|
|
||||||
|
check_receiver_addr = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Example: send back to Testnet Faucet
|
||||||
|
|
||||||
|
token_name = "USD"
|
||||||
|
|
||||||
|
amount_to_deliver = 10.00
|
||||||
|
|
||||||
|
token_issuer = "r9CEVt4Cmcjt68ME6GKyhf2DyEGo2rG8AW"
|
||||||
|
|
||||||
|
# Set check to expire after 5 days
|
||||||
|
expiry_date = datetime_to_ripple_time(datetime.now() + timedelta(days=5))
|
||||||
|
|
||||||
|
# Generate wallet
|
||||||
|
sender_wallet = generate_faucet_wallet(client=client)
|
||||||
|
|
||||||
|
# Build check create transaction
|
||||||
|
check_txn = CheckCreate(account=sender_wallet.classic_address, destination=receiver_addr,
|
||||||
|
send_max=IssuedCurrencyAmount(
|
||||||
|
currency=str_to_hex(token),
|
||||||
|
issuer=issuer,
|
||||||
|
value=amount),
|
||||||
|
expiration=expiry_date)
|
||||||
|
|
||||||
|
# Sign, submit transaction and wait for result
|
||||||
|
stxn = safe_sign_and_autofill_transaction(check_txn, sender_wallet, client)
|
||||||
|
stxn_response = send_reliable_submission(stxn, client)
|
||||||
|
|
||||||
|
# Parse response for result
|
||||||
|
stxn_result = stxn_response.result
|
||||||
|
|
||||||
|
# Print result and transaction hash
|
||||||
|
print(stxn_result["meta"]["TransactionResult"])
|
||||||
|
print(stxn_result["hash"])
|
||||||
|
|
||||||
|
|
||||||
|
############### CREATE XRP CHECK ################################
|
||||||
|
"""Create xrp check"""
|
||||||
|
|
||||||
|
|
||||||
|
check_receiver_addr = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" # Example: send back to Testnet Faucet
|
||||||
|
|
||||||
|
amount_to_deliver = 10.00
|
||||||
|
|
||||||
|
# Set check to expire after 5 days
|
||||||
|
expiry_date = datetime_to_ripple_time(datetime.now() + timedelta(days=5))
|
||||||
|
|
||||||
|
|
||||||
|
# Generate wallet
|
||||||
|
sender_wallet = generate_faucet_wallet(client=client)
|
||||||
|
|
||||||
|
# Build check create transaction
|
||||||
|
check_txn = CheckCreate(account=sender_wallet.classic_address,
|
||||||
|
destination=receiver_addr,
|
||||||
|
send_max=xrp_to_drops(amount),
|
||||||
|
expiration=expiry_date)
|
||||||
|
|
||||||
|
# Sign, submit transaction and wait for result
|
||||||
|
stxn = safe_sign_and_autofill_transaction(check_txn, sender_wallet, client)
|
||||||
|
stxn_response = send_reliable_submission(stxn, client)
|
||||||
|
|
||||||
|
# Parse response for result
|
||||||
|
stxn_result = stxn_response.result
|
||||||
|
|
||||||
|
# Print result and transaction hash
|
||||||
|
print(stxn_result["meta"]["TransactionResult"])
|
||||||
|
print(stxn_result["hash"])
|
||||||
24
content/_code-samples/key-derivation/js/README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# JavaScript key derivation examples
|
||||||
|
|
||||||
|
Generates key from a given input in Ed25519 and Secp256k1 format. On first run, you
|
||||||
|
have to install the necessary node.js dedpendencies:
|
||||||
|
|
||||||
|
npm install
|
||||||
|
|
||||||
|
## Command-line usage:
|
||||||
|
|
||||||
|
### Base58 formatted seed:
|
||||||
|
|
||||||
|
npm start "snJj9fYixUfpNCBn9LzLdLv5QqUKZ"
|
||||||
|
|
||||||
|
### Hex formatted seed:
|
||||||
|
|
||||||
|
npm start "BB664A14F510A366404BC4352A2230A5"
|
||||||
|
|
||||||
|
### Password like seed:
|
||||||
|
|
||||||
|
npm start "sEdSKaCy2JT7JaM7v95H9SxkhP9wS2r"
|
||||||
|
|
||||||
|
### Random seed
|
||||||
|
|
||||||
|
npm start
|
||||||
198
content/_code-samples/key-derivation/js/index.js
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Organize imports
|
||||||
|
const assert = require("assert")
|
||||||
|
const brorand = require("brorand")
|
||||||
|
const BN = require("bn.js")
|
||||||
|
const elliptic = require("elliptic");
|
||||||
|
const Ed25519 = elliptic.eddsa('ed25519');
|
||||||
|
const Secp256k1 = elliptic.ec('secp256k1');
|
||||||
|
const hashjs = require("hash.js");
|
||||||
|
const Sha512 = require("ripple-keypairs/dist/Sha512")
|
||||||
|
const { codec, encodeAccountPublic, encodeNodePublic } = require("ripple-address-codec");
|
||||||
|
|
||||||
|
const XRPL_SEED_PREFIX = 0x21
|
||||||
|
|
||||||
|
const isHex = function(value) {
|
||||||
|
const regex = new RegExp(/^[0-9a-f]+$/, 'i')
|
||||||
|
return regex.test(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bytesToHex = function(bytes) {
|
||||||
|
return Array.from(bytes, (byteValue) => {
|
||||||
|
const hex = byteValue.toString(16).toUpperCase();
|
||||||
|
return hex.length > 1 ? hex : `0${hex}`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hexToBytes = function(hex) {
|
||||||
|
assert.ok(hex.length % 2 === 0);
|
||||||
|
return hex.length === 0 ? [] : new BN(hex, 16).toArray(null, hex.length / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const encodeUTF8 = function (string) {
|
||||||
|
return encodeURIComponent(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sha512half = function (value) {
|
||||||
|
return hashjs.sha512().update(value).digest().slice(0, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Seed {
|
||||||
|
|
||||||
|
constructor(seedValue = '', checkRFC1751 = false) {
|
||||||
|
this.bytes = this._initSeed(seedValue)
|
||||||
|
this.ED25519Keypair = this._deriveED25519Keypair(this.bytes)
|
||||||
|
this.Secp256K1Keypair = this._deriveSecp256K1Keypair(this.bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
_initSeed(seedValue) {
|
||||||
|
|
||||||
|
// No input given - default to random seed
|
||||||
|
if (!seedValue || seedValue === '') {
|
||||||
|
const randomBuffer = brorand(16)
|
||||||
|
return [...randomBuffer]
|
||||||
|
}
|
||||||
|
|
||||||
|
// From base58 formatted seed
|
||||||
|
try {
|
||||||
|
const base58decoded = codec.decodeChecked(seedValue)
|
||||||
|
if (base58decoded[0] === XRPL_SEED_PREFIX && base58decoded.length === 17) {
|
||||||
|
return [...base58decoded.slice(1)];
|
||||||
|
}
|
||||||
|
} catch (exception) {
|
||||||
|
// Continue probing the seed for different format
|
||||||
|
}
|
||||||
|
|
||||||
|
// From hex formatted seed
|
||||||
|
if (isHex(seedValue)) {
|
||||||
|
const decoded = hexToBytes(seedValue);
|
||||||
|
if(decoded.length === 16) {
|
||||||
|
return decoded
|
||||||
|
} else {
|
||||||
|
// Raise Error
|
||||||
|
throw new Error("incorrect decoded seed length")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From password seed
|
||||||
|
const encoded = encodeUTF8(seedValue)
|
||||||
|
return hashjs.sha512().update(encoded).digest().slice(0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
_deriveED25519Keypair() {
|
||||||
|
const prefix = 'ED';
|
||||||
|
const rawPrivateKey = sha512half(this.bytes);
|
||||||
|
const privateKey = prefix + bytesToHex(rawPrivateKey);
|
||||||
|
const publicKey = prefix + bytesToHex(Ed25519.keyFromSecret(rawPrivateKey).pubBytes());
|
||||||
|
return { privateKey, publicKey };
|
||||||
|
}
|
||||||
|
|
||||||
|
_deriveSecp256K1Keypair(entropy, options) {
|
||||||
|
const prefix = '00';
|
||||||
|
const privateKey = prefix + this._deriveSecp256K1PrivateKey(entropy, options).toString(16, 64).toUpperCase()
|
||||||
|
const publicKey = bytesToHex(Secp256k1.keyFromPrivate(privateKey.slice(2))
|
||||||
|
.getPublic()
|
||||||
|
.encodeCompressed());
|
||||||
|
return { privateKey, publicKey };
|
||||||
|
}
|
||||||
|
|
||||||
|
_deriveSecp256K1PrivateKey(seed, opts = {}) {
|
||||||
|
const root = opts.validator;
|
||||||
|
const order = Secp256k1.curve.n;
|
||||||
|
// This private generator represents the `root` private key, and is what's
|
||||||
|
// used by validators for signing when a keypair is generated from a seed.
|
||||||
|
const privateGen = this._deriveScalar(seed);
|
||||||
|
if (root) {
|
||||||
|
// As returned by validation_create for a given seed
|
||||||
|
return privateGen;
|
||||||
|
}
|
||||||
|
const publicGen = Secp256k1.g.mul(privateGen);
|
||||||
|
// A seed can generate many keypairs as a function of the seed and a uint32.
|
||||||
|
// Almost everyone just uses the first account, `0`.
|
||||||
|
const accountIndex = opts.accountIndex || 0;
|
||||||
|
|
||||||
|
return this._deriveScalar(publicGen.encodeCompressed(), accountIndex)
|
||||||
|
.add(privateGen)
|
||||||
|
.mod(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
_deriveScalar(bytes, discrim) {
|
||||||
|
const order = Secp256k1.curve.n;
|
||||||
|
for (let i = 0; i <= 0xffffffff; i++) {
|
||||||
|
// We hash the bytes to find a 256 bit number, looping until we are sure it
|
||||||
|
// is less than the order of the curve.
|
||||||
|
const hasher = new Sha512.default().add(bytes);
|
||||||
|
// If the optional discriminator index was passed in, update the hash.
|
||||||
|
if (discrim !== undefined) {
|
||||||
|
hasher.addU32(discrim);
|
||||||
|
}
|
||||||
|
hasher.addU32(i);
|
||||||
|
const key = hasher.first256BN();
|
||||||
|
/* istanbul ignore else */
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (key.cmpn(0) > 0 && key.cmp(order) < 0) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This error is practically impossible to reach.
|
||||||
|
// The order of the curve describes the (finite) amount of points on the curve
|
||||||
|
// https://github.com/indutny/elliptic/blob/master/lib/elliptic/curves.js#L182
|
||||||
|
// How often will an (essentially) random number generated by Sha512 be larger than that?
|
||||||
|
// There's 2^32 chances (the for loop) to get a number smaller than the order,
|
||||||
|
// and it's rare that you'll even get past the first loop iteration.
|
||||||
|
// Note that in TypeScript we actually need the throw, otherwise the function signature would be BN | undefined
|
||||||
|
//
|
||||||
|
/* istanbul ignore next */
|
||||||
|
throw new Error('impossible unicorn ;)');
|
||||||
|
}
|
||||||
|
|
||||||
|
getBase58ED25519Account() {
|
||||||
|
const buffer = Buffer.from(this.ED25519Keypair.publicKey, "hex")
|
||||||
|
return encodeAccountPublic([...buffer])
|
||||||
|
}
|
||||||
|
|
||||||
|
getBase58ESecp256k1Account(validator = false) {
|
||||||
|
const buffer = Buffer.from(this.Secp256K1Keypair.publicKey, "hex")
|
||||||
|
if (validator) {
|
||||||
|
return encodeNodePublic([...buffer])
|
||||||
|
} else {
|
||||||
|
return encodeAccountPublic([...buffer])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
const base58Seed = codec.encodeChecked([XRPL_SEED_PREFIX].concat(this.bytes))
|
||||||
|
const hexSeed = Buffer.from(this.bytes).toString('hex').toUpperCase()
|
||||||
|
|
||||||
|
const base58ED25519Account = this.getBase58ED25519Account();
|
||||||
|
|
||||||
|
const base58Secp256k1Account = this.getBase58ESecp256k1Account();
|
||||||
|
const base58Secp256k1AccountValidator = this.getBase58ESecp256k1Account(true);
|
||||||
|
|
||||||
|
const out = `
|
||||||
|
Seed (base58): ${base58Seed}
|
||||||
|
Seed (hex): ${hexSeed}
|
||||||
|
|
||||||
|
Ed25519 Secret Key (hex): ${this.ED25519Keypair.privateKey}
|
||||||
|
Ed25519 Public Key (hex): ${this.ED25519Keypair.publicKey}
|
||||||
|
Ed25519 Public Key (base58 - Account): ${base58ED25519Account}
|
||||||
|
|
||||||
|
secp256k1 Secret Key (hex): ${this.Secp256K1Keypair.privateKey}
|
||||||
|
secp256k1 Public Key (hex): ${this.Secp256K1Keypair.publicKey}
|
||||||
|
secp256k1 Public Key (base58 - Account): ${base58Secp256k1Account}
|
||||||
|
secp256k1 Public Key (base58 - Validator): ${base58Secp256k1AccountValidator}
|
||||||
|
`
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv[2] !== undefined) {
|
||||||
|
console.log(process.argv[2]);
|
||||||
|
const seed = new Seed(process.argv[2]);
|
||||||
|
console.log(seed.toString())
|
||||||
|
} else {
|
||||||
|
const seed = new Seed();
|
||||||
|
console.log(seed.toString())
|
||||||
|
}
|
||||||
12
content/_code-samples/key-derivation/js/package.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "key-derivation-examples",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"xrpl": "^2.0.0"
|
||||||
|
},
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ The following is a comprehensive list of all known [amendments](amendments.html)
|
|||||||
| [fixRemoveNFTokenAutoTrustLine][] | v1.9.4 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.4.html "BADGE_80d0e0") |
|
| [fixRemoveNFTokenAutoTrustLine][] | v1.9.4 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.4.html "BADGE_80d0e0") |
|
||||||
| [fixNFTokenNegOffer][] | v1.9.2 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.2.html "BADGE_80d0e0") |
|
| [fixNFTokenNegOffer][] | v1.9.2 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.2.html "BADGE_80d0e0") |
|
||||||
| [NonFungibleTokensV1_1][] | v1.9.2 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.2.html "BADGE_80d0e0") |
|
| [NonFungibleTokensV1_1][] | v1.9.2 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.2.html "BADGE_80d0e0") |
|
||||||
| [ExpandedSignerList][] | v1.9.1 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.1.html "BADGE_80d0e0") |
|
| [ExpandedSignerList][] | v1.9.1 | [Enabled: 2022-10-13](https://livenet.xrpl.org/transactions/802E2446547BB86397217E32A78CB9857F21B048B91C81BCC6EF837BE9C72C87 "BADGE_GREEN") |
|
||||||
| [fixNFTokenDirV1][] | v1.9.1 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.1.html "BADGE_80d0e0") |
|
| [fixNFTokenDirV1][] | v1.9.1 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.1.html "BADGE_80d0e0") |
|
||||||
| [NonFungibleTokensV1][] | v1.9.0 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.0.html "BADGE_80d0e0") |
|
| [NonFungibleTokensV1][] | v1.9.0 | [Open for Voting: TBD](https://xrpl.org/blog/2022/rippled-1.9.0.html "BADGE_80d0e0") |
|
||||||
| [CheckCashMakesTrustLine][] | v1.8.0 | [Open for Voting: TBD](https://xrpl.org/blog/2021/rippled-1.8.1.html "BADGE_80d0e0") |
|
| [CheckCashMakesTrustLine][] | v1.8.0 | [Open for Voting: TBD](https://xrpl.org/blog/2021/rippled-1.8.1.html "BADGE_80d0e0") |
|
||||||
@@ -234,7 +234,7 @@ Provides "suspended payments" for XRP for escrow within the XRP Ledger, includin
|
|||||||
| Amendment | ExpandedSignerList |
|
| Amendment | ExpandedSignerList |
|
||||||
|:----------|:-----------|
|
|:----------|:-----------|
|
||||||
| Amendment ID | B2A4DB846F0891BF2C76AB2F2ACC8F5B4EC64437135C6E56F3F859DE5FFD5856 |
|
| Amendment ID | B2A4DB846F0891BF2C76AB2F2ACC8F5B4EC64437135C6E56F3F859DE5FFD5856 |
|
||||||
| Status | Open for Voting |
|
| Status | Enabled |
|
||||||
| Default Vote (Latest stable release) | No |
|
| Default Vote (Latest stable release) | No |
|
||||||
| Pre-amendment functionality retired? | No |
|
| Pre-amendment functionality retired? | No |
|
||||||
|
|
||||||
|
|||||||
42
content/concepts/interoperability/intro-to-evm-sidechain.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
html: intro-to-evm-sidechain.html
|
||||||
|
parent: xrpl-interoperability.html
|
||||||
|
blurb: Introduction to the EVM compatible XRP Ledger Sidechain
|
||||||
|
labels:
|
||||||
|
- Interoperability
|
||||||
|
status: not_enabled
|
||||||
|
---
|
||||||
|
# Introduction to EVM Compatible XRP Ledger Sidechain
|
||||||
|
|
||||||
|
The Etherium Virtual Machine (EVM) compatible XRP Ledger sidechain is a secure and fast public blockchain that brings all kinds of web3 applications to the XRP Ledger community.
|
||||||
|
|
||||||
|
- Explorer: [https://evm-sidechain.xrpl.org](https://evm-sidechain.xrpl.org/)
|
||||||
|
- Public RPC: [https://rpc.evm-sidechain.xrpl.org](https://evm-sidechain.xrpl.org/)
|
||||||
|
|
||||||
|
|
||||||
|
The EVM Sidechain is a powerful latest generation blockchain with the following features:
|
||||||
|
|
||||||
|
- Supports up to 1000 transactions per second, thus handling large loads and throughput.
|
||||||
|
- Has a low transaction confirmation time, on average, as a block is produced every 5 seconds.
|
||||||
|
- Once a block is added to the chain and confirmed, it is considered final (1 block finalization time).
|
||||||
|
- Provides full Ethereum Virtual Machine (EVM) compatibility, enabling you to connect your wallet and interact or deploy smart contracts written in Solidity.
|
||||||
|
|
||||||
|
## Consensus
|
||||||
|
|
||||||
|
The EVM Sidechain runs on a proof-of-stake (PoS) consensus algorithm. Staking is when you pledge your coins to be used for verifying transactions. The proof-of-stake model allows you to stake cryptocurrency (also referred to as "coins") and create your own validator nodes. Your coins are locked up while you stake them, but you can unstake them if you want to trade your coins.
|
||||||
|
|
||||||
|
In a proof-of-stake blockchain, mining power depends on the amount of coins a validator is staking. Participants who stake more coins are more likely to be chosen to add new blocks.
|
||||||
|
|
||||||
|
If you are interested in staking cryptocurrency and running your own validator, see [Join EVM Sidechain Devnet](join-evm-sidechain-devnet.html) for more information.
|
||||||
|
|
||||||
|
The underlying technology for the XRP Ledger EVM Sidechain consensus is [Tendermint](https://tendermint.com/), a Byzantine-Fault Tolerant engine for building blockchains.
|
||||||
|
|
||||||
|
The blockchain uses the `cosmos-sdk` library on top of Tendermint to create and customize the blockchain using its built-in modules. The EVM sidechain uses the [Ethermint](https://github.com/evmos/ethermint) `cosmos-sdk` module, which provides EVM compatibility
|
||||||
|
|
||||||
|
## Interoperability Using the EVM Sidechain
|
||||||
|
|
||||||
|
The EVM sidechain is directly connected to XRP Ledger through the XRP Ledger bridge ([https://bridge.devnet.xrpl.](https://bridge.devnet.xrpl.org/). Through this bridge component, you can move your XRP to the EVM Sidechain and use its features.
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
[Get Started with EVM Sidechain](get-started-evm-sidechain.html)
|
||||||
13
content/concepts/interoperability/xrpl-interoperability.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
html: xrpl-interoperability.html
|
||||||
|
parent: concepts.html
|
||||||
|
blurb: Learn about capabilities that bring programmability and ability to interact with other chains to the XRP Ledger.
|
||||||
|
template: pagetype-category.html.jinja
|
||||||
|
labels:
|
||||||
|
- Interoperability
|
||||||
|
status: not_enabled
|
||||||
|
---
|
||||||
|
# Interoperability
|
||||||
|
|
||||||
|
The XRP Ledger is known for its transaction throughput, speed, and low fees. With the addition of programmabilty and interoperability, developers can access features such as smart contracts and can build apps with cross-chain interoperability.
|
||||||
|
|
||||||
@@ -38,9 +38,9 @@ The transaction cost is not paid to any party: the XRP is irrevocably destroyed.
|
|||||||
|
|
||||||
## Load Cost and Open Ledger Cost
|
## Load Cost and Open Ledger Cost
|
||||||
|
|
||||||
When the [FeeEscalation amendment][] is enabled, there are two thresholds for the transaction cost:
|
There are two thresholds for the transaction cost:
|
||||||
|
|
||||||
* If the transaction cost does not meet a `rippled` server's [load-based transaction cost threshold](#local-load-cost), the server ignores the transaction completely. (This logic is essentially unchanged with or without the amendment.)
|
* If the transaction cost does not meet a `rippled` server's [load-based transaction cost threshold](#local-load-cost), the server ignores the transaction completely.
|
||||||
* If the transaction cost does not meet a `rippled` server's [open ledger cost threshold](#open-ledger-cost), the server queues the transaction for a later ledger.
|
* If the transaction cost does not meet a `rippled` server's [open ledger cost threshold](#open-ledger-cost), the server queues the transaction for a later ledger.
|
||||||
|
|
||||||
This divides transactions into roughly three categories:
|
This divides transactions into roughly three categories:
|
||||||
@@ -90,7 +90,7 @@ _Fee levels_ represent the proportional difference between the minimum cost and
|
|||||||
|
|
||||||
The `rippled` APIs have two ways to query the local load-based transaction cost: the `server_info` command (intended for humans) and the `server_state` command (intended for machines).
|
The `rippled` APIs have two ways to query the local load-based transaction cost: the `server_info` command (intended for humans) and the `server_state` command (intended for machines).
|
||||||
|
|
||||||
If the [FeeEscalation amendment][] is enabled, you can use the [fee method][] to check the open ledger cost.
|
You can use the [fee method][] to check the open ledger cost.
|
||||||
|
|
||||||
### server_info
|
### server_info
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ This feature is designed to allow you to recover an account if the regular key i
|
|||||||
|
|
||||||
The [`lsfPasswordSpent` flag](accountroot.html) starts out disabled. It gets enabled when you send a SetRegularKey transaction signed by the master key pair. It gets disabled again when the account receives a [Payment](payment.html) of XRP.
|
The [`lsfPasswordSpent` flag](accountroot.html) starts out disabled. It gets enabled when you send a SetRegularKey transaction signed by the master key pair. It gets disabled again when the account receives a [Payment](payment.html) of XRP.
|
||||||
|
|
||||||
When the [FeeEscalation amendment][] is enabled, `rippled` prioritizes key reset transactions above other transactions even though the nominal transaction cost of a key reset transaction is zero.
|
`rippled` prioritizes key reset transactions above other transactions even though the nominal transaction cost of a key reset transaction is zero.
|
||||||
|
|
||||||
|
|
||||||
## Changing the Transaction Cost
|
## Changing the Transaction Cost
|
||||||
|
|||||||
@@ -37,6 +37,6 @@ For a practical example, see the [Batch Mint NFTokens](batch-minting.html) tutor
|
|||||||
* NFToken objects are minted ahead of time
|
* NFToken objects are minted ahead of time
|
||||||
* Market activity for the initial sale of the NFToken object is captured on the ledger
|
* Market activity for the initial sale of the NFToken object is captured on the ledger
|
||||||
|
|
||||||
Downside
|
### Downside
|
||||||
|
|
||||||
You need to retain the appropriate XRP reserve for all of the NFToken objects you mint. As a rule of thumb, this is roughly 1/12th XRP per NFToken object at the current reserve rate. In the event that you do not have sufficient XRP in reserve, your mint transactions fail until you add sufficient XRP to your account.
|
You need to retain the appropriate XRP reserve for all of the NFToken objects you mint. As a rule of thumb, this is roughly 1/12th XRP per NFToken object at the current reserve rate. In the event that you do not have sufficient XRP in reserve, your mint transactions fail until you add sufficient XRP to your account.
|
||||||
@@ -4,11 +4,11 @@ The software that powers the XRP Ledger is open-source, so anyone can download,
|
|||||||
|
|
||||||
| XRP Ledger Source Code | |
|
| XRP Ledger Source Code | |
|
||||||
|:-----------------------|:----------------------------------------------------|
|
|:-----------------------|:----------------------------------------------------|
|
||||||
| Repository | <https://github.com/ripple/rippled/> |
|
| Repository | <https://github.com/XRPLF/rippled> |
|
||||||
| License | [Multiple; ISC (permissive)](https://github.com/ripple/rippled/blob/develop/LICENSE.md) |
|
| License | [Multiple; ISC (permissive)](https://github.com/ripple/rippled/blob/develop/LICENSE.md) |
|
||||||
| Programming Language | C++ |
|
| Programming Language | C++ |
|
||||||
|
|
||||||
If you're not sure where to start, Dev Null Productions provides a detailed and thorough [**Source Code Guide**](https://xrpintel.com/source) that describes the structure and functions of the `rippled` XRP Ledger server implementation.
|
If you're not sure where to start, Dev Null Productions provides a detailed and thorough [**Source Code Guide**](https://xrpintel.com/source) that describes the structure and functions of the core XRP Ledger server (`rippled`) implementation.
|
||||||
|
|
||||||
## Standards Drafts
|
## Standards Drafts
|
||||||
|
|
||||||
|
|||||||
@@ -112,12 +112,12 @@ status: not_enabled
|
|||||||
|
|
||||||
### 送金手数料
|
### 送金手数料
|
||||||
|
|
||||||
送金手数料には、トークンの二次販売時に発行者が請求する手数料を1/10,000単位で指定します。このフィールドの有効な値は0から50,000までです。1の値は1bpsまたは0.01%に相当し、0%から50%の間の転送レートを許容します。
|
送金手数料には、トークンの二次販売時に発行者が請求する手数料を1/100,000単位で指定します。このフィールドの有効な値は0から50,000までです。1の値は1bpsまたは0.01%に相当し、0%から50%の間の転送レートを許容します。
|
||||||
|
|
||||||
|
|
||||||
### 例
|
### 例
|
||||||
|
|
||||||
この値では、転送手数料は314bps(3.14%)に設定されます。
|
この値では、転送手数料は31.4bps(0.314%)に設定されます。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ The example sets three flags: `lsfBurnable` (`0x0001`), `lsfOnlyXRP` (`0x0002`),
|
|||||||
|
|
||||||
### TransferFee
|
### TransferFee
|
||||||
|
|
||||||
The `TransferFee` value specifies the percentage fee, in units of 1/10,000, charged by the issuer for secondary sales of the token. Valid values for this field are between 0 and 50,000, inclusive. A value of 1 is equivalent to 1bps or 0.00001%, allowing transfer rates between 0% and 50%.
|
The `TransferFee` value specifies the percentage fee, in units of 1/100,000, charged by the issuer for secondary sales of the token. Valid values for this field are between 0 and 50,000, inclusive. A value of 1 is equivalent to 0.001% or 1/10 of a basis point (bps), allowing transfer rates between 0% and 50%.
|
||||||
|
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
This value sets the transfer fee to 314, or .00314%.
|
This value sets the transfer fee to 314, or 0.314%.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
html: connect-metamask-to-xrpl-evm-sidechain.html
|
||||||
|
parent: get-started-evm-sidechain.html
|
||||||
|
blurb: Learn how to connect MetaMask wallet to the EVM Sidechain for the XRP Ledger.
|
||||||
|
labels:
|
||||||
|
- Development, Interoperability
|
||||||
|
status: not_enabled
|
||||||
|
---
|
||||||
|
# Connect MetaMask to XRP Ledger Sidechain
|
||||||
|
|
||||||
|
MetaMask is an extension for accessing Harmony-enabled distributed applications (_dapps_) from your browser. The extension injects the XRP Ledger EVM sidechain Web3 API into every website's Javascript context, so that Web3 applications can read from the blockchain.
|
||||||
|
This tutorial walks through the process of installing MetaMask, configuring it on the XRP Ledger EVM sidechain network, and importing an existing account using a previously generated private key.
|
||||||
|
|
||||||
|
## 1. Installing MetaMask
|
||||||
|
|
||||||
|
Install the MetaMask extension on your browser from **[https://metamask.io/download/](https://metamask.io/download/)**. The extension supports most desktop browsers.
|
||||||
|
|
||||||
|
## 2. Create an Account on MetaMask
|
||||||
|
|
||||||
|
To create a new account on MetaMask:
|
||||||
|
|
||||||
|
1. Click the MetaMask icon.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. Choose **Create Account**.
|
||||||
|
|
||||||
|
3. Enter the Account Name.
|
||||||
|
|
||||||
|
4. Click **Create**.
|
||||||
|
|
||||||
|
### 3. Adding XRP Ledger EVM Sidechain to MetaMask
|
||||||
|
|
||||||
|
To add XRP Ledger EVM Sidechain to MetaMask:
|
||||||
|
|
||||||
|
1. Open the MetaMask extension.
|
||||||
|
|
||||||
|
2. Use the drop-down menu to choose **Add Network**.
|
||||||
|

|
||||||
|
|
||||||
|
3. Enter the XRP Ledger Devnet endpoint information.
|
||||||
|
|
||||||
|
* **Network Name**: XRP Ledger EVM Sidechain
|
||||||
|
* **New RPC URL**: http://rpc.evm-sidechain.xrpl.org
|
||||||
|
* **Chain ID**: 1440001
|
||||||
|
* **Currency Symbol**: XRP
|
||||||
|
* **Block Explorer**: https://evm-sidechain.xrpl.org
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
---
|
||||||
|
html: evm-sidechain-run-a-validator-node.html
|
||||||
|
parent: evm-sidechains.html
|
||||||
|
blurb: Learn how to run a validator node on the EVM Sidechain Devnet.
|
||||||
|
labels:
|
||||||
|
- Development, Interoperability
|
||||||
|
status: not_enabled
|
||||||
|
---
|
||||||
|
# Run a Validator Node on an EVM Sidechain
|
||||||
|
|
||||||
|
## Create Your Validator
|
||||||
|
|
||||||
|
Use your node consensus public key (`exrpvalconspub...`) to create a new validator by staking XRP tokens. You can find your validator pubkey by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd tendermint show-validator
|
||||||
|
```
|
||||||
|
|
||||||
|
To create your validator on Devnet, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd tx staking create-validator \
|
||||||
|
--amount=1000000000000000000000axrp \
|
||||||
|
--pubkey=$(exrpd tendermint show-validator) \
|
||||||
|
--moniker="<your_custom_moniker>" \
|
||||||
|
--chain-id=<chain_id> \
|
||||||
|
--commission-rate="0.05" \
|
||||||
|
--commission-max-rate="0.10" \
|
||||||
|
--commission-max-change-rate="0.01" \
|
||||||
|
--min-self-delegation="1000000" \
|
||||||
|
--gas="auto" \
|
||||||
|
--gas-prices="0.025aphoton" \
|
||||||
|
--from=<key_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note** When specifying commission parameters, the `commission-max-change-rate` is used to measure % *point* change over the `commission-rate`. For example, 1% to 2% is a 100% rate increase, but only 1 percentage point.
|
||||||
|
|
||||||
|
|
||||||
|
**Note** `Min-self-delegation` is a strictly positive integer that represents the minimum amount of self-delegated voting power your validator must always have. A `min-self-delegation` of `1000000` means your validator will never have a self-delegation lower than `1 axrp`.
|
||||||
|
|
||||||
|
You can confirm that you are in the validator set by using a third-party explorer.
|
||||||
|
|
||||||
|
## Edit Validator Description
|
||||||
|
|
||||||
|
You can edit your validator's public description. This info is to identify your validator, and is relied on by delegators when they decide to stake XRP tokens to a particular validator. Make sure to provide input for every flag below. If a flag is not included in the command, the field defaults to empty (`--moniker` defaults to the machine name), if the field has never been set, or remains the same, if it has been set in the past.
|
||||||
|
|
||||||
|
The <key_name> specifies which validator you are editing. If you choose to not include certain flags, remember that the --from flag must be included to identify the validator to update.
|
||||||
|
|
||||||
|
The `--identity` can be used as to verify identity with systems like Keybase or UPort. When using with Keybase `--identity` must be populated with a 16-digit string that is generated with a [keybase.io](https://keybase.io/) account. It is a cryptographically secure method of verifying your identity across multiple online networks. The Keybase API allows us to retrieve your Keybase avatar. This is how you can add a logo to your validator profile.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd tx staking edit-validator
|
||||||
|
--moniker="<your_custom_moniker>" \
|
||||||
|
--website="https://xrpl.org" \
|
||||||
|
--identity=6A0D65E29A4CBC8E \
|
||||||
|
--details="<your_validator_description>" \
|
||||||
|
--chain-id=<chain_id> \
|
||||||
|
--gas="auto" \
|
||||||
|
--gas-prices="0.025axrp" \
|
||||||
|
--from=<key_name> \
|
||||||
|
--commission-rate="0.10"
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the `commission-rate` value must adhere to the following invariants:
|
||||||
|
|
||||||
|
* Must be between 0 and the validator's `commission-max-rate`
|
||||||
|
* Must not exceed the validator's `commission-max-change-rate` which is the maximum % point change rate **per day**. In other words, a validator can only change its commission once per day and within `commission-max-change-rate` bounds.
|
||||||
|
|
||||||
|
## View Validator Description
|
||||||
|
|
||||||
|
View the validator's information with this command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd query staking validator <account>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Track Validator Signing Information
|
||||||
|
|
||||||
|
To track a validator's signatures from past transactions use the `signing-info` command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd query slashing signing-info <validator-pubkey> --chain-id=<chain_id>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unjail Validator
|
||||||
|
|
||||||
|
When a validator is "jailed" for downtime, you must submit an `Unjail` transaction from the operator account in order to restore block proposer awards (depending on the zone fee distribution).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd tx slashing unjail --from=<key_name> --chain-id=<chain_id>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Confirm Your Validator is Running
|
||||||
|
|
||||||
|
Your validator is active if the following command returns anything:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd query tendermint-validator-set | grep "$(exrpd tendermint show-address)"
|
||||||
|
```
|
||||||
|
|
||||||
|
You should now see your validator in one of the Exrp explorers. You are looking for the `bech32` encoded `address` in the `~/.exprd/config/priv_validator.json` file.
|
||||||
|
|
||||||
|
**Note** To be in the validator set, you must have more total voting power than the 100th validator.
|
||||||
|
|
||||||
|
## Halting Your Validator
|
||||||
|
|
||||||
|
When attempting to perform routine maintenance or planning for an upcoming coordinated upgrade, it can be useful to have your validator systematically and gracefully halt. Set the `halt-height` to the height at which you want your node to shut down, or pass the `--halt-height` flag to `exrpd`. The node shuts down with a 0 exit code at that given height after committing the block.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
html: evm-sidechain-validator-security.html
|
||||||
|
parent: join-evm-sidechain-devnet.html
|
||||||
|
blurb: Learn how to join the XRP Ledger EVM Sidechain Devnet.
|
||||||
|
labels:
|
||||||
|
- Development, Interoperability
|
||||||
|
status: not_enabled
|
||||||
|
---
|
||||||
|
# EVM Sidechain Validator Security
|
||||||
|
|
||||||
|
Each validator candidate is encouraged to run its operations independently, as diverse setups increase the resilience of the network. Validator candidates should commence their setup phase now, in order to be on time for launch.
|
||||||
|
|
||||||
|
## Horcrux
|
||||||
|
|
||||||
|
Horcrux is a [multi-party-computation (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) signing service for Tendermint nodes.
|
||||||
|
|
||||||
|
- Composed of a cluster of signer nodes in place of the [remote signer](https://docs.tendermint.com/master/nodes/remote-signer.html), thereby enabling High Availability (HA) for block signing through fault tolerance.
|
||||||
|
- Secures your validator private key by splitting it across multiple private signer nodes using threshold Ed25519 signatures.
|
||||||
|
- Adds security and availability without sacrificing block sign performance.
|
||||||
|
|
||||||
|
For information on how to upgrade your validator infrastructure with Horcrux, refer to the [documentation](https://github.com/strangelove-ventures/horcrux/blob/main/docs/migrating.md).
|
||||||
|
|
||||||
|
## Tendermint KMS
|
||||||
|
|
||||||
|
Tendermint KMS is a signature service with support for Hardware Security Modules (HSMs), such as YubiHSM2 and Ledger Nano. It is intended to be run alongside XRP Ledger EVM Sidechain validators, ideally on separate physical hosts, providing defense-in-depth for online validator signing keys, double signing protection, and a central signing service that can be used when operating multiple validators in several zones.
|
||||||
|
|
||||||
|
## Hardware Security Modules (HSM)
|
||||||
|
|
||||||
|
You must ensure that an attacker cannot steal a validator's key. Otherwise, the entire stake delegated to the compromised validator at risk. Hardware security modules (HSM) help mitigate this risk.
|
||||||
|
|
||||||
|
HSMs must support `ed25519` signatures for Evmos. The [YubiHSM 2](https://www.yubico.com/products/hardware-security-module/) supports `ed25519` and can be used with this YubiKey [library](https://github.com/iqlusioninc/yubihsm.rs).
|
||||||
|
|
||||||
|
**IMPORTANT**: The YubiHSM can protect a private key but **cannot ensure** in a secure setting that it will not sign the same block twice.
|
||||||
|
|
||||||
|
## Sentry Nodes (DDOS Protection)
|
||||||
|
|
||||||
|
Validators are responsible for ensuring that the network can sustain denial of service attacks.
|
||||||
|
|
||||||
|
One recommended way to mitigate these risks is for validators to carefully structure their network topology in a sentry node architecture.
|
||||||
|
|
||||||
|
Validator nodes should only connect to full-nodes they trust; either they operate these nodes themselves, or the nodes are run by other validator administrators they know personally. A validator node typically runs in a data center. Most data centers provide direct links to the networks of major cloud providers. The validator can use those links to connect to sentry nodes in the cloud. This shifts the burden of denial-of-service from the validator's node directly to its sentry nodes, and might require new sentry nodes be spun up or activated to mitigate attacks on existing ones.
|
||||||
|
|
||||||
|
Sentry nodes can be quickly spun up or change their IP addresses. Because the links to the sentry nodes are in private IP space, an internet-based attacked cannot disturb them directly. This ensures that the validator's block proposals and votes always make it to the rest of the network.
|
||||||
|
|
||||||
|
To setup your sentry node architecture:
|
||||||
|
|
||||||
|
1. Edit your validator node's `config.toml` file:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
#Comma separated list of nodes to keep persistent connections to
|
||||||
|
#Do not add private peers to this list if you don't want them advertised
|
||||||
|
persistent_peers =[list of sentry nodes]
|
||||||
|
|
||||||
|
# Set true to enable the peer-exchange reactor
|
||||||
|
pex = false
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Edit your sentry node's `config.toml` file:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
#Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
|
||||||
|
#Example ID: 3e16af0cead27979e1fc3dac57d03df3c7a77acc@3.87.179.235:26656
|
||||||
|
|
||||||
|
private_peer_ids = "node_ids_of_private_peers"
|
||||||
|
```
|
||||||
116
content/tutorials/interoperability/get-started-evm-sidechain.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
html: get-started-evm-sidechain.html
|
||||||
|
parent: evm-sidechains.html
|
||||||
|
blurb: Get started with the EVM compatible sidechain for the XRP Ledger.
|
||||||
|
labels:
|
||||||
|
- Development, Interoperability
|
||||||
|
status: not_enabled
|
||||||
|
---
|
||||||
|
# Get Started with the EVM Sidechain
|
||||||
|
|
||||||
|
This getting started tutorial walks you through the steps to set up your account and transfer funds using the EVM sidechain bridge.
|
||||||
|
|
||||||
|
## 1. Create an Account Using an EVM Compatible Wallet
|
||||||
|
|
||||||
|
In order to interact with the network, you need to create an account in the EVM sidechain. To create and manage this account you can use any EVM compatible wallet such as MetaMask.
|
||||||
|
|
||||||
|
For instructions on how to install and create an account using MetaMask, then send and receive tokens, see [Connect MetaMask to XRP Ledger EVM Sidechain](connect-metamask-to-xrpl-evm-sidechain.html).
|
||||||
|
|
||||||
|
## 2. Move XRP Ledger Devnet Tokens to the EVM Sidechain
|
||||||
|
|
||||||
|
Before you can start interacting with the EVM blockchain, you need to transfer some tokens from the XRP Ledger Devnet to the EVM sidechain.
|
||||||
|
|
||||||
|
To obtain tokens in the XRP Ledger Devnet, go to the [XRP Faucets](xrp-testnet-faucet.html) page and click *Generate Devnet credentials* to generate a new Devnet account with some test XRP in it.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Note the address and secret associated with your Devnet address. You need this information to set up your preferred XRP Ledger wallet.
|
||||||
|
|
||||||
|
## 3. Transfer Funds Using the EVM Sidechain Bridge
|
||||||
|
|
||||||
|
Once you have your accounts set up and test fund allocated, you can use the EVM Sidechain bridge to move the test XRP tokens to the EVM Sidechain.
|
||||||
|
|
||||||
|
The EVM Sidechain bridge is a tool that allows you to transfer funds between chains in a fast and secure way.
|
||||||
|
|
||||||
|
To start using the bridge, go to [https://bridge.devnet.xrpl.org](https://bridge.devnet.xrpl.org/)
|
||||||
|
|
||||||
|
### 1. Connect Both Wallets
|
||||||
|
|
||||||
|
**Connect Xumm Wallet**
|
||||||
|
|
||||||
|
Use your Xumm wallet to interact with the XRP Ledger Devnet chain.
|
||||||
|
Ensure that you have created an account on the public XRP Ledger Devnet as described in Step 1.
|
||||||
|
|
||||||
|
To connect a Xumm wallet to the bridge, go to the [EVM Sidechain bridge](https://bridge.devnet.xrpl.org) and click “Connect with Xumm Wallet”.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
**Note:** Ensure that you are connected to XRP Ledger Devnet and that the application that you are connecting with is the correct one.
|
||||||
|
|
||||||
|
Follow the instructions on screen to scan the QR code using the Xumm app. The Xumm wallet app displays a confirmation page.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Connect MetaMask Wallet**
|
||||||
|
|
||||||
|
Use your MetaMask wallet to interact with the XRP Ledger EVM Sidechain.
|
||||||
|
Ensure that you have created a MetaMask account and connected to the public XRP Ledger Devnet as described in [Connect MetaMask to XRP Ledger EVM Sidechain](connect-metamask-to-xrpl-evm-sidechain.html).
|
||||||
|
|
||||||
|
To connect a MetaMask wallet to the bridge, go to the [EVM Sidechain bridge](https://bridge.devnet.xrpl.org) and click “Connect with Metamask Wallet”.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 2. Initiate the Transfer of Funds
|
||||||
|
|
||||||
|
Now that both Xumm and MetaMask wallets are connected to the bridge, you can select the direction of the bridge, the amount to send, and the destination address.
|
||||||
|
|
||||||
|
- **Direction of the bridge**: This is the direction of transfer; it can be either EVM sidechain → XRP Ledger Devnet or XRP Ledger Devnet → EVM sidechain. Use the “Switch Network” button to switch the direction of transfer.
|
||||||
|
- **Amount to send**: This is the amount that you want to transfer to the other side. Note that there is a fee to use the bridge.
|
||||||
|
- Network fees: The fees required by the network for your transactions.
|
||||||
|
- Commission: The bridge applies a commission for every transaction completed. This is to prevent spam and distributed denial of service attacks (DDOS).
|
||||||
|
- **Destination address**: The address on the destination chain where you want to receive funds.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Enter the details for your transaction and click **Transfer**. Review the details of the transaction carefully before accepting the transaction in the corresponding wallet.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Depending on the direction of the transfer, you need to approve the transaction in the Xumm Wallet or in the Metamask.
|
||||||
|
|
||||||
|
**XRP Ledger Devnet → EVM sidechain**
|
||||||
|
|
||||||
|
For this direction you must approve the transaction in your Xumm Wallet. Before doing so, please check that the details are correct:
|
||||||
|
|
||||||
|
- Destination address has to be: `radjmEZTb4zsyNUJGV4gcVPXrFTJAuskKa`
|
||||||
|
- Memo has to be the same address that you entered in the destination field
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**EVM sidechain → XRP Ledger Devnet**
|
||||||
|
|
||||||
|
If this is the case, then you must approve the transaction in your Metamask. Before doing so, please check the details are correct:
|
||||||
|
|
||||||
|
- Destination address has to be: `0x8cDE56336E289c028C8f7CF5c20283fF02272182`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
Once you approve the transaction either in Xumm wallet or in Metamask, a loading screen displays. This process can take up to a few minutes.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 3. Receive the Funds
|
||||||
|
|
||||||
|
Following a few minutes of transaction processing time, you are redirected to the **Transaction Confirmation** screen where you can verify the details of the bridge transaction.
|
||||||
|
|
||||||
|
- **Origin transaction hash**: Hash of the transaction in the origin chain.
|
||||||
|
- **Destination transaction hash**: Hash of the transaction in the destination chain.
|
||||||
|
- **From address**: Origin address of the transfer.
|
||||||
|
- **To address**: Destination address of the transfer.
|
||||||
|
- **Receive**: The amount received in the destination address.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Your test XRP tokens have been successfully transferred and are now available in the other chain.
|
||||||
119
content/tutorials/interoperability/join-evm-sidechain-devnet.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
---
|
||||||
|
html: join-evm-sidechain-devnet.html
|
||||||
|
parent: evm-sidechains.html
|
||||||
|
blurb: Learn how to join the XRP Ledger EVM Sidechain Devnet.
|
||||||
|
labels:
|
||||||
|
- Development, Interoperability
|
||||||
|
status: not_enabled
|
||||||
|
---
|
||||||
|
# Join the XRP Ledger EVM Sidechain Devnet
|
||||||
|
|
||||||
|
This tutorial walks you through the steps to join the existing **XRP Ledger EVM Sidechain Devnet**.
|
||||||
|
|
||||||
|
For ease of use, create an alias, `exprd`, to run all commands inside your Docker container.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
Before proceeding to initialize the node, ensure that the following pre-requisites are installed and running:
|
||||||
|
|
||||||
|
* Docker 19+
|
||||||
|
* Create an alias to run all commands in this tutorial inside a Docker container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
alias exrpd="docker run -it --rm -v ~/.exrpd:/root/.exrpd peersyst/xrp-evm-client:latest exrpd"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Initialize Node
|
||||||
|
|
||||||
|
The first task is to initialize the node, which creates the necessary validator and node configuration files.
|
||||||
|
|
||||||
|
1. Initialize the chain parameters using the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd config chain-id exrp_1440001-1
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create or add a key to your node. For this tutorial, we use the `test` keyring:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd keys add <key_name> --keyring-backend test
|
||||||
|
```
|
||||||
|
|
||||||
|
Note the `key_name` you enter as you need to reference it in subsequent steps.
|
||||||
|
|
||||||
|
**Note** For more information on a more secure setup for your validator, refer to [cosmos-sdk keys and keyrings](https://docs.cosmos.network/v0.46/run-node/keyring.html) and [validator security](evm-sidechain-validator-security.html).
|
||||||
|
|
||||||
|
|
||||||
|
3. Initialize the node using the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd init <your_custom_moniker> --chain-id exrp_1440001-1
|
||||||
|
```
|
||||||
|
|
||||||
|
Monikers can contain only ASCII characters. Using Unicode characters renders your node unreachable.
|
||||||
|
|
||||||
|
All these commands create your `~/.exrpd` (i.e `$HOME`) directory with subfolders `config/` and `data/`. In the `config` directory, the most important files for configuration are `app.toml` and `config.toml`.
|
||||||
|
|
||||||
|
## Genesis & Seeds
|
||||||
|
|
||||||
|
1. Copy the Genesis File.
|
||||||
|
|
||||||
|
Download the `genesis.json` file from here and copy it to the `config` directory: `~/.exrpd/config/genesis.json`. This is a genesis file with the chain-id and genesis accounts balances.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget [https://raw.githubusercontent.com/Peersyst/xrp-evm-archive/main/devnet/genesis.json](https://raw.githubusercontent.com/Peersyst/xrp-evm-archive/main/devnet/genesis.json) ~/.exrpd/config/
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify the genesis configuration file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd validate-genesis
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Add Persistent Peer Nodes
|
||||||
|
|
||||||
|
Set the [`persistent_peer`](https://docs.tendermint.com/master/tendermint-core/using-tendermint.html#persistent-peer)s field in `~/.exrpd/config/config.toml` to specify peers with which your node maintains persistent connections. You can retrieve them from the list of available peers on the archive repo ([https://raw.githubusercontent.com/Peersyst/xrp-evm-archive/main/devnet/peers.txt](https://raw.githubusercontent.com/Peersyst/xrp-evm-archive/main/devnet/peers.txt)).
|
||||||
|
|
||||||
|
To get a list of entries from the `peers.txt` file in the `PEERS` variable, run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PEERS=`curl -sL https://raw.githubusercontent.com/Peersyst/xrp-evm-archive/main/devnet/peers.txt | sort -R | head -n 10 | awk '{print $1}' | paste -s -d, -`
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `sed` to include them in the configuration. You can also add them manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sed -i.bak -e "s/^persistent_peers *=.*/persistent_peers = \"$PEERS\"/" ~/.exrpd/config/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run a Devnet Validator Node
|
||||||
|
|
||||||
|
Run the Devnet validator node using following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd tx staking create-validator \
|
||||||
|
--amount=1000000000000axrp \
|
||||||
|
--pubkey=$(exrpd tendermint show-validator) \
|
||||||
|
--moniker="<your_custom_moniker>" \
|
||||||
|
--chain-id=<chain_id> \
|
||||||
|
--commission-rate="0.05" \
|
||||||
|
--commission-max-rate="0.20" \
|
||||||
|
--commission-max-change-rate="0.01" \
|
||||||
|
--min-self-delegation="1000000" \
|
||||||
|
--gas="auto" \
|
||||||
|
--gas-prices="0.025axrp" \
|
||||||
|
--from=<key_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note** For more information on running a validator note, see [Run a validator](evm-sidechain-run-a-validator-node.html)
|
||||||
|
|
||||||
|
|
||||||
|
## Start the Node
|
||||||
|
|
||||||
|
Start the node.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exrpd start
|
||||||
|
```
|
||||||
|
|
||||||
|
Once enough voting power (+2/3) from the genesis validators is up-and-running, the node starts producing blocks.
|
||||||
@@ -1060,6 +1060,22 @@ pages:
|
|||||||
- en
|
- en
|
||||||
- ja
|
- ja
|
||||||
|
|
||||||
|
- md: concepts/interoperability/xrpl-interoperability.md
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
|
||||||
|
- md: concepts/interoperability/intro-to-evm-sidechain.md
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
|
||||||
|
- name: Hooks
|
||||||
|
html: https://xrpl-hooks.readme.io/
|
||||||
|
parent: xrpl-interoperability.html
|
||||||
|
blurb: Smart contract proposal for the XRP Ledger.
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
|
||||||
|
|
||||||
# Redirect old "the-rippled-server.html"
|
# Redirect old "the-rippled-server.html"
|
||||||
- name: The rippled Server
|
- name: The rippled Server
|
||||||
html: the-rippled-server.html
|
html: the-rippled-server.html
|
||||||
@@ -2071,6 +2087,34 @@ pages:
|
|||||||
- en
|
- en
|
||||||
- ja
|
- ja
|
||||||
|
|
||||||
|
- name: Interoperability - EVM Sidechain
|
||||||
|
html: evm-sidechains.html
|
||||||
|
parent: tutorials.html
|
||||||
|
blurb: Learn how to interact with the EVM Sidechain Devnet.
|
||||||
|
template: pagetype-category.html.jinja
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
- ja
|
||||||
|
|
||||||
|
- md: tutorials/interoperability/get-started-evm-sidechain.md
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
|
||||||
|
- md: tutorials/interoperability/connect-metamask-to-xrpl-evm-sidechain.md
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
|
||||||
|
- md: tutorials/interoperability/join-evm-sidechain-devnet.md
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
|
||||||
|
- md: tutorials/interoperability/evm-sidechain-validator-security.md
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
|
||||||
|
- md: tutorials/interoperability/evm-sidechain-run-a-validator-node.md
|
||||||
|
targets:
|
||||||
|
- en
|
||||||
|
|
||||||
# References -------------------------------------------------------------------
|
# References -------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
BIN
img/evm-sidechain-add-metamask-network.png
Normal file
|
After Width: | Height: | Size: 290 KiB |
BIN
img/evm-sidechain-approve-transaction.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
BIN
img/evm-sidechain-bridge-sign-in.jpg
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
img/evm-sidechain-connect-metamask.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
img/evm-sidechain-connect-wallet.png
Normal file
|
After Width: | Height: | Size: 351 KiB |
BIN
img/evm-sidechain-connect-xumm-wallet.png
Normal file
|
After Width: | Height: | Size: 311 KiB |
BIN
img/evm-sidechain-create-metamask-account.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
img/evm-sidechain-initiate-transfer.png
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
img/evm-sidechain-metamask-confirmation.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
img/evm-sidechain-review-transaction.jpg
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
img/evm-sidechain-transaction-confirmation.png
Normal file
|
After Width: | Height: | Size: 360 KiB |
BIN
img/evm-sidechain-transfer-in-progress.png
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
img/evm-sidechain-xrpl-devnet-faucet.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
@@ -994,6 +994,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#xrpl-blog {
|
||||||
|
padding-bottom: 5rem;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding-top: 104px;
|
||||||
|
padding-bottom: 104px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#xrpl-events {
|
#xrpl-events {
|
||||||
padding-bottom: 5rem;
|
padding-bottom: 5rem;
|
||||||
@include media-breakpoint-up(md) {
|
@include media-breakpoint-up(md) {
|
||||||
|
|||||||
@@ -677,6 +677,10 @@ pre code {
|
|||||||
#run-a-network-node .text-cards a {
|
#run-a-network-node .text-cards a {
|
||||||
color: $black;
|
color: $black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#xrpl-blog .blog-graphic {
|
||||||
|
content: url(../img/community/lightmode/community-blog@2x.png);
|
||||||
|
}
|
||||||
|
|
||||||
#xrpl-events .text-light {
|
#xrpl-events .text-light {
|
||||||
color: $black !important;
|
color: $black !important;
|
||||||
|
|||||||
@@ -132,15 +132,15 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="mb-4 pb-3 mb-lg-3 pb-lg-5">
|
<div class="mb-4 pb-3 mb-lg-3 pb-lg-5">
|
||||||
<h6 class="eyebrow mb-2">{% trans %}Awarded in a single grant{% endtrans %}</h6>
|
<h6 class="eyebrow mb-2">{% trans %}Awarded in a single grant{% endtrans %}</h6>
|
||||||
<img src="./assets/img/community/community-grants-1.svg" class="w-100">
|
<img src="./assets/img/community/community-grants-1.svg">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 pb-3 mb-lg-3 pb-lg-5">
|
<div class="mb-4 pb-3 mb-lg-3 pb-lg-5">
|
||||||
<h6 class="eyebrow mb-2">{% trans %}Distributed to grant recipients{% endtrans %}</h6>
|
<h6 class="eyebrow mb-2">{% trans %}Distributed to grant recipients{% endtrans %}</h6>
|
||||||
<img src="./assets/img/community/community-grants-2.svg" class="w-100">
|
<img src="./assets/img/community/community-grants-2.svg">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 pb-3 mb-lg-3 pb-lg-5">
|
<div class="mb-4 pb-3 mb-lg-3 pb-lg-5">
|
||||||
<h6 class="eyebrow mb-2">{% trans %}Open-source projects funded {% endtrans %}</h6>
|
<h6 class="eyebrow mb-2">{% trans %}Open-source projects funded {% endtrans %}</h6>
|
||||||
<img src="./assets/img/community/community-grants-3.svg" class="w-100">
|
<img src="./assets/img/community/community-grants-3.svg">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-lg-none d-block mt-4 pt-3">
|
<div class="d-lg-none d-block mt-4 pt-3">
|
||||||
@@ -151,6 +151,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Blog -->
|
||||||
|
<section class="container-new" id="xrpl-blog">
|
||||||
|
<div class="card-grid card-grid-2xN align-items-lg-center">
|
||||||
|
|
||||||
|
<div class="col pr-2 d-lg-block d-none">
|
||||||
|
<img src="./assets/img/community/community-blog@2x.png" class="w-100 blog-graphic">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex flex-column-reverse mb-lg-2 pl-0">
|
||||||
|
<h2 class="h4 h2-sm">{% trans %}Showcase your XRPL project, application or product{% endtrans %}</h2>
|
||||||
|
<h6 class="eyebrow mb-3">{% trans %}XRPL Community Spotlight{% endtrans %}</h6>
|
||||||
|
</div>
|
||||||
|
<p class="mb-3 py-4">{% trans %}Get featured on the Developer Reflections blog or Use Cases page, and explore XRPL community highlights.{% endtrans %}</p>
|
||||||
|
|
||||||
|
<div class="d-lg-none d-block">
|
||||||
|
<img src="./assets/img/community/community-blog@2x.png" class="w-100 blog-graphic">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-lg-left text-center">
|
||||||
|
<a class="btn btn-primary btn-arrow mb-4 mb-md-0" data-tf-popup="ssHZA7Ly" data-tf-iframe-props="title=Developer Reflections" data-tf-medium="snippet">{% trans %}Submit Your Projects{% endtrans %}</a>
|
||||||
|
<a class="ml-lg-4 video-external-link" target="_blank" href="https://xrpl.org/blog/">{% trans %}Read the Blog{% endtrans %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Events -->
|
<!-- Events -->
|
||||||
<section class="container-new" id="xrpl-events">
|
<section class="container-new" id="xrpl-events">
|
||||||
@@ -279,6 +306,10 @@
|
|||||||
|
|
||||||
|
|
||||||
{% block endbody %}
|
{% block endbody %}
|
||||||
|
|
||||||
|
<!-- TypeFrom for blog -->
|
||||||
|
<script src="//embed.typeform.com/next/embed.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="{{currentpage.prefix}}assets/js/bodymovin.min.js"></script>
|
<script type="text/javascript" src="{{currentpage.prefix}}assets/js/bodymovin.min.js"></script>
|
||||||
|
|
||||||
<!-- Light version for network node looks ok for both light/dark.-->
|
<!-- Light version for network node looks ok for both light/dark.-->
|
||||||
|
|||||||