Consolidate webapp structure by merging nested components into the main repository

This commit is contained in:
defiQUG
2025-11-05 16:12:53 -08:00
parent 09c5a1fd5e
commit 3b09c35c47
55 changed files with 10240 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
import type { Plan } from "../types/plan";
import { generatePacs008 } from "./iso20022";
/**
* Prepare bank instruction (2PC prepare phase)
* Sends provisional ISO-20022 message
*/
export async function prepareBankInstruction(plan: Plan): Promise<boolean> {
console.log(`[Bank] Preparing instruction for plan ${plan.plan_id}`);
// Mock: In real implementation, this would:
// 1. Generate provisional ISO-20022 message (pacs.008 with conditional settlement)
// 2. Send to bank connector
// 3. Receive provisional acceptance
await new Promise((resolve) => setTimeout(resolve, 100));
return true;
}
/**
* Commit bank instruction (2PC commit phase)
* Confirms final settlement
*/
export async function commitBankInstruction(plan: Plan): Promise<{
success: boolean;
isoMessageId?: string;
error?: string;
}> {
console.log(`[Bank] Committing instruction for plan ${plan.plan_id}`);
try {
// Generate final ISO-20022 message
const isoMessage = await generatePacs008(plan);
// Mock: In real implementation, this would:
// 1. Send ISO message to bank connector
// 2. Receive confirmation and message ID
// 3. Store message ID for audit trail
const isoMessageId = `MSG-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
// Simulate processing delay
await new Promise((resolve) => setTimeout(resolve, 300));
return {
success: true,
isoMessageId,
};
} catch (error: any) {
return {
success: false,
error: error.message,
};
}
}
/**
* Abort bank instruction (2PC abort phase)
* Cancels provisional instruction
*/
export async function abortBankInstruction(planId: string): Promise<void> {
console.log(`[Bank] Aborting instruction for plan ${planId}`);
// Mock: In real implementation, this would:
// 1. Generate cancellation message (camt.056)
// 2. Send to bank connector
// 3. Confirm cancellation
await new Promise((resolve) => setTimeout(resolve, 100));
}

View File

@@ -0,0 +1,102 @@
import type { Plan } from "../types/plan";
import { checkKYC, checkAML, getIdentityData } from "../integrations/compliance";
export interface ComplianceStatus {
lei?: string;
did?: string;
kyc?: {
level: number;
verified: boolean;
expiresAt?: string;
};
aml?: {
passed: boolean;
lastCheck?: string;
riskLevel?: string;
};
valid: boolean;
}
/**
* Get compliance status for a user/creator
*/
export async function getComplianceStatus(creator: string): Promise<ComplianceStatus> {
try {
const identity = await getIdentityData(creator);
const kyc = await checkKYC(creator);
const aml = await checkAML(creator);
return {
lei: identity?.lei,
did: identity?.did,
kyc: {
level: kyc?.level || 0,
verified: kyc?.verified || false,
expiresAt: kyc?.expiresAt,
},
aml: {
passed: aml?.passed || false,
lastCheck: aml?.lastCheck,
riskLevel: aml?.riskLevel || "LOW",
},
valid: !!(identity?.lei && identity?.did && kyc?.verified && aml?.passed),
};
} catch (error) {
console.error("Compliance check failed:", error);
return {
valid: false,
};
}
}
/**
* Get compliance data for ISO message injection
*/
export async function getComplianceData(creator: string): Promise<ComplianceStatus> {
return await getComplianceStatus(creator);
}
/**
* Validate workflow compliance requirements
*/
export async function validateWorkflowCompliance(plan: Plan): Promise<{
valid: boolean;
required: string[];
missing: string[];
warnings: string[];
}> {
const status = await getComplianceStatus(plan.creator);
const hasFiatSteps = plan.steps.some((s) => s.type === "pay" || s.type === "repay");
const required: string[] = [];
const missing: string[] = [];
const warnings: string[] = [];
if (hasFiatSteps) {
required.push("LEI", "DID", "KYC", "AML");
if (!status.lei) missing.push("LEI");
if (!status.did) missing.push("DID");
if (!status.kyc?.verified) missing.push("KYC");
if (!status.aml?.passed) missing.push("AML");
// Check KYC expiration
if (status.kyc?.expiresAt) {
const expiresAt = new Date(status.kyc.expiresAt);
if (expiresAt < new Date()) {
warnings.push("KYC has expired");
} else if (expiresAt < new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)) {
warnings.push("KYC expires within 30 days");
}
}
}
return {
valid: missing.length === 0,
required,
missing,
warnings,
};
}

View File

@@ -0,0 +1,77 @@
import type { Plan } from "../types/plan";
/**
* Prepare DLT execution (2PC prepare phase)
* Reserves collateral and locks amounts
*/
export async function prepareDLTExecution(plan: Plan): Promise<boolean> {
// Mock: In real implementation, this would call the handler contract's prepare() function
// For now, simulate preparation
console.log(`[DLT] Preparing execution for plan ${plan.plan_id}`);
// Simulate async preparation
await new Promise((resolve) => setTimeout(resolve, 100));
return true;
}
/**
* Commit DLT execution (2PC commit phase)
* Executes all DLT steps atomically
*/
export async function commitDLTExecution(plan: Plan): Promise<{
success: boolean;
txHash?: string;
error?: string;
}> {
console.log(`[DLT] Committing execution for plan ${plan.plan_id}`);
try {
// Mock: In real implementation, this would:
// 1. Call handler contract's executeCombo() function
// 2. Wait for transaction confirmation
// 3. Return transaction hash
const txHash = `0x${Math.random().toString(16).substr(2, 64)}`;
// Simulate execution delay
await new Promise((resolve) => setTimeout(resolve, 500));
return {
success: true,
txHash,
};
} catch (error: any) {
return {
success: false,
error: error.message,
};
}
}
/**
* Abort DLT execution (2PC abort phase)
* Releases reserved collateral and unlocks amounts
*/
export async function abortDLTExecution(planId: string): Promise<void> {
console.log(`[DLT] Aborting execution for plan ${planId}`);
// Mock: In real implementation, this would call handler contract's abort() function
// to release any reserved resources
await new Promise((resolve) => setTimeout(resolve, 100));
}
/**
* Get DLT execution status
*/
export async function getDLTStatus(planId: string): Promise<{
status: string;
txHash?: string;
blockNumber?: number;
}> {
// Mock implementation
return {
status: "pending",
};
}

View File

@@ -0,0 +1,202 @@
import { EventEmitter } from "events";
import { getPlanById, updatePlanStatus } from "../db/plans";
import { prepareDLTExecution, commitDLTExecution, abortDLTExecution } from "./dlt";
import { prepareBankInstruction, commitBankInstruction, abortBankInstruction } from "./bank";
import { registerPlan, finalizePlan } from "./notary";
import type { PlanStatusEvent } from "../types/execution";
export class ExecutionCoordinator extends EventEmitter {
private executions: Map<string, {
planId: string;
status: string;
phase: string;
startedAt: Date;
error?: string;
}> = new Map();
/**
* Execute a plan using 2PC (two-phase commit) pattern
*/
async executePlan(planId: string): Promise<{ executionId: string }> {
const executionId = `exec-${Date.now()}`;
this.executions.set(executionId, {
planId,
status: "pending",
phase: "prepare",
startedAt: new Date(),
});
this.emitStatus(executionId, {
phase: "prepare",
status: "in_progress",
timestamp: new Date().toISOString(),
});
try {
// Get plan
const plan = await getPlanById(planId);
if (!plan) {
throw new Error("Plan not found");
}
// PHASE 1: PREPARE
await this.preparePhase(executionId, plan);
// PHASE 2: EXECUTE DLT
await this.executeDLTPhase(executionId, plan);
// PHASE 3: BANK INSTRUCTION
await this.bankInstructionPhase(executionId, plan);
// PHASE 4: COMMIT
await this.commitPhase(executionId, plan);
this.emitStatus(executionId, {
phase: "complete",
status: "complete",
timestamp: new Date().toISOString(),
});
await updatePlanStatus(planId, "complete");
return { executionId };
} catch (error: any) {
// Rollback on error
await this.abortExecution(executionId, planId, error.message);
throw error;
}
}
private async preparePhase(executionId: string, plan: any) {
this.emitStatus(executionId, {
phase: "prepare",
status: "in_progress",
timestamp: new Date().toISOString(),
});
// Prepare DLT execution
const dltPrepared = await prepareDLTExecution(plan);
if (!dltPrepared) {
throw new Error("DLT preparation failed");
}
// Prepare bank instruction (provisional)
const bankPrepared = await prepareBankInstruction(plan);
if (!bankPrepared) {
await abortDLTExecution(plan.plan_id);
throw new Error("Bank preparation failed");
}
// Register plan with notary
await registerPlan(plan);
this.emitStatus(executionId, {
phase: "prepare",
status: "complete",
timestamp: new Date().toISOString(),
});
}
private async executeDLTPhase(executionId: string, plan: any) {
this.emitStatus(executionId, {
phase: "execute_dlt",
status: "in_progress",
timestamp: new Date().toISOString(),
});
const result = await commitDLTExecution(plan);
if (!result.success) {
await abortDLTExecution(plan.plan_id);
await abortBankInstruction(plan.plan_id);
throw new Error("DLT execution failed: " + result.error);
}
this.emitStatus(executionId, {
phase: "execute_dlt",
status: "complete",
dltTxHash: result.txHash,
timestamp: new Date().toISOString(),
});
}
private async bankInstructionPhase(executionId: string, plan: any) {
this.emitStatus(executionId, {
phase: "bank_instruction",
status: "in_progress",
timestamp: new Date().toISOString(),
});
const result = await commitBankInstruction(plan);
if (!result.success) {
// DLT already committed, need to handle rollback
throw new Error("Bank instruction failed: " + result.error);
}
this.emitStatus(executionId, {
phase: "bank_instruction",
status: "complete",
isoMessageId: result.isoMessageId,
timestamp: new Date().toISOString(),
});
}
private async commitPhase(executionId: string, plan: any) {
this.emitStatus(executionId, {
phase: "commit",
status: "in_progress",
timestamp: new Date().toISOString(),
});
// Finalize with notary
await finalizePlan(plan.plan_id, {
dltTxHash: "mock-tx-hash",
isoMessageId: "mock-iso-id",
});
this.emitStatus(executionId, {
phase: "commit",
status: "complete",
timestamp: new Date().toISOString(),
});
}
async abortExecution(executionId: string, planId: string, error: string) {
const execution = this.executions.get(executionId);
if (!execution) return;
try {
// Abort DLT
await abortDLTExecution(planId);
// Abort bank
await abortBankInstruction(planId);
await updatePlanStatus(planId, "aborted");
this.emitStatus(executionId, {
phase: "aborted",
status: "failed",
error,
timestamp: new Date().toISOString(),
});
} catch (abortError: any) {
console.error("Abort failed:", abortError);
}
}
async getExecutionStatus(executionId: string) {
return this.executions.get(executionId);
}
private emitStatus(executionId: string, event: PlanStatusEvent) {
this.emit("status", executionId, event);
}
onStatus(callback: (executionId: string, event: PlanStatusEvent) => void) {
this.on("status", callback);
}
}
export const executionCoordinator = new ExecutionCoordinator();

View File

@@ -0,0 +1,179 @@
import type { Plan } from "../types/plan";
import { getComplianceData } from "./compliance";
/**
* Generate ISO-20022 pacs.008 (Customer Credit Transfer) message
*/
export async function generatePacs008(plan: Plan): Promise<string> {
const complianceData = await getComplianceData(plan.creator);
// Find pay step
const payStep = plan.steps.find((s) => s.type === "pay");
if (!payStep || payStep.type !== "pay") {
throw new Error("Plan must contain a pay step");
}
const isoMessage = {
Document: {
"@xmlns": "urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10",
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
CstmrCdtTrfInitn: {
GrpHdr: {
MsgId: `MSG-${plan.plan_id}`,
CreDtTm: new Date().toISOString(),
NbOfTxs: "1",
CtrlSum: payStep.amount.toString(),
InitgPty: {
Nm: complianceData?.lei || "Unknown",
Id: {
OrgId: {
Othr: {
Id: complianceData?.lei || "",
SchmeNm: {
Cd: "LEI",
},
},
},
},
},
},
PmtInf: {
PmtInfId: `PMT-${plan.plan_id}`,
PmtMtd: "TRF",
NbOfTxs: "1",
CtrlSum: payStep.amount.toString(),
PmtTpInf: {
SvcLvl: {
Cd: "SEPA",
},
},
ReqdExctnDt: new Date().toISOString().split("T")[0],
Dbtr: {
Nm: complianceData?.lei || "Unknown",
Id: {
OrgId: {
Othr: {
Id: complianceData?.lei || "",
},
},
},
},
DbtrAcct: {
Id: {
IBAN: "DE89370400440532013000", // Mock
},
},
DbtrAgt: {
FinInstnId: {
BICFI: "DEUTDEFF", // Mock
},
},
CdtTrfTxInf: {
PmtId: {
InstrId: `INSTR-${plan.plan_id}`,
EndToEndId: plan.plan_id,
},
Amt: {
InstdAmt: {
"@Ccy": payStep.asset,
"#text": payStep.amount.toString(),
},
},
CdtrAgt: {
FinInstnId: {
BICFI: payStep.beneficiary.BIC || "UNKNOWN",
},
},
Cdtr: {
Nm: payStep.beneficiary.name || "Unknown",
},
CdtrAcct: {
Id: {
IBAN: payStep.beneficiary.IBAN || "",
},
},
RmtInf: {
Ustrd: `Plan ID: ${plan.plan_id}, Plan Hash: ${plan.plan_hash}`,
},
SplmtryData: {
PlcAndNm: "ComplianceData",
Envlp: {
Compl: {
LEI: complianceData?.lei || "",
DID: complianceData?.did || "",
KYC: {
Level: complianceData?.kyc?.level || 0,
Verified: complianceData?.kyc?.verified || false,
},
AML: {
Passed: complianceData?.aml?.passed || false,
RiskLevel: complianceData?.aml?.riskLevel || "UNKNOWN",
},
},
},
},
},
},
},
},
};
// Convert to XML string (simplified - in production use proper XML builder)
return JSON.stringify(isoMessage, null, 2);
}
/**
* Generate ISO-20022 camt.052 (Bank Statement) message
*/
export async function generateCamt052(planId: string, accountId: string): Promise<string> {
// Mock implementation
return JSON.stringify({
Document: {
"@xmlns": "urn:iso:std:iso:20022:tech:xsd:camt.052.001.10",
BkToCstmrAcctRpt: {
GrpHdr: {
MsgId: `MSG-${planId}`,
CreDtTm: new Date().toISOString(),
},
Rpt: {
Id: `RPT-${planId}`,
Acct: {
Id: {
IBAN: accountId,
},
},
},
},
},
});
}
/**
* Generate ISO-20022 camt.056 (Cancellation Request) message
*/
export async function generateCamt056(planId: string, originalMessageId: string): Promise<string> {
// Mock implementation
return JSON.stringify({
Document: {
"@xmlns": "urn:iso:std:iso:20022:tech:xsd:camt.056.001.10",
CstmrPmtCxlReq: {
Assgnmt: {
Id: `ASSGN-${planId}`,
},
Case: {
Id: `CASE-${planId}`,
Cretr: {
Nm: "Orchestrator",
},
},
Undrlyg: {
TxInf: {
OrgnlInstrId: originalMessageId,
OrgnlEndToEndId: planId,
},
},
},
},
});
}

View File

@@ -0,0 +1,78 @@
import { createHash } from "crypto";
import type { Plan } from "../types/plan";
/**
* Register plan with notary service
* Stores plan hash and metadata for audit trail
*/
export async function registerPlan(plan: Plan): Promise<{
notaryProof: string;
registeredAt: string;
}> {
console.log(`[Notary] Registering plan ${plan.plan_id}`);
// Compute plan hash
const planHash = createHash("sha256")
.update(JSON.stringify(plan))
.digest("hex");
// Mock: In real implementation, this would:
// 1. Call NotaryRegistry contract's registerPlan() function
// 2. Store plan hash, metadata, timestamp
// 3. Get notary signature/proof
const notaryProof = `0x${createHash("sha256")
.update(planHash + "notary-secret")
.digest("hex")}`;
return {
notaryProof,
registeredAt: new Date().toISOString(),
};
}
/**
* Finalize plan with execution results
* Records final execution state and receipts
*/
export async function finalizePlan(
planId: string,
results: {
dltTxHash?: string;
isoMessageId?: string;
}
): Promise<{
receiptId: string;
finalizedAt: string;
}> {
console.log(`[Notary] Finalizing plan ${planId}`);
// Mock: In real implementation, this would:
// 1. Call NotaryRegistry contract's finalizePlan() function
// 2. Store execution results, receipts
// 3. Get final notary proof
const receiptId = `receipt-${planId}-${Date.now()}`;
return {
receiptId,
finalizedAt: new Date().toISOString(),
};
}
/**
* Get notary proof for a plan
*/
export async function getNotaryProof(planId: string): Promise<{
planHash: string;
notaryProof: string;
registeredAt: string;
} | null> {
// Mock implementation
return {
planHash: `0x${Math.random().toString(16).substr(2, 64)}`,
notaryProof: `0x${Math.random().toString(16).substr(2, 64)}`,
registeredAt: new Date().toISOString(),
};
}

View File

@@ -0,0 +1,125 @@
import type { Plan, PlanStep } from "../types/plan";
export interface ValidationResult {
valid: boolean;
errors: string[];
}
const MAX_RECURSION_DEPTH = 3;
const MAX_LTV = 0.6;
/**
* Validate plan structure
*/
export function validatePlan(plan: Plan): ValidationResult {
const errors: string[] = [];
// Check required fields
if (!plan.steps || plan.steps.length === 0) {
errors.push("Plan must contain at least one step");
}
// Check recursion depth
const borrowSteps = plan.steps.filter((s) => s.type === "borrow");
const recursionDepth = borrowSteps.length - 1;
if (recursionDepth > MAX_RECURSION_DEPTH) {
errors.push(`Recursion depth ${recursionDepth} exceeds maximum ${MAX_RECURSION_DEPTH}`);
}
// Check LTV
if (plan.maxLTV && plan.maxLTV > MAX_LTV) {
errors.push(`Max LTV ${plan.maxLTV} exceeds maximum ${MAX_LTV}`);
}
// Validate each step
plan.steps.forEach((step, index) => {
const stepErrors = validateStep(step, index);
errors.push(...stepErrors);
});
return {
valid: errors.length === 0,
errors,
};
}
/**
* Validate individual step
*/
function validateStep(step: PlanStep, index: number): string[] {
const errors: string[] = [];
switch (step.type) {
case "borrow":
if (!step.asset || step.amount <= 0) {
errors.push(`Step ${index + 1}: Invalid borrow step (asset or amount missing)`);
}
break;
case "swap":
if (!step.from || !step.to || step.amount <= 0) {
errors.push(`Step ${index + 1}: Invalid swap step (from/to/amount missing)`);
}
break;
case "repay":
if (!step.asset || step.amount <= 0) {
errors.push(`Step ${index + 1}: Invalid repay step (asset or amount missing)`);
}
break;
case "pay":
if (!step.asset || step.amount <= 0 || !step.beneficiary?.IBAN) {
errors.push(`Step ${index + 1}: Invalid pay step (asset/amount/IBAN missing)`);
}
break;
}
return errors;
}
/**
* Check step dependencies
*/
export function checkStepDependencies(steps: PlanStep[]): ValidationResult {
const errors: string[] = [];
for (let i = 1; i < steps.length; i++) {
const prevStep = steps[i - 1];
const currentStep = steps[i];
// Check if current step depends on previous step output
if (currentStep.type === "swap") {
// Swap should receive from previous step
const prevOutput = getStepOutput(prevStep);
if (prevOutput && currentStep.from !== prevOutput.asset) {
errors.push(`Step ${i + 1}: Swap expects ${currentStep.from} but previous step outputs ${prevOutput.asset}`);
}
}
if (currentStep.type === "repay") {
// Repay should use same asset as previous step
const prevOutput = getStepOutput(prevStep);
if (prevOutput && currentStep.asset !== prevOutput.asset) {
errors.push(`Step ${i + 1}: Repay expects ${currentStep.asset} but previous step outputs ${prevOutput.asset}`);
}
}
}
return {
valid: errors.length === 0,
errors,
};
}
/**
* Get step output (what asset/amount this step produces)
*/
function getStepOutput(step: PlanStep): { asset: string; amount: number } | null {
switch (step.type) {
case "borrow":
return { asset: step.asset, amount: step.amount };
case "swap":
return { asset: step.to, amount: step.amount };
default:
return null;
}
}

View File

@@ -0,0 +1,79 @@
import type { Plan } from "../types/plan";
import { getNotaryProof } from "./notary";
import { getDLTStatus } from "./dlt";
export interface Receipt {
receiptId: string;
planId: string;
planHash: string;
dltTransaction?: {
txHash: string;
blockNumber: number;
timestamp: string;
};
isoMessage?: {
messageId: string;
messageType: string;
timestamp: string;
};
notaryProof?: {
proof: string;
registeredAt: string;
finalizedAt?: string;
};
status: string;
createdAt: string;
}
/**
* Generate receipt for a plan execution
*/
export async function generateReceipt(plan: Plan): Promise<Receipt> {
const notaryProof = await getNotaryProof(plan.plan_id);
const dltStatus = await getDLTStatus(plan.plan_id);
const receipt: Receipt = {
receiptId: `receipt-${plan.plan_id}-${Date.now()}`,
planId: plan.plan_id,
planHash: plan.plan_hash || "",
status: "complete",
createdAt: new Date().toISOString(),
};
if (dltStatus.txHash) {
receipt.dltTransaction = {
txHash: dltStatus.txHash,
blockNumber: dltStatus.blockNumber || 0,
timestamp: new Date().toISOString(),
};
}
// Find pay step to get ISO message ID
const payStep = plan.steps.find((s) => s.type === "pay");
if (payStep) {
receipt.isoMessage = {
messageId: `MSG-${plan.plan_id}`,
messageType: "pacs.008",
timestamp: new Date().toISOString(),
};
}
if (notaryProof) {
receipt.notaryProof = {
proof: notaryProof.notaryProof,
registeredAt: notaryProof.registeredAt,
};
}
return receipt;
}
/**
* Get all receipts for a plan
*/
export async function getPlanReceipts(planId: string): Promise<Receipt[]> {
// Mock: In real implementation, this would query database
// For now, return empty array or mock data
return [];
}