Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Move reactive config part into session state
Sebastian Martinez committed 4 years ago
commit e3de337769e7d5a535b4a03f0fb725bc84188ce7
parent eb3b683935cac6c83837a7005f2890d037c9474b
5 files changed +71 -42
modified src/Header.svelte
@@ -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}
modified src/base/faucet/Index.svelte
@@ -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.
modified src/base/faucet/Withdraw.svelte
@@ -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;
modified src/base/faucet/lib.ts
@@ -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);
modified src/session.ts
@@ -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
  }));
}