From 147d964598b76fe406aeaf866cd3d72dccb162c3 Mon Sep 17 00:00:00 2001 From: Mudassir Shabbir Date: Mon, 6 Oct 2025 13:22:13 +0500 Subject: [PATCH] chore: ymax1 support --- ui/src/App.tsx | 158 +++++++++++++++------- ui/src/components/Admin.tsx | 14 +- ui/src/components/ContractControlCard.tsx | 14 +- ui/src/components/Trade.tsx | 64 ++++++--- ui/src/constants.ts | 5 +- ui/src/utils/stepUtils.ts | 14 +- ui/src/utils/walletUtils.ts | 19 ++- ui/src/ymax-client.ts | 2 + 8 files changed, 199 insertions(+), 91 deletions(-) diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 4f58bb5..73a3a7c 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -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; @@ -36,14 +37,16 @@ let watcher = makeAgoricChainStorageWatcher(ENDPOINTS.API, ENDPOINTS.CHAIN_ID); const useAppStore = create(() => ({}) as AppState); -const setup = async () => { +const setup = async (contractVersion: ContractVersion) => { watcher.watchLatest>( [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), }); }, ); @@ -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); } @@ -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 @@ -215,10 +233,7 @@ const makeOffer = ( publicInvitationMaker: 'makeOpenPortfolioInvitation', }, { - give: { - ...give, - Access: { brand: poc26Brand, value: 1n }, - }, + give: proposalGive, }, { flow: steps }, (update: { status: string; data?: unknown }) => { @@ -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: '', + { + src: '@agoric', + dest: '', 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( @@ -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 }, @@ -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); } @@ -741,6 +781,9 @@ const MainPage = () => { const [environment, setEnvironment] = useState( getInitialEnvironment(), ); + const [contractVersion, setContractVersion] = useState( + (localStorage.getItem('contractVersion') as ContractVersion) || 'ymax0', + ); const { wallet, purses, offerId } = useAppStore((state: AppState) => ({ wallet: state.wallet, @@ -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 */} @@ -816,6 +875,8 @@ const MainPage = () => {
{ }; function App() { + const [contractVersion] = useState( + (localStorage.getItem('contractVersion') as ContractVersion) || 'ymax0', + ); + useEffect(() => { - setup(); - }, []); + setup(contractVersion); + }, [contractVersion]); const { wallet, instances, purses } = useAppStore((state: AppState) => ({ wallet: state.wallet, @@ -890,6 +955,7 @@ function App() { purses={purses} keplr={(window as any).keplr} chainId={ENDPOINTS.CHAIN_ID} + contractVersion={contractVersion} /> } /> diff --git a/ui/src/components/Admin.tsx b/ui/src/components/Admin.tsx index 762568c..17bf9af 100644 --- a/ui/src/components/Admin.tsx +++ b/ui/src/components/Admin.tsx @@ -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'; @@ -19,6 +20,7 @@ type AdminProps = { purses?: Array; keplr?: any; chainId?: string; + contractVersion?: ContractVersion; }; const Admin: React.FC = ({ @@ -29,10 +31,11 @@ const Admin: React.FC = ({ purses, keplr, chainId, + contractVersion = 'ymax0', }) => { const [transactions, setTransactions] = useState([]); const [instanceInfo, setInstanceInfo] = useState<{ - ymax0?: string; + contract?: string; postalService?: string; } | null>(null); const [instanceBlockHeight, setInstanceBlockHeight] = useState(null); @@ -101,8 +104,8 @@ const Admin: React.FC = ({ 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 }>({ targetName: 'ymaxControl', @@ -377,9 +380,9 @@ const Admin: React.FC = ({ return instancePair ? String(instancePair[1]) : undefined; }; - const ymax0 = findInstance('ymax0'); + const contract = findInstance(contractVersion); const postalService = findInstance('postalService'); - setInstanceInfo({ ymax0, postalService }); + setInstanceInfo({ contract, postalService }); }, ); @@ -614,6 +617,7 @@ const Admin: React.FC = ({ onTerminate={handleTerminate} onUpgrade={handleUpgrade} onInstallAndStart={handleInstallAndStart} + contractVersion={contractVersion} /> void; @@ -16,10 +16,12 @@ interface ContractControlCardProps { onTerminate: () => void; onUpgrade: () => void; onInstallAndStart: () => void; + contractVersion?: string; } const ContractControlCard: React.FC = ({ instanceInfo, + contractVersion = 'ymax0', instanceBlockHeight, creatorFacetName, setCreatorFacetName, @@ -37,16 +39,16 @@ const ContractControlCard: React.FC = ({ }) => { return (
-

YMax Contract Control

- +

YMax Contract Control ({contractVersion})

+
{/* Left: Instance Info */}
- {instanceInfo?.ymax0 && ( + {instanceInfo?.contract && (
- Instance: {instanceInfo.ymax0}{' '} + Instance: {instanceInfo.contract}{' '}