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
44 changes: 36 additions & 8 deletions src/integration/blockchain/spark/spark-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export interface SparkFeeEstimate {
}

export class SparkClient extends BlockchainClient {
private static readonly INIT_TIMEOUT_MS = 60_000;

private readonly logger = new DfxLogger(SparkClient);

private wallet: AsyncField<SparkWallet>;
Expand All @@ -59,6 +61,13 @@ export class SparkClient extends BlockchainClient {
this.startTokenOptimization();
}

resetWallet(): void {
this.logger.warn('Spark wallet reset triggered externally');
this.wallet.reset();
this.cachedAddress.reset();
this.reconnectWallet();
}

private async call<T>(operation: (wallet: SparkWallet) => Promise<T>): Promise<T> {
try {
const wallet = await this.wallet;
Expand Down Expand Up @@ -153,14 +162,7 @@ export class SparkClient extends BlockchainClient {

for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const { wallet } = await SparkWallet.initialize({
mnemonicOrSeed: GetConfig().blockchain.spark.sparkWalletSeed,
accountNumber: 0,
options: {
network: 'MAINNET',
tokenOptimizationOptions: { enabled: false },
},
});
const wallet = await this.initializeWithTimeout();

wallet.on('stream:disconnected', () => this.reconnectWallet());

Expand All @@ -180,6 +182,32 @@ export class SparkClient extends BlockchainClient {
throw new Error('Spark wallet initialization failed after all retries');
}

private initializeWithTimeout(): Promise<SparkWallet> {
return new Promise<SparkWallet>((resolve, reject) => {
const timer = setTimeout(
() => reject(new Error(`Spark wallet initialization timed out after ${SparkClient.INIT_TIMEOUT_MS / 1000}s`)),
SparkClient.INIT_TIMEOUT_MS,
);

SparkWallet.initialize({
mnemonicOrSeed: GetConfig().blockchain.spark.sparkWalletSeed,
accountNumber: 0,
options: {
network: 'MAINNET',
tokenOptimizationOptions: { enabled: false },
},
})
.then(({ wallet }) => {
clearTimeout(timer);
resolve(wallet);
})
.catch((e) => {
clearTimeout(timer);
reject(e);
});
});
}

private startTokenOptimization(): void {
if (this.tokenOptimizationInterval) clearInterval(this.tokenOptimizationInterval);

Expand Down
5 changes: 4 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import { PricingService } from './subdomains/supporting/pricing/services/pricing
process.on('uncaughtException', (error) => {
const logger = new DfxLogger('UncaughtException');

if (error?.constructor?.name?.includes('Spark') || error?.message?.includes('Channel has been shut down')) {
const isSparkError =
error?.constructor?.name?.includes('Spark') || error?.message?.includes('Channel has been shut down');

if (isSparkError) {
logger.error('Spark SDK uncaught exception (process kept alive):', error);
return;
}
Expand Down
Loading