{contractAddress}
{#if $state === State.Withdrawn}
- Tokens successfully withdrawn to {formatAddress($info.beneficiary)}.
+ Tokens successfully withdrawn to {formatAddress(info.beneficiary)}.
{:else}
-
{/if}
| Beneficiary | {$info.beneficiary} |
| Allocation | {$info.totalVesting} {$info.symbol} |
| Withdrawn | {$info.withdrawn} {$info.symbol} |
| Withdrawable | {$info.withdrawableBalance} {$info.symbol} |
| Beneficiary | {info.beneficiary} |
| Allocation | {info.totalVesting} {info.symbol} |
| Withdrawn | {info.withdrawn} {info.symbol} |
| Withdrawable | {info.withdrawableBalance} {info.symbol} |
- {#if $isBeneficiary}
+ {#if isBeneficiary}
{#if $state === State.WithdrawingSign}
Waiting for signature...
diff --git a/src/base/vesting/vesting.ts b/src/base/vesting/vesting.ts
index 0fd85e8ef..33ecd53ac 100644
--- a/src/base/vesting/vesting.ts
+++ b/src/base/vesting/vesting.ts
@@ -1,6 +1,6 @@
import { ethers } from "ethers";
import { formatBalance } from "@app/utils";
-import { refreshBalance } from "@app/session";
+import * as session from "@app/session";
import { State, state } from "./state";
const abi = [
@@ -30,7 +30,7 @@ export async function withdrawVested(address, config) {
state.set(State.Withdrawing);
await tx.wait();
- refreshBalance(config);
+ session.state.refreshBalance(config);
state.set(State.Withdrawn);
}
diff --git a/src/config.ts b/src/config.ts
index e9ceec5cc..16ff0fb41 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -10,10 +10,12 @@ export type Config = {
registrar: { address: string },
radToken: { address: string },
orgFactory: { address: string },
+ gasLimits: { createOrg: number },
provider: ethers.providers.JsonRpcProvider,
signer: ethers.Signer,
};
+// TODO: Move to JSON.
const addresses = {
homestead: {
registrar: {
@@ -34,11 +36,16 @@ const addresses = {
address: "0x59b5eee36f5fa52400A136Fd4630Ee2bF126a4C0",
},
orgFactory: {
- address: "0xe30aA5594FFB52B6bF5bbB21eB7e71Ac525bB028",
+ address: "0xF3D04e874D07d680e8b26332eEae5b9B1c263121",
},
}
};
+/// Gas limits for various transactions.
+const gasLimits = {
+ createOrg: 1_000_000,
+};
+
function isMetamaskInstalled(): boolean {
const { ethereum } = window;
return Boolean(ethereum && ethereum.isMetaMask);
@@ -58,6 +65,7 @@ export async function getConfig(): Promise {
orgFactory: cfg.orgFactory,
provider: provider,
signer: provider.getSigner(),
+ gasLimits: gasLimits,
};
} else {
throw `Wrong network: ${network.name}`;
diff --git a/src/error.ts b/src/error.ts
index 058da42b9..fd6a73dcf 100644
--- a/src/error.ts
+++ b/src/error.ts
@@ -5,3 +5,35 @@ export enum Failure {
}
export const error = writable(null);
+
+export class Unreachable extends Error {
+ constructor(value?: never) {
+ if (value) {
+ super('unreachable value reached: ' + value);
+ } else {
+ super('unreachable code reached');
+ }
+ }
+}
+
+class AssertionError extends Error {
+ constructor(message?: string) {
+ if (message) {
+ super(`assertion failed: ${message}`);
+ } else {
+ super(`assertion failed`);
+ }
+ }
+}
+
+export function assert(value: any, message?: string): asserts value {
+ if (! value) {
+ throw new AssertionError(message);
+ }
+}
+
+export function assertEq(actual: any, expected: any, message?: string) {
+ if (actual !== expected) {
+ throw new AssertionError(`assertion failed: expected '${expected}', got '${actual}'`);
+ }
+}
diff --git a/src/session.ts b/src/session.ts
index 19fbb1d46..f2849ebc0 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -1,6 +1,8 @@
-import { get, writable, Writable } from "svelte/store";
+import { get, writable, derived, Writable } from "svelte/store";
import { ethers } from "ethers";
-import { getConfig } from "./config";
+import type { TransactionReceipt, TransactionResponse } from '@ethersproject/providers';
+import type { Config } from "@app/config";
+import { Unreachable, assert, assertEq } from "@app/error";
export enum Connection {
Disconnected,
@@ -8,18 +10,146 @@ export enum Connection {
Connected
}
-export type Session = {
- connection: Connection
- address?: string
- tokenBalance?: any
+export type TxState =
+ { state: 'signing' }
+ | { state: 'pending', hash: string }
+ | { state: 'success', hash: string, blockHash: string, blockNumber: number }
+ | { state: 'fail', hash: string, blockHash: string, blockNumber: number, error: string }
+ | null;
+
+export type State =
+ { connection: Connection.Disconnected }
+ | { connection: Connection.Connecting }
+ | { connection: Connection.Connected, session: Session };
+
+export interface Session {
+ address: string
+ tokenBalance: any
+ tx: TxState
+}
+
+export const createState = (initial: State) => {
+ const store = writable(initial)
+
+ return {
+ subscribe: store.subscribe,
+ connect: async (config: Config) => {
+ let state = get(store);
+
+ assertEq(state.connection, Connection.Disconnected);
+ store.set({ connection: Connection.Connecting });
+
+ // TODO: This hangs on Brave, if you have to unlock your wallet..
+ try {
+ let accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
+ } catch (e) {
+ console.error(e);
+ }
+
+ const token = new ethers.Contract(config.radToken.address, tokenAbi, config.provider);
+ const signer = config.provider.getSigner();
+ const address = await signer.getAddress();
+
+ try {
+ const tokenBalance = await token.balanceOf(address);
+ store.set({
+ connection: Connection.Connected,
+ session: { address, tokenBalance, tx: null }
+ });
+ } catch (e) {
+ console.error(e);
+ }
+ },
+
+ updateBalance: n => {
+ store.update((s) => {
+ assert(s.connection === Connection.Connected);
+ s.session.tokenBalance = s.session.tokenBalance.add(n);
+ return s;
+ });
+ },
+
+ refreshBalance: async (config) => {
+ let state = get(store);
+ assert(state.connection === Connection.Connected);
+ const addr = state.session.address;
+
+ try {
+ const token = new ethers.Contract(config.radToken.address, tokenAbi, config.provider);
+ const tokenBalance = await token.balanceOf(addr);
+
+ state.session.tokenBalance = tokenBalance;
+ store.set(state);
+ } catch (e) {
+ console.error(e);
+ }
+ },
+
+ setTxSigning: () => {
+ store.update(s => {
+ switch (s.connection) {
+ case Connection.Connected:
+ s.session.tx = { state: 'signing' };
+ return s;
+ default:
+ throw new Unreachable();
+ }
+ });
+ },
+
+ setTxPending: (tx: TransactionResponse) => {
+ store.update(s => {
+ switch (s.connection) {
+ case Connection.Connected:
+ s.session.tx = { state: 'pending', hash: tx.hash };
+ return s;
+ default:
+ throw new Unreachable();
+ }
+ });
+ },
+
+ setTxConfirmed: (tx: TransactionReceipt) => {
+ store.update(s => {
+ switch (s.connection) {
+ case Connection.Connected:
+ assert(s.session.tx.state === 'pending');
+
+ if (tx.status === 1) {
+ s.session.tx = {
+ state: 'success',
+ hash: s.session.tx.hash,
+ blockHash: tx.blockHash,
+ blockNumber: tx.blockNumber
+ };
+ } else {
+ s.session.tx = {
+ state: 'fail',
+ hash: s.session.tx.hash,
+ blockHash: tx.blockHash,
+ blockNumber: tx.blockNumber,
+ error: "Failed"
+ };
+ }
+ return s;
+ default:
+ throw new Unreachable();
+ }
+ });
+ },
+ };
};
-export const session: Writable = writable({
- connection: Connection.Disconnected,
+export const state = createState({ connection: Connection.Disconnected });
+export const session = derived(state, s => {
+ if (s.connection === Connection.Connected) {
+ return s.session;
+ }
+ return null;
});
-session.subscribe(s => {
- console.log("Session", s);
+state.subscribe(s => {
+ console.log("session.state", s);
});
const tokenAbi = [
@@ -28,35 +158,6 @@ const tokenAbi = [
"function allowance(address, address) view returns (uint256)",
];
-export async function connectWallet() {
- session.set({ connection: Connection.Connecting });
-
- // TODO: This hangs on Brave, if you have to unlock your wallet..
- try {
- let accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
- } catch (e) {
- console.error(e);
- }
-
- const config = await getConfig();
-
- const token = new ethers.Contract(config.radToken.address, tokenAbi, config.provider);
- const signer = config.provider.getSigner();
-
- let addr = await signer.getAddress();
-
- try {
- let tokenBalance = await token.balanceOf(addr);
- session.set({
- address: addr,
- tokenBalance: tokenBalance,
- connection: Connection.Connected
- });
- } catch (e) {
- console.error(e);
- }
-}
-
export async function approveSpender(spender, amount, config) {
const token = new ethers.Contract(config.radToken.address, tokenAbi, config.provider);
const signer = config.provider.getSigner();
@@ -70,32 +171,6 @@ export async function approveSpender(spender, amount, config) {
}
}
-export async function updateBalance(n) {
- session.update((s) => {
- s.tokenBalance = s.tokenBalance.add(n);
- return s;
- });
-}
-
-export async function refreshBalance(config) {
- const addr = get(session).address;
-
- if (addr) {
- try {
- const token = new ethers.Contract(config.radToken.address, tokenAbi, config.provider);
- const tokenBalance = await token.balanceOf(addr);
- console.log("new balance", tokenBalance);
-
- session.update((s) => {
- s.tokenBalance = tokenBalance;
- return s;
- });
- } catch (e) {
- console.error(e);
- }
- }
-}
-
export function disconnectWallet() {
location.reload();
}