Skip to content
Open
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
2 changes: 2 additions & 0 deletions ui-fusdc-lp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
"@cosmos-kit/core": "2.8.9"
},
"devDependencies": {
"@agoric/fast-usdc": "0.1.1-dev-ddff762.0",
"@agoric/react-components": "^0.1.1-dev-ca0ddde.0",
"@agoric/smart-wallet": "0.5.4-dev-ddff762.0",
"@agoric/ui-components": "^0.3.9-u13.0",
"@agoric/web-components": "0.15.1-dev-8fc28e8.0",
"@endo/eventual-send": "^1.2.8",
Expand Down
195 changes: 111 additions & 84 deletions ui-fusdc-lp/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,134 +1,158 @@
import { AmountMath } from '@agoric/ertp';
import type { PoolMetrics } from '@agoric/fast-usdc/src/types.ts';
import { subscribeLatest } from '@agoric/notifier';
import { AgoricProvider } from '@agoric/react-components';
import '@agoric/react-components/dist/style.css';
import {
makeAgoricChainStorageWatcher,
AgoricChainStoragePathKind as Kind,
} from "@agoric/rpc";
import {
multiplyBy,
parseRatio,
ceilDivideBy,
} from "@agoric/zoe/src/contractSupport/ratio.js";
import { AmountMath } from "@agoric/ertp";
import { create } from "zustand";
makeAgoricChainStorageWatcher,
} from '@agoric/rpc';
import type { OfferSpec } from '@agoric/smart-wallet/src/offers';
import type { CurrentWalletRecord } from '@agoric/smart-wallet/src/smartWallet.d.ts';
import {
makeAgoricWalletConnection,
suggestChain,
} from "@agoric/web-components";
import { subscribeLatest } from "@agoric/notifier";
import { AgoricProvider } from "@agoric/react-components";
import { wallets } from "cosmos-kit";
import { ThemeProvider, useTheme } from "@interchain-ui/react";
import "./index.css";
import "@agoric/react-components/dist/style.css";
import { Navbar } from "./components/Navbar";
import Dashboard from "./components/Dashboard";
import { useEffect, useState } from "react";
} from '@agoric/web-components';
import {
ceilDivideBy,
multiplyBy,
parseRatio,
} from '@agoric/zoe/src/contractSupport/ratio.js';
import { ThemeProvider, useTheme } from '@interchain-ui/react';
import { wallets } from 'cosmos-kit';
import { useEffect, useState } from 'react';
import { create } from 'zustand';
import Dashboard from './components/Dashboard';
import { Navbar } from './components/Navbar';
import './index.css';

// XXX import type { USDCProposalShapes } from '@agoric/fast-usdc/src/types.ts';
type USDCProposalShapes = {
deposit: {
give: { USDC: Amount<'nat'> };
want?: { PoolShare: Amount<'nat'> };
};
withdraw: {
give: { PoolShare: Amount<'nat'> };
want: { USDC: Amount<'nat'> };
};
};
type PurseRecord = CurrentWalletRecord['purses'];

type Wallet = Awaited<ReturnType<typeof makeAgoricWalletConnection>>;

const { fromEntries } = Object;

const ENDPOINTS = {
RPC: "http://localhost:26657",
API: "http://localhost:1317",
RPC: 'http://localhost:26657',
API: 'http://localhost:1317',
};

const watcher = makeAgoricChainStorageWatcher(ENDPOINTS.API, "agoriclocal");
const watcher = makeAgoricChainStorageWatcher(ENDPOINTS.API, 'agoriclocal');
interface AppState {
wallet?: Wallet;
fusdcInstance?: unknown;
brands?: Record<string, unknown>;
purses?: Array<Purse>;
brands?: Record<string, Brand>;
purses?: Array<PurseRecord>;
metrics?: Record<string, unknown>;
}
const useAppStore = create<AppState>(() => ({}));

