mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-31 01:00:47 +08:00
Move reactive config part into session state
This PR moves the signer "clones" the signer property from the `Config` object into the `session` state, to allow Svelte reactivity, e.g. in the Testnet Faucet, where we rely on `config.signer` but since it only changes when the `Config` object in `App.svelte` changes we can't react on it. For now I only cloned the signer property, if we see in the future that there are other properties that need to change with session we can add them. Signed-off-by: Sebastian Martinez <sebastinez@me.com> Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
+1
-2
@@ -17,7 +17,6 @@
|
||||
export let session: Session | null;
|
||||
export let config: Config;
|
||||
|
||||
let sessionButton: HTMLElement | null = null;
|
||||
let sessionButtonHover = false;
|
||||
let mobileNavbarDisplayed = false;
|
||||
|
||||
@@ -182,7 +181,7 @@
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<button class="address outline small" bind:this={sessionButton}
|
||||
<button class="address outline small"
|
||||
on:click={() => disconnectWallet(config)}
|
||||
on:mouseover={() => sessionButtonHover = true}
|
||||
on:focus={() => sessionButtonHover = true}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { Config } from "@app/config";
|
||||
import type { BigNumber } from "@ethersproject/bignumber";
|
||||
import { session } from "@app/session";
|
||||
import { toWei } from "@app/utils";
|
||||
import { formatEther } from "@ethersproject/units";
|
||||
import { onMount } from "svelte";
|
||||
import { navigate } from "svelte-routing";
|
||||
import { getMaxWithdrawAmount, lastWithdrawalByUser, calculateTimeLock } from "./lib";
|
||||
|
||||
@@ -21,22 +21,30 @@
|
||||
}
|
||||
|
||||
async function isAbleToWithdraw(amount: string): Promise<[boolean, string?]> {
|
||||
if (!amount || amount === "0") { return [false, "Not able to withdraw zero tokens"]; }
|
||||
if (toWei(amount).gt(maxWithdrawAmount)) return [false, `Reduce amount, max withdrawal is ${formatEther(maxWithdrawAmount)}`];
|
||||
let currentTime = new Date().getTime();
|
||||
let timelock = await calculateTimeLock(amount, config);
|
||||
// Converting a 10 digit to 13 digit timestamp by multiplying by 1000
|
||||
// since JS doesn't display a correct Date string when passing a 10 digit timestamp.
|
||||
let nextAvailableWithdraw = lastWithdrawal.add(timelock).mul(1000);
|
||||
if (nextAvailableWithdraw.gt(currentTime)) return [false, `Not ready to withdraw, return after ${new Date(nextAvailableWithdraw.toNumber()).toLocaleString('en-GB')}`];
|
||||
try {
|
||||
if (!$session) { return [false]; }
|
||||
if (!amount || amount === "0") { return [false, "Not able to withdraw zero tokens"]; }
|
||||
if (toWei(amount).gt(maxWithdrawAmount)) return [false, `Reduce amount, max withdrawal is ${formatEther(maxWithdrawAmount)}`];
|
||||
let currentTime = new Date().getTime();
|
||||
let timelock = await calculateTimeLock(amount, $session.config.signer, config);
|
||||
// Converting a 10 digit to 13 digit timestamp by multiplying by 1000
|
||||
// since JS doesn't display a correct Date string when passing a 10 digit timestamp.
|
||||
let nextAvailableWithdraw = lastWithdrawal.add(timelock).mul(1000);
|
||||
if (nextAvailableWithdraw.gt(currentTime)) return [false, `Not ready to withdraw, return after ${new Date(nextAvailableWithdraw.toNumber()).toLocaleString('en-GB')}`];
|
||||
|
||||
return [true];
|
||||
return [true];
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
error = e.message;
|
||||
|
||||
return [false];
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
maxWithdrawAmount = await getMaxWithdrawAmount(config);
|
||||
lastWithdrawal = await lastWithdrawalByUser(config);
|
||||
});
|
||||
$: if ($session) {
|
||||
getMaxWithdrawAmount($session.config.signer, config).then(x => maxWithdrawAmount = x);
|
||||
lastWithdrawalByUser($session.config.signer, config).then(x => lastWithdrawal = x);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -89,7 +97,7 @@
|
||||
To get RAD tokens on <strong>{config.network.name}</strong>, please
|
||||
check the known exchanges.
|
||||
</div>
|
||||
{:else if !config.signer}
|
||||
{:else if !$session}
|
||||
<div class="input-caption">
|
||||
To get RAD tokens on <strong>{config.network.name}</strong>, please
|
||||
connect your wallet.
|
||||
|
||||
@@ -23,11 +23,15 @@
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
state.status = Status.Signing;
|
||||
const tx = await withdraw(amount, config);
|
||||
state.status = Status.Pending;
|
||||
await tx.wait();
|
||||
state.status = Status.Success;
|
||||
if ($session) {
|
||||
state.status = Status.Signing;
|
||||
const tx = await withdraw(amount, $session.config.signer, config);
|
||||
state.status = Status.Pending;
|
||||
await tx.wait();
|
||||
state.status = Status.Success;
|
||||
} else {
|
||||
back();
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
error = e;
|
||||
|
||||
+17
-13
@@ -4,52 +4,56 @@ import type { Config } from '@app/config';
|
||||
import { assert } from '@app/error';
|
||||
import type { TransactionResponse } from '@ethersproject/providers';
|
||||
import { toWei } from '@app/utils';
|
||||
import type { WalletConnectSigner } from "@app/WalletConnectSigner";
|
||||
import type { TypedDataSigner } from '@ethersproject/abstract-signer';
|
||||
|
||||
export async function withdraw(amount: string, config: Config): Promise<TransactionResponse> {
|
||||
assert(config.signer);
|
||||
type Signer = ethers.Signer & TypedDataSigner | WalletConnectSigner | null;
|
||||
|
||||
export async function withdraw(amount: string, signer: Signer, config: Config): Promise<TransactionResponse> {
|
||||
assert(signer);
|
||||
|
||||
const faucet = new ethers.Contract(
|
||||
config.radToken.faucet,
|
||||
config.abi.faucet,
|
||||
config.signer
|
||||
signer
|
||||
);
|
||||
|
||||
return faucet.withdraw(config.radToken.address, toWei(amount));
|
||||
}
|
||||
|
||||
export async function getMaxWithdrawAmount(config: Config): Promise<ethers.BigNumber> {
|
||||
assert(config.signer);
|
||||
export async function getMaxWithdrawAmount(signer: Signer, config: Config): Promise<ethers.BigNumber> {
|
||||
assert(signer);
|
||||
|
||||
const faucet = new ethers.Contract(
|
||||
config.radToken.faucet,
|
||||
config.abi.faucet,
|
||||
config.signer
|
||||
signer
|
||||
);
|
||||
|
||||
return faucet.maxWithdrawAmount();
|
||||
}
|
||||
|
||||
export async function calculateTimeLock(amount: string, config: Config): Promise<ethers.BigNumber> {
|
||||
assert(config.signer);
|
||||
export async function calculateTimeLock(amount: string, signer: Signer, config: Config): Promise<ethers.BigNumber> {
|
||||
assert(signer);
|
||||
|
||||
const faucet = new ethers.Contract(
|
||||
config.radToken.faucet,
|
||||
config.abi.faucet,
|
||||
config.signer
|
||||
signer
|
||||
);
|
||||
|
||||
return faucet.calculateTimeLock(toWei(amount));
|
||||
}
|
||||
|
||||
export async function lastWithdrawalByUser(config: Config): Promise<ethers.BigNumber> {
|
||||
assert(config.signer);
|
||||
export async function lastWithdrawalByUser(signer: Signer, config: Config): Promise<ethers.BigNumber> {
|
||||
assert(signer);
|
||||
|
||||
const address = config.signer.getAddress();
|
||||
const address = signer.getAddress();
|
||||
|
||||
const faucet = new ethers.Contract(
|
||||
config.radToken.faucet,
|
||||
config.abi.faucet,
|
||||
config.signer
|
||||
signer
|
||||
);
|
||||
|
||||
return faucet.lastWithdrawalByUser(address);
|
||||
|
||||
+21
-7
@@ -4,6 +4,8 @@ import type { BigNumber } from 'ethers';
|
||||
import type { TransactionReceipt, TransactionResponse } from '@ethersproject/providers';
|
||||
import { Config, getConfig, isMetamaskInstalled } from "@app/config";
|
||||
import { Unreachable, assert, assertEq } from "@app/error";
|
||||
import type { TypedDataSigner } from '@ethersproject/abstract-signer';
|
||||
import type { WalletConnectSigner } from "./WalletConnectSigner";
|
||||
import * as ethers from "ethers";
|
||||
|
||||
export enum Connection {
|
||||
@@ -19,6 +21,13 @@ export type TxState =
|
||||
| { state: 'fail'; hash: string; blockHash: string; blockNumber: number; error: string }
|
||||
| null;
|
||||
|
||||
// Definitions made in `Config` that need to be part of the session,
|
||||
// to provide reactivity based on events i.e. accountsChanged
|
||||
// TODO: Eventually there will be more reactive config needs, e.g. seeds
|
||||
export interface SessionConfig {
|
||||
signer: ethers.Signer & TypedDataSigner | WalletConnectSigner | null;
|
||||
}
|
||||
|
||||
export type State =
|
||||
{ connection: Connection.Disconnected }
|
||||
| { connection: Connection.Connecting }
|
||||
@@ -26,6 +35,7 @@ export type State =
|
||||
|
||||
export interface Session {
|
||||
address: string;
|
||||
config: SessionConfig;
|
||||
tokenBalance: BigNumber | null; // `null` means it isn't loaded yet.
|
||||
tx: TxState;
|
||||
}
|
||||
@@ -38,7 +48,7 @@ export interface Store extends Readable<State> {
|
||||
setTxSigning(): void;
|
||||
setTxPending(tx: TransactionResponse): void;
|
||||
setTxConfirmed(tx: TransactionReceipt): void;
|
||||
setChangedAccount(address: string): void;
|
||||
setChangedAccount(address: string, config: Config): void;
|
||||
}
|
||||
|
||||
export const loadState = (initial: State): Store => {
|
||||
@@ -52,8 +62,9 @@ export const loadState = (initial: State): Store => {
|
||||
// Re-connect using previous session.
|
||||
if (config.metamask.connected) {
|
||||
const metamask = config.metamask.session;
|
||||
const sessionConfig = { signer: config.signer };
|
||||
const tokenBalance: BigNumber = await config.token.balanceOf(metamask.address);
|
||||
const session = { tokenBalance, tx: null, address: metamask.address };
|
||||
const session = { tokenBalance, tx: null, config: sessionConfig, address: metamask.address };
|
||||
|
||||
store.set({ connection: Connection.Connected, session });
|
||||
config.setSigner(config.metamask.signer);
|
||||
@@ -77,7 +88,8 @@ export const loadState = (initial: State): Store => {
|
||||
config.walletConnect.state.set({ state: "close" });
|
||||
|
||||
const tokenBalance: BigNumber = await config.token.balanceOf(address);
|
||||
const session = { address, tokenBalance, tx: null };
|
||||
const sessionConfig = { signer: config.signer };
|
||||
const session = { config: sessionConfig, address, tokenBalance, tx: null };
|
||||
|
||||
store.set({
|
||||
connection: Connection.Connected,
|
||||
@@ -99,7 +111,8 @@ export const loadState = (initial: State): Store => {
|
||||
|
||||
const address = await signer.getAddress();
|
||||
const tokenBalance: BigNumber = await config.token.balanceOf(address);
|
||||
const session = { address, tokenBalance, tx: null };
|
||||
const sessionConfig = { signer: config.signer };
|
||||
const session = { config: sessionConfig, address, tokenBalance, tx: null };
|
||||
const network = await ethers.providers.getNetwork(
|
||||
signer.walletConnect.chainId
|
||||
);
|
||||
@@ -232,7 +245,7 @@ export const loadState = (initial: State): Store => {
|
||||
});
|
||||
},
|
||||
|
||||
setChangedAccount: (address: string) => {
|
||||
setChangedAccount: (address: string, config: Config) => {
|
||||
store.update(s => {
|
||||
switch (s.connection) {
|
||||
case Connection.Connected:
|
||||
@@ -242,6 +255,7 @@ export const loadState = (initial: State): Store => {
|
||||
disconnectMetamask();
|
||||
} else {
|
||||
s.session.address = address;
|
||||
s.session.config = { signer: config.signer };
|
||||
saveSession(s.session);
|
||||
}
|
||||
return s;
|
||||
@@ -275,7 +289,7 @@ window.ethereum?.on("accountsChanged", async ([address]: string) => {
|
||||
|
||||
export async function changeAccounts(address: string): Promise<void> {
|
||||
const config = await getConfig();
|
||||
state.setChangedAccount(address);
|
||||
state.setChangedAccount(address, config);
|
||||
state.refreshBalance(config);
|
||||
}
|
||||
|
||||
@@ -311,6 +325,6 @@ export function disconnectWallet(config: Config): void {
|
||||
|
||||
function saveSession(session: Session): void {
|
||||
window.localStorage.setItem("metamask", JSON.stringify({
|
||||
...session, tokenBalance: null
|
||||
...session, tokenBalance: null, config: null
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user