Skip to content

Commit

Permalink
Introduce Aggregates & Transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
lucemans committed Mar 19, 2024
1 parent d5a3a63 commit b299411
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 155 deletions.
68 changes: 62 additions & 6 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,40 @@
import { useMemo } from 'react';
import useSWR from 'swr';

import { getTransactions } from './etherscan/getTransactions';
import { TransactionHistory } from './txHistory/TransactionHistory';
import { Aggregates, aggregateTotals } from './utils/aggregateTotals';
import {
AllMultiReturnTypes,
decodeTransaction,
} from './utils/decodeTransaction';
import { formatThousands } from './utils/formatThousands';

const resolverAddress = '0xFC0a4A934410F34A9bb8b4F28bEd6b960C943a7E';
const contractAddress = '0xFC0a4A934410F34A9bb8b4F28bEd6b960C943a7E';

export const App = () => {
const { data, isLoading, error } = useSWR(
'/transactions',
async () => await getTransactions(contractAddress),
{
keepPreviousData: true,
}
);

const decodedTransactions = useMemo(
() =>
data?.result.map(decodeTransaction).filter(Boolean) as
| AllMultiReturnTypes[]
| undefined,
[data]
);

const totals = useMemo(() => {
if (!decodedTransactions) return;

return aggregateTotals(decodedTransactions) as Aggregates;
}, [decodedTransactions]);

return (
<div className="w-full h-full min-h-screen bg-light-background-secondary dark:bg-dark-background-secondary px-4">
<div className="mx-auto w-full max-w-3xl space-y-4 py-8">
Expand All @@ -18,7 +50,7 @@ export const App = () => {
<a
href={
'https://etherscan.io/address/' +
resolverAddress
contractAddress
}
className="link"
target="_blank"
Expand All @@ -30,18 +62,42 @@ export const App = () => {
<div className="flex gap-4 w-full flex-col md:flex-row">
<div className="card w-full px-4 py-2">
<div>Commit Fee</div>
<div className="text-right font-bold text-lg">0.00</div>
{totals?.commitAverage ? (
<div className="text-right font-bold text-lg">
{formatThousands(totals.commitAverage)}
</div>
) : (
<div>Loading...</div>
)}
</div>
<div className="card w-full px-4 py-2">
<div>Registration Fee</div>
<div className="text-right font-bold text-lg">0.00</div>
{totals?.registerAverage ? (
<div className="text-right font-bold text-lg">
{formatThousands(totals.registerAverage)}
</div>
) : (
<div>Loading...</div>
)}{' '}
</div>
<div className="card w-full px-4 py-2">
<div>Renewal Fee</div>
<div className="text-right font-bold text-lg">0.00</div>
{totals?.renewAverage ? (
<div className="text-right font-bold text-lg">
{formatThousands(totals.renewAverage)}
</div>
) : (
<div>Loading...</div>
)}{' '}
</div>
</div>
<TransactionHistory contractAddress={resolverAddress} />
<TransactionHistory
contractAddress={contractAddress}
totals={totals}
txs={decodedTransactions}
error={error}
isLoading={isLoading}
/>
</div>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions web/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import ReactDOM from 'react-dom/client';

import { App } from './App';

// @ts-ignore
BigInt.prototype.toJSON = function () {
return this.toString();
};

ReactDOM.createRoot(document.querySelector('#root') as HTMLElement).render(
<React.StrictMode>
<App />
Expand Down
176 changes: 45 additions & 131 deletions web/src/txHistory/TransactionEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,141 +3,58 @@ import { FC } from 'react';
import { BsFuelPump } from 'react-icons/bs';
import { FiBox, FiChevronDown } from 'react-icons/fi';
import { LuFlame } from 'react-icons/lu';
import { decodeFunctionData } from 'viem';

import { ultraBulkAbi } from '../abi';
import { EtherscanTx } from '../etherscan/getTransactions';
import { formatFullAndRelativeDate } from '../utils/date';
import {
AllMultiReturnTypes,
deriveLabelFromFunctionName,
} from '../utils/decodeTransaction';
import { useEthUsd } from '../utils/ethUsd';
import { formatThousands } from '../utils/formatThousands';
import { gasToEth } from '../utils/gas';

type DecodedFunction<K, V> = { functionName: K; args: V };
type MultiRegisterType = DecodedFunction<
'multiRegister',
[string[], string[], bigint, string, string]
>;
type MultiCommitType = DecodedFunction<'multiCommit', [string[]]>;
type MultiRenewType = DecodedFunction<'renewAll', [string[], bigint, bigint]>;

type AllMultiReturnTypes = MultiRegisterType | MultiCommitType | MultiRenewType;

const decodeFunctionInput = (
inputData: string,
to: string
): AllMultiReturnTypes | undefined => {
// If contract Create
if (to == '') {
return;
}

try {
const { args, functionName } = decodeFunctionData({
abi: ultraBulkAbi,
data: inputData as '0x{string}',
});

if (functionName == 'multiRegister' && args) {
return {
functionName,
args,
} as MultiRegisterType;
}

if (functionName == 'multiCommit' && args) {
return {
functionName,
args,
} as MultiCommitType;
}

if (functionName == 'renewAll' && args) {
return {
functionName,
args,
} as MultiRenewType;
}
} catch (error) {
console.error({ e: error });
}
};

const deriveLabelFromFunctionName = (functionName: string, to: string) => {
if (to == '') return 'Deploy';

if (functionName.startsWith('multiRegister(')) return 'Register';

if (functionName.startsWith('multiCommit(')) return 'Commit';

if (functionName.startsWith('renewAll(')) return 'Renew';

return `Unknown (${functionName.split('(').shift()})`;
};

const getNameLength = (inputData?: AllMultiReturnTypes) => {
if (!inputData) return;

if (inputData.functionName == 'multiRegister') {
return inputData.args[0].length;
}

if (inputData.functionName == 'multiCommit') {
return inputData.args[0].length;
}

if (inputData.functionName == 'renewAll') {
return inputData.args[0].length;
}
};

export const TransactionEntry: FC<{ txHash: EtherscanTx }> = ({ txHash }) => {
const actionLabel = deriveLabelFromFunctionName(
txHash.functionName,
txHash.to
);
const inputData = decodeFunctionInput(txHash.input, txHash.to);
const ethUsd = useEthUsd(Number(txHash.timeStamp) * 1000);

const namesLength = getNameLength(inputData);
export const TransactionEntry: FC<{ tx: AllMultiReturnTypes }> = ({ tx }) => {
const actionLabel = deriveLabelFromFunctionName(tx.functionName, tx.tx.to);
const ethUsd = useEthUsd(Number(tx.tx.timeStamp) * 1000);

const { full, relative } = formatFullAndRelativeDate(
new Date(Number(txHash.timeStamp) * 1000)
new Date(Number(tx.tx.timeStamp) * 1000)
);

return (
<div className="p-4 card w-full space-y-3">
<div className="flex justify-between items-center gap-4 flex-wrap">
<div>
<a
href={'https://etherscan.io/tx/' + txHash.hash}
href={'https://etherscan.io/tx/' + tx.tx.hash}
className="link"
>
{txHash.hash.slice(0, 8)}...
{tx.tx.hash.slice(0, 8)}...
</a>
</div>
<div
className="flex justify-center items-center gap-1"
title="Blocknumber"
>
<FiBox />
{txHash.blockNumber}
{tx.tx.blockNumber}
</div>
<div title={full}>{relative}</div>
<div>
<span className="label label-blue">{actionLabel}</span>
</div>
<div className="grow"></div>
{namesLength && (
{tx.length > 0 && (
<div className="text-center">
<div>{namesLength}</div>
<div>{tx.length}</div>
<div className="text-xs">names</div>
</div>
)}
{namesLength && (
{tx.length > 0 && (
<div className="text-center">
<div>
{formatThousands(
BigInt(txHash.gasUsed) / BigInt(namesLength)
BigInt(tx.tx.gasUsed) / BigInt(tx.length)
)}
</div>
<div className="text-xs">Per Name</div>
Expand All @@ -146,15 +63,15 @@ export const TransactionEntry: FC<{ txHash: EtherscanTx }> = ({ txHash }) => {
<div className="flex flex-col justify-center items-center gap-1">
<div className="flex justify-center items-center gap-1">
<BsFuelPump />
<div>{formatThousands(BigInt(txHash.gasUsed))}</div>
<div>{formatThousands(BigInt(tx.tx.gasUsed))}</div>
</div>
{ethUsd.data && (
<div className="text-sm opacity-70">
{(
ethUsd.data *
gasToEth(
BigInt(txHash.gasUsed),
BigInt(txHash.gasPrice)
BigInt(tx.tx.gasUsed),
BigInt(tx.tx.gasPrice)
)
).toFixed(2)}{' '}
USD
Expand All @@ -163,60 +80,57 @@ export const TransactionEntry: FC<{ txHash: EtherscanTx }> = ({ txHash }) => {
</div>
<div className="flex justify-center items-center gap-1">
<LuFlame />
{(Number(BigInt(txHash.gasPrice) / 100_000_000n) / 10)
{(Number(BigInt(tx.tx.gasPrice) / 100_000_000n) / 10)
.toPrecision(2)
.toString()}
</div>
<label
htmlFor={'car-' + txHash.hash}
htmlFor={'car-' + tx.tx.hash}
className="cursor-pointer rounded-full p-2 hover:bg-light-background-secondary dark:hover:bg-dark-background-secondary"
>
<FiChevronDown />
</label>
</div>
<input
type="checkbox"
id={'car-' + txHash.hash}
id={'car-' + tx.tx.hash}
className="hidden"
/>
<div className="open-if-checkbox space-y-2">
{inputData?.functionName === 'multiRegister' ||
(inputData?.functionName === 'renewAll' && (
<div className="flex gap-2 border border-light-border dark:border-dark-border p-2 rounded-lg">
<div>Names</div>
<div>
<ul className="grid grid-cols-2 gap-2">
{inputData?.args[0].map((name, _index) => (
<li>
<a
href={'https://ens.app/' + name}
className="link"
target="_blank"
>
{name}
</a>
</li>
))}
</ul>
</div>
{['renewAll', 'multiRegister'].includes(tx.functionName) && (
<div className="flex gap-2 border border-light-border dark:border-dark-border p-2 rounded-lg">
<div>Names</div>
<div>
<ul className="grid grid-cols-2 gap-2">
{tx.args[0].map((name, _index) => (
<li>
<a
href={'https://ens.app/' + name}
className="link"
target="_blank"
>
{name}
</a>
</li>
))}
</ul>
</div>
))}
{inputData?.functionName === 'multiCommit' && (
</div>
)}
{tx.functionName === 'multiCommit' && (
<div className="flex gap-2 border border-light-border dark:border-dark-border p-2 rounded-lg">
<div>Commitments</div>
<div>
<ul className="grid grid-cols-2 gap-2">
{inputData?.args[0].map(
(commitment, _index) => (
<li>{commitment}</li>
)
)}
{tx.args[0].map((commitment, _index) => (
<li>{commitment}</li>
))}
</ul>
</div>
</div>
)}
<div className="whitespace-break-spaces break-words w-full overflow-hidden bg-light-background-secondary rounded-lg p-4">
<span>{JSON.stringify(txHash)}</span>
<span>{JSON.stringify(tx)}</span>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit b299411

Please sign in to comment.