Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Get everything properly typed
Alexis Sellier committed 5 years ago
commit 8c623ceaed9eef1e787f146f8ce5d0bfec44978a
parent 4ab188b6ed718b7dc1843a90549f410d121bd2fd
12 files changed +153 -35
modified package-lock.json
@@ -8,7 +8,7 @@
        "@snowpack/plugin-typescript": "^1.2.1",
        "ethers": "^5.0.31",
        "svelte": "^3.32.3",
-
        "svelte-routing": "^1.5.0"
+
        "svelte-routing": "^1.6.0"
      },
      "devDependencies": {
        "@snowpack/plugin-svelte": "^3.5.2",
@@ -852,6 +852,11 @@
        "node": ">= 8"
      }
    },
+
    "node_modules/dedent-js": {
+
      "version": "1.0.1",
+
      "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
+
      "integrity": "sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU="
+
    },
    "node_modules/detect-indent": {
      "version": "6.0.0",
      "dev": true,
@@ -1148,6 +1153,14 @@
      "version": "0.5.7",
      "license": "MIT"
    },
+
    "node_modules/lower-case": {
+
      "version": "2.0.2",
+
      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+
      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+
      "dependencies": {
+
        "tslib": "^2.0.3"
+
      }
+
    },
    "node_modules/merge-stream": {
      "version": "2.0.0",
      "license": "MIT"
@@ -1191,6 +1204,15 @@
      "dev": true,
      "license": "MIT"
    },
+
    "node_modules/no-case": {
+
      "version": "3.0.4",
+
      "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+
      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+
      "dependencies": {
+
        "lower-case": "^2.0.2",
+
        "tslib": "^2.0.3"
+
      }
+
    },
    "node_modules/normalize-path": {
      "version": "3.0.0",
      "dev": true,
@@ -1256,6 +1278,15 @@
        "node": ">=6"
      }
    },
+
    "node_modules/pascal-case": {
+
      "version": "3.1.2",
+
      "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+
      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+
      "dependencies": {
+
        "no-case": "^3.0.4",
+
        "tslib": "^2.0.3"
+
      }
+
    },
    "node_modules/path-is-absolute": {
      "version": "1.0.1",
      "dev": true,
@@ -1527,12 +1558,29 @@
      }
    },
    "node_modules/svelte-routing": {
-
      "version": "1.5.0",
-
      "license": "MIT",
+
      "version": "1.6.0",
+
      "resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-1.6.0.tgz",
+
      "integrity": "sha512-+DbrSGttLA6lan7oWFz1MjyGabdn3tPRqn8Osyc471ut2UgCrzM5x1qViNMc2gahOP6fKbKK1aNtZMJEQP2vHQ==",
+
      "dependencies": {
+
        "svelte2tsx": "^0.1.157"
+
      },
      "peerDependencies": {
        "svelte": "^3.20.x"
      }
    },
+
    "node_modules/svelte2tsx": {
+
      "version": "0.1.189",
+
      "resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.1.189.tgz",
+
      "integrity": "sha512-Mo4Sei1tNYthzSZx6biGSK7pI6/vj7nGvvmSevmLIiws/o1hyj1UIHn+AwqogeA9L46fcvy6WU3t7HxDg+LbLg==",
+
      "dependencies": {
+
        "dedent-js": "^1.0.1",
+
        "pascal-case": "^3.1.1"
+
      },
+
      "peerDependencies": {
+
        "svelte": "^3.24",
+
        "typescript": "^4.1.2"
+
      }
+
    },
    "node_modules/to-regex-range": {
      "version": "5.0.1",
      "dev": true,
@@ -1544,6 +1592,11 @@
        "node": ">=8.0"
      }
    },
+
    "node_modules/tslib": {
+
      "version": "2.2.0",
+
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
+
      "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
+
    },
    "node_modules/typescript": {
      "version": "4.2.4",
      "license": "Apache-2.0",
@@ -2033,6 +2086,11 @@
        "which": "^2.0.1"
      }
    },
