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
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { render, screen } from '@testing-library/react';

import { OperatingProtocols } from '../../../src/components/Chat/SystemChat';

// Regression test: with live data the Basius backend reports `velodrome`
// (Aerodrome is a Velodrome fork) — the chat protocols message must normalize
// it, same as the Strategy card / Allocation table. agentMap resolves the
// agent from process.env at module load time, so we mock the basius mapping
// here; the env-driven branch itself is unit-covered in utils/agentMap.spec.ts.
jest.mock('../../../src/utils/agentMap', () => ({
...jest.requireActual('../../../src/utils/agentMap'),
normalizeProtocol: (protocol: string) => (protocol === 'velodrome' ? 'aerodrome' : protocol),
}));

describe('OperatingProtocols (basius)', () => {
it('renders backend-reported velodrome as Aerodrome', () => {
render(<OperatingProtocols protocols={['velodrome']} />);
expect(screen.getByText('Aerodrome')).toBeInTheDocument();
expect(screen.queryByText('Velodrome')).not.toBeInTheDocument();
expect(screen.getByAltText('aerodrome')).toHaveAttribute(
'src',
'/logos/protocols/aerodrome.png',
);
});

it('leaves other protocols untouched', () => {
render(<OperatingProtocols protocols={['balancerPool', 'velodrome']} />);
expect(screen.getByText('Balancer')).toBeInTheDocument();
expect(screen.getByText('Aerodrome')).toBeInTheDocument();
});
});
63 changes: 63 additions & 0 deletions apps/babydegen-ui/__tests__/utils/agentMap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,67 @@ describe('agentMap (babydegen-ui)', () => {

expect(() => require('../../src/utils/agentMap')).not.toThrow();
});

describe('normalizeProtocol', () => {
it('maps velodrome to aerodrome for basius', () => {
process.env.REACT_APP_AGENT_NAME = 'basius';

const { normalizeProtocol } = require('../../src/utils/agentMap');
expect(normalizeProtocol('velodrome')).toBe('aerodrome');
});

it('leaves other protocols untouched for basius', () => {
process.env.REACT_APP_AGENT_NAME = 'basius';

const { normalizeProtocol } = require('../../src/utils/agentMap');
expect(normalizeProtocol('balancerPool')).toBe('balancerPool');
expect(normalizeProtocol('uniswapV3')).toBe('uniswapV3');
expect(normalizeProtocol('aerodrome')).toBe('aerodrome');
});

it('leaves velodrome untouched for modius', () => {
process.env.REACT_APP_AGENT_NAME = 'modius';

const { normalizeProtocol } = require('../../src/utils/agentMap');
expect(normalizeProtocol('velodrome')).toBe('velodrome');
});

it('leaves velodrome untouched for optimus', () => {
process.env.REACT_APP_AGENT_NAME = 'optimus';

const { normalizeProtocol } = require('../../src/utils/agentMap');
expect(normalizeProtocol('velodrome')).toBe('velodrome');
});
});

describe('normalizeDetails', () => {
it('relabels velodrome case-insensitively for basius', () => {
process.env.REACT_APP_AGENT_NAME = 'basius';

const { normalizeDetails } = require('../../src/utils/agentMap');
expect(normalizeDetails('Velodrome pool')).toBe('Aerodrome pool');
expect(normalizeDetails('velodrome CL pool')).toBe('Aerodrome CL pool');
});

it('leaves details without velodrome untouched for basius', () => {
process.env.REACT_APP_AGENT_NAME = 'basius';

const { normalizeDetails } = require('../../src/utils/agentMap');
expect(normalizeDetails('Balancer pool')).toBe('Balancer pool');
});

it('leaves details untouched for modius', () => {
process.env.REACT_APP_AGENT_NAME = 'modius';

const { normalizeDetails } = require('../../src/utils/agentMap');
expect(normalizeDetails('Velodrome pool')).toBe('Velodrome pool');
});

it('leaves details untouched for optimus', () => {
process.env.REACT_APP_AGENT_NAME = 'optimus';

const { normalizeDetails } = require('../../src/utils/agentMap');
expect(normalizeDetails('Velodrome pool')).toBe('Velodrome pool');
});
});
});
Binary file added apps/babydegen-ui/public/logos/tokens/msusd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useMemo } from 'react';
import { PROTOCOLS_MAP } from '../../constants/textMaps';
import { usePortfolio } from '../../hooks/usePortfolio';
import { SelectedProtocol } from '../../types';
import { normalizeDetails, normalizeProtocol } from '../../utils/agentMap';
import { piePalette } from '../../utils/chartjs/palette';
import { AssetBadges } from './AllocationAssets';

