inpage updates wip

This commit is contained in:
Andrew Morris
2024-01-15 10:49:05 +11:00
parent 7b6d83cd2a
commit ae78184eb1
9 changed files with 279 additions and 2 deletions

View File

@@ -107,7 +107,7 @@ module.exports = {
camelcase: [
'error',
{
allow: ['^[a-zA-Z]+__factory$', '^eth_[a-zA-Z]+$'],
allow: ['^[a-zA-Z0-9]+__factory$', '^eth_[a-zA-Z]+$'],
},
],

View File

@@ -9,7 +9,7 @@ This is an in-page wallet for demonstrating WAX modules.
## See the Demo
(TBA: Public URL.)
https://wax-demo.org
```sh
yarn setup

View File

@@ -0,0 +1,11 @@
.erc20-page {
display: flex;
flex-direction: column;
width: 300px;
gap: 1em;
}
.erc20-page input {
width: 100%;
padding: 0.5em;
}

View File

@@ -0,0 +1,163 @@
import './ERC20Page.css';
import { useEffect, useRef, useState } from 'react';
import { ethers } from 'ethers';
import Button from '../src/Button';
import Heading from '../src/Heading';
import DemoContext from './DemoContext';
import Loading from './Loading';
import useRefresh from './useRefresh';
import { ERC20Mock__factory, ERC20__factory } from '../hardhat/typechain-types';
import receiptOf from '../src/helpers/receiptOf';
const ERC20Page = () => {
const demo = DemoContext.use();
const contracts = demo.useContracts();
const signer = demo.useSigner();
const refresh = useRefresh();
const [testTokenAddress, setTestTokenAddress] = useState(
'(test token loading)',
);
const [tokenAddress, setTokenAddress] = useState('');
const [tokenBalance, setTokenBalance] = useState('');
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const balanceRefreshTag = useRef(0);
useEffect(() => {
void (async () => {
if (contracts) {
const addr = await contracts.testToken.getAddress();
setTestTokenAddress(addr);
setTokenAddress(addr);
}
})();
}, [contracts]);
useEffect(() => {
void (async () => {
if (!(contracts && tokenAddress && signer)) {
return;
}
const code = await demo.waxInPage.ethersProvider.getCode(tokenAddress);
if (code === '0x') {
setTokenBalance('');
return;
}
const token = ERC20__factory.connect(tokenAddress, signer);
const symbol = await token.symbol();
const decimals = await token.decimals();
const balance = await token.balanceOf(await signer.getAddress());
setTokenBalance(`${ethers.formatUnits(balance, decimals)} ${symbol}`);
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
contracts,
demo.waxInPage.ethersProvider,
signer,
tokenAddress,
balanceRefreshTag.current,
]);
if (!signer) {
return (
<Loading>
<Heading>ERC20</Heading>
<div>Waiting for signer</div>
</Loading>
);
}
if (!contracts) {
return (
<Loading>
<Heading>ERC20</Heading>
<div>Waiting for contracts</div>
<Button onPress={() => demo.getContracts()}>Deploy</Button>
</Loading>
);
}
return (
<div className="erc20-page">
<Heading>ERC20</Heading>
<div>
<input
type="text"
onInput={(e) => setTokenAddress(e.currentTarget.value)}
placeholder="Token address"
value={tokenAddress}
/>
</div>
<div>
<Button
onPress={async () => {
const testToken = ERC20Mock__factory.connect(
testTokenAddress,
signer,
);
await receiptOf(
testToken.mint(
await signer.getAddress(),
10n ** (await testToken.decimals()),
),
);
balanceRefreshTag.current += 1;
refresh();
}}
secondary={
tokenAddress !== testTokenAddress || parseFloat(tokenBalance) > 0
}
>
Mint
</Button>
</div>
<div>
Balance: <span>{tokenBalance}</span>
</div>
<div>
<input
type="text"
onInput={(e) => setRecipient(e.currentTarget.value)}
placeholder="Recipient address"
/>
</div>
<div>
<input
type="text"
onInput={(e) => setAmount(e.currentTarget.value)}
placeholder="Amount"
/>
</div>
<div>
<Button
onPress={async () => {
const token = ERC20__factory.connect(tokenAddress, signer);
await receiptOf(
token.transfer(
recipient,
ethers.parseUnits(amount, await token.decimals()),
),
);
balanceRefreshTag.current += 1;
refresh();
}}
>
Send
</Button>
</div>
</div>
);
};
export default ERC20Page;

View File

@@ -64,6 +64,9 @@ const LinksPage = () => {
<Button secondary onPress={() => setPath('/sendEth')}>
Send ETH
</Button>
<Button secondary onPress={() => setPath('/erc20')}>
ERC20
</Button>
<Button secondary onPress={() => setPath('/greeter')}>
Greeter dApp
</Button>

View File

@@ -4,6 +4,7 @@ import SendEthPage from './SendEthPage';
import Recovery from './Recovery';
import usePath from './usePath';
import RegisterAddressPage from './RegisterAddressPage';
import ERC20Page from './ERC20Page';
const PageRouter = () => {
const [path] = usePath();
@@ -20,6 +21,10 @@ const PageRouter = () => {
return <SendEthPage />;
}
if (path === '/erc20') {
return <ERC20Page />;
}
if (path === '/recovery') {
return <Recovery />;
}

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
contract ERC20Mock is ERC20 {
constructor() ERC20("ERC20Mock", "E20M") {}
function mint(address account, uint256 amount) external {
_mint(account, amount);
}
function burn(address account, uint256 amount) external {
_burn(account, amount);
}
}

View File

@@ -9,6 +9,8 @@ import makeLocalWaxStorage, { WaxStorage } from './WaxStorage';
import {
AddressRegistry,
AddressRegistry__factory,
ERC20Mock,
ERC20Mock__factory,
EntryPoint,
EntryPoint__factory,
FallbackDecompressor,
@@ -79,6 +81,7 @@ export type Contracts = {
fallbackDecompressor: FallbackDecompressor;
addressRegistry: AddressRegistry;
safeECDSARecoveryPlugin: SafeECDSARecoveryPlugin;
testToken: ERC20Mock;
};
export default class WaxInPage {
@@ -213,6 +216,7 @@ export default class WaxInPage {
SafeECDSARecoveryPlugin__factory,
[],
),
testToken: viewer.connectAssume(ERC20Mock__factory, []),
};
if (this.#contractsDeployed) {
@@ -260,6 +264,7 @@ export default class WaxInPage {
addressRegistry: () => Promise.resolve(addressRegistry),
safeECDSARecoveryPlugin: () =>
factory.connectOrDeploy(SafeECDSARecoveryPlugin__factory, []),
testToken: () => factory.connectOrDeploy(ERC20Mock__factory, []),
};
for (const deployment of Object.values(deployments)) {

View File

@@ -45,6 +45,47 @@ export default class SimulatedBundler implements IBundler {
const contracts = await this.#waxInPage.getContracts();
// const data = contracts.entryPoint.interface.encodeFunctionData(
// 'handleAggregatedOps',
// [
// [
// {
// userOps: [
// {
// sender: '0xb734eb54c90c363d017b27641cc534caf7004fc4',
// nonce: 1,
// initCode: '0x',
// callData: [
// '0x',
// 'b61d27f6',
// '000000000000000000000000c845d6b81d6d1f3b45f2353fec8c960085a9a42e',
// '0000000000000000000000000000000000000000000000000000000000000000',
// '0000000000000000000000000000000000000000000000000000000000000060',
// '0000000000000000000000000000000000000000000000000000000000000024',
// 'a9059cbb',
// '000000000000000000000000e30a735c9b90549f8171f17dd698ab6048dde5ab',
// '0000000000000000000000000000000000000000000000000de0b6b3a7640000',
// '00000000000000000000000000000000000000000000000000000000',
// ].join(''),
// callGasLimit: 0x1228f,
// verificationGasLimit: 0x186a0,
// preVerificationGas: 0xd494,
// maxFeePerGas: 0x3e08feb0,
// maxPriorityFeePerGas: 0x3b9aca00,
// paymasterAndData: '0x',
// signature: '0x',
// },
// ],
// aggregator: '0x61381c18845A464b5A9CFddA2466A12365889d5B',
// signature: '0x',
// },
// ],
// await adminAccount.getAddress(),
// ],
// );
// console.log(data);
// *not* the confirmation, just the response (don't add .wait(), that's
// wrong).
let txResponse;
@@ -393,3 +434,36 @@ const decompressAndPerformSelector = ethers.FunctionFragment.getSelector(
'decompressAndPerform',
['bytes'],
);
async function calculateMedianBasefee(provider: ethers.Provider) {
const latestBlock = (await provider.getBlock('latest'))!;
// Get 100 blocks for the last month and then calculate median basefee
// Estimate block rate
const oldBlock = (await provider.getBlock(latestBlock.number - 1_000_000))!;
const secondsPerBlock =
(latestBlock.timestamp - oldBlock.timestamp) / 1_000_000;
const blockSpan = (86400 * 30) / secondsPerBlock;
const firstBlockNumber = latestBlock.number - blockSpan;
const blocks: ethers.Block[] = [];
for (let i = 0; i < 100; i++) {
blocks.push(
(await provider.getBlock(
firstBlockNumber + Math.round((i * blockSpan) / 99),
))!,
);
}
const basefees = blocks.map((block) => block.baseFeePerGas!);
basefees.sort((a, b) => Number(a - b));
const lowerMedian = basefees[Math.floor(basefees.length / 2)];
const upperMedian = basefees[Math.ceil(basefees.length / 2)];
return (lowerMedian + upperMedian) / 2n;
}