+
    "dedent-js": {
+
      "version": "1.0.1",
+
      "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
+
      "integrity": "sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU="
+
    },
    "detect-indent": {
      "version": "6.0.0",
      "dev": true
@@ -2222,6 +2280,14 @@
    "js-sha3": {
      "version": "0.5.7"
    },
+
    "lower-case": {
+
      "version": "2.0.2",
+
      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+
      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+
      "requires": {
+
        "tslib": "^2.0.3"
+
      }
+
    },
    "merge-stream": {
      "version": "2.0.0"
    },
@@ -2249,6 +2315,15 @@
      "version": "1.2.5",
      "dev": true
    },
+
    "no-case": {
+
      "version": "3.0.4",
+
      "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+
      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+
      "requires": {
+
        "lower-case": "^2.0.2",
+
        "tslib": "^2.0.3"
+
      }
+
    },
    "normalize-path": {
      "version": "3.0.0",
      "dev": true
@@ -2287,6 +2362,15 @@
        "callsites": "^3.0.0"
      }
    },
+
    "pascal-case": {
+
      "version": "3.1.2",
+
      "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+
      "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+
      "requires": {
+
        "no-case": "^3.0.4",
+
        "tslib": "^2.0.3"
+
      }
+
    },
    "path-is-absolute": {
      "version": "1.0.1",
      "dev": true
@@ -2416,8 +2500,21 @@
      }
    },
    "svelte-routing": {
-
      "version": "1.5.0",
-
      "requires": {}
+
      "version": "1.6.0",
+
      "resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-1.6.0.tgz",
+
      "integrity": "sha512-+DbrSGttLA6lan7oWFz1MjyGabdn3tPRqn8Osyc471ut2UgCrzM5x1qViNMc2gahOP6fKbKK1aNtZMJEQP2vHQ==",
+
      "requires": {
+
        "svelte2tsx": "^0.1.157"
+
      }
+
    },
+
    "svelte2tsx": {
+
      "version": "0.1.189",
+
      "resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.1.189.tgz",
+
      "integrity": "sha512-Mo4Sei1tNYthzSZx6biGSK7pI6/vj7nGvvmSevmLIiws/o1hyj1UIHn+AwqogeA9L46fcvy6WU3t7HxDg+LbLg==",
+
      "requires": {
+
        "dedent-js": "^1.0.1",
+
        "pascal-case": "^3.1.1"
+
      }
    },
    "to-regex-range": {
      "version": "5.0.1",
@@ -2426,6 +2523,11 @@
        "is-number": "^7.0.0"
      }
    },
+
    "tslib": {
+
      "version": "2.2.0",
+
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
+
      "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
+
    },
    "typescript": {
      "version": "4.2.4"
    },
modified package.json
@@ -17,6 +17,6 @@
    "@snowpack/plugin-typescript": "^1.2.1",
    "ethers": "^5.0.31",
    "svelte": "^3.32.3",
-
    "svelte-routing": "^1.5.0"
+
    "svelte-routing": "^1.6.0"
  }
}
modified src/Header.svelte
@@ -12,11 +12,12 @@
  import type { Session } from '@app/session';
  import Logo from './Logo.svelte';
  import Connect from './Connect.svelte';
+
  import type { Config } from '@app/config';

  export let session: Session | null;
-
  export let config;
+
  export let config: Config;

-
  let sessionButton = null;
+
  let sessionButton: HTMLElement | null = null;
  let sessionButtonHover = false;

  $: address = session && session.address;
modified src/Modal.svelte
@@ -2,7 +2,7 @@
  import { createEventDispatcher } from 'svelte';

  export let floating = false;
-
  export let error = null;
+
  export let error: Error | null = null;

  let dispatch = createEventDispatcher();

modified src/base/orgs/CreateOrg.svelte
@@ -15,8 +15,8 @@
  }

  let state = State.Idle;
-
  let error = null;
-
  let org = null;
+
  let error: Error | null = null;
