@robertprp/intents-sdk
Version:
Shogun Network Intent-based cross-chain swaps SDK
123 lines (103 loc) • 4.62 kB
text/typescript
import type { CreateCrossChainOrderParams } from '../core/orders/cross-chain.js';
import type { CreateDcaSingleChainOrderParams } from '../core/orders/dca-single-chain.js';
import type { CreateSingleChainOrderParams } from '../core/orders/single-chain.js';
import { ValidationError } from '../errors/index.js';
import type { IChainValidator } from './order-validator.js';
/**
* BaseValidator - Abstract class implementing common validation logic
* This follows the Template Method pattern
*/
export abstract class BaseValidator implements IChainValidator {
abstract isValidAddress(address: string): boolean;
abstract isValidTokenAddress(tokenAddress: string): boolean;
abstract isValidAmount(amount: bigint): boolean;
protected abstract getChainName(): string;
abstract validateCrossChainOrderFeasability(order: CreateCrossChainOrderParams & { user: string }): Promise<void>;
abstract validateSingleChainOrderFeasability(order: CreateSingleChainOrderParams & { user: string }): Promise<void>;
/**
* Validate source chain specific parameters of the order
*/
async validateSourceChain(order: CreateCrossChainOrderParams & { user: string }): Promise<void> {
this.validateTokenAddress(order.sourceTokenAddress);
this.validateAmount(order.sourceTokenAmount);
this.validateAmount(order.minStablecoinAmount ?? 1n);
await this.validateCrossChainOrderFeasability({ ...order, user: order.user });
}
async validateSingleChainOrder(order: CreateSingleChainOrderParams & { user: string }): Promise<void> {
this.validateTokenAddress(order.tokenIn);
this.validateTokenAddress(order.tokenOut);
this.validateAmount(order.amountIn);
this.validateAmount(order.amountOutMin ?? 1n);
if (order.stopLossMaxOut) {
this.validateAmount(order.stopLossMaxOut);
}
if (order.takeProfitMinOut) {
this.validateAmount(order.takeProfitMinOut);
}
await this.validateSingleChainOrderFeasability(order);
}
/**
* Validate destination chain specific parameters of the order
*/
validateDestinationChain(order: CreateCrossChainOrderParams): void {
this.validateAddress(order.destinationAddress);
this.validateTokenAddress(order.destinationTokenAddress);
this.validateAmount(order.destinationTokenMinAmount ?? 1n);
if (order.extraTransfers) {
for (const extraTransfer of order.extraTransfers) {
this.validateAddress(extraTransfer.receiver);
this.validateTokenAddress(extraTransfer.token);
this.validateAmount(extraTransfer.amount);
}
}
}
abstract validateDcaSingleChainOrderFeasability(
order: CreateDcaSingleChainOrderParams & { user: string },
): Promise<void>;
async validateDcaSingleChainOrder(order: CreateDcaSingleChainOrderParams & { user: string }): Promise<void> {
this.validateTokenAddress(order.tokenIn);
this.validateTokenAddress(order.tokenOut);
this.validateAmount(order.amountInPerInterval);
this.validateAmount(order.amountOutMin ?? 1n);
this.validateAddress(order.destinationAddress);
// Validate DCA-specific parameters
if (order.totalIntervals <= 0) {
throw new ValidationError('Total intervals must be greater than 0');
}
if (order.intervalDuration <= 0) {
throw new ValidationError('Interval duration must be greater than 0');
}
if (order.startTime < 0) {
throw new ValidationError('Start time must be non-negative');
}
// Validate total amount doesn't overflow
const totalAmount = order.amountInPerInterval * BigInt(order.totalIntervals);
this.validateAmount(totalAmount);
if (order.extraTransfers) {
for (const extraTransfer of order.extraTransfers) {
this.validateAddress(extraTransfer.receiver);
this.validateTokenAddress(extraTransfer.token);
this.validateAmount(extraTransfer.amount);
}
}
await this.validateDcaSingleChainOrderFeasability(order);
}
/**
* Helper methods that throw specific validation errors
*/
protected validateAddress(address: string): void {
if (!this.isValidAddress(address)) {
throw new ValidationError(`Invalid ${this.getChainName()} address: ${address}`);
}
}
protected validateTokenAddress(tokenAddress: string): void {
if (!this.isValidTokenAddress(tokenAddress)) {
throw new ValidationError(`Invalid ${this.getChainName()} token address: ${tokenAddress}`);
}
}
protected validateAmount(amount: bigint): void {
if (!this.isValidAmount(amount)) {
throw new ValidationError(`Amount must be greater than 0. Value: ${amount}`);
}
}
}