mirror of
https://github.com/getwax/wax.git
synced 2026-01-08 22:57:58 -05:00
inpage updates wip
This commit is contained in:
@@ -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]+$'],
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
11
demos/inpage/demo/ERC20Page.css
Normal file
11
demos/inpage/demo/ERC20Page.css
Normal file
@@ -0,0 +1,11 @@
|
||||
.erc20-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 300px;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.erc20-page input {
|
||||
width: 100%;
|
||||
padding: 0.5em;
|
||||
}
|
||||
163
demos/inpage/demo/ERC20Page.tsx
Normal file
163
demos/inpage/demo/ERC20Page.tsx
Normal 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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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 />;
|
||||
}
|
||||
|
||||
16
demos/inpage/hardhat/contracts/ERC20Mock.sol
Normal file
16
demos/inpage/hardhat/contracts/ERC20Mock.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user