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
1 change: 1 addition & 0 deletions src/integration/blockchain/juice/juice-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export class JuiceClient {
case 'USDC':
return this.getBridgeUSDCContract();
case 'USDT':
case 'USDT.e':
return this.getBridgeUSDTContract();
case 'CTUSD':
return this.getBridgeCTUSDContract();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ import { JuiceService } from 'src/integration/blockchain/juice/juice.service';
import { Blockchain } from 'src/integration/blockchain/shared/enums/blockchain.enum';
import { Asset, AssetType } from 'src/shared/models/asset/asset.entity';
import { AssetService } from 'src/shared/models/asset/asset.service';
import { LiquidityManagementOrder } from '../../entities/liquidity-management-order.entity';
import { LiquidityManagementSystem } from '../../enums';
import { OrderNotProcessableException } from '../../exceptions/order-not-processable.exception';
import { CorrelationId } from '../../interfaces';
import { LiquidityManagementBalanceService } from '../../services/liquidity-management-balance.service';
import { FrankencoinBasedAdapter, FrankencoinBasedAdapterCommands } from './base/frankencoin-based.adapter';

export enum JuiceAdapterCommands {
BRIDGE_IN = 'bridge-in',
BRIDGE_OUT = 'bridge-out',
}

@Injectable()
export class JuiceAdapter extends FrankencoinBasedAdapter {
constructor(
Expand All @@ -18,6 +26,40 @@ export class JuiceAdapter extends FrankencoinBasedAdapter {

// Juice doesn't have a wrapper contract
this.commands.delete(FrankencoinBasedAdapterCommands.WRAP);

this.commands.set(JuiceAdapterCommands.BRIDGE_IN, this.bridgeIn.bind(this));
this.commands.set(JuiceAdapterCommands.BRIDGE_OUT, this.bridgeOut.bind(this));
}

async checkCompletion(order: LiquidityManagementOrder): Promise<boolean> {
if (
order.action.command === JuiceAdapterCommands.BRIDGE_IN ||
order.action.command === JuiceAdapterCommands.BRIDGE_OUT
) {
const client = this.juiceService.getEvmClient();
const txHash = order.correlationId;

try {
return await client.isTxComplete(txHash);
} catch {
return false;
}
}

return super.checkCompletion(order);
}

validateParams(command: string, params: Record<string, unknown>): boolean {
switch (command) {
case JuiceAdapterCommands.BRIDGE_IN:
return this.validateBridgeInParams(params);

case JuiceAdapterCommands.BRIDGE_OUT:
return false; // not yet implemented

default:
return super.validateParams(command, params);
}
}

async getStableToken(): Promise<Asset> {
Expand All @@ -35,4 +77,56 @@ export class JuiceAdapter extends FrankencoinBasedAdapter {
blockchain: Blockchain.CITREA,
});
}

private async bridgeIn(order: LiquidityManagementOrder): Promise<CorrelationId> {
const { asset } = this.parseBridgeInParams(order.action.paramMap);

const jusdAsset = await this.getStableToken();
const sourceAsset = await this.assetService.getAssetByQuery({
name: asset,
type: AssetType.TOKEN,
blockchain: jusdAsset.blockchain,
});

const sourceBalance = await this.juiceService.getEvmClient().getTokenBalance(sourceAsset);

if (sourceBalance < order.minAmount) {
throw new OrderNotProcessableException(
`Not enough ${sourceAsset.name} liquidity (balance: ${sourceBalance}, min. requested: ${order.minAmount}, max. requested: ${order.maxAmount})`,
);
}

const amount = Math.min(order.maxAmount, sourceBalance);

order.inputAmount = amount;
order.inputAsset = sourceAsset.name;
order.outputAmount = amount;
order.outputAsset = jusdAsset.name;

return this.juiceService.bridgeToJusd(sourceAsset, amount);
}

private async bridgeOut(_order: LiquidityManagementOrder): Promise<CorrelationId> {
// TODO: Implement bridge out (JUSD → USD stablecoins)
throw new OrderNotProcessableException('Bridge out is not yet implemented');
}

private validateBridgeInParams(params: Record<string, unknown>): boolean {
try {
this.parseBridgeInParams(params);
return true;
} catch {
return false;
}
}

private parseBridgeInParams(params: Record<string, unknown>): { asset: string } {
const asset = params.asset as string;

if (!asset) {
throw new Error('asset parameter is required for bridge-in command');
}

return { asset };
}
}
Loading