const setup = async () => {
watcher.watchLatest<Array<[string, unknown]>>(
[Kind.Data, "published.agoricNames.instance"],
(instances) => {
console.log("got instances", instances);
[Kind.Data, 'published.agoricNames.instance'],
pairs => {
const instances = fromEntries(pairs);
console.log('got instances', instances);
useAppStore.setState({
fusdcInstance: instances.find(([name]) => name === "fastUsdc")!.at(1),
fusdcInstance: instances.fastUsdc,
});
}
);

watcher.watchLatest<Array<[string, unknown]>>(
[Kind.Data, "published.agoricNames.brand"],
(brands) => {
console.log("Got brands", brands);
useAppStore.setState({
brands: Object.fromEntries(brands),
});
watcher.watchLatest<Array<[string, Brand]>>(
[Kind.Data, 'published.agoricNames.brand'],
pairs => {
const brands = fromEntries(pairs);
console.log('Got brands', brands);
useAppStore.setState({ brands });
}
);

watcher.watchLatest<Array<[string, unknown]>>(
[Kind.Data, "published.fastUsdc.poolMetrics"],
(metrics) => {
console.log("Got metrics", metrics);
watcher.watchLatest<Array<[string, PoolMetrics]>>(
[Kind.Data, 'published.fastUsdc.poolMetrics'],
metrics => {
console.log('Got metrics', metrics);
useAppStore.setState({
metrics: {...metrics},
metrics: { ...metrics },
});
}
);

};
const parseUSDCAmount = (amountString, usdc) => {

const parseUSDCAmount = (numeral: string, usdc: Brand): Amount<'nat'> => {
const USDC_DECIMALS = 6;
const unit = AmountMath.make(usdc, 10n ** BigInt(USDC_DECIMALS));
return multiplyBy(unit, parseRatio(amountString, usdc));
return multiplyBy(unit, parseRatio(numeral, usdc));
};

const connectWallet = async () => {
await suggestChain("https://local.agoric.net/network-config");
const wallet = await makeAgoricWalletConnection(watcher, ENDPOINTS.RPC);
const info = await suggestChain('https://local.agoric.net/network-config');
console.log('connectWallet: chain info', info);
const wallet = await makeAgoricWalletConnection(watcher, info.rpc);
useAppStore.setState({ wallet });
console.log("wallet", wallet);
console.log('wallet', wallet);
const { pursesNotifier } = wallet;
for await (const purses of subscribeLatest<Purse[]>(pursesNotifier)) {
console.log("got purses", purses);
for await (const purses of subscribeLatest<PurseRecord[]>(pursesNotifier)) {
console.log('got purses', purses);
useAppStore.setState({ purses });
}
};

const makeDepositOffer = () => {
const { wallet, fusdcInstance, brands } = useAppStore.getState();
if (!fusdcInstance) throw Error("no contract instance");
if (!(brands && brands.USDC)) throw Error("brands not available");
const proposal = {
give: {
USDC: parseUSDCAmount('10', brands.USDC),
},
if (!fusdcInstance) throw Error('no contract instance');
if (!(brands && brands.USDC)) throw Error('brands not available');
const proposal: USDCProposalShapes['deposit'] = {
give: { USDC: parseUSDCAmount('10', brands.USDC) },
};
console.log("about to make offer", wallet);
wallet?.makeOffer(
{
source: "agoricContract",
const depositSpec: OfferSpec = {
id: 'unused',
invitationSpec: {
source: 'contract',
instance: fusdcInstance,
callPipe: [["makeDepositInvitation", []]],
publicInvitationMaker: 'makeDepositInvitation',
},
proposal,
};
console.log('about to make offer', wallet, depositSpec);
wallet?.makeOffer(
depositSpec.invitationSpec,
depositSpec.proposal,
undefined,
(update: { status: string; data?: unknown }) => {
if (update.status === "error") {
console.log('@@@@offer update', update);
if (update.status === 'error') {
alert(`Offer error: ${update.data}`);
}
if (update.status === "accepted") {
alert("Offer accepted");
if (update.status === 'accepted') {
alert('Offer accepted');
}
if (update.status === "refunded") {
alert("Offer rejected");
if (update.status === 'refunded') {
alert('Offer rejected');
}
}
);
};

const makeWithdrawOffer = () => {
const { wallet, fusdcInstance, brands, metrics } = useAppStore.getState();
if (!fusdcInstance) throw Error("no contract instance");
if (!(brands && brands.USDC && brands.FastLP)) throw Error("brands not available");
if (!(metrics && metrics.shareWorth)) throw Error("metrics not available");
if (!fusdcInstance) throw Error('no contract instance');
if (!(brands && brands.USDC && brands.FastLP))
throw Error('brands not available');
if (!(metrics && metrics.shareWorth)) throw Error('metrics not available');

const usdcAmount = parseUSDCAmount('10', brands.USDC)
const usdcAmount = parseUSDCAmount('10', brands.USDC);
const fastLPAmount = ceilDivideBy(usdcAmount, metrics.shareWorth);
const proposal = {
give: {
Expand All @@ -139,39 +163,39 @@
},
};
console.log('fastLPAmount', fastLPAmount);
console.log("about to make withdraw offer");
console.log('about to make withdraw offer');
wallet?.makeOffer(
{
source: "agoricContract",
source: 'agoricContract',
instance: fusdcInstance,
callPipe: [["makeWithdrawInvitation", []]],
callPipe: [['makeWithdrawInvitation', []]],
},
proposal,
undefined,
(update: { status: string; data?: unknown }) => {
if (update.status === "error") {
if (update.status === 'error') {
alert(`Offer error: ${update.data}`);
}
if (update.status === "accepted") {
alert("Offer accepted");
if (update.status === 'accepted') {
alert('Offer accepted');
}
if (update.status === "refunded") {
alert("Offer rejected");
if (update.status === 'refunded') {
alert('Offer rejected');
}
}
);
};

function App() {
const [metrics, setMetrics] = useState<Record<string, unknown>>({});

Check failure on line 190 in ui-fusdc-lp/src/App.tsx

View workflow job for this annotation

GitHub Actions / unit (20)

'metrics' is assigned a value but never used

Check failure on line 190 in ui-fusdc-lp/src/App.tsx

View workflow job for this annotation

GitHub Actions / unit (20)

'setMetrics' is assigned a value but never used
useEffect(() => {
setup();
}, []);
const tryConnectWallet = () => {
connectWallet().catch((err) => {
connectWallet().catch(err => {
switch (err.message) {
case "KEPLR_CONNECTION_ERROR_NO_SMART_WALLET":
alert("no smart wallet at that address");
case 'KEPLR_CONNECTION_ERROR_NO_SMART_WALLET':
alert('no smart wallet at that address');
break;
default:
alert(err.message);
Expand All @@ -187,19 +211,22 @@
agoricNetworkConfigs={[
{
testChain: {
chainId: "agoriclocal",
chainName: "agoric-local",
chainId: 'agoriclocal',
chainName: 'agoric-local',
},
apis: {
rest: ["http://localhost:1317"],
rpc: ["http://localhost:26657"],
rest: ['http://localhost:1317'],
rpc: ['http://localhost:26657'],
},
},
]}
defaultChainName="agoric-local"
>
<Navbar onConnectClick={tryConnectWallet} />
<Dashboard makeDepositOffer={makeDepositOffer} makeWithdrawOffer={makeWithdrawOffer} />
<Dashboard
makeDepositOffer={makeDepositOffer}
makeWithdrawOffer={makeWithdrawOffer}
/>
</AgoricProvider>
</div>
</ThemeProvider>
Expand Down
24 changes: 15 additions & 9 deletions ui-fusdc-lp/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import Form from "./Form";
import Cards from "./Cards";
import Transactions from "./Transactions";
import Form from './Form';
import Cards from './Cards';
import Transactions from './Transactions';

const headerData = [
{ title: "Total Pool Balance", value: "$1,234,567", description: "USDC" },
{ title: "Your Pool Share", value: "$123,456", description: "10% of pool" },
{ title: "Awaiting Settlement", value: "$5,678", description: "USDC" },
{ title: "Fees Earned (24h)", value: "$890", description: "USDC" },
{ title: 'Total Pool Balance', value: '$1,234,567', description: 'USDC' },
{ title: 'Your Pool Share', value: '$123,456', description: '10% of pool' },
{ title: 'Awaiting Settlement', value: '$5,678', description: 'USDC' },
{ title: 'Fees Earned (24h)', value: '$890', description: 'USDC' },
];

const Dashboard = ({ makeDepositOffer, makeWithdrawOffer }) => (
const Dashboard = ({
makeDepositOffer,
makeWithdrawOffer,
}: Record<string, Function>) => (
<div className="p-6 bg-gray-100 min-h-screen">
<Cards data={headerData} />
<Form makeDepositOffer={makeDepositOffer} makeWithdrawOffer={makeWithdrawOffer} />
<Form
makeDepositOffer={makeDepositOffer}
makeWithdrawOffer={makeWithdrawOffer}
/>
<Transactions />
</div>
);
Expand Down
Loading
Loading