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
50 changes: 50 additions & 0 deletions agent/strategyParser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { parseStrategyIntent, validateStrategyPayload, StrategyPayload } from './strategyParser';

describe('Strategy Parser', () => {
it('parses valid input and sums allocations to 100', () => {
const input = {
monthlyContributionAmount: 500,
blendAllocationX: 40,
soroswapAllocationX: 30,
goldAllocationX: 30,
};
const result = parseStrategyIntent(input);
expect(result).toEqual({
monthlyContributionAmount: 500,
blendAllocationX: 40,
soroswapAllocationX: 30,
goldAllocationX: 30,
});
expect(validateStrategyPayload(result)).toBe(true);
});

it('normalizes allocations if they do not sum to 100', () => {
const input = {
monthlyContributionAmount: 1000,
blendAllocationX: 50,
soroswapAllocationX: 30,
goldAllocationX: 10,
};
const result = parseStrategyIntent(input);
expect(result.blendAllocationX + result.soroswapAllocationX + result.goldAllocationX).toBe(100);
expect(validateStrategyPayload(result)).toBe(true);
});

it('handles malformed input with fallbacks', () => {
const input = {
monthlyContributionAmount: 'not-a-number',
blendAllocationX: null,
soroswapAllocationX: undefined,
goldAllocationX: -10,
};
const result = parseStrategyIntent(input);
expect(result.monthlyContributionAmount).toBe(0);
expect(result.blendAllocationX + result.soroswapAllocationX + result.goldAllocationX).toBe(100);
expect(validateStrategyPayload(result)).toBe(true);
});

it('returns false for invalid schema', () => {
const invalid = { foo: 1, bar: 2 };
expect(validateStrategyPayload(invalid)).toBe(false);
});
});
67 changes: 67 additions & 0 deletions agent/strategyParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Strategy Parser Tool for Issue 1.3
// Converts conversational intent into a strict JSON payload for the agent

export interface StrategyPayload {
monthlyContributionAmount: number;
blendAllocationX: number;
soroswapAllocationX: number;
goldAllocationX: number;
}

/**
* Validates and normalizes the strategy payload.
* Ensures allocations sum to 100 and all fields are present.
* Applies fallbacks for malformed input.
*/
export function parseStrategyIntent(input: any): StrategyPayload {
// Fallbacks for missing or malformed fields
let monthlyContributionAmount = Number(input.monthlyContributionAmount);
if (isNaN(monthlyContributionAmount) || monthlyContributionAmount < 0) {
monthlyContributionAmount = 0;
}

// Parse allocations, fallback to 0 if missing or invalid
let blend = Number(input.blendAllocationX);
let soroswap = Number(input.soroswapAllocationX);
let gold = Number(input.goldAllocationX);
if (isNaN(blend) || blend < 0) blend = 0;
if (isNaN(soroswap) || soroswap < 0) soroswap = 0;
if (isNaN(gold) || gold < 0) gold = 0;

// Normalize allocations to sum to 100
const total = blend + soroswap + gold;
if (total === 0) {
// All allocations are invalid or zero, fallback to 100/0/0
blend = 100;
soroswap = 0;
gold = 0;
} else if (total !== 100) {
blend = Math.round((blend / total) * 100);
soroswap = Math.round((soroswap / total) * 100);
gold = 100 - blend - soroswap; // Ensure sum is exactly 100
}

return {
monthlyContributionAmount,
blendAllocationX: blend,
soroswapAllocationX: soroswap,
goldAllocationX: gold,
};
}

/**
* Validates that the payload matches the schema and allocations sum to 100.
*/
export function validateStrategyPayload(payload: any): boolean {
if (
typeof payload !== 'object' ||
typeof payload.monthlyContributionAmount !== 'number' ||
typeof payload.blendAllocationX !== 'number' ||
typeof payload.soroswapAllocationX !== 'number' ||
typeof payload.goldAllocationX !== 'number'
) {
return false;
}
const sum = payload.blendAllocationX + payload.soroswapAllocationX + payload.goldAllocationX;
return sum === 100;
}
2 changes: 1 addition & 1 deletion contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
soroban-sdk = "22.0.0"

[dev_dependencies]
[dev-dependencies]
soroban-sdk = { version = "22.0.0", features = ["testutils"] }

[features]
Expand Down
Loading
Loading