diff --git a/packages/xrpl/src/sugar/submit.ts b/packages/xrpl/src/sugar/submit.ts index 48e85a4b01..2fac20b352 100644 --- a/packages/xrpl/src/sugar/submit.ts +++ b/packages/xrpl/src/sugar/submit.ts @@ -2,6 +2,7 @@ import { decode, encode } from 'ripple-binary-codec' import type { Client, SubmitRequest, SubmitResponse, Wallet } from '..' import { ValidationError, XrplError } from '../errors' +import { Signer } from '../models/common' import { TxResponse } from '../models/methods' import { Transaction } from '../models/transactions' import { hashes } from '../utils' @@ -222,10 +223,26 @@ async function waitForFinalTransactionOutcome( // checks if the transaction has been signed function isSigned(transaction: Transaction | string): boolean { const tx = typeof transaction === 'string' ? decode(transaction) : transaction - return ( - typeof tx !== 'string' && - (tx.SigningPubKey != null || tx.TxnSignature != null) - ) + if (typeof tx === 'string') { + return false + } + if (tx.Signers != null) { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we know that tx.Signers is an array of Signers + const signers = tx.Signers as Signer[] + for (const signer of signers) { + // eslint-disable-next-line max-depth -- necessary for checking if signer is signed + if ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- necessary check + signer.Signer.SigningPubKey == null || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- necessary check + signer.Signer.TxnSignature == null + ) { + return false + } + } + return true + } + return tx.SigningPubKey != null && tx.TxnSignature != null } // initializes a transaction for a submit request diff --git a/packages/xrpl/test/client/submit.test.ts b/packages/xrpl/test/client/submit.test.ts index c2c578909b..5673be83d0 100644 --- a/packages/xrpl/test/client/submit.test.ts +++ b/packages/xrpl/test/client/submit.test.ts @@ -1,7 +1,8 @@ +/* eslint-disable @typescript-eslint/restrict-template-expressions -- error type thrown can be any */ import { assert } from 'chai' import cloneDeep from 'lodash/cloneDeep' -import { ValidationError } from '../../src' +import { multisign, ValidationError } from '../../src' import { Transaction } from '../../src/models/transactions' import Wallet from '../../src/Wallet' import rippled from '../fixtures/rippled' @@ -56,7 +57,6 @@ describe('client.submit', function () { const response = await testContext.client.submit(tx, { wallet }) 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}`) } }) @@ -114,7 +114,31 @@ describe('client.submit', function () { const response = await testContext.client.submit(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 submit a multisigned transaction', async function () { + const signerWallet1 = Wallet.generate() + const signerWallet2 = Wallet.generate() + const accountSetTx: Transaction = { + TransactionType: 'AccountSet', + Account: 'rhvh5SrgBL5V8oeV9EpDuVszeJSSCEkbPc', + Sequence: 1, + Fee: '12', + LastLedgerSequence: 12312, + } + + testContext.mockRippled!.addResponse('submit', rippled.submit.success) + + const signed1 = signerWallet1.sign(accountSetTx, true) + const signed2 = signerWallet2.sign(accountSetTx, true) + const multisignedTxEncoded = multisign([signed1.tx_blob, signed2.tx_blob]) + + try { + const response = await testContext.client.submit(multisignedTxEncoded) + assert(response.result.engine_result, 'tesSUCCESS') + } catch (error) { assert(false, `Did not expect an error to be thrown: ${error}`) } })