Skip to content

Instantly share code, notes, and snippets.

@ajb413

ajb413/App.tsx Secret

Last active December 12, 2022 17:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajb413/716d49ffbe279f6487b8bfa288c739b3 to your computer and use it in GitHub Desktop.
Save ajb413/716d49ffbe279f6487b8bfa288c739b3 to your computer and use it in GitHub Desktop.
Example Extension App Code
import '../styles/main.scss';
import { RPC } from '@compound-finance/comet-extension';
import { Fragment, useEffect, useMemo, useState } from 'react';
import ERC20 from '../abis/ERC20';
import Comet from '../abis/Comet';
import { CTokenSym, Network, NetworkConfig, getNetwork, getNetworkById, getNetworkConfig, isNetwork, showNetwork } from './Network';
import { JsonRpcProvider } from '@ethersproject/providers';
import { Contract, ContractInterface } from '@ethersproject/contracts';
import { Close } from './Icons/Close';
import { CircleCheckmark } from './Icons/CircleCheckmark';
interface AppProps {
rpc?: RPC,
web3: JsonRpcProvider
}
type AppPropsExt<N extends Network> = AppProps & {
account: string,
networkConfig: NetworkConfig<N>
}
interface AccountState<Network> {
extEnabled: boolean;
}
function usePoll(timeout: number) {
const [timer, setTimer] = useState(0);
useEffect(() => {
let t: NodeJS.Timer;
function loop(x: number, delay: number) {
t = setTimeout(() => {
requestAnimationFrame(() => {
setTimer(x);
loop(x + 1, delay);
});
}, delay);
}
loop(1, timeout);
return () => clearTimeout(t)
}, []);
return timer;
}
function useAsyncEffect(fn: () => Promise<void>, deps: any[] = []) {
useEffect(() => {
(async () => {
await fn();
})();
}, deps);
}
// https://sebhastian.com/javascript-format-number-commas/
function commafy(num, tf=2) {
return num.toFixed(tf).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
function renderTableRows(borrowerData, from=0, to=999) {
let htmlRows = [
(
<tr key="head">
<th>Account</th>
<th>Borrow Balance</th>
<th>Liquidation Price</th>
<th>Percent To Liquidation</th>
<th>Borrow Limit</th>
<th>Get Liquidated Borrow Balance</th>
</tr>
)
];
borrowerData.borrowers.forEach((row, i) => {
if (i >= from && i <= to) {
const lp = +row.liquidationPrice;
htmlRows.push((
<tr key={row.account}>
<td>{ row.account }</td>
<td>{ commafy(row.borrowBalance, 2) }</td>
<td>{ lp ? '$' + commafy(lp, 2) : '' }</td>
<td>{ commafy(row.percentToLiquidation, 0) }%</td>
<td>{ commafy(row.borrowLimit, 2) }</td>
<td>{ commafy(row.liquidationLimit, 2) }</td>
</tr>
));
}
});
return htmlRows;
}
export function App<N extends Network>({rpc, web3, account, networkConfig, borrowers}: AppPropsExt<N>) {
let { cTokenNames } = networkConfig;
const [selfBorrowBalance, setBorrowBalance] = useState<string | null>(null);
const signer = useMemo(() => {
return web3.getSigner().connectUnchecked();
}, [web3, account]);
const initialAccountState = () => ({
extEnabled: false,
});
const [accountState, setAccountState] = useState<AccountState<Network>>(initialAccountState);
const ext = useMemo(() => new Contract(networkConfig.extAddress, networkConfig.extAbi, signer), [signer]);
const comet = useMemo(() => new Contract(networkConfig.rootsV3.comet, Comet, signer), [signer]);
useAsyncEffect(async () => {
const decimals = +(await comet.callStatic.decimals()).toString();
const borrowBalance = +(await comet.callStatic.borrowBalanceOf(account)).toString();
setBorrowBalance(borrowBalance / Math.pow(10, decimals));
}, []);
async function enableExt() {
console.log("enabling ext");
await comet.allow(ext.address, true);
console.log("enabled ext");
}
async function disableExt() {
console.log("disabling ext");
await comet.allow(ext.address, false);
console.log("disabling ext");
}
return (
<div className="page home">
<div className="container">
<div className="masthead L1">
<h1 className="L0 heading heading--emphasized">Account Health Dashboard</h1>
{ accountState.extEnabled ?
<button className="button button--large button--supply" onClick={disableExt}>
<CircleCheckmark />
<label>Enabled</label>
</button> :
<button className="button button--large button--supply" onClick={enableExt}>Enable</button> }
</div>
<div className="home__content">
<div className="home__assets">
<div className="panel panel--assets">
<div className="account-health-table-container">
<table className="account-health-table">
<tbody>
{ renderTableRows(borrowers) }
</tbody>
</table>
</div>
</div>
</div>
<div className="home__sidebar">
<div className="position-card__summary">
<div className="panel position-card L3">
<div className="panel__header-row">
<label className="L1 label text-color--1">My Account Summary</label>
</div>
<div className="panel__header-row">
<p className="text-color--1">
Address: { account }
</p>
</div>
<div className="panel__header-row">
<p className="text-color--1">
Borrow Balance: { selfBorrowBalance }
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default ({rpc, web3}: AppProps) => {
let timer = usePoll(10000);
const [account, setAccount] = useState<string | null>(null);
const [networkConfig, setNetworkConfig] = useState<NetworkConfig<Network> | 'unsupported' | null>(null);
const [market, setSelectedMarket] = useState<string | null>(null);
const [borrowers, setBorrowers] = useState<Any>(null);
if (!window.localStorage.getItem('borrowerData')) {
window.localStorage.setItem(borrowerData, '{}')
}
useAsyncEffect(async () => {
let accounts = await web3.listAccounts();
if (accounts.length > 0) {
let [account] = accounts;
setAccount(account);
}
}, [web3, timer]);
useAsyncEffect(async () => {
let networkWeb3 = await web3.getNetwork();
let network = getNetworkById(networkWeb3.chainId);
if (network) {
setNetworkConfig(getNetworkConfig(network));
} else {
setNetworkConfig('unsupported');
}
}, [web3, timer]);
useAsyncEffect(async () => {
const selectedMarket = new URLSearchParams(window.location.search).get('market');
setSelectedMarket(selectedMarket || '1_USDC_0xc3d688B66703497DAA19211EEdff47f25384cdc3');
}, [web3, timer]);
useAsyncEffect(async () => {
let borrowerData;
try {
borrowerData = JSON.parse(window.localStorage.getItem('borrowerData'));
borrowerData = borrowerData[market] || {};
} catch(e) {
borrowerData = {};
}
if ((!borrowerData.lastFetch || borrowerData.lastFetch < (Date.now() - 60 * 1000)) && market) {
const res = await fetch(`http://localhost:3000/borrowers/${market}`);
borrowerData = await res.json();
borrowerData.lastFetch = Date.now();
window.localStorage.setItem('borrowerData', JSON.stringify({ [market]: borrowerData }));
}
setBorrowers(borrowerData);
}, [web3, timer]);
if (networkConfig && account && market && borrowers) {
if (networkConfig === 'unsupported') {
return <div>Unsupported network...</div>;
} else {
return <App
rpc={rpc}
web3={web3}
account={account}
networkConfig={networkConfig}
borrowers={borrowers}
market={market} />;
}
} else {
return <div>Loading...</div>;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment