@noves/noves-sdk
Version:
Noves Developer Kit
244 lines (216 loc) • 10.4 kB
text/typescript
/**
* 1.5.0 Release Test — Core Translate APIs (regression)
*
* Validates that all existing Translate ecosystems still work:
* - EVM, SVM, UTXO, Cosmos, TVM, Polkadot, XRPL
* - Factory pattern, getChains(), getTransaction(), pagination
* - Uses real hashes obtained dynamically from getTransactions()
*/
import { Translate, TranslateEVM, TransactionsPage, ErrorType, TransactionError } from '../dist/index';
import { section, pass, fail, runTest, skip } from './helpers';
const API_KEY = process.env.NOVES_API_KEY!;
/** Extract a tx hash from a transaction object, trying multiple field paths */
function extractHash(tx: any, debug?: string): string | null {
const hash = tx?.rawTransactionData?.transactionHash
|| tx?.rawTransactionData?.txHash
|| tx?.rawTransactionData?.hash
|| tx?.rawTransactionData?.digest
|| tx?.rawTransactionData?.signature
|| tx?.txHash
|| tx?.transactionHash
|| tx?.hash
|| tx?.digest
|| tx?.signature
|| null;
if (!hash && debug) {
console.log(` DEBUG ${debug} tx keys: ${JSON.stringify(Object.keys(tx || {}))}`);
if (tx?.rawTransactionData) {
console.log(` DEBUG ${debug} rawTxData keys: ${JSON.stringify(Object.keys(tx.rawTransactionData))}`);
}
}
return hash;
}
export async function testCoreTranslate() {
section('CORE: Translate APIs (Regression)');
// ===== EVM =====
await runTest('EVM.getChains()', async () => {
const translate = Translate.evm(API_KEY);
const chains = await translate.getChains();
if (!chains || (Array.isArray(chains) && chains.length === 0)) {
throw new Error('EVM getChains() empty');
}
pass('EVM.getChains()', `OK`);
});
await runTest('EVM.getTransactions(eth, pagination)', async () => {
const translate = Translate.evm(API_KEY);
const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; // vitalik.eth
const page = await translate.getTransactions('eth', address, { pageSize: 3 });
const txs = page.getTransactions();
if (!txs || txs.length === 0) throw new Error('No EVM transactions');
pass('EVM.getTransactions()', `Got ${txs.length} txs, hasNext=${page.hasNext()}`);
});
// EVM getTransaction — get a real hash first
await runTest('EVM.getTransaction(eth, real hash)', async () => {
const translate = Translate.evm(API_KEY);
const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
const page = await translate.getTransactions('eth', address, { pageSize: 1 });
const txs = page.getTransactions();
if (!txs || txs.length === 0) throw new Error('No txs to get hash');
const hash = extractHash(txs[0]);
if (!hash) throw new Error('Could not extract hash');
const tx = await translate.getTransaction('eth', hash);
if (!tx || !tx.classificationData) throw new Error('getTransaction returned invalid');
pass('EVM.getTransaction()', `type="${tx.classificationData.type}", hash=${hash.substring(0, 18)}...`);
});
await runTest('EVM.getTransaction(v5 format, real hash)', async () => {
const translate = Translate.evm(API_KEY);
const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
const page = await translate.getTransactions('eth', address, { pageSize: 1 });
const txs = page.getTransactions();
const hash = extractHash(txs[0]);
if (!hash) throw new Error('No hash');
const tx = await translate.getTransaction('eth', hash, 5);
if (!tx) throw new Error('v5 returned null');
pass('EVM.getTransaction(v5)', `keys: ${Object.keys(tx).join(', ').substring(0, 80)}`);
});
// ===== SVM (Solana) =====
await runTest('SVM.getChains()', async () => {
const translate = Translate.svm(API_KEY);
const chains = await translate.getChains();
if (!chains) throw new Error('SVM getChains() empty');
pass('SVM.getChains()', `OK`);
});
await runTest('SVM.getTransaction(solana, real hash)', async () => {
const translate = Translate.svm(API_KEY);
// Get a real hash from getTransactions
const address = 'DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK'; // known active
const page = await translate.getTransactions('solana', address, { pageSize: 1 });
const txs = page.getTransactions();
if (!txs || txs.length === 0) throw new Error('No SVM transactions found');
const hash = extractHash(txs[0], 'SVM');
if (!hash) throw new Error('No SVM hash');
const tx = await translate.getTransaction('solana', hash);
if (!tx) throw new Error('SVM getTransaction returned null');
pass('SVM.getTransaction()', `hash=${hash.substring(0, 18)}..., type="${(tx as any).classificationData?.type || (tx as any).type}"`);
});
// ===== UTXO (Bitcoin) =====
await runTest('UTXO.getChains()', async () => {
const translate = Translate.utxo(API_KEY);
const chains = await translate.getChains();
if (!chains || chains.length === 0) throw new Error('UTXO getChains() empty');
pass('UTXO.getChains()', `Got ${chains.length} chains`);
});
await runTest('UTXO.getAddressesByMasterKey()', async () => {
const translate = Translate.utxo(API_KEY);
const result = await translate.getAddressesByMasterKey(
'xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz',
{ count: 3, addressType: 'Legacy' }
);
if (!result) throw new Error('getAddressesByMasterKey returned null');
pass('UTXO.getAddressesByMasterKey()', `Result keys: ${Object.keys(result).join(', ')}`);
});
// ===== Cosmos =====
await runTest('Cosmos.getChains()', async () => {
const translate = Translate.cosmos(API_KEY);
const chains = await translate.getChains();
if (!chains) throw new Error('Cosmos getChains() empty');
pass('Cosmos.getChains()', `OK`);
});
// ===== TVM (Tron) =====
await runTest('TVM.getChains()', async () => {
const translate = Translate.tvm(API_KEY);
const chains = await translate.getChains();
if (!chains) throw new Error('TVM getChains() empty');
pass('TVM.getChains()', `OK`);
});
// ===== Polkadot =====
await runTest('Polkadot.getChains()', async () => {
const translate = Translate.polkadot(API_KEY);
const chains = await translate.getChains();
if (!chains) throw new Error('Polkadot getChains() empty');
pass('Polkadot.getChains()', `OK`);
});
// ===== XRPL =====
await runTest('XRPL.getChains()', async () => {
const translate = Translate.xrpl(API_KEY);
const chains = await translate.getChains();
if (!chains) throw new Error('XRPL getChains() empty');
pass('XRPL.getChains()', `OK`);
});
await runTest('XRPL.getTransactions(xrpl)', async () => {
const translate = Translate.xrpl(API_KEY);
const address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe';
const page = await translate.getTransactions('xrpl', address, { pageSize: 2 });
const txs = page.getTransactions();
if (!txs || txs.length === 0) throw new Error('No XRPL transactions');
pass('XRPL.getTransactions()', `Got ${txs.length} txs, hasNext=${page.hasNext()}`);
});
await runTest('XRPL.getTransaction(xrpl, real hash)', async () => {
const translate = Translate.xrpl(API_KEY);
const address = 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe';
const page = await translate.getTransactions('xrpl', address, { pageSize: 1 });
const txs = page.getTransactions();
if (!txs || txs.length === 0) throw new Error('No XRPL transactions');
// XRPL uses 'signature' in rawTransactionData instead of 'transactionHash'
const tx0 = txs[0] as any;
const hash = tx0?.rawTransactionData?.signature
|| tx0?.rawTransactionData?.transactionHash
|| tx0?.rawTransactionData?.hash;
if (!hash) {
const keys = tx0?.rawTransactionData ? Object.keys(tx0.rawTransactionData).join(', ') : 'no rawTxData';
throw new Error(`No hash found in XRPL tx. rawTransactionData keys: ${keys}`);
}
const tx = await translate.getTransaction('xrpl', hash);
if (!tx) throw new Error('XRPL getTransaction returned null');
pass('XRPL.getTransaction()', `type="${(tx as any).classificationData?.type}", hash=${hash.substring(0, 16)}...`);
});
// ===== Factory pattern with SDKOptions =====
await runTest('Factory: SDKOptions with retryConfig string', async () => {
const translate = Translate.evm({ apiKey: API_KEY, retryConfig: 'PRODUCTION' });
const chains = await translate.getChains();
if (!chains) throw new Error('Factory with PRODUCTION preset failed');
pass('Factory: SDKOptions', 'retryConfig="PRODUCTION" works');
});
await runTest('Factory: SDKOptions with environment', async () => {
const translate = Translate.evm({ apiKey: API_KEY, environment: 'production' });
const chains = await translate.getChains();
if (!chains) throw new Error('Factory with environment failed');
pass('Factory: environment', 'environment="production" works');
});
// ===== Direct class instantiation =====
await runTest('Direct: new TranslateEVM()', async () => {
const translate = new TranslateEVM(API_KEY);
const chains = await translate.getChains();
if (!chains) throw new Error('Direct class instantiation failed');
pass('Direct: TranslateEVM', 'Direct class instantiation works');
});
// ===== Error types =====
await runTest('ErrorType: exports are available', async () => {
if (!ErrorType || !TransactionError) {
throw new Error('ErrorType or TransactionError not exported');
}
const types = [
ErrorType.UNAUTHORIZED,
ErrorType.RATE_LIMIT_EXCEEDED,
ErrorType.NETWORK_ERROR,
ErrorType.UNKNOWN_ERROR
];
if (types.some(t => t === undefined)) {
throw new Error('Some ErrorType values are undefined');
}
pass('ErrorType: exports', `Available: ${Object.keys(ErrorType).length} error types`);
});
// ===== TransactionsPage export =====
await runTest('TransactionsPage: export and static methods', async () => {
if (!TransactionsPage) {
throw new Error('TransactionsPage not exported');
}
if (typeof TransactionsPage.fromCursor !== 'function') {
throw new Error('TransactionsPage.fromCursor not available');
}
if (typeof TransactionsPage.decodeCursor !== 'function') {
throw new Error('TransactionsPage.decodeCursor not available');
}
pass('TransactionsPage', 'Exported with fromCursor() and decodeCursor()');
});
}