mirror of
				https://github.com/Xahau/xahau.js.git
				synced 2025-11-04 04:55:48 +00:00 
			
		
		
		
	Aliter: Implement DeliverMax alias in Payment transactions, through autofill method (#2689)
Co-authored-by: Omar Khan <khancodegt@gmail.com>
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							a0678857a1
						
					
				
				
					commit
					39fed49654
				
			@@ -226,7 +226,7 @@ class Client extends EventEmitter<EventTypes> {
 | 
			
		||||
   * const client = new Client('wss://s.altnet.rippletest.net:51233')
 | 
			
		||||
   * ```
 | 
			
		||||
   */
 | 
			
		||||
  // eslint-disable-next-line max-lines-per-function -- okay because we have to set up all the connection handlers
 | 
			
		||||
  /* eslint-disable max-lines-per-function -- the constructor requires more lines to implement the logic */
 | 
			
		||||
  public constructor(server: string, options: ClientOptions = {}) {
 | 
			
		||||
    super()
 | 
			
		||||
    if (typeof server !== 'string' || !/wss?(?:\+unix)?:\/\//u.exec(server)) {
 | 
			
		||||
@@ -290,6 +290,7 @@ class Client extends EventEmitter<EventTypes> {
 | 
			
		||||
      this.emit('path_find', path)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
  /* eslint-enable max-lines-per-function */
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the url that the client is connected to.
 | 
			
		||||
@@ -638,7 +639,10 @@ class Client extends EventEmitter<EventTypes> {
 | 
			
		||||
   * @param signersCount - The expected number of signers for this transaction.
 | 
			
		||||
   * Only used for multisigned transactions.
 | 
			
		||||
   * @returns The autofilled transaction.
 | 
			
		||||
   * @throws ValidationError If Amount and DeliverMax fields are not identical in a Payment Transaction
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  // eslint-disable-next-line complexity -- handling Payment transaction API v2 requires more logic
 | 
			
		||||
  public async autofill<T extends SubmittableTransaction>(
 | 
			
		||||
    transaction: T,
 | 
			
		||||
    signersCount?: number,
 | 
			
		||||
@@ -646,7 +650,6 @@ class Client extends EventEmitter<EventTypes> {
 | 
			
		||||
    const tx = { ...transaction }
 | 
			
		||||
 | 
			
		||||
    setValidAddresses(tx)
 | 
			
		||||
 | 
			
		||||
    setTransactionFlagsToNumber(tx)
 | 
			
		||||
 | 
			
		||||
    const promises: Array<Promise<void>> = []
 | 
			
		||||
@@ -666,6 +669,34 @@ class Client extends EventEmitter<EventTypes> {
 | 
			
		||||
      promises.push(checkAccountDeleteBlockers(this, tx))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type-assertions on the DeliverMax property
 | 
			
		||||
    // @ts-expect-error -- DeliverMax property exists only at the RPC level, not at the protocol level
 | 
			
		||||
    if (tx.TransactionType === 'Payment' && tx.DeliverMax != null) {
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- This is a valid null check for Amount
 | 
			
		||||
      if (tx.Amount == null) {
 | 
			
		||||
        // If only DeliverMax is provided, use it to populate the Amount field
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type-assertions on the DeliverMax property
 | 
			
		||||
        // @ts-expect-error -- DeliverMax property exists only at the RPC level, not at the protocol level
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- DeliverMax is a known RPC-level property
 | 
			
		||||
        tx.Amount = tx.DeliverMax
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type-assertions on the DeliverMax property
 | 
			
		||||
      // @ts-expect-error -- DeliverMax property exists only at the RPC level, not at the protocol level
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- This is a valid null check for Amount
 | 
			
		||||
      if (tx.Amount != null && tx.Amount !== tx.DeliverMax) {
 | 
			
		||||
        return Promise.reject(
 | 
			
		||||
          new ValidationError(
 | 
			
		||||
            'PaymentTransaction: Amount and DeliverMax fields must be identical when both are provided',
 | 
			
		||||
          ),
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type-assertions on the DeliverMax property
 | 
			
		||||
      // @ts-expect-error -- DeliverMax property exists only at the RPC level, not at the protocol level
 | 
			
		||||
      delete tx.DeliverMax
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Promise.all(promises).then(() => tx)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -909,7 +940,7 @@ class Client extends EventEmitter<EventTypes> {
 | 
			
		||||
   * @param options.limit - Limit number of balances to return.
 | 
			
		||||
   * @returns An array of XRP/non-XRP balances for the given account.
 | 
			
		||||
   */
 | 
			
		||||
  // eslint-disable-next-line max-lines-per-function -- Longer definition is required for end users to see the definition.
 | 
			
		||||
  /* eslint-disable max-lines-per-function -- getBalances requires more lines to implement logic */
 | 
			
		||||
  public async getBalances(
 | 
			
		||||
    address: string,
 | 
			
		||||
    options: {
 | 
			
		||||
@@ -957,6 +988,7 @@ class Client extends EventEmitter<EventTypes> {
 | 
			
		||||
    )
 | 
			
		||||
    return balances.slice(0, options.limit)
 | 
			
		||||
  }
 | 
			
		||||
  /* eslint-enable max-lines-per-function */
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Fetch orderbook (buy/sell orders) between two currency pairs. This checks both sides of the orderbook
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import {
 | 
			
		||||
  Payment,
 | 
			
		||||
  Transaction,
 | 
			
		||||
} from '../../src'
 | 
			
		||||
import { ValidationError } from '../../src/errors'
 | 
			
		||||
import rippled from '../fixtures/rippled'
 | 
			
		||||
import {
 | 
			
		||||
  setupClient,
 | 
			
		||||
@@ -22,6 +23,8 @@ const LastLedgerSequence = 2908734
 | 
			
		||||
 | 
			
		||||
describe('client.autofill', function () {
 | 
			
		||||
  let testContext: XrplTestContext
 | 
			
		||||
  const AMOUNT = '1234'
 | 
			
		||||
  let paymentTx: Payment
 | 
			
		||||
 | 
			
		||||
  async function setupMockRippledVersionAndID(
 | 
			
		||||
    buildVersion: string,
 | 
			
		||||
@@ -40,10 +43,68 @@ describe('client.autofill', function () {
 | 
			
		||||
    await testContext.client.connect()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    testContext = await setupClient()
 | 
			
		||||
  })
 | 
			
		||||
  afterEach(async () => teardownClient(testContext))
 | 
			
		||||
  afterAll(async () => teardownClient(testContext))
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    paymentTx = {
 | 
			
		||||
      TransactionType: 'Payment',
 | 
			
		||||
      Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
 | 
			
		||||
      Amount: AMOUNT,
 | 
			
		||||
      Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy',
 | 
			
		||||
      DestinationTag: 1,
 | 
			
		||||
      Fee: '12',
 | 
			
		||||
      Flags: 2147483648,
 | 
			
		||||
      LastLedgerSequence: 65953073,
 | 
			
		||||
      Sequence: 65923914,
 | 
			
		||||
      SigningPubKey:
 | 
			
		||||
        '02F9E33F16DF9507705EC954E3F94EB5F10D1FC4A354606DBE6297DBB1096FE654',
 | 
			
		||||
      TxnSignature:
 | 
			
		||||
        '3045022100E3FAE0EDEC3D6A8FF6D81BC9CF8288A61B7EEDE8071E90FF9314CB4621058D10022043545CF631706D700CEE65A1DB83EFDD185413808292D9D90F14D87D3DC2D8CB',
 | 
			
		||||
      InvoiceID:
 | 
			
		||||
        '6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B',
 | 
			
		||||
      Paths: [
 | 
			
		||||
        [{ currency: 'BTC', issuer: 'r9vbV3EHvXWjSkeQ6CAcYVPGeq7TuiXY2X' }],
 | 
			
		||||
      ],
 | 
			
		||||
      SendMax: '100000000',
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('Validate Payment transaction API v2: Payment Transaction: Specify Only Amount field', async function () {
 | 
			
		||||
    const txResult = await testContext.client.autofill(paymentTx)
 | 
			
		||||
 | 
			
		||||
    assert.strictEqual(txResult.Amount, AMOUNT)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('Validate Payment transaction API v2: Payment Transaction: Specify Only DeliverMax field', async function () {
 | 
			
		||||
    // @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
 | 
			
		||||
    paymentTx.DeliverMax = paymentTx.Amount
 | 
			
		||||
    // @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
 | 
			
		||||
    delete paymentTx.Amount
 | 
			
		||||
    const txResult = await testContext.client.autofill(paymentTx)
 | 
			
		||||
 | 
			
		||||
    assert.strictEqual(txResult.Amount, AMOUNT)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('Validate Payment transaction API v2: Payment Transaction: identical DeliverMax and Amount fields', async function () {
 | 
			
		||||
    // @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
 | 
			
		||||
    paymentTx.DeliverMax = paymentTx.Amount
 | 
			
		||||
 | 
			
		||||
    const txResult = await testContext.client.autofill(paymentTx)
 | 
			
		||||
 | 
			
		||||
    assert.strictEqual(txResult.Amount, AMOUNT)
 | 
			
		||||
    assert.strictEqual('DeliverMax' in txResult, false)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('Validate Payment transaction API v2: Payment Transaction: differing DeliverMax and Amount fields', async function () {
 | 
			
		||||
    // @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
 | 
			
		||||
    paymentTx.DeliverMax = '6789'
 | 
			
		||||
    paymentTx.Amount = '1234'
 | 
			
		||||
 | 
			
		||||
    await assertRejects(testContext.client.autofill(paymentTx), ValidationError)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('should not autofill if fields are present', async function () {
 | 
			
		||||
    const tx: Transaction = {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
import { Payment } from '../../../src'
 | 
			
		||||
import { assert } from 'chai'
 | 
			
		||||
 | 
			
		||||
import { Payment, Wallet } from '../../../src'
 | 
			
		||||
import serverUrl from '../serverUrl'
 | 
			
		||||
import {
 | 
			
		||||
  setupClient,
 | 
			
		||||
@@ -12,24 +14,92 @@ const TIMEOUT = 20000
 | 
			
		||||
 | 
			
		||||
describe('Payment', function () {
 | 
			
		||||
  let testContext: XrplIntegrationTestContext
 | 
			
		||||
  let paymentTx: Payment
 | 
			
		||||
  const AMOUNT = '10000000'
 | 
			
		||||
  // This wallet is used for DeliverMax related tests
 | 
			
		||||
  let senderWallet: Wallet
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    testContext = await setupClient(serverUrl)
 | 
			
		||||
    // this payment transaction JSON needs to be refreshed before every test.
 | 
			
		||||
    // Because, we tinker with Amount and DeliverMax fields in the API v2 tests
 | 
			
		||||
    paymentTx = {
 | 
			
		||||
      TransactionType: 'Payment',
 | 
			
		||||
      Account: senderWallet.classicAddress,
 | 
			
		||||
      Amount: AMOUNT,
 | 
			
		||||
      Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy',
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  afterEach(async () => teardownClient(testContext))
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    testContext = await setupClient(serverUrl)
 | 
			
		||||
    senderWallet = await generateFundedWallet(testContext.client)
 | 
			
		||||
  })
 | 
			
		||||
  afterAll(async () => teardownClient(testContext))
 | 
			
		||||
 | 
			
		||||
  it(
 | 
			
		||||
    'base',
 | 
			
		||||
    async () => {
 | 
			
		||||
      const wallet2 = await generateFundedWallet(testContext.client)
 | 
			
		||||
      const tx: Payment = {
 | 
			
		||||
        TransactionType: 'Payment',
 | 
			
		||||
        Account: testContext.wallet.classicAddress,
 | 
			
		||||
        Destination: wallet2.classicAddress,
 | 
			
		||||
        Destination: senderWallet.classicAddress,
 | 
			
		||||
        Amount: '1000',
 | 
			
		||||
      }
 | 
			
		||||
      await testTransaction(testContext.client, tx, testContext.wallet)
 | 
			
		||||
    },
 | 
			
		||||
    TIMEOUT,
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  it(
 | 
			
		||||
    'Validate Payment transaction API v2: Payment Transaction: Specify Only Amount field',
 | 
			
		||||
    async () => {
 | 
			
		||||
      const result = await testTransaction(
 | 
			
		||||
        testContext.client,
 | 
			
		||||
        paymentTx,
 | 
			
		||||
        senderWallet,
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      assert.equal(result.result.engine_result_code, 0)
 | 
			
		||||
      assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
 | 
			
		||||
    },
 | 
			
		||||
    TIMEOUT,
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  it(
 | 
			
		||||
    'Validate Payment transaction API v2: Payment Transaction: Specify Only DeliverMax field',
 | 
			
		||||
    async () => {
 | 
			
		||||
      // @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
 | 
			
		||||
      paymentTx.DeliverMax = paymentTx.Amount
 | 
			
		||||
      // @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
 | 
			
		||||
      delete paymentTx.Amount
 | 
			
		||||
 | 
			
		||||
      const result = await testTransaction(
 | 
			
		||||
        testContext.client,
 | 
			
		||||
        paymentTx,
 | 
			
		||||
        senderWallet,
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      assert.equal(result.result.engine_result_code, 0)
 | 
			
		||||
      assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
 | 
			
		||||
    },
 | 
			
		||||
    TIMEOUT,
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  it(
 | 
			
		||||
    'Validate Payment transaction API v2: Payment Transaction: identical DeliverMax and Amount fields',
 | 
			
		||||
    async () => {
 | 
			
		||||
      // @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
 | 
			
		||||
      paymentTx.DeliverMax = paymentTx.Amount
 | 
			
		||||
 | 
			
		||||
      const result = await testTransaction(
 | 
			
		||||
        testContext.client,
 | 
			
		||||
        paymentTx,
 | 
			
		||||
        senderWallet,
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      assert.equal(result.result.engine_result_code, 0)
 | 
			
		||||
      assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
 | 
			
		||||
    },
 | 
			
		||||
    TIMEOUT,
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user