Skip to content

Error decoding access list when using holding/locals and manually specifying a nested resource in a different order #1013

@neilcampbell

Description

@neilcampbell

Subject of the issue

When encoding an application call transaction containing an access list, the resources used within either a holding or locals reference are always adjusted to be ordered above that holding/locals, even when explicitly supplied after the holding/locals.

This behaviour is probably ok for encoding, however it appears the decoding logic also expects this to be case. When trying to decode an on chain transaction which specifies those nested resources after the linked holding/locals, an error is returned.

Your environment

  • Software version: 3.5.2

Steps to reproduce

  1. Add this test case to 5.Transaction.ts
it('should decode access list', () => {
  const addr1 = algosdk.Address.fromString(
    'FDMKB5D72THLYSJEBHBDHUE7XFRDOM5IHO44SOJ7AWPD6EZMWOQ2WKN7HQ'
  );
  const txn = algosdk.makeApplicationCallTxnFromObject({
    sender: 'BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4',
    appIndex: 111,
    onComplete: algosdk.OnApplicationComplete.NoOpOC,
    access: [
      {
        holding: {
          assetIndex: 123,
          address: addr1,
        },
      },
      { address: addr1 },
      { assetIndex: 123 },
    ],
    suggestedParams: {
      minFee: 1000,
      fee: 0,
      firstValid: 322575,
      lastValid: 323575,
      genesisID: 'testnet-v1.0',
      genesisHash: algosdk.base64ToBytes(
        'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI='
      ),
    },
  });

  const encodingData = txn.toEncodingData();

  // This code is here to demonstrate the problem.
  // When encoding, the cross product references are added first,
  // so modify the access list encoding data to simulate how it may be encoded on chain.
  const accessList = encodingData.get('al') as Array<Map<string, unknown>>;
  // Index 2 is actually the holding reference.
  // Manually adjust the indexes, because we'll be re-ording the list.
  (accessList[2].get('h') as any).set('d', 2);
  (accessList[2].get('h') as any).set('s', 3);
  const updateAccessList: Array<Map<string, unknown>> = [];
  updateAccessList.push(accessList[2]);
  updateAccessList.push(accessList[0]);
  updateAccessList.push(accessList[1]);
  encodingData.set('al', updateAccessList);

  const decodedTxn = algosdk.Transaction.fromEncodingData(encodingData);

  assert.deepStrictEqual(
    txn.applicationCall?.access,
    decodedTxn.applicationCall?.access
  );
});
  1. Run the test, which should return the error.

Expected behaviour

The above tests passes. This tests also asserts the order isn't changed from how the transaction is defined, so depending on the solution you may or may not want that assert.

Actual behaviour

TypeError: Cannot read properties of undefined (reading 'address')
      at convertIndicesToResourceReferences (src/appAccess.ts:168:44)
      at Function.fromEncodingData (src/transaction.ts:1200:32)
      at Context.<anonymous> (tests/5.Transaction.ts:2605:46)

Metadata

Metadata

Assignees

No one assigned

    Labels

    new-bugBug report that needs triage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions