feat: extra protection for AccountDelete transactions (#1626)

* add deletion blockers check to autofill

* add tests

* add fail_hard: true

* pass in account_objects response to error

* only fail_hard for AccountDelete

* reject promise instead of throwing error

* fix rebase issue
This commit is contained in:
Mayukha Vadari
2021-09-24 15:05:38 -04:00
parent bfeb737ad7
commit b7c4b16a8d
5 changed files with 92 additions and 49 deletions

View File

@@ -2,8 +2,12 @@ import BigNumber from 'bignumber.js'
import { xAddressToClassicAddress, isValidXAddress } from 'ripple-address-codec'
import type { Client } from '..'
import { ValidationError } from '../errors'
import { AccountInfoRequest, LedgerRequest } from '../models/methods'
import { ValidationError, XrplError } from '../errors'
import {
AccountInfoRequest,
AccountObjectsRequest,
LedgerRequest,
} from '../models/methods'
import { Transaction } from '../models/transactions'
import setTransactionFlagsToNumber from '../models/utils/flags'
import { xrpToDrops } from '../utils'
@@ -46,6 +50,9 @@ async function autofill<T extends Transaction>(
if (tx.LastLedgerSequence == null) {
promises.push(setLatestValidatedLedgerSequence(this, tx))
}
if (tx.TransactionType === 'AccountDelete') {
promises.push(checkAccountDeleteBlockers(this, tx))
}
return Promise.all(promises).then(() => tx)
}
@@ -193,4 +200,28 @@ async function setLatestValidatedLedgerSequence(
tx.LastLedgerSequence = ledgerSequence + LEDGER_OFFSET
}
async function checkAccountDeleteBlockers(
client: Client,
tx: Transaction,
): Promise<void> {
const request: AccountObjectsRequest = {
command: 'account_objects',
account: tx.Account,
ledger_index: 'validated',
deletion_blockers_only: true,
}
const response = await client.request(request)
return new Promise((resolve, reject) => {
if (response.result.account_objects.length > 0) {
reject(
new XrplError(
`Account ${tx.Account} cannot be deleted; there are Escrows, PayChannels, RippleStates, or Checks associated with the account.`,
response.result.account_objects,
),
)
}
resolve()
})
}
export default autofill

View File

@@ -51,6 +51,7 @@ async function submitSignedTransaction(
const request: SubmitRequest = {
command: 'submit',
tx_blob: signedTxEncoded,
fail_hard: isAccountDelete(signedTransaction),
}
return this.request(request)
}
@@ -63,4 +64,9 @@ function isSigned(transaction: Transaction | string): boolean {
)
}
function isAccountDelete(transaction: Transaction | string): boolean {
const tx = typeof transaction === 'string' ? decode(transaction) : transaction
return tx.TransactionType === 'AccountDelete'
}
export { submitTransaction, submitSignedTransaction }