diff --git a/.eslintrc.js b/.eslintrc.js index 6c76ac01f..2329d391e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -41,6 +41,29 @@ module.exports = { '@typescript-eslint/no-redeclare': ['error'], 'no-shadow': 'off', '@typescript-eslint/no-shadow': ['error'], + 'no-underscore-dangle': [ + 'error', + { + allow: [ + '_isAlgosdkAddress', + '_isAlgosdkTransaction', + '_isAlgosdkSignedTransaction', + '_isAlgosdkLogicSig', + '_isAlgosdkLogicSigAccount', + '_isAlgosdkAtomicTransactionComposer', + '_isAlgosdkABIContract', + '_isAlgosdkABIUintType', + '_isAlgosdkABIUfixedType', + '_isAlgosdkABIAddressType', + '_isAlgosdkABIBoolType', + '_isAlgosdkABIByteType', + '_isAlgosdkABIStringType', + '_isAlgosdkABIArrayStaticType', + '_isAlgosdkABIArrayDynamicType', + '_isAlgosdkABITupleType', + ], + }, + ], }, overrides: [ { diff --git a/src/abi/abi_type.ts b/src/abi/abi_type.ts index bd467a90d..15e820471 100644 --- a/src/abi/abi_type.ts +++ b/src/abi/abi_type.ts @@ -128,6 +128,20 @@ export abstract class ABIType { export class ABIUintType extends ABIType { bitSize: number; + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIUintType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIUintType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIUintType === true); + } + constructor(size: number) { super(); if (size % 8 !== 0 || size < 8 || size > 512) { @@ -181,6 +195,20 @@ export class ABIUfixedType extends ABIType { bitSize: number; precision: number; + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIUfixedType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIUfixedType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIUfixedType === true); + } + constructor(size: number, denominator: number) { super(); if (size % 8 !== 0 || size < 8 || size > 512) { @@ -239,6 +267,20 @@ export class ABIUfixedType extends ABIType { } export class ABIAddressType extends ABIType { + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIAddressType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIAddressType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIAddressType === true); + } + toString() { return 'address'; } @@ -285,6 +327,20 @@ export class ABIAddressType extends ABIType { } export class ABIBoolType extends ABIType { + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIBoolType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIBoolType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIBoolType === true); + } + toString() { return 'bool'; } @@ -327,6 +383,20 @@ export class ABIBoolType extends ABIType { } export class ABIByteType extends ABIType { + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIByteType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIByteType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIByteType === true); + } + toString() { return 'byte'; } @@ -366,6 +436,20 @@ export class ABIByteType extends ABIType { } export class ABIStringType extends ABIType { + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIStringType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIStringType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIStringType === true); + } + toString() { return 'string'; } @@ -433,6 +517,20 @@ export class ABIArrayStaticType extends ABIType { childType: ABIType; staticLength: number; + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIArrayStaticType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIArrayStaticType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIArrayStaticType === true); + } + constructor(argType: ABIType, arrayLength: number) { super(); if (arrayLength < 0) { @@ -493,6 +591,20 @@ export class ABIArrayStaticType extends ABIType { export class ABIArrayDynamicType extends ABIType { childType: ABIType; + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIArrayDynamicType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIArrayDynamicType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIArrayDynamicType === true); + } + constructor(argType: ABIType) { super(); this.childType = argType; @@ -548,6 +660,20 @@ export class ABIArrayDynamicType extends ABIType { export class ABITupleType extends ABIType { childTypes: ABIType[]; + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABITupleType = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABITupleType, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABITupleType === true); + } + constructor(argTypes: ABIType[]) { super(); if (argTypes.length >= MAX_LEN) { diff --git a/src/abi/contract.ts b/src/abi/contract.ts index a3613ff08..ae345f09a 100644 --- a/src/abi/contract.ts +++ b/src/abi/contract.ts @@ -25,6 +25,20 @@ export class ABIContract { /** [ARC-28](https://arc.algorand.foundation/ARCs/arc-0028) events that MAY be emitted by this contract */ public readonly events?: ARC28Event[]; + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkABIContract = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an ABIContract, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkABIContract === true); + } + constructor(params: ABIContractParams) { if ( typeof params.name !== 'string' || diff --git a/src/composer.ts b/src/composer.ts index e4d1ec180..9186f2010 100644 --- a/src/composer.ts +++ b/src/composer.ts @@ -124,6 +124,22 @@ export class AtomicTransactionComposer { /** The maximum size of an atomic transaction group. */ static MAX_GROUP_SIZE: number = 16; + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkAtomicTransactionComposer = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an AtomicTransactionComposer, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!( + instance && instance._isAlgosdkAtomicTransactionComposer === true + ); + } + private status = AtomicTransactionComposerStatus.BUILDING; private transactions: TransactionWithSigner[] = []; private methodCalls: Map = new Map(); @@ -632,7 +648,7 @@ export class AtomicTransactionComposer { * * @param client - An Algodv2 client * @param request - SimulateRequest with options in simulation. - * If provided, the request's transaction group will be overrwritten by the composer's group, + * If provided, the request's transaction group will be overwritten by the composer's group, * only simulation related options will be used. * * @returns A promise that, upon success, resolves to an object containing an diff --git a/src/encoding/address.ts b/src/encoding/address.ts index 77a2f7509..ac2a39b85 100644 --- a/src/encoding/address.ts +++ b/src/encoding/address.ts @@ -33,6 +33,20 @@ export class Address { */ public readonly publicKey: Uint8Array; + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkAddress = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is an Address, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkAddress === true); + } + /** * Create a new Address object from its binary form. * @param publicKey - The binary form of the address. Must be 32 bytes. diff --git a/src/logicsig.ts b/src/logicsig.ts index 3267c24d9..a2053caaa 100644 --- a/src/logicsig.ts +++ b/src/logicsig.ts @@ -88,6 +88,20 @@ export class LogicSig implements encoding.Encodable { ]) ); + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkLogicSig = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is a LogicSig, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkLogicSig === true); + } + logic: Uint8Array; args: Uint8Array[]; sig?: Uint8Array; @@ -268,6 +282,20 @@ export class LogicSigAccount implements encoding.Encodable { ]) ); + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkLogicSigAccount = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is a LogicSigAccount, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkLogicSigAccount === true); + } + lsig: LogicSig; sigkey?: Uint8Array; diff --git a/src/signedTransaction.ts b/src/signedTransaction.ts index 03bf4d44f..4299c3301 100644 --- a/src/signedTransaction.ts +++ b/src/signedTransaction.ts @@ -46,6 +46,20 @@ export class SignedTransaction implements Encodable { ]) ); + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkSignedTransaction = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is a SignedTransaction, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkSignedTransaction === true); + } + /** * The transaction that was signed */ diff --git a/src/transaction.ts b/src/transaction.ts index bd9864ec2..4acd80936 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -453,6 +453,20 @@ export class Transaction implements encoding.Encodable { ]) ); + /** + * Unique property marker for Symbol.hasInstance compatibility across module boundaries + */ + private readonly _isAlgosdkTransaction = true; + + /** + * Custom Symbol.hasInstance to handle dual package hazard + * @param instance - The instance to check + * @returns true if the instance is a Transaction, regardless of which module loaded it + */ + static [Symbol.hasInstance](instance: any): boolean { + return !!(instance && instance._isAlgosdkTransaction === true); + } + /** common */ public readonly type: TransactionType; public readonly sender: Address; diff --git a/tests/10.ABI.ts b/tests/10.ABI.ts index 0ff7f8c93..256c24f1c 100644 --- a/tests/10.ABI.ts +++ b/tests/10.ABI.ts @@ -29,7 +29,7 @@ import { decodeAddress } from '../src/encoding/address'; describe('ABI type checking', () => { it('should create the correct type from the string', () => { for (let i = 8; i < 513; i += 8) { - let expected = new ABIUintType(i); + let expected: ABIType = new ABIUintType(i); let actual = ABIType.from(`uint${i}`); assert.deepStrictEqual(actual, expected); for (let j = 1; j < 161; j++) { diff --git a/tests/11.DualPackageHazard.ts b/tests/11.DualPackageHazard.ts new file mode 100644 index 000000000..a049811a8 --- /dev/null +++ b/tests/11.DualPackageHazard.ts @@ -0,0 +1,411 @@ +/* eslint-env mocha */ +import assert from 'assert'; +import algosdk from '../src/index.js'; +import { ABIContract } from '../src/abi/index.js'; + +describe('Dual Package Hazard Solution', () => { + describe('Address Symbol.hasInstance', () => { + it('should work with regular instanceof', () => { + const address = new algosdk.Address(new Uint8Array(32)); + assert.strictEqual(address instanceof algosdk.Address, true); + }); + + it('should work with custom Symbol.hasInstance', () => { + const address = new algosdk.Address(new Uint8Array(32)); + assert.strictEqual(algosdk.Address[Symbol.hasInstance](address), true); + }); + + it('should work with cross-module simulation', () => { + // Simulate an Address object from a different module instance + const mockAddress = { + _isAlgosdkAddress: true, + publicKey: new Uint8Array(32), + }; + assert.strictEqual( + algosdk.Address[Symbol.hasInstance](mockAddress), + true + ); + }); + + it('should reject objects without marker', () => { + const fakeAddress = { publicKey: new Uint8Array(32) }; + assert.strictEqual( + algosdk.Address[Symbol.hasInstance](fakeAddress), + false + ); + }); + + it('should handle null and undefined', () => { + assert.strictEqual(algosdk.Address[Symbol.hasInstance](null), false); + assert.strictEqual(algosdk.Address[Symbol.hasInstance](undefined), false); + }); + }); + + describe('Transaction Symbol.hasInstance', () => { + let transaction: algosdk.Transaction; + + beforeEach(() => { + const sender = algosdk.encodeAddress(new Uint8Array(32)); + const receiver = algosdk.encodeAddress(new Uint8Array(32)); + transaction = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, + amount: 1000, + suggestedParams: { + fee: 1000, + firstValid: 1, + lastValid: 100, + genesisHash: new Uint8Array(32), + genesisID: 'test', + minFee: 1000, + }, + }); + }); + + it('should work with regular instanceof', () => { + assert.strictEqual(transaction instanceof algosdk.Transaction, true); + }); + + it('should work with custom Symbol.hasInstance', () => { + assert.strictEqual( + algosdk.Transaction[Symbol.hasInstance](transaction), + true + ); + }); + + it('should work with cross-module simulation', () => { + const mockTransaction = { + _isAlgosdkTransaction: true, + type: 'pay' as any, // Mock object doesn't need strict typing + }; + assert.strictEqual( + algosdk.Transaction[Symbol.hasInstance](mockTransaction), + true + ); + }); + + it('should reject objects without marker', () => { + const fakeTransaction = { type: 'pay' as any }; + assert.strictEqual( + algosdk.Transaction[Symbol.hasInstance](fakeTransaction), + false + ); + }); + }); + + describe('SignedTransaction Symbol.hasInstance', () => { + let signedTransaction: algosdk.SignedTransaction; + + beforeEach(() => { + const sender = algosdk.encodeAddress(new Uint8Array(32)); + const receiver = algosdk.encodeAddress(new Uint8Array(32)); + const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + sender, + receiver, + amount: 1000, + suggestedParams: { + fee: 1000, + firstValid: 1, + lastValid: 100, + genesisHash: new Uint8Array(32), + genesisID: 'test', + minFee: 1000, + }, + }); + signedTransaction = new algosdk.SignedTransaction({ txn }); + }); + + it('should work with regular instanceof', () => { + assert.strictEqual( + signedTransaction instanceof algosdk.SignedTransaction, + true + ); + }); + + it('should work with custom Symbol.hasInstance', () => { + assert.strictEqual( + algosdk.SignedTransaction[Symbol.hasInstance](signedTransaction), + true + ); + }); + + it('should work with cross-module simulation', () => { + const mockSignedTransaction = { + _isAlgosdkSignedTransaction: true, + txn: {}, + }; + assert.strictEqual( + algosdk.SignedTransaction[Symbol.hasInstance](mockSignedTransaction), + true + ); + }); + }); + + describe('LogicSig Symbol.hasInstance', () => { + let logicSig: algosdk.LogicSig; + + beforeEach(() => { + const program = new Uint8Array([1, 32, 1, 1, 34]); // Simple program + logicSig = new algosdk.LogicSig(program); + }); + + it('should work with regular instanceof', () => { + assert.strictEqual(logicSig instanceof algosdk.LogicSig, true); + }); + + it('should work with custom Symbol.hasInstance', () => { + assert.strictEqual(algosdk.LogicSig[Symbol.hasInstance](logicSig), true); + }); + + it('should work with cross-module simulation', () => { + const mockLogicSig = { + _isAlgosdkLogicSig: true, + logic: new Uint8Array([1, 32, 1, 1, 34]), + args: [], + }; + assert.strictEqual( + algosdk.LogicSig[Symbol.hasInstance](mockLogicSig), + true + ); + }); + }); + + describe('LogicSigAccount Symbol.hasInstance', () => { + let logicSigAccount: algosdk.LogicSigAccount; + + beforeEach(() => { + const program = new Uint8Array([1, 32, 1, 1, 34]); // Simple program + logicSigAccount = new algosdk.LogicSigAccount(program); + }); + + it('should work with regular instanceof', () => { + assert.strictEqual( + logicSigAccount instanceof algosdk.LogicSigAccount, + true + ); + }); + + it('should work with custom Symbol.hasInstance', () => { + assert.strictEqual( + algosdk.LogicSigAccount[Symbol.hasInstance](logicSigAccount), + true + ); + }); + + it('should work with cross-module simulation', () => { + const mockLogicSigAccount = { + _isAlgosdkLogicSigAccount: true, + lsig: {}, + }; + assert.strictEqual( + algosdk.LogicSigAccount[Symbol.hasInstance](mockLogicSigAccount), + true + ); + }); + }); + + describe('AtomicTransactionComposer Symbol.hasInstance', () => { + let composer: algosdk.AtomicTransactionComposer; + + beforeEach(() => { + composer = new algosdk.AtomicTransactionComposer(); + }); + + it('should work with regular instanceof', () => { + assert.strictEqual( + composer instanceof algosdk.AtomicTransactionComposer, + true + ); + }); + + it('should work with custom Symbol.hasInstance', () => { + assert.strictEqual( + algosdk.AtomicTransactionComposer[Symbol.hasInstance](composer), + true + ); + }); + + it('should work with cross-module simulation', () => { + const mockComposer = { + _isAlgosdkAtomicTransactionComposer: true, + status: 'BUILDING', + }; + assert.strictEqual( + algosdk.AtomicTransactionComposer[Symbol.hasInstance](mockComposer), + true + ); + }); + }); + + describe('ABIContract Symbol.hasInstance', () => { + let contract: ABIContract; + + beforeEach(() => { + contract = new ABIContract({ + name: 'TestContract', + methods: [{ name: 'test', args: [], returns: { type: 'void' } }], + }); + }); + + it('should work with regular instanceof', () => { + assert.strictEqual(contract instanceof ABIContract, true); + }); + + it('should work with custom Symbol.hasInstance', () => { + assert.strictEqual(ABIContract[Symbol.hasInstance](contract), true); + }); + + it('should work with cross-module simulation', () => { + const mockContract = { + _isAlgosdkABIContract: true, + name: 'MockContract', + methods: [], + }; + assert.strictEqual(ABIContract[Symbol.hasInstance](mockContract), true); + }); + }); + + describe('ABI Type Symbol.hasInstance', () => { + it('should work for ABIUintType', () => { + const uintType = new algosdk.ABIUintType(64); + assert.strictEqual(uintType instanceof algosdk.ABIUintType, true); + assert.strictEqual( + algosdk.ABIUintType[Symbol.hasInstance](uintType), + true + ); + + const mockUintType = { _isAlgosdkABIUintType: true, bitSize: 64 }; + assert.strictEqual( + algosdk.ABIUintType[Symbol.hasInstance](mockUintType), + true + ); + }); + + it('should work for ABIAddressType', () => { + const addressType = new algosdk.ABIAddressType(); + assert.strictEqual(addressType instanceof algosdk.ABIAddressType, true); + assert.strictEqual( + algosdk.ABIAddressType[Symbol.hasInstance](addressType), + true + ); + + const mockAddressType = { _isAlgosdkABIAddressType: true }; + assert.strictEqual( + algosdk.ABIAddressType[Symbol.hasInstance](mockAddressType), + true + ); + }); + + it('should work for ABIBoolType', () => { + const boolType = new algosdk.ABIBoolType(); + assert.strictEqual(boolType instanceof algosdk.ABIBoolType, true); + assert.strictEqual( + algosdk.ABIBoolType[Symbol.hasInstance](boolType), + true + ); + + const mockBoolType = { _isAlgosdkABIBoolType: true }; + assert.strictEqual( + algosdk.ABIBoolType[Symbol.hasInstance](mockBoolType), + true + ); + }); + + it('should work for ABIStringType', () => { + const stringType = new algosdk.ABIStringType(); + assert.strictEqual(stringType instanceof algosdk.ABIStringType, true); + assert.strictEqual( + algosdk.ABIStringType[Symbol.hasInstance](stringType), + true + ); + + const mockStringType = { _isAlgosdkABIStringType: true }; + assert.strictEqual( + algosdk.ABIStringType[Symbol.hasInstance](mockStringType), + true + ); + }); + + it('should work for ABIArrayStaticType', () => { + const elementType = new algosdk.ABIUintType(64); + const arrayType = new algosdk.ABIArrayStaticType(elementType, 5); + assert.strictEqual(arrayType instanceof algosdk.ABIArrayStaticType, true); + assert.strictEqual( + algosdk.ABIArrayStaticType[Symbol.hasInstance](arrayType), + true + ); + + const mockArrayType = { + _isAlgosdkABIArrayStaticType: true, + childType: {}, + staticLength: 5, + }; + assert.strictEqual( + algosdk.ABIArrayStaticType[Symbol.hasInstance](mockArrayType), + true + ); + }); + + it('should work for ABIArrayDynamicType', () => { + const elementType = new algosdk.ABIUintType(64); + const arrayType = new algosdk.ABIArrayDynamicType(elementType); + assert.strictEqual( + arrayType instanceof algosdk.ABIArrayDynamicType, + true + ); + assert.strictEqual( + algosdk.ABIArrayDynamicType[Symbol.hasInstance](arrayType), + true + ); + + const mockArrayType = { + _isAlgosdkABIArrayDynamicType: true, + childType: {}, + }; + assert.strictEqual( + algosdk.ABIArrayDynamicType[Symbol.hasInstance](mockArrayType), + true + ); + }); + + it('should work for ABITupleType', () => { + const childTypes = [ + new algosdk.ABIUintType(64), + new algosdk.ABIBoolType(), + ]; + const tupleType = new algosdk.ABITupleType(childTypes); + assert.strictEqual(tupleType instanceof algosdk.ABITupleType, true); + assert.strictEqual( + algosdk.ABITupleType[Symbol.hasInstance](tupleType), + true + ); + + const mockTupleType = { _isAlgosdkABITupleType: true, childTypes: [] }; + assert.strictEqual( + algosdk.ABITupleType[Symbol.hasInstance](mockTupleType), + true + ); + }); + }); + + describe('Edge cases', () => { + it('should handle primitive values', () => { + assert.strictEqual(algosdk.Address[Symbol.hasInstance]('string'), false); + assert.strictEqual(algosdk.Address[Symbol.hasInstance](123), false); + assert.strictEqual(algosdk.Address[Symbol.hasInstance](true), false); + }); + + it('should handle empty objects', () => { + assert.strictEqual(algosdk.Address[Symbol.hasInstance]({}), false); + assert.strictEqual(algosdk.Transaction[Symbol.hasInstance]({}), false); + }); + + it('should handle objects with wrong marker values', () => { + const wrongMarker = { _isAlgosdkAddress: 'true' }; // string instead of boolean + assert.strictEqual( + algosdk.Address[Symbol.hasInstance](wrongMarker), + false + ); + }); + }); +});