Skip to content

Commit 42d5844

Browse files
authored
Merge pull request #91 from worm-privacy/dev
New Release
2 parents fd8a0c9 + c6a198f commit 42d5844

9 files changed

Lines changed: 167 additions & 22 deletions

File tree

src/app/tools/claim-worm/claim-list.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const ReadyToClaimItem = (props: { epochNumber: bigint; share: bigint; refresh:
7878
</div>
7979
<div className="grow" />
8080
<div className="text-[14px]">Your share:</div>
81-
<div className="font-orbitron">{roundEther(props.share, 4)}</div>
81+
<div className="font-orbitron">{roundEther(props.share)}</div>
8282
<div className="text-[14px] text-brand">WORM</div>
8383
</div>
8484
</DialogTrigger>
@@ -98,7 +98,7 @@ const ReadyToClaimItem = (props: { epochNumber: bigint; share: bigint; refresh:
9898
<div className="mb-5 opacity-70">Epoch number {props.epochNumber}</div>
9999
<div className="mb-1">Your reward share: </div>
100100
<div className="flex flex-row">
101-
<div className="mr-2 font-bold text-white">{roundEther(props.share, 4)}</div>
101+
<div className="mr-2 font-bold text-white">{roundEther(props.share)}</div>
102102
<div className="font-bold text-brand">WORM</div>
103103
</div>
104104
<button
@@ -130,7 +130,7 @@ const UpComingEpochItem = (props: { epochNumber: bigint; share: bigint }) => {
130130
) : (
131131
<>
132132
<div className="text-[14px]">Your share: ~</div>
133-
<div className="font-orbitron">{roundEther(props.share, 4)}</div>
133+
<div className="font-orbitron">{roundEther(props.share)}</div>
134134
<div className=" text-[14px] text-brand">WORM</div>
135135
</>
136136
)}

src/app/tools/claim-worm/total-claim.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default function TotalClaim(props: { result: UseClaimListResult; refresh:
5353
<div className="text-[24px] font-bold">Rewards are ready</div>
5454
<div className="mt-5 text-white opacity-80">Total Claimable reward: </div>
5555
<div className="flex flex-row items-center gap-2">
56-
<div className="text-[24px] font-bold text-white">{roundEther(totalClaimAmount, 4)} </div>
56+
<div className="text-[24px] font-bold text-white">{roundEther(totalClaimAmount)} </div>
5757
<div className="text-[24px] text-brand">WORM </div>
5858
</div>
5959
<div className="grow" />

src/app/tools/mine-worm/participate-inputs.tsx

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import ErrorComponent from '@/components/tools/error-component';
22
import InputComponent from '@/components/tools/input-text';
33
import LoadingComponent from '@/components/tools/loading';
4+
import { useDebounceEffect } from '@/hooks/use-debounce-effect';
45
import { UseEpochListResult } from '@/hooks/use-epoch-list';
56
import { useInput } from '@/hooks/use-input';
67
import { BETHContract, BETHContractABI, BETHContractAddress } from '@/lib/core/contracts/beth';
78
import { WORMContract } from '@/lib/core/contracts/worm';
9+
import { roundEther } from '@/lib/core/utils/round-ether';
810
import { validateAll, validateETHAmount, validatePositiveInteger } from '@/lib/core/utils/validator';
911
import { useState } from 'react';
1012
import { formatEther, parseEther } from 'viem';
@@ -19,6 +21,8 @@ export default function ParticipateInputs(props: { result: UseEpochListResult; r
1921
const [participateLoading, setParticipateLoading] = useState(false);
2022
const client = useClient();
2123

24+
const [approximatedAmount, setApproximatedAmount] = useState(0n);
25+
2226
const { mutateAsync } = useWriteContract();
2327

2428
const { address, isConnected } = useConnection();
@@ -39,6 +43,24 @@ export default function ParticipateInputs(props: { result: UseEpochListResult; r
3943
return <ErrorComponent title="Balance Error" details="error happens while getting your balance" />;
4044
}
4145

46+
let perEpoch = 0n;
47+
try {
48+
perEpoch = parseEther(bethAmount.value) / BigInt(parseInt(numberOfEpochs.value));
49+
} catch (e) {}
50+
51+
useDebounceEffect(
52+
() => {
53+
WORMContract.approximate(client!, perEpoch, BigInt(parseInt(numberOfEpochs.value))).then(
54+
(_approximatedAmount) => {
55+
console.log('approximated value:', _approximatedAmount);
56+
setApproximatedAmount(_approximatedAmount);
57+
}
58+
);
59+
},
60+
2000,
61+
[bethAmount.value, numberOfEpochs.value]
62+
);
63+
4264
if (!isConnected) {
4365
return (
4466
<>
@@ -76,7 +98,7 @@ export default function ParticipateInputs(props: { result: UseEpochListResult; r
7698
setParticipateLoading(true);
7799
setIsParticipateError(false);
78100
try {
79-
await BETHContract.approve(mutateAsync, client!, beth);
101+
await BETHContract.approveInfiniteIfAllowanceNotEnough(mutateAsync, client!, address!, beth);
80102
await WORMContract.participate(mutateAsync, client!, bethPerEpoch, epochs);
81103
props.refresh();
82104
setParticipated(true);
@@ -118,6 +140,27 @@ export default function ParticipateInputs(props: { result: UseEpochListResult; r
118140
optional={false}
119141
/>
120142

143+
{perEpoch !== 0n ? (
144+
<div className="text-white ">
145+
<span className="opacity-70">BETH amount per epoch:</span>{' '}
146+
<span className="font-orbitron">{roundEther(perEpoch)}</span> <span className="text-pink-400">BETH</span>
147+
</div>
148+
) : (
149+
<></>
150+
)}
151+
152+
{approximatedAmount !== 0n ? (
153+
<div className="flex flex-col rounded-lg bg-[rgba(var(--neutral-low-rgb),0.15)] p-3 text-white">
154+
<div className="opacity-70">Approximated reward</div>
155+
<div className="text-[20px]">
156+
{roundEther(approximatedAmount)}
157+
<span className="ml-2 font-bold text-brand">WORM</span>
158+
</div>
159+
</div>
160+
) : (
161+
<></>
162+
)}
163+
121164
<div className="grow" />
122165

123166
<button onClick={onParticipateClick} className="w-full rounded-lg bg-brand px-4 py-3 text-black">

src/app/tools/stake-worm/claim-list.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ const StakingItemComponent = (props: { staking: StakingItem; currentWeek: bigint
156156
<div className="flex flex-col">
157157
<div className="mb-2">Staked amount</div>
158158
<div className="flex flex-row">
159-
<div className="mr-2 text-[24px] font-bold text-white">{roundEther(staking.stakeAmount, 4)}</div>
159+
<div className="mr-2 text-[24px] font-bold text-white">{roundEther(staking.stakeAmount)}</div>
160160
<div className="text-brand">WORM</div>
161161
</div>
162162
{props.staking.state === 'Ended' && (
@@ -237,7 +237,7 @@ const StakingWeekItemComponent = (props: {
237237
<div className="text-[12px]">
238238
<span className="font-orbitron">{roundEtherF(week.yourShare)}</span>
239239
<span className="text-[#727E8F]">% </span>
240-
<span className="ml-1 font-orbitron">{formatEther(week.yourReward)}</span>
240+
<span className="ml-1 font-orbitron">{roundEther(week.yourReward, 12)}</span>
241241
<span className="ml-1 text-[#FF47C0]">BETH</span>
242242
</div>
243243
</div>
@@ -265,7 +265,7 @@ const StakingWeekItemComponent = (props: {
265265
</div>
266266
<div className="mt-5 mb-1">Total reward share: </div>
267267
<div className="flex flex-row">
268-
<div className="mr-2 text-[24px] font-bold text-white">{roundEther(week.totalReward, 4)}</div>
268+
<div className="mr-2 text-[24px] font-bold text-white">{roundEther(week.totalReward)}</div>
269269
<div className="text-[#FF47C0]">BETH</div>
270270
</div>
271271
{props.week.weekNumber < props.currentWeek ? (

src/app/tools/stake-worm/staking-inputs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export default function StakingInputs(props: { result: UseStakingListResult; ref
7979
setStakeLoading(true);
8080
setIsStakeError(false);
8181
try {
82-
await WORMContract.approve(mutateAsync, client!, worm);
82+
await WORMContract.approveInfiniteIfAllowanceNotEnough(mutateAsync, client!, address!, worm);
8383
await StakingContract.lock(mutateAsync, client!, worm, weeks);
8484
props.refresh();
8585
refetchBalance();

src/app/wormhole/page.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { validateAddress, validateAll, validateETHAmount } from '@/lib/core/util
2727
import { loadJson } from '@/lib/utils/load-json';
2828
import { newSavableRecoverData, recoverDataFromJson } from '@/lib/utils/recover-data';
2929
import { saveJson } from '@/lib/utils/save-json';
30-
import { useEffect, useState } from 'react';
30+
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
3131
import { Client, formatEther, hexToBytes, parseEther, toHex } from 'viem';
3232
import { waitForTransactionReceipt } from 'viem/actions';
3333
import { useClient, usePublicClient, useSendTransaction } from 'wagmi';
@@ -50,7 +50,7 @@ export default function Wormhole() {
5050

5151
// state
5252
const [wormholeState, setWormholeState] = useState<WormholeState>('Start');
53-
const isLoadingState = wormholeState === 'Calculating' || wormholeState == 'Sending' || wormholeState == 'Minting';
53+
const isLoadingState = !(wormholeState === 'Send' || wormholeState === 'Start');
5454
const [error, setError] = useState<string | null>(null); // null means no error state
5555

5656
const [burnAddress, setBurnAddress] = useState<BurnAddressContent | null>(null);
@@ -124,11 +124,18 @@ export default function Wormhole() {
124124
};
125125

126126
const onSendClick = async () => {
127-
setWormholeState('Sending');
128127
try {
128+
setWormholeState('Sending to burn address');
129129
await transferETH(mutateAsync, client!, burnAddress!.revealAmount, burnAddress!.burnAddress);
130-
setWormholeState('Minting');
131-
await generateAndSubmit(client!, burnAddress!, publicClient, burnAddress!.revealAmount, network, proverAddress!);
130+
await generateAndSubmit(
131+
client!,
132+
burnAddress!,
133+
setWormholeState,
134+
publicClient,
135+
burnAddress!.revealAmount,
136+
network,
137+
proverAddress!
138+
);
132139
resetStates();
133140
} catch (e) {
134141
console.error('onSend', e);
@@ -180,11 +187,11 @@ export default function Wormhole() {
180187
burnAmount.update(formatEther(recoverData.burn.revealAmount));
181188
receiverAddress.update(recoverData.burn.receiverAddr);
182189

183-
setWormholeState('Sending');
184190
try {
185191
await generateAndSubmit(
186192
client!,
187193
recoverData.burn,
194+
setWormholeState,
188195
publicClient,
189196
recoverData.burn.revealAmount,
190197
network,
@@ -278,11 +285,13 @@ export default function Wormhole() {
278285
const generateAndSubmit = async (
279286
client: Client,
280287
burnAddress: BurnAddressContent,
288+
setWormholeState: Dispatch<SetStateAction<WormholeState>>,
281289
publicClient: any, // pass whatever usePublicClient() returns
282290
burnAmount: bigint,
283291
network: WormNetwork,
284292
proverAddress?: `0x${string}`
285293
) => {
294+
setWormholeState('Generating proof');
286295
let blockNumber = (await publicClient!.getBlock()).number;
287296
let accountProof = await publicClient?.getProof({
288297
address: burnAddress.burnAddress as `0x${string}`,
@@ -314,6 +323,7 @@ const generateAndSubmit = async (
314323
}
315324
console.log('rapidsnarkProof:', rapidsnarkProof);
316325

326+
setWormholeState('Submitting proof');
317327
const remainingCoin = calculateRemainingCoinHash(
318328
burnAddress.burnKey,
319329
burnAddress.revealAmount,
@@ -348,4 +358,11 @@ const generateAndSubmit = async (
348358
}
349359
};
350360

351-
type WormholeState = 'Start' | 'Calculating' | 'Send' | 'Sending' | 'Minting';
361+
/// primary button texts
362+
type WormholeState =
363+
| 'Start'
364+
| 'Calculating'
365+
| 'Send'
366+
| 'Sending to burn address'
367+
| 'Generating proof'
368+
| 'Submitting proof';

src/lib/core/contracts/beth.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Client, hexToBigInt } from 'viem';
1+
import { Client, hexToBigInt, parseEther } from 'viem';
22
import { readContract, waitForTransactionReceipt } from 'viem/actions';
33
import { Config } from 'wagmi';
44
import { WriteContractMutateAsync } from 'wagmi/query';
@@ -29,6 +29,16 @@ export namespace BETHContract {
2929
console.log('got approve receipt');
3030
};
3131

32+
export const approveInfiniteIfAllowanceNotEnough = async (
33+
mutateAsync: WriteContractMutateAsync<Config, unknown>,
34+
client: Client,
35+
address: `0x${string}`,
36+
amount: bigint
37+
) => {
38+
const allowance = await BETHContract.allowance(client, address, WORMcontractAddress);
39+
if (amount > allowance) await approve(mutateAsync, client, parseEther('1000000'));
40+
};
41+
3242
export const mintCoin = async (
3343
mutateAsync: WriteContractMutateAsync<Config, unknown>,
3444
client: Client,
@@ -89,6 +99,15 @@ export namespace BETHContract {
8999
args: [nullifier],
90100
});
91101
};
102+
103+
export const allowance = async (client: Client, account: `0x${string}`, spender: `0x${string}`) => {
104+
return await readContract(client, {
105+
address: BETHContractAddress,
106+
abi: BETHContractABI,
107+
functionName: 'allowance',
108+
args: [account, spender],
109+
});
110+
};
92111
}
93112

94113
export const BETHContractABI = [

src/lib/core/contracts/worm.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { participationLogsRepo } from '@/lib/data/participation-logs-repo';
2-
import { Address, Client, parseEventLogs } from 'viem';
2+
import { Address, Client, parseEther, parseEventLogs } from 'viem';
33
import { getContractEvents, readContract, waitForTransactionReceipt } from 'viem/actions';
44
import { Config } from 'wagmi';
55
import { WriteContractMutateAsync } from 'wagmi/query';
@@ -28,6 +28,16 @@ export namespace WORMContract {
2828
});
2929
};
3030

31+
export const approximate = async (client: Client, amountPerEpoch: bigint, numEpochs: bigint) => {
32+
if (amountPerEpoch === 0n || numEpochs === 0n) return 0n;
33+
return await readContract(client, {
34+
address: WORMcontractAddress,
35+
abi: WORMcontractABI,
36+
functionName: 'approximate',
37+
args: [amountPerEpoch, numEpochs],
38+
});
39+
};
40+
3141
export const discoverRewards = async (
3242
client: Client,
3343
fromEpoch: bigint,
@@ -131,6 +141,25 @@ export namespace WORMContract {
131141
if (r.status == 'reverted') throw 'allowance reverted';
132142
console.log('got approve receipt');
133143
};
144+
145+
export const approveInfiniteIfAllowanceNotEnough = async (
146+
mutateAsync: WriteContractMutateAsync<Config, unknown>,
147+
client: Client,
148+
address: `0x${string}`,
149+
amount: bigint
150+
) => {
151+
const allowance = await WORMContract.allowance(client, address, StakingContractAddress);
152+
if (amount > allowance) await approve(mutateAsync, client, parseEther('1000000'));
153+
};
154+
155+
export const allowance = async (client: Client, account: `0x${string}`, spender: `0x${string}`) => {
156+
return await readContract(client, {
157+
address: WORMcontractAddress,
158+
abi: WORMcontractABI,
159+
functionName: 'allowance',
160+
args: [account, spender],
161+
});
162+
};
134163
}
135164

136165
export const WORMcontractABI = [

src/lib/core/utils/round-ether.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,47 @@
1-
import { formatUnits } from 'ethers';
1+
import { formatEther, parseEther } from 'viem';
22

3-
export const roundEther = (amount: bigint, precision?: number): string => {
4-
const f = parseFloat(formatUnits(amount, 18));
5-
return f < 1 ? f.toPrecision(1) : f.toFixed(precision ?? 4);
3+
export const roundEther = (amount: bigint, maxLen: number = 7): string => {
4+
if (amount === 0n) return '0';
5+
const sn = maxLen - 4 > 0 ? '0.' + '0'.repeat(maxLen - 4) + '1' : '0';
6+
if (amount < parseEther(sn)) return '<' + sn;
7+
8+
let str = formatEther(amount);
9+
if (str.indexOf('.') !== -1) {
10+
str = str.substring(0, maxLen);
11+
while (str.endsWith('0')) str = str.substring(0, str.length - 1);
12+
if (str.endsWith('.')) str = str.substring(0, str.length - 1);
13+
}
14+
return str;
615
};
716

817
export const roundEtherF = (f: number, precision?: number): string => {
918
return f < 1 ? f.toPrecision(1) : f.toFixed(precision ?? 4);
1019
};
20+
21+
export const test_roundEther = () => {
22+
const assert = (a: any, b: any) => (a !== b ? console.log(`assert(${a}, ${b})`) : undefined);
23+
console.log('----- test_roundEther -----');
24+
25+
assert(roundEther(0n), '0');
26+
assert(roundEther(parseEther('1')), '1');
27+
assert(roundEther(parseEther('1'), 10), '1');
28+
29+
assert(roundEther(parseEther('1.000100001'), 5), '1');
30+
assert(roundEther(parseEther('1.000100001'), 6), '1.0001');
31+
assert(roundEther(parseEther('1.000100001'), 7), '1.0001');
32+
assert(roundEther(parseEther('1.000100001'), 10), '1.0001');
33+
assert(roundEther(parseEther('1.000100001'), 11), '1.000100001');
34+
assert(roundEther(parseEther('1.000100001'), 12), '1.000100001');
35+
36+
assert(roundEther(parseEther('10')), '10');
37+
assert(roundEther(parseEther('10'), 1), '10');
38+
39+
assert(roundEther(parseEther('0.000000000001')), '<0.0001');
40+
assert(roundEther(parseEther('0.000000000001'), 9), '<0.000001');
41+
42+
assert(roundEther(parseEther('1.123456789')), '1.12345');
43+
44+
assert(roundEther(1n), '<0.0001');
45+
46+
assert(roundEther(parseEther('12345678'), 1), '12345678');
47+
};

0 commit comments

Comments
 (0)