Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/__tests__/token-registry-functions/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { ethers as ethersV5 } from 'ethers';
import { JsonRpcProvider as JsonRpcProviderV6 } from 'ethersV6';
export const MOCK_V5_ADDRESS = '0xV5TokenRegistryContract';
export const MOCK_V4_ADDRESS = '0xV4TokenRegistryContract';
export const MOCK_OWNER_ADDRESS = '0xowner';

vi.mock('src/core', () => ({
vi.mock('../../core', () => ({
encrypt: vi.fn(() => 'encrypted_remarks'),
getTitleEscrowAddress: vi.fn(),
isTitleEscrowVersion: vi.fn(() => Promise.resolve(true)),
Expand All @@ -16,7 +17,7 @@ vi.mock('src/core', () => ({
},
}));

vi.mock('src/token-registry-v5', () => {
vi.mock('../../token-registry-v5', () => {
return {
v5Contracts: {
TitleEscrow__factory: {
Expand All @@ -34,11 +35,12 @@ vi.mock('src/token-registry-v5', () => {
TradeTrustTokenMintable: '0xTradeTrustTokenMintableIdV5',
TradeTrustTokenRestorable: '0xTradeTrustTokenRestorableIdV5',
TradeTrustTokenBurnable: '0xTradeTrustTokenBurnableIdV5',
SBT: '0xSBTIdV5',
},
};
});

vi.mock('src/token-registry-v4', () => {
vi.mock('../../token-registry-v4', () => {
return {
v4Contracts: {
TitleEscrow__factory: {
Expand All @@ -56,6 +58,7 @@ vi.mock('src/token-registry-v4', () => {
TradeTrustTokenMintable: '0xTradeTrustTokenMintableIdV4',
TradeTrustTokenRestorable: '0xTradeTrustTokenRestorableIdV4',
TradeTrustTokenBurnable: '0xTradeTrustTokenBurnableIdV4',
SBT: '0xSBTIdV4',
},
};
});
Expand All @@ -78,6 +81,7 @@ export const mockV5TradeTrustTokenContract = {
burn: vi.fn(() => Promise.resolve('v5_burn_tx_hash')),
restore: vi.fn(() => Promise.resolve('v5_restore_tx_hash')),
mint: vi.fn(() => Promise.resolve('v5_mint_tx_hash')),
ownerOf: vi.fn(() => Promise.resolve(MOCK_OWNER_ADDRESS)),
};

export const mockV5TitleEscrowContract = {
Expand Down Expand Up @@ -139,6 +143,7 @@ export const mockV4TradeTrustTokenContract = {
burn: vi.fn(() => Promise.resolve('v4_burn_tx_hash')),
restore: vi.fn(() => Promise.resolve('v4_restore_tx_hash')),
mint: vi.fn(() => Promise.resolve('v4_mint_tx_hash')),
ownerOf: vi.fn(() => Promise.resolve(MOCK_OWNER_ADDRESS)),
};

export const PRIVATE_KEY = '0x59c6995e998f97a5a004497e5f1ebce0c16828d44b3f8d0bfa3a89d271d5b6b9'; // random local key
Expand Down
14 changes: 7 additions & 7 deletions src/__tests__/token-registry-functions/mint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import './fixtures.js';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ethers as ethersV5, Wallet as WalletV5 } from 'ethers';
import { Wallet as WalletV6, Network, ethers as ethersV6 } from 'ethersV6';
import * as coreModule from 'src/core';
import * as coreModule from '../../core';

import { CHAIN_ID } from '@tradetrust-tt/tradetrust-utils';
import { v5Contracts } from 'src/token-registry-v5';
import { v4Contracts } from 'src/token-registry-v4';
import { mint } from 'src/token-registry-functions/mint';
import { v5Contracts } from '../../token-registry-v5';
import { v4Contracts } from '../../token-registry-v4';
import { mint } from '../../token-registry-functions';
import {
MOCK_V4_ADDRESS,
MOCK_V5_ADDRESS,
Expand All @@ -17,7 +17,7 @@ import {
providerV5,
providerV6,
} from './fixtures.js';
import { ProviderInfo } from 'src/token-registry-functions/types.js';
import { ProviderInfo } from '../../token-registry-functions/types';

const providers: ProviderInfo[] = [
{
Expand Down Expand Up @@ -55,11 +55,11 @@ describe('Mint Token', () => {
let wallet: ethersV5.Wallet | ethersV6.Wallet;
if (ethersVersion === 'v5') {
wallet = new WalletV5(PRIVATE_KEY, Provider as any) as ethersV5.Wallet;
vi.spyOn(wallet, 'getChainId').mockResolvedValue(CHAIN_ID.local as unknown as number);
vi.spyOn(wallet, 'getChainId').mockResolvedValue(mockChainId as unknown as number);
} else {
wallet = new WalletV6(PRIVATE_KEY, Provider as any);
vi.spyOn(Provider, 'getNetwork').mockResolvedValue({
chainId: CHAIN_ID.local,
chainId: mockChainId,
} as unknown as Network);
}
const mockTokenRegistryAddress = isV5TT ? MOCK_V5_ADDRESS : MOCK_V4_ADDRESS;
Expand Down
146 changes: 146 additions & 0 deletions src/__tests__/token-registry-functions/ownerOf.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import './fixtures.js';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ethers as ethersV5, Wallet as WalletV5 } from 'ethers';
import { Wallet as WalletV6, Network, ethers as ethersV6 } from 'ethersV6';
import * as coreModule from '../../core';
import { CHAIN_ID } from '@tradetrust-tt/tradetrust-utils';
import { ownerOf } from '../../token-registry-functions';
import { v5Contracts } from '../../token-registry-v5';
import { v4Contracts } from '../../token-registry-v4';
import {
MOCK_OWNER_ADDRESS,
MOCK_V4_ADDRESS,
MOCK_V5_ADDRESS,
PRIVATE_KEY,
providerV5,
providerV6,
} from './fixtures';
import { ProviderInfo } from '../../token-registry-functions/types';

const providers: ProviderInfo[] = [
{
Provider: providerV5,
ethersVersion: 'v5',
titleEscrowVersion: 'v5',
},
{
Provider: providerV5,
ethersVersion: 'v5',
titleEscrowVersion: 'v4',
},
{
Provider: providerV6,
ethersVersion: 'v6',
titleEscrowVersion: 'v5',
},
{
Provider: providerV6,
ethersVersion: 'v6',
titleEscrowVersion: 'v4',
},
];

describe.each(providers)(
'ownerOf function for ethers version $ethersVersion and TR version $titleEscrowVersion',
({ Provider, ethersVersion, titleEscrowVersion }) => {
const mockTokenId = '0xTokenId';
const mockChainId = CHAIN_ID.local;
const isV5TT = titleEscrowVersion === 'v5';
// let mockContract = isV5TT ? mockV5TradeTrustTokenContract : mockV4TradeTrustTokenContract;

let wallet: ethersV5.Wallet | ethersV6.Wallet;
if (ethersVersion === 'v5') {
wallet = new WalletV5(PRIVATE_KEY, Provider as any) as ethersV5.Wallet;
vi.spyOn(wallet, 'getChainId').mockResolvedValue(mockChainId as unknown as number);
} else {
wallet = new WalletV6(PRIVATE_KEY, Provider as any);
vi.spyOn(Provider, 'getNetwork').mockResolvedValue({
chainId: mockChainId,
} as unknown as Network);
}
const mockTokenRegistryAddress = isV5TT ? MOCK_V5_ADDRESS : MOCK_V4_ADDRESS;

beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(coreModule, 'checkSupportsInterface').mockImplementation(
async (address, interfaceId) => {
return interfaceId === (isV5TT ? '0xSBTIdV5' : '0xSBTIdV4');
},
);
});

// afterEach(() => {
// vi.restoreAllMocks();
// });

describe('Successful Calls', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should return owner for V5/v4 contract (auto-detected)', async () => {
const result = await ownerOf(
{ tokenRegistryAddress: mockTokenRegistryAddress },
wallet,
{ tokenId: mockTokenId },
{},
);

expect(result).toBe(MOCK_OWNER_ADDRESS);
expect(
(isV5TT ? v5Contracts : v4Contracts).TradeTrustToken__factory.connect,
).toHaveBeenCalled();
});

it('should return owner for V5/v4 contract (explicit version)', async () => {
const result = await ownerOf(
{ tokenRegistryAddress: mockTokenRegistryAddress },
wallet,
{ tokenId: mockTokenId },
{ titleEscrowVersion },
);

expect(result).toBe(MOCK_OWNER_ADDRESS);
expect(coreModule.checkSupportsInterface).not.toHaveBeenCalled();
});
});

describe('Error Handling', () => {
it('should throw when token registry address is missing', async () => {
await expect(
ownerOf(
{ tokenRegistryAddress: '' },
wallet,
{ tokenId: mockTokenId },
{ chainId: mockChainId },
),
).rejects.toThrow('Token registry address is required');
});

it('should throw when provider is missing', async () => {
const signerWithoutProvider = new WalletV5('0x'.padEnd(66, '1'));

await expect(
ownerOf(
{ tokenRegistryAddress: mockTokenRegistryAddress },
signerWithoutProvider,
{ tokenId: mockTokenId },
{ chainId: mockChainId },
),
).rejects.toThrow('Provider is required');
});

it('should throw when version is unsupported', async () => {
vi.spyOn(coreModule, 'checkSupportsInterface').mockResolvedValue(false);

await expect(
ownerOf(
{ tokenRegistryAddress: mockTokenRegistryAddress },
wallet,
{ tokenId: mockTokenId },
{ chainId: mockChainId },
),
).rejects.toThrow('Only Token Registry V4/V5 is supported');
});
});
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import './fixtures.js';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ethers as ethersV5, Wallet as WalletV5 } from 'ethers';
import { ethers as ethersV6, Network, Wallet as WalletV6 } from 'ethersV6';
import * as coreModule from 'src/core';
import * as coreModule from '../../core';
import { CHAIN_ID } from '@tradetrust-tt/tradetrust-utils';
import {
rejectTransferBeneficiary,
rejectTransferHolder,
rejectTransferOwners,
} from 'src/token-registry-functions/rejectTransfers';
} from '../../token-registry-functions/rejectTransfers';
import { mockV5TitleEscrowContract, PRIVATE_KEY, providerV5, providerV6 } from './fixtures';
import { ProviderInfo } from 'src/token-registry-functions/types.js';
import { ProviderInfo } from '../../token-registry-functions/types.js';

const providers: ProviderInfo[] = [
{
Expand Down
10 changes: 5 additions & 5 deletions src/__tests__/token-registry-functions/returnToken.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import './fixtures.js';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ethers as ethersV5, Wallet as WalletV5 } from 'ethers';
import { Wallet as WalletV6, Network, ethers as ethersV6 } from 'ethersV6';
import * as coreModule from 'src/core';
import * as coreModule from '../../core';

import { CHAIN_ID } from '@tradetrust-tt/tradetrust-utils';
import { v5Contracts } from 'src/token-registry-v5';
import { v4Contracts } from 'src/token-registry-v4';
import { v5Contracts } from '../../token-registry-v5';
import { v4Contracts } from '../../token-registry-v4';
import {
acceptReturned,
rejectReturned,
returnToIssuer,
} from 'src/token-registry-functions/returnToken';
} from '../../token-registry-functions/returnToken';
import {
MOCK_V4_ADDRESS,
MOCK_V5_ADDRESS,
Expand All @@ -23,7 +23,7 @@ import {
providerV5,
providerV6,
} from './fixtures.js';
import { ProviderInfo } from 'src/token-registry-functions/types.js';
import { ProviderInfo } from '../../token-registry-functions/types.js';

const providers: ProviderInfo[] = [
{
Expand Down
8 changes: 4 additions & 4 deletions src/__tests__/token-registry-functions/transfers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import './fixtures.js';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ethers as ethersV5, Wallet as WalletV5 } from 'ethers';
import { ethers as ethersV6, Network, Wallet as WalletV6 } from 'ethersV6';
import * as coreModule from 'src/core';
import { encrypt } from 'src/core';
import * as coreModule from '../../core';
import { encrypt } from '../../core';
import { CHAIN_ID } from '@tradetrust-tt/tradetrust-utils';
import {
transferBeneficiary,
transferHolder,
transferOwners,
nominate,
} from 'src/token-registry-functions';
import { ProviderInfo } from 'src/token-registry-functions/types';
} from '../../token-registry-functions';
import { ProviderInfo } from '../../token-registry-functions/types';
import {
mockV4TitleEscrowContract,
mockV5TitleEscrowContract,
Expand Down
10 changes: 4 additions & 6 deletions src/core/endorsement-chain/useEndorsementChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,16 @@ export const getDocumentOwner = async (

// Check Title Escrow Interface Support
export const checkSupportsInterface = async (
titleEscrowAddress: string,
contractAddress: string,
interfaceId: string,
provider: Provider | ethersV6.Provider,
): Promise<boolean> => {
try {
const Contract = getEthersContractFromProvider(provider);
const titleEscrowAbi = [
'function supportsInterface(bytes4 interfaceId) external view returns (bool)',
];
const abi = ['function supportsInterface(bytes4 interfaceId) external view returns (bool)'];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const titleEscrowContract = new Contract(titleEscrowAddress, titleEscrowAbi, provider as any);
return await titleEscrowContract.supportsInterface(interfaceId);
const contract = new Contract(contractAddress, abi, provider as any);
return await contract.supportsInterface(interfaceId);
} catch {
return false;
}
Expand Down
1 change: 1 addition & 0 deletions src/token-registry-functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './transfer';
export * from './rejectTransfers';
export * from './returnToken';
export * from './mint';
export * from './ownerOf';
6 changes: 3 additions & 3 deletions src/token-registry-functions/mint.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { checkSupportsInterface, encrypt } from 'src/core';
import { v5Contracts, v5SupportInterfaceIds } from 'src/token-registry-v5';
import { v4Contracts, v4SupportInterfaceIds } from 'src/token-registry-v4';
import { checkSupportsInterface, encrypt } from '../core';
import { v5Contracts, v5SupportInterfaceIds } from '../token-registry-v5';
import { v4Contracts, v4SupportInterfaceIds } from '../token-registry-v4';
import { Signer as SignerV6 } from 'ethersV6';
import { ContractTransaction, Signer } from 'ethers';
import { getTxOptions } from './utils';
Expand Down
Loading