+
  let org: Org | null = null;

  const dispatch = createEventDispatcher();
  const createOrg = async () => {
modified src/base/orgs/Org.ts
@@ -20,12 +20,16 @@ export class Org {
    this.safe = safe;
  }

-
  static fromReceipt(receipt: ContractReceipt): Org {
-
    let event = receipt.events.find(e => e.event === 'OrgCreated');
-
    let address = event.args[0];
-
    let safe = event.args[1];
+
  static fromReceipt(receipt: ContractReceipt): Org | null {
+
    let event = receipt.events?.find(e => e.event === 'OrgCreated');

-
    return new Org(address, safe);
+
    if (event && event.args) {
+
      let address = event.args[0];
+
      let safe = event.args[1];
+

+
      return new Org(address, safe);
+
    }
+
    return null;
  }

  static async create(
modified src/base/orgs/Orgs.svelte
@@ -6,12 +6,13 @@
  import { error } from '@app/error';
  import { session } from '@app/session';
  import CreateOrg from '@app/base/orgs/CreateOrg.svelte';
+
  import type { Config } from '@app/config';

  enum State {
    Idle,
  }

-
  export let config;
+
  export let config: Config;
  export const query = {};

  let modal = null;
modified src/base/orgs/Routes.svelte
@@ -1,8 +1,9 @@
<script lang="typescript">
  import { Route } from "svelte-routing";
  import Orgs from '@app/base/orgs/Orgs.svelte';
+
  import type { Config } from '@app/config';

-
  export let config;
+
  export let config: Config;
  export let query;
</script>

modified src/base/register/registrar.ts
@@ -1,9 +1,11 @@
// TODO: Show "look at your wallet" / "confirm tx" before state change.
// TODO: Two registration actions with same label
-
import { ethers } from "ethers";
+
import { ethers } from 'ethers';
+
import type { BigNumber } from 'ethers';
import { State, state } from './state';
import * as session from '@app/session';
import { Failure } from '@app/error';
+
import type { Config } from '@app/config';

const registrarAbi = [
  {"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"commitment","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"CommitmentMade","type":"event"},
@@ -20,15 +22,15 @@ const registrarAbi = [
  {"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"valid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}
];

-
export function registrar(config) {
+
export function registrar(config: Config) {
  return new ethers.Contract(config.registrar.address, registrarAbi, config.provider);
}

-
export async function registrationFee(config) {
+
export async function registrationFee(config: Config) {
  return await registrar(config).registrationFeeRad();
}

-
export async function registerName(name, owner, config) {
+
export async function registerName(name: string, owner: string, config: Config) {
  if (! name) return;

  let commitmentJson = window.localStorage.getItem('commitment');
@@ -47,14 +49,14 @@ export async function registerName(name, owner, config) {
  }
}

-
async function approveRegistrar(owner, config) {
+
async function approveRegistrar(owner: string, config: Config) {
  state.set(State.Approving);

  const amount = await registrationFee(config);
  await session.approveSpender(config.registrar.address, amount, config);
}

-
async function commitAndRegister(name, owner, config) {
+
async function commitAndRegister(name: string, owner: string, config: Config) {
  let salt = ethers.utils.randomBytes(32);
  let minAge = (await registrar(config).minCommitmentAge()).toNumber();
  let fee = await registrationFee(config);
@@ -71,14 +73,14 @@ async function commitAndRegister(name, owner, config) {
  await register(name, owner, salt, config);
}

-
async function commit(commitment, fee, minAge, config) {
+
async function commit(commitment: string, fee: BigNumber, minAge: number, config: Config) {
  state.set(State.Committing);

  const signer = config.provider.getSigner();
  const tx = await registrar(config)
    .connect(signer)
    .commit(commitment, { gasLimit: 150000 })
-
    .catch(e => console.error(e));
+
    .catch((e: Error) => console.error(e));

  await tx.wait(1);
  session.state.updateBalance(fee.mul(-1));
@@ -88,7 +90,7 @@ async function commit(commitment, fee, minAge, config) {
  await tx.wait(minAge + 1);
}

-
async function register(name, owner, salt, config) {
+
async function register(name: string, owner: string, salt: Uint8Array, config: Config) {
  state.set(State.Registering);

  const signer = config.provider.getSigner();
@@ -102,7 +104,7 @@ async function register(name, owner, salt, config) {
  state.set(State.Registered);
}

-
function makeCommitment(name, owner, salt) {
+
function makeCommitment(name: string, owner: string, salt: Uint8Array): string {
  let bytes = ethers.utils.concat([
    ethers.utils.toUtf8Bytes(name),
    ethers.utils.getAddress(owner),
modified src/base/vesting/vesting.ts
@@ -2,6 +2,7 @@ import { ethers } from "ethers";
import { formatBalance } from "@app/utils";
import * as session from "@app/session";
import { State, state } from "./state";
+
import type { Config } from "@app/config";

const abi = [
  "function token() view returns (address)",
@@ -20,7 +21,7 @@ const tokenAbi = [
  "function symbol() view returns (string)",
];

-
export async function withdrawVested(address, config) {
+
export async function withdrawVested(address: string, config: Config) {
  const contract = new ethers.Contract(address, abi, config.provider);
  const signer = config.provider.getSigner();

@@ -34,7 +35,7 @@ export async function withdrawVested(address, config) {
  state.set(State.Withdrawn);
}

-
export async function getInfo(address, config) {
+
export async function getInfo(address: string, config: Config) {
  const contract = new ethers.Contract(address, abi, config.provider);
  const signer = config.provider.getSigner();

modified src/session.ts
@@ -1,6 +1,7 @@
// TODO: Handle wallet account change.
import { get, writable, derived, Writable } from "svelte/store";
import { ethers } from "ethers";
+
import type { BigNumber } from 'ethers';
import type { TransactionReceipt, TransactionResponse } from '@ethersproject/providers';
import type { Config } from "@app/config";
import { Unreachable, assert, assertEq } from "@app/error";
@@ -62,7 +63,7 @@ export const createState = (initial: State) => {
      }
    },

-
    updateBalance: n => {
+
    updateBalance: (n: BigNumber) => {
      store.update((s) => {
        assert(s.connection === Connection.Connected);
        s.session.tokenBalance = s.session.tokenBalance.add(n);
@@ -70,7 +71,7 @@ export const createState = (initial: State) => {
      });
    },

-
    refreshBalance: async (config) => {
+
    refreshBalance: async (config: Config) => {
      let state = get(store);
      assert(state.connection === Connection.Connected);
      const addr = state.session.address;
@@ -102,6 +103,9 @@ export const createState = (initial: State) => {
      store.update(s => {
        switch (s.connection) {
          case Connection.Connected:
+
            assert(s.session.tx !== null);
+
            assert(s.session.tx.state === 'signing');
+

            s.session.tx = { state: 'pending', hash: tx.hash };
            return s;
          default:
@@ -114,6 +118,7 @@ export const createState = (initial: State) => {
      store.update(s => {
        switch (s.connection) {
          case Connection.Connected:
+
            assert(s.session.tx !== null);
            assert(s.session.tx.state === 'pending');

            if (tx.status === 1) {
@@ -159,7 +164,7 @@ const tokenAbi = [
  "function allowance(address, address) view returns (uint256)",
];

-
export async function approveSpender(spender, amount, config) {
+
export async function approveSpender(spender: string, amount: BigNumber, config: Config) {
  const token = new ethers.Contract(config.radToken.address, tokenAbi, config.provider);
  const signer = config.provider.getSigner();
  const addr = await signer.getAddress();
modified src/utils.ts
@@ -1,10 +1,11 @@
import { ethers } from "ethers";
+
import type { BigNumber } from "ethers";

-
export function formatBalance(n) {
+
export function formatBalance(n: BigNumber) {
  return ethers.utils.commify(parseFloat(ethers.utils.formatUnits(n)).toFixed(2));
}

-
export function formatAddress(addr) {
+
export function formatAddress(addr: string) {
  return addr.substring(0, 6)
    + '...'
    + addr.substring(addr.length - 4, addr.length);