Expand Down Expand Up @@ -66,9 +67,9 @@ export const AllocationTable = () => {
data?.allocations?.map(({ type, assets, details, apr }) => ({
key: `${type}-${assets.join('-')}-${details}`,
pool: assets,
details: `${details}`,
details: normalizeDetails(`${details}`),
apr: `${apr}`,
type,
type: normalizeProtocol(type),
})),
[data?.allocations],
);
Expand Down
24 changes: 14 additions & 10 deletions apps/babydegen-ui/src/components/Chat/SystemChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { COLOR } from '../../constants/colors';
import { PROTOCOLS_MAP, TRADING_TYPE_MAP } from '../../constants/textMaps';
import { SelectedProtocol, TradingType } from '../../types';
import { Pill } from '../../ui/Pill';
import { normalizeProtocol } from '../../utils/agentMap';

const { Text } = Typography;

Expand Down Expand Up @@ -73,15 +74,18 @@ export const OperatingProtocols = ({ protocols }: OperatingProtocolsProps) => (
<SystemMessage label="Operating protocols updated:" type="protocols">
{protocols.length === 0
? NA
: protocols.map((protocol) => (
<Pill size="large" key={protocol} style={{ marginLeft: 0, paddingRight: 16 }}>
<img
src={PROTOCOLS_MAP[protocol].logo}
alt={protocol}
style={{ width: 18, height: 18 }}
/>
{PROTOCOLS_MAP[protocol].name}
</Pill>
))}
: protocols.map((protocol) => {
const displayProtocol = normalizeProtocol(protocol);
return (
<Pill size="large" key={displayProtocol} style={{ marginLeft: 0, paddingRight: 16 }}>
<img
src={PROTOCOLS_MAP[displayProtocol].logo}
alt={displayProtocol}
style={{ width: 18, height: 18 }}
/>
{PROTOCOLS_MAP[displayProtocol].name}
</Pill>
);
})}
</SystemMessage>
);
21 changes: 12 additions & 9 deletions apps/babydegen-ui/src/components/Strategy/Strategy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { PROTOCOLS_MAP, TRADING_TYPE_MAP } from '../../constants/textMaps';
import { usePortfolio } from '../../hooks/usePortfolio';
import { SelectedProtocol } from '../../types';
import { Pill } from '../../ui/Pill';
import { agentName } from '../../utils/agentMap';
import { agentName, normalizeProtocol } from '../../utils/agentMap';

const { Title, Text } = Typography;

Expand Down Expand Up @@ -40,14 +40,17 @@ const StrategyContent = () => {
const operatingProtocols = useMemo(() => {
if (!data?.selected_protocols) return [];

return data.selected_protocols.map((protocol: SelectedProtocol) => (
<Avatar
key={protocol}
size={36}
src={PROTOCOLS_MAP[protocol].logo}
style={{ border: `1px solid ${COLOR.lightGrey}`, padding: 6 }}
/>
));
return data.selected_protocols.map((protocol: SelectedProtocol) => {
const displayProtocol = normalizeProtocol(protocol);
return (
<Avatar
key={displayProtocol}
size={36}
src={PROTOCOLS_MAP[displayProtocol].logo}
style={{ border: `1px solid ${COLOR.lightGrey}`, padding: 6 }}
/>
);
});
}, [data?.selected_protocols]);

return (
Expand Down
17 changes: 17 additions & 0 deletions apps/babydegen-ui/src/utils/agentMap.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { SelectedProtocol } from '../types';

/**
* Supported agents map.
* - modius: Modius agent (Mode chain)
Expand All @@ -23,3 +25,18 @@ export const agentChainName = ((): 'mode' | 'base' | 'optimism' => {
if (agentType === 'basius') return 'base';
return 'optimism';
})();

/**
* Aerodrome (Base) is a Velodrome fork sharing the same contract ABIs, so the
* agent backend reports it as `velodrome`. Basius runs only on Base, so display
* that protocol as Aerodrome.
*/
export const normalizeProtocol = (protocol: SelectedProtocol): SelectedProtocol =>
agentType === 'basius' && protocol === 'velodrome' ? 'aerodrome' : protocol;

/**
* Backend-provided free-text detail strings (e.g. "Velodrome pool") reference
* the reported protocol name. Relabel for Basius to match {@link normalizeProtocol}.
*/
export const normalizeDetails = (details: string): string =>
agentType === 'basius' ? details.replace(/velodrome/gi, 'Aerodrome') : details;
Loading