Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
828150c
test: shared sendTransaction tests for both facades
tuliomir Mar 31, 2026
ca9c346
Merge remote-tracking branch 'origin/master' into test/shared-send-tr…
tuliomir Apr 1, 2026
e2624ee
fix(test): resolve integration test failures
tuliomir Apr 1, 2026
6240d5a
fix: null guard
tuliomir Apr 1, 2026
9acf2d4
fix(test): ensure walletTxs cleanup and assertions
tuliomir Apr 1, 2026
fc3fb68
Merge branch 'master' into test/shared-send-transaction
tuliomir Apr 1, 2026
baf474f
fix(test): address PR review comments for send-transaction tests
tuliomir Apr 1, 2026
59ab89a
refact(test): move service-specific sendTransaction tests to shared
tuliomir Apr 2, 2026
629eed7
chore: remove unused imports in hathorwallet_facade test
tuliomir Apr 2, 2026
89ccc84
test: migrate token send tests to shared suite
tuliomir Apr 2, 2026
2f7804f
test: migrate address tracking test to shared suite
tuliomir Apr 2, 2026
9490cee
fix: address review comments and itest failures
tuliomir Apr 2, 2026
43059a9
test: add fee token edge case tests
tuliomir Apr 2, 2026
ef3e2b1
fix: native token version defaulting to deposit (#1054)
raul-oliveira Apr 2, 2026
a8ad5f7
fix: isolates change address test
tuliomir Apr 3, 2026
c1bcc92
chore: workaround for input bug
tuliomir Apr 6, 2026
3e77f0f
Merge branch 'master' into test/shared-send-transaction
tuliomir Apr 6, 2026
4c027f2
fix: address PR review on fee validation
tuliomir Apr 8, 2026
dc321ca
Merge remote-tracking branch 'origin/master' into test/shared-send-tr…
tuliomir Apr 8, 2026
5b978aa
fix: linter
tuliomir Apr 8, 2026
b81a2d0
docs: improves comment messages
tuliomir Apr 9, 2026
18bc8e9
Merge remote-tracking branch 'origin/master'
tuliomir Apr 17, 2026
fdef446
feat(test): add recvWallet option to adapters
tuliomir Apr 22, 2026
10e61f1
test: address Carneiro's review on PR 1053
tuliomir Apr 22, 2026
d200936
refactor(test): migrate remaining sendTransaction
tuliomir Apr 22, 2026
a3147fd
Merge branch 'master' into test/shared-send-transaction
tuliomir Apr 22, 2026
4a58909
refact(test): move recvWallet to waitForTx helper
tuliomir Apr 22, 2026
b556e5c
fix(test): restore dropped coverage from migration
tuliomir Apr 22, 2026
87ff71d
fix(test): fix integration test failures
tuliomir Apr 23, 2026
eef4dad
refact(test): rename TTS to DBT for clarity
tuliomir Apr 23, 2026
9619ae2
Merge remote-tracking branch 'origin/master' into test/shared-send-tr…
tuliomir Apr 23, 2026
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
91 changes: 86 additions & 5 deletions __tests__/integration/adapters/fullnode.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,21 @@ import { GenesisWalletHelper } from '../helpers/genesis-wallet.helper';
import { precalculationHelpers } from '../helpers/wallet-precalculation.helper';
import type { WalletStopOptions } from '../../../src/new/types';
import { FULLNODE_URL, NETWORK_NAME } from '../configuration/test-constants';
import type { FullNodeTxResponse } from '../../../src/wallet/types';
import type {
FuzzyWalletType,
IWalletTestAdapter,
WalletCapabilities,
CreateWalletOptions,
CreateWalletResult,
SendTransactionOptions,
SendTransactionResult,
CreateTokenOptions,
CreateTokenResult,
GetUtxosAdapterOptions,
GetUtxosResult,
AdapterOutput,
SendManyOutputsAdapterOptions,
AuthorityUtxoResult,
GetAuthorityUtxosOptions,
DelegateAuthorityAdapterOptions,
Expand Down Expand Up @@ -186,16 +193,57 @@ export class FullnodeWalletTestAdapter implements IWalletTestAdapter {
return result.hash;
}

async waitForTx(wallet: FuzzyWalletType, txId: string): Promise<void> {
async waitForTx(
wallet: FuzzyWalletType,
txId: string,
recvWallet?: FuzzyWalletType
): Promise<void> {
const hWallet = this.concrete(wallet);
await waitForTxReceived(hWallet, txId);
if (recvWallet) {
const hRecv = this.concrete(recvWallet);
await waitForTxReceived(hRecv, txId);
}
await waitUntilNextTimestamp(hWallet, txId);
}

getPrecalculatedWallet(): PrecalculatedWalletData {
return precalculationHelpers.test!.getPrecalculatedWallet();
}

async sendTransaction(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually, we have the "wait for tx received" on both wallets, the sending and receiving.
This is to avoid testing someting related to the wallet on either one and it still has not processed or received the tx.

Maybe we could have an optional recvWallet and call the wait helpers for it as well.
If recvWallet is not giver we ignore it.

What's your opinion on this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Added a recvWallet option to SendTransactionOptions — when provided, the adapter waits for the tx on the receiving wallet too. Backward-compatible since it's optional.

Implemented on fdef446.

wallet: FuzzyWalletType,
address: string,
amount: bigint,
options?: SendTransactionOptions
): Promise<SendTransactionResult> {
const hWallet = this.concrete(wallet);
const { recvWallet, ...txOptions } = options ?? {};
const result = await hWallet.sendTransaction(address, amount, {
pinCode: DEFAULT_PIN_CODE,
...txOptions,
});
if (!result || !result.hash) {
throw new Error('sendTransaction: transaction had no hash');
}
await this.waitForTx(wallet, result.hash, recvWallet);
return { hash: result.hash, transaction: result };
}

async getTx(wallet: FuzzyWalletType, txId: string) {
const result = await this.concrete(wallet).getTx(txId);
if (!result) {
throw new Error(`getTx: transaction ${txId} not found`);
}
return result;
}

async getFullTxById(wallet: FuzzyWalletType, txId: string): Promise<FullNodeTxResponse> {
// The fullnode facade returns FullNodeTxApiResponse (zod-inferred), which is structurally
// compatible with FullNodeTxResponse but has minor nullability differences.
return this.concrete(wallet).getFullTxById(txId) as Promise<FullNodeTxResponse>;
}

async createToken(
wallet: FuzzyWalletType,
name: string,
Expand All @@ -204,13 +252,46 @@ export class FullnodeWalletTestAdapter implements IWalletTestAdapter {
options?: CreateTokenOptions
): Promise<CreateTokenResult> {
const hWallet = this.concrete(wallet);
const response = await hWallet.createNewToken(name, symbol, amount, {
const result = await hWallet.createNewToken(name, symbol, amount, {
pinCode: DEFAULT_PIN_CODE,
...options,
});
await waitForTxReceived(hWallet, response.hash);
await waitUntilNextTimestamp(hWallet, response.hash);
return { hash: response.hash };
if (!result?.hash) {
throw new Error('createToken: transaction had no hash');
}
await waitForTxReceived(hWallet, result.hash);
await waitUntilNextTimestamp(hWallet, result.hash);
return { hash: result.hash, transaction: result };
}

async getUtxos(
wallet: FuzzyWalletType,
options?: GetUtxosAdapterOptions
): Promise<GetUtxosResult> {
const result = await this.concrete(wallet).getUtxos(options);
return {
total_amount_available: result.total_amount_available,
total_utxos_available: result.total_utxos_available,
utxos: result.utxos,
};
}

async sendManyOutputsTransaction(
wallet: FuzzyWalletType,
outputs: AdapterOutput[],
options?: SendManyOutputsAdapterOptions
): Promise<SendTransactionResult> {
const hWallet = this.concrete(wallet);
const { recvWallet, ...txOptions } = options ?? {};
const result = await hWallet.sendManyOutputsTransaction(outputs, {
pinCode: DEFAULT_PIN_CODE,
...txOptions,
});
if (!result?.hash) {
throw new Error('sendManyOutputsTransaction: transaction had no hash');
}
await this.waitForTx(wallet, result.hash, recvWallet);
return { hash: result.hash, transaction: result };
}

async getAuthorityUtxos(
Expand Down
102 changes: 96 additions & 6 deletions __tests__/integration/adapters/service.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import { HathorWalletServiceWallet } from '../../../src';
import { NATIVE_TOKEN_UID } from '../../../src/constants';
import config from '../../../src/config';
import { WalletTracker } from '../utils/wallet-tracker.util';
import type Transaction from '../../../src/models/transaction';
Expand All @@ -20,16 +21,24 @@ import {
import { GenesisWalletServiceHelper } from '../helpers/genesis-wallet.helper';
import { precalculationHelpers } from '../helpers/wallet-precalculation.helper';
import type { WalletStopOptions } from '../../../src/new/types';
import type { IHistoryTx } from '../../../src/types';
import { AuthorityType } from '../../../src/types';
import { NETWORK_NAME } from '../configuration/test-constants';
import type { FullNodeTxResponse } from '../../../src/wallet/types';
import type {
FuzzyWalletType,
IWalletTestAdapter,
WalletCapabilities,
CreateWalletOptions,
CreateWalletResult,
SendTransactionOptions,
SendTransactionResult,
CreateTokenOptions,
CreateTokenResult,
GetUtxosAdapterOptions,
GetUtxosResult,
AdapterOutput,
SendManyOutputsAdapterOptions,
AuthorityUtxoResult,
GetAuthorityUtxosOptions,
DelegateAuthorityAdapterOptions,
Expand Down Expand Up @@ -216,14 +225,65 @@ export class ServiceWalletTestAdapter implements IWalletTestAdapter {
return fundTx.hash;
}

async waitForTx(wallet: FuzzyWalletType, txId: string): Promise<void> {
async waitForTx(
wallet: FuzzyWalletType,
txId: string,
recvWallet?: FuzzyWalletType
): Promise<void> {
await pollForTx(this.concrete(wallet), txId);
if (recvWallet) {
await pollForTx(this.concrete(recvWallet), txId);
}
}

getPrecalculatedWallet(): PrecalculatedWalletData {
return precalculationHelpers.test!.getPrecalculatedWallet();
}

async sendTransaction(
wallet: FuzzyWalletType,
address: string,
amount: bigint,
options?: SendTransactionOptions
): Promise<SendTransactionResult> {
const sw = this.concrete(wallet);
const { recvWallet, ...txOptions } = options ?? {};
const result = await sw.sendTransaction(address, amount, {
pinCode: SERVICE_PIN,
...txOptions,
});
if (!result.hash) {
throw new Error('sendTransaction: transaction had no hash');
}
await this.waitForTx(wallet, result.hash, recvWallet);
return { hash: result.hash, transaction: result };
}

async getTx(wallet: FuzzyWalletType, txId: string) {
// HathorWalletServiceWallet.getTx() is not implemented — use fullnode API
// and map the response to IHistoryTx format (adding token UID to each output/input)
Comment on lines +262 to +264
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

polemic: getTx() is not implemented on the Wallet Service yet, but I wanted to add it to the shared tests to make them more complete. So I implemented a side-implementation as an adapter to allow for more shared tests before we fix the underlying issue.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you have the pr that address this guy? I believe it makes sense if you already have the implementation in hand.

const fullNodeResponse = await this.concrete(wallet).getFullTxById(txId);
const { tx } = fullNodeResponse;
const tokenUids = tx.tokens.map(t => t.uid);
const resolveToken = (tokenData: number) =>
tokenData === 0 ? NATIVE_TOKEN_UID : tokenUids[tokenData - 1];
return {
tx_id: tx.hash,
version: tx.version,
timestamp: tx.timestamp,
inputs: tx.inputs.map(i => ({ ...i, token: resolveToken(i.token_data) })),
outputs: tx.outputs.map(o => ({ ...o, token: resolveToken(o.token_data) })),
parents: tx.parents,
tokens: tx.tokens,
weight: tx.weight,
nonce: Number(tx.nonce),
} as unknown as IHistoryTx;
}

async getFullTxById(wallet: FuzzyWalletType, txId: string): Promise<FullNodeTxResponse> {
return this.concrete(wallet).getFullTxById(txId);
}

async createToken(
wallet: FuzzyWalletType,
name: string,
Expand All @@ -232,16 +292,46 @@ export class ServiceWalletTestAdapter implements IWalletTestAdapter {
options?: CreateTokenOptions
): Promise<CreateTokenResult> {
const sw = this.concrete(wallet);
const response = await sw.createNewToken(name, symbol, amount, {
const result = await sw.createNewToken(name, symbol, amount, {
...options,
pinCode: SERVICE_PIN,
});
if (!response.hash) {
if (!result?.hash) {
throw new Error('createToken: transaction had no hash');
}
await pollForTx(sw, response.hash);
await pollForTokenDetails(sw, response.hash);
return { hash: response.hash };
await pollForTx(sw, result.hash);
await pollForTokenDetails(sw, result.hash);
return { hash: result.hash, transaction: result };
}

async getUtxos(
wallet: FuzzyWalletType,
options?: GetUtxosAdapterOptions
): Promise<GetUtxosResult> {
const result = await this.concrete(wallet).getUtxos(options);
return {
total_amount_available: result.total_amount_available,
total_utxos_available: result.total_utxos_available,
utxos: result.utxos,
};
}

async sendManyOutputsTransaction(
wallet: FuzzyWalletType,
outputs: AdapterOutput[],
options?: SendManyOutputsAdapterOptions
): Promise<SendTransactionResult> {
const sw = this.concrete(wallet);
const { recvWallet, ...txOptions } = options ?? {};
const result = await sw.sendManyOutputsTransaction(outputs, {
pinCode: SERVICE_PIN,
...txOptions,
});
if (!result?.hash) {
throw new Error('sendManyOutputsTransaction: transaction had no hash');
}
await this.waitForTx(wallet, result.hash, recvWallet);
return { hash: result.hash, transaction: result };
}

async getAuthorityUtxos(
Expand Down
Loading
Loading