Compare commits

...

1 Commits

Author SHA1 Message Date
mDuo13
c5bf019608 Add example JS code for deleting an account 2026-02-19 17:35:19 -08:00
5 changed files with 207 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
# Delete Account
Delete an account from the XRP Ledger, removing its data and sending its XRP to another account.

View File

@@ -0,0 +1,3 @@
# Delete Account (JavaScript)
JavaScript sample code showing how to delete an account from the XRP Ledger.

View 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()

View 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████████████████████████████

View 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"
}