Skip to content
Draft
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
158 changes: 112 additions & 46 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type {
import type { Brand } from '@agoric/ertp/src/types.js';
import { getBrand } from './utils';
import { getInitialEnvironment, configureEndpoints } from './config';
import { ContractVersion } from './constants';

const { fromEntries } = Object;

Expand All @@ -36,14 +37,16 @@ let watcher = makeAgoricChainStorageWatcher(ENDPOINTS.API, ENDPOINTS.CHAIN_ID);

const useAppStore = create<AppState>(() => ({}) as AppState);

const setup = async () => {
const setup = async (contractVersion: ContractVersion) => {
watcher.watchLatest<Array<[string, unknown]>>(
[Kind.Data, 'published.agoricNames.instance'],
instances => {
console.log('got instances', instances);
useAppStore.setState({
instances,
offerUpInstance: instances.find(([name]) => name === 'ymax0')!.at(1),
offerUpInstance: instances
.find(([name]) => name === contractVersion)!
.at(1),
});
},
);
Expand Down Expand Up @@ -153,11 +156,20 @@ const makeOffer = (
brand: usdcBrand as Brand<'nat'>,
value: customStep.movement.amount.value,
},
fee: customStep.movement.fee || (getBrand(useAppStore.getState().purses, 'BLD') ? {
brand: getBrand(useAppStore.getState().purses, 'BLD') as Brand<'nat'>,
value: 40n
} : undefined),
detail: customStep.movement.detail || { evmGas: 200_000_000_000_000n },
fee:
customStep.movement.fee ||
(getBrand(useAppStore.getState().purses, 'BLD')
? {
brand: getBrand(
useAppStore.getState().purses,
'BLD',
) as Brand<'nat'>,
value: 40n,
}
: undefined),
detail: customStep.movement.detail || {
evmGas: 200_000_000_000_000n,
},
};
customMovements.push(movement);
}
Expand Down Expand Up @@ -195,12 +207,18 @@ const makeOffer = (
// Combine base steps with custom steps
const steps = [...baseSteps, ...customMovements];

// Build proposal: Access + Deposit (NOT GmpFee - contract handles fees internally)
const proposalGive = {
Access: { brand: poc26Brand, value: 1n },
Deposit: give.Deposit,
};

console.log('Making offer with:', {
instance: offerUpInstance,
give: {
...give,
Access: { brand: poc26Brand, value: 1n },
},
proposal: { give: proposalGive },
offerArgs: { flow: steps },
stepsCount: steps.length,
note: 'GmpFee not in proposal - contract handles fees internally via flow',
});

// Generate a unique offerId
Expand All @@ -215,10 +233,7 @@ const makeOffer = (
publicInvitationMaker: 'makeOpenPortfolioInvitation',
},
{
give: {
...give,
Access: { brand: poc26Brand, value: 1n },
},
give: proposalGive,
},
{ flow: steps },
(update: { status: string; data?: unknown }) => {
Expand Down Expand Up @@ -278,35 +293,50 @@ const withdrawUSDC = () => {
const { yProtocol = 'USDN' } = useAppStore.getState();

const steps: MovementDesc[] = [
{
src: yProtocol,
dest: '@noble',
{
src: yProtocol,
dest: '@noble',
amount,
fee: getBrand(useAppStore.getState().purses, 'BLD') ? {
brand: getBrand(useAppStore.getState().purses, 'BLD') as Brand<'nat'>,
value: 0n
} : undefined,
detail: {}
fee: getBrand(useAppStore.getState().purses, 'BLD')
? {
brand: getBrand(
useAppStore.getState().purses,
'BLD',
) as Brand<'nat'>,
value: 0n,
}
: undefined,
detail: {},
},
{
src: '@noble',
dest: '@agoric',
{
src: '@noble',
dest: '@agoric',
amount,
fee: getBrand(useAppStore.getState().purses, 'BLD') ? {
brand: getBrand(useAppStore.getState().purses, 'BLD') as Brand<'nat'>,
value: 0n
} : undefined,
detail: {}
fee: getBrand(useAppStore.getState().purses, 'BLD')
? {
brand: getBrand(
useAppStore.getState().purses,
'BLD',
) as Brand<'nat'>,
value: 0n,
}
: undefined,
detail: {},
},
{
src: '@agoric',
dest: '<Cash>',
{
src: '@agoric',
dest: '<Cash>',
amount,
fee: getBrand(useAppStore.getState().purses, 'BLD') ? {
brand: getBrand(useAppStore.getState().purses, 'BLD') as Brand<'nat'>,
value: 0n
} : undefined,
detail: {}
fee: getBrand(useAppStore.getState().purses, 'BLD')
? {
brand: getBrand(
useAppStore.getState().purses,
'BLD',
) as Brand<'nat'>,
value: 0n,
}
: undefined,
detail: {},
},
];
wallet?.makeOffer(
Expand Down Expand Up @@ -412,6 +442,7 @@ const withdrawFromProtocol = (
dest: `@${chain}`,
amount,
fee: defaultFee,
detail: { evmGas: 200_000_000_000_000n },
},
{ src: `@${chain}`, dest: '@noble', amount, fee: defaultFee },
{ src: '@noble', dest: '@agoric', amount },
Expand All @@ -436,11 +467,20 @@ const withdrawFromProtocol = (
brand: usdcBrand as Brand<'nat'>,
value: customStep.movement.amount.value,
},
fee: customStep.movement.fee || (getBrand(useAppStore.getState().purses, 'BLD') ? {
brand: getBrand(useAppStore.getState().purses, 'BLD') as Brand<'nat'>,
value: 40n
} : undefined),
detail: customStep.movement.detail || { evmGas: 200_000_000_000_000n },
fee:
customStep.movement.fee ||
(getBrand(useAppStore.getState().purses, 'BLD')
? {
brand: getBrand(
useAppStore.getState().purses,
'BLD',
) as Brand<'nat'>,
value: 40n,
}
: undefined),
detail: customStep.movement.detail || {
evmGas: 200_000_000_000_000n,
},
};
customMovements.push(movement);
}
Expand Down Expand Up @@ -741,6 +781,9 @@ const MainPage = () => {
const [environment, setEnvironment] = useState<Environment>(
getInitialEnvironment(),
);
const [contractVersion, setContractVersion] = useState<ContractVersion>(
(localStorage.getItem('contractVersion') as ContractVersion) || 'ymax0',
);

const { wallet, purses, offerId } = useAppStore((state: AppState) => ({
wallet: state.wallet,
Expand Down Expand Up @@ -770,6 +813,22 @@ const MainPage = () => {
}
};

const handleContractVersionChange = (newVersion: ContractVersion) => {
setContractVersion(newVersion);
localStorage.setItem('contractVersion', newVersion);

// Re-run setup to update the contract instance
setup(newVersion);

// If wallet was connected, notify user
if (wallet) {
alert(
`Contract version changed to ${newVersion}. The page will reload to apply changes.`,
);
window.location.reload();
}
};

return (
<>
{/* Top header with logo and wallet status */}
Expand Down Expand Up @@ -816,6 +875,8 @@ const MainPage = () => {
<div className="main-content">
<div className="card">
<Trade
contractVersion={contractVersion}
onContractVersionChange={handleContractVersionChange}
makeOffer={(
usdcAmount,
bldFeeAmount,
Expand Down Expand Up @@ -866,9 +927,13 @@ const MainPage = () => {
};

function App() {
const [contractVersion] = useState<ContractVersion>(
(localStorage.getItem('contractVersion') as ContractVersion) || 'ymax0',
);

useEffect(() => {
setup();
}, []);
setup(contractVersion);
}, [contractVersion]);

const { wallet, instances, purses } = useAppStore((state: AppState) => ({
wallet: state.wallet,
Expand All @@ -890,6 +955,7 @@ function App() {
purses={purses}
keplr={(window as any).keplr}
chainId={ENDPOINTS.CHAIN_ID}
contractVersion={contractVersion}
/>
}
/>
Expand Down
14 changes: 9 additions & 5 deletions ui/src/components/Admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { makeVstorageKit, makeVStorage } from '@agoric/client-utils';
import { makeAgoricChainStorageWatcher, AgoricChainStoragePathKind as Kind } from '@agoric/rpc';
import { reifyWalletEntry } from '../walletEntryProxy';
import type { Environment } from '../types';
import { ContractVersion } from '../constants';
import WalletEntriesCard from './WalletEntriesCard';
import ContractControlCard from './ContractControlCard';
import CreatorFacetCard from './CreatorFacetCard';
Expand All @@ -19,6 +20,7 @@ type AdminProps = {
purses?: Array<Purse>;
keplr?: any;
chainId?: string;
contractVersion?: ContractVersion;
};

const Admin: React.FC<AdminProps> = ({
Expand All @@ -29,10 +31,11 @@ const Admin: React.FC<AdminProps> = ({
purses,
keplr,
chainId,
contractVersion = 'ymax0',
}) => {
const [transactions, setTransactions] = useState<any[]>([]);
const [instanceInfo, setInstanceInfo] = useState<{
ymax0?: string;
contract?: string;
postalService?: string;
} | null>(null);
const [instanceBlockHeight, setInstanceBlockHeight] = useState<string | null>(null);
Expand Down Expand Up @@ -101,8 +104,8 @@ const Admin: React.FC<AdminProps> = ({

try {
// Get board ID for target confirmation
const ymax0Instance = instanceInfo?.ymax0 ? instances?.find(([n]) => n === 'ymax0')?.[1] : null;
const [boardId] = ymax0Instance ? watcherRef.current.marshaller.toCapData(ymax0Instance).slots : [''];
const contractInstance = instanceInfo?.contract ? instances?.find(([n]) => n === contractVersion)?.[1] : null;
const [boardId] = contractInstance ? watcherRef.current.marshaller.toCapData(contractInstance).slots : [''];

const { target, tools } = reifyWalletEntry<{ terminate: (args?: { message?: string; target?: string }) => Promise<any> }>({
targetName: 'ymaxControl',
Expand Down Expand Up @@ -377,9 +380,9 @@ const Admin: React.FC<AdminProps> = ({
return instancePair ? String(instancePair[1]) : undefined;
};

const ymax0 = findInstance('ymax0');
const contract = findInstance(contractVersion);
const postalService = findInstance('postalService');
setInstanceInfo({ ymax0, postalService });
setInstanceInfo({ contract, postalService });
},
);

Expand Down Expand Up @@ -614,6 +617,7 @@ const Admin: React.FC<AdminProps> = ({
onTerminate={handleTerminate}
onUpgrade={handleUpgrade}
onInstallAndStart={handleInstallAndStart}
contractVersion={contractVersion}
/>

<CreatorFacetCard
Expand Down
14 changes: 8 additions & 6 deletions ui/src/components/ContractControlCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

interface ContractControlCardProps {
instanceInfo: { ymax0?: string; postalService?: string } | null;
instanceInfo: { contract?: string; postalService?: string } | null;
instanceBlockHeight: string | null;
creatorFacetName: string;
setCreatorFacetName: (value: string) => void;
Expand All @@ -16,10 +16,12 @@ interface ContractControlCardProps {
onTerminate: () => void;
onUpgrade: () => void;
onInstallAndStart: () => void;
contractVersion?: string;
}

const ContractControlCard: React.FC<ContractControlCardProps> = ({
instanceInfo,
contractVersion = 'ymax0',
instanceBlockHeight,
creatorFacetName,
setCreatorFacetName,
Expand All @@ -37,16 +39,16 @@ const ContractControlCard: React.FC<ContractControlCardProps> = ({
}) => {
return (
<div style={{ border: '2px solid #007bff', padding: '1rem', borderRadius: '8px', backgroundColor: '#f8f9fa' }}>
<h3 style={{ color: '#007bff', marginTop: 0, textAlign: 'left' }}>YMax Contract Control</h3>
<h3 style={{ color: '#007bff', marginTop: 0, textAlign: 'left' }}>YMax Contract Control ({contractVersion})</h3>

<div style={{ display: 'flex', gap: '2rem', alignItems: 'flex-start' }}>
{/* Left: Instance Info */}
<div style={{ flex: '0 0 300px' }}>
{instanceInfo?.ymax0 && (
{instanceInfo?.contract && (
<div style={{ marginBottom: '0.5rem' }}>
<strong>Instance:</strong> {instanceInfo.ymax0}{' '}
<strong>Instance:</strong> {instanceInfo.contract}{' '}
<button
onClick={() => navigator.clipboard.writeText(instanceInfo.ymax0!)}
onClick={() => navigator.clipboard.writeText(instanceInfo.contract!)}
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '0' }}
title="Copy instance"
>
Expand Down
Loading