mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-12-06 17:27:59 +00:00
add submit transaction methods (#1611)
Adds submit transaction methods: submitTransaction and submitSignedTransaction.
This commit is contained in:
committed by
Mayukha Vadari
parent
851b352f32
commit
dc6baf7f39
@@ -26,6 +26,7 @@ import autofill from '../ledger/autofill'
|
||||
import getBalances from '../ledger/balances'
|
||||
import { getOrderbook, formatBidsAndAsks } from '../ledger/orderbook'
|
||||
import getPaths from '../ledger/pathfind'
|
||||
import { submitTransaction, submitSignedTransaction } from '../ledger/submit'
|
||||
import getTrustlines from '../ledger/trustlines'
|
||||
import { clamp } from '../ledger/utils'
|
||||
import {
|
||||
@@ -160,6 +161,7 @@ function getCollectKeyFromCommand(command: string): string | null {
|
||||
* @param params - Parameters to prepend to a function.
|
||||
* @returns A function bound with params.
|
||||
*/
|
||||
// TODO Need to refactor prepend so TS can infer the correct function signature type
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- expected param types
|
||||
function prepend(func: Function, ...params: unknown[]): Function {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return -- safe to return
|
||||
@@ -536,6 +538,10 @@ class Client extends EventEmitter {
|
||||
// @deprecated Use autofill instead
|
||||
public prepareTransaction = prepend(autofill, this)
|
||||
|
||||
public submitTransaction = prepend(submitTransaction, this)
|
||||
|
||||
public submitSignedTransaction = prepend(submitSignedTransaction, this)
|
||||
|
||||
public getFee = getFee
|
||||
|
||||
public getTrustlines = getTrustlines
|
||||
|
||||
69
src/ledger/submit.ts
Normal file
69
src/ledger/submit.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { decode, encode } from 'ripple-binary-codec'
|
||||
|
||||
import type { Client, SubmitRequest, SubmitResponse, Wallet } from '..'
|
||||
import { ValidationError } from '../common/errors'
|
||||
import { Transaction } from '../models/transactions'
|
||||
import { sign } from '../wallet/signer'
|
||||
|
||||
import autofill from './autofill'
|
||||
|
||||
/**
|
||||
* Submits an unsigned transaction.
|
||||
* Steps performed on a transaction:
|
||||
* 1. Autofill.
|
||||
* 2. Sign & Encode.
|
||||
* 3. Submit.
|
||||
*
|
||||
* @param client - A Client.
|
||||
* @param wallet - A Wallet to sign a transaction.
|
||||
* @param transaction - A transaction to autofill, sign & encode, and submit.
|
||||
* @returns A promise that contains SubmitResponse.
|
||||
* @throws RippledError if submit request fails.
|
||||
*/
|
||||
async function submitTransaction(
|
||||
client: Client,
|
||||
wallet: Wallet,
|
||||
transaction: Transaction,
|
||||
): Promise<SubmitResponse> {
|
||||
// TODO: replace with client.autofill(transaction) once prepend refactor is fixed.
|
||||
const tx = await autofill(client, transaction)
|
||||
const signedTxEncoded = sign(wallet, tx)
|
||||
return submitSignedTransaction(client, signedTxEncoded)
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes and submits a signed transaction.
|
||||
*
|
||||
* @param client - A Client.
|
||||
* @param signedTransaction - A signed transaction to encode (if not already) and submit.
|
||||
* @returns A promise that contains SubmitResponse.
|
||||
* @throws RippledError if submit request fails.
|
||||
*/
|
||||
async function submitSignedTransaction(
|
||||
client: Client,
|
||||
signedTransaction: Transaction | string,
|
||||
): Promise<SubmitResponse> {
|
||||
if (!isSigned(signedTransaction)) {
|
||||
throw new ValidationError('Transaction must be signed')
|
||||
}
|
||||
|
||||
const signedTxEncoded =
|
||||
typeof signedTransaction === 'string'
|
||||
? signedTransaction
|
||||
: encode(signedTransaction)
|
||||
const request: SubmitRequest = {
|
||||
command: 'submit',
|
||||
tx_blob: signedTxEncoded,
|
||||
}
|
||||
return client.request(request)
|
||||
}
|
||||
|
||||
function isSigned(transaction: Transaction | string): boolean {
|
||||
const tx = typeof transaction === 'string' ? decode(transaction) : transaction
|
||||
return (
|
||||
typeof tx !== 'string' &&
|
||||
(tx.SigningPubKey != null || tx.TxnSignature != null)
|
||||
)
|
||||
}
|
||||
|
||||
export { submitTransaction, submitSignedTransaction }
|
||||
71
test/client/submitSignedTransaction.ts
Normal file
71
test/client/submitSignedTransaction.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { ValidationError } from 'xrpl-local/common/errors'
|
||||
import { Transaction } from 'xrpl-local/models/transactions'
|
||||
|
||||
import rippled from '../fixtures/rippled'
|
||||
import { setupClient, teardownClient } from '../setupClient'
|
||||
import { assertRejects } from '../testUtils'
|
||||
|
||||
describe('client.submitSignedTransaction', function () {
|
||||
beforeEach(setupClient)
|
||||
afterEach(teardownClient)
|
||||
|
||||
const signedTransaction: Transaction = {
|
||||
TransactionType: 'Payment',
|
||||
Sequence: 1,
|
||||
LastLedgerSequence: 12312,
|
||||
Amount: '20000000',
|
||||
Fee: '12',
|
||||
SigningPubKey:
|
||||
'030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D',
|
||||
TxnSignature:
|
||||
'3045022100B3D311371EDAB371CD8F2B661A04B800B61D4B132E09B7B0712D3B2F11B1758302203906B44C4A150311D74FF6A35B146763C0B5B40AC30BD815113F058AA17B3E63',
|
||||
Account: 'rhvh5SrgBL5V8oeV9EpDuVszeJSSCEkbPc',
|
||||
Destination: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
|
||||
}
|
||||
|
||||
it('should submit a signed transaction', async function () {
|
||||
const signedTx: Transaction = { ...signedTransaction }
|
||||
|
||||
this.mockRippled.addResponse('submit', rippled.submit.success)
|
||||
|
||||
try {
|
||||
const response = await this.client.submitSignedTransaction(signedTx)
|
||||
assert(response.result.engine_result, 'tesSUCCESS')
|
||||
} catch (_error) {
|
||||
assert(false, 'Did not expect an error to be thrown')
|
||||
}
|
||||
})
|
||||
|
||||
it("should submit a signed transaction that's already encoded", async function () {
|
||||
const signedTxEncoded =
|
||||
'1200002400000001201B00003018614000000001312D0068400000000000000C7321030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D74473045022100B3D311371EDAB371CD8F2B661A04B800B61D4B132E09B7B0712D3B2F11B1758302203906B44C4A150311D74FF6A35B146763C0B5B40AC30BD815113F058AA17B3E6381142AF1861DEC1316AEEC995C94FF9E2165B1B784608314FDB08D07AAA0EB711793A3027304D688E10C3648'
|
||||
|
||||
this.mockRippled.addResponse('submit', rippled.submit.success)
|
||||
|
||||
try {
|
||||
const response = await this.client.submitSignedTransaction(
|
||||
signedTxEncoded,
|
||||
)
|
||||
assert(response.result.engine_result, 'tesSUCCESS')
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- error type thrown can be any
|
||||
assert(false, `Did not expect an error to be thrown: ${error}`)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw a ValidationError when submitting an unsigned transaction', async function () {
|
||||
const signedTx: Transaction = { ...signedTransaction }
|
||||
delete signedTx.SigningPubKey
|
||||
delete signedTx.TxnSignature
|
||||
|
||||
this.mockRippled.addResponse('submit', rippled.submit.success)
|
||||
|
||||
assertRejects(
|
||||
this.client.submitSignedTransaction(signedTx),
|
||||
ValidationError,
|
||||
'Transaction must be signed',
|
||||
)
|
||||
})
|
||||
})
|
||||
45
test/client/submitTransaction.ts
Normal file
45
test/client/submitTransaction.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/* eslint-disable mocha/no-hooks-for-single-case -- expected for setupClient & teardownClient */
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { Transaction } from 'xrpl-local/models/transactions'
|
||||
import Wallet from 'xrpl-local/wallet'
|
||||
|
||||
import rippled from '../fixtures/rippled'
|
||||
import { setupClient, teardownClient } from '../setupClient'
|
||||
|
||||
describe('client.submitTransaction', function () {
|
||||
beforeEach(setupClient)
|
||||
afterEach(teardownClient)
|
||||
|
||||
const publicKey =
|
||||
'030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D'
|
||||
const privateKey =
|
||||
'00141BA006D3363D2FB2785E8DF4E44D3A49908780CB4FB51F6D217C08C021429F'
|
||||
const address = 'rhvh5SrgBL5V8oeV9EpDuVszeJSSCEkbPc'
|
||||
|
||||
it('should submit an unsigned transaction', async function () {
|
||||
const tx: Transaction = {
|
||||
TransactionType: 'Payment',
|
||||
Account: address,
|
||||
Destination: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
|
||||
Amount: '20000000',
|
||||
Sequence: 1,
|
||||
Fee: '12',
|
||||
LastLedgerSequence: 12312,
|
||||
}
|
||||
const wallet = new Wallet(publicKey, privateKey)
|
||||
|
||||
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
|
||||
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
|
||||
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
|
||||
this.mockRippled.addResponse('submit', rippled.submit.success)
|
||||
|
||||
try {
|
||||
const response = await this.client.submitTransaction(wallet, tx)
|
||||
assert(response.result.engine_result, 'tesSUCCESS')
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- error type thrown can be any
|
||||
assert(false, `Did not expect an error to be thrown: ${error}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user