mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-15 11:20:36 +08:00
Improve session code
This commit is contained in:
+3
-3
@@ -38,13 +38,13 @@
|
||||
<!-- Loading wallet -->
|
||||
{:then config}
|
||||
<div class="app">
|
||||
<Header/>
|
||||
<Header session={$session} {config} />
|
||||
<div class="wrapper">
|
||||
<Router url="{url}">
|
||||
<Route path="vesting">
|
||||
<Vesting {config} />
|
||||
<Vesting {config} session={$session} />
|
||||
</Route>
|
||||
<Register {config} {query} />
|
||||
<Register {config} {query} session={$session} />
|
||||
<Orgs {config} {query} />
|
||||
</Router>
|
||||
</div>
|
||||
|
||||
+5
-3
@@ -1,21 +1,23 @@
|
||||
<script lang="typescript">
|
||||
import { derived } from "svelte/store";
|
||||
import { Connection, session, connectWallet } from "./session";
|
||||
import { Connection } from "@app/session";
|
||||
import { state } from '@app/session';
|
||||
|
||||
export let config;
|
||||
export let caption = "Connect";
|
||||
export let className = "";
|
||||
export let style = "";
|
||||
|
||||
let walletUnavailable = !window.ethereum;
|
||||
|
||||
$: connecting = $session.connection === Connection.Connecting;
|
||||
$: connecting = $state.connection === Connection.Connecting;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<button
|
||||
on:click={connectWallet}
|
||||
on:click={() => state.connect(config)}
|
||||
{style}
|
||||
class="connect {className}"
|
||||
disabled={connecting || walletUnavailable}
|
||||
|
||||
+10
-4
@@ -1,20 +1,26 @@
|
||||
<script lang="typescript">
|
||||
// TODO: Shorten tx hash
|
||||
// TODO: Link to correct network on etherscan
|
||||
// TODO: There's a bug where sometimes on first load, the 'Connect' button
|
||||
// won't display the address even though we're connected.
|
||||
import { derived } from "svelte/store";
|
||||
import { ethers } from "ethers";
|
||||
import { link } from "svelte-routing";
|
||||
import { formatBalance, formatAddress } from "@app/utils";
|
||||
import { error, Failure } from '@app/error';
|
||||
import { session, disconnectWallet } from "@app/session";
|
||||
import { disconnectWallet } from "@app/session";
|
||||
import type { Session } from '@app/session';
|
||||
import Logo from './Logo.svelte';
|
||||
import Connect from './Connect.svelte';
|
||||
|
||||
export let session: Session | null;
|
||||
export let config;
|
||||
|
||||
let sessionButton = null;
|
||||
let sessionButtonHover = false;
|
||||
|
||||
$: address = $session.address;
|
||||
$: tokenBalance = $session.tokenBalance;
|
||||
$: address = session && session.address;
|
||||
$: tokenBalance = session && session.tokenBalance;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -99,7 +105,7 @@
|
||||
{/if}
|
||||
</button>
|
||||
{:else}
|
||||
<Connect className="small" />
|
||||
<Connect className="small" {config} />
|
||||
{/if}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@
|
||||
{/if}
|
||||
|
||||
<div class:modal-floating={floating}>
|
||||
<div class="modal" class:error={error}>
|
||||
<div class="modal" class:error>
|
||||
<div class="modal-title">
|
||||
{#if error}
|
||||
Error
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
<script lang="typescript">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { session } from '@app/session';
|
||||
import Modal from '@app/Modal.svelte';
|
||||
import { Org } from '@app/base/orgs/Org';
|
||||
import type { Config } from '@app/config';
|
||||
|
||||
export let config;
|
||||
export let owner;
|
||||
export let config: Config;
|
||||
export let owner: string;
|
||||
|
||||
enum State {
|
||||
Idle,
|
||||
Waiting,
|
||||
Signing,
|
||||
Pending,
|
||||
Success,
|
||||
}
|
||||
|
||||
let state = State.Idle;
|
||||
let error = null;
|
||||
let org = null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const createOrg = async () => {
|
||||
state = State.Waiting;
|
||||
state = State.Signing;
|
||||
|
||||
console.log("creating org");
|
||||
try {
|
||||
let tx = await Org.create(owner, config);
|
||||
state = State.Pending;
|
||||
|
||||
let receipt = await tx.wait();
|
||||
console.log(receipt);
|
||||
let org = Org.fromReceipt(receipt);
|
||||
console.log(org);
|
||||
org = Org.fromReceipt(receipt);
|
||||
state = State.Success;
|
||||
} catch (e) {
|
||||
state = State.Idle;
|
||||
console.error(e);
|
||||
|
||||
state = State.Idle;
|
||||
error = e;
|
||||
}
|
||||
};
|
||||
@@ -38,23 +40,45 @@
|
||||
|
||||
<Modal floating {error} on:close>
|
||||
<span slot="title">
|
||||
Create an Org
|
||||
{#if !org}
|
||||
Create an Org
|
||||
{:else}
|
||||
Success
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<span slot="body">
|
||||
<table>
|
||||
<tr><td class="label">Owner</td><td>{owner}</td></tr>
|
||||
<tr><td class="label">Member</td><td>{owner}</td></tr>
|
||||
{#if org}
|
||||
<tr><td class="label">Address</td><td>{org.address}</td></tr>
|
||||
<tr><td class="label">Safe</td><td>{org.safe}</td></tr>
|
||||
{/if}
|
||||
</table>
|
||||
</span>
|
||||
<span slot="actions">
|
||||
<button
|
||||
on:click={createOrg}
|
||||
class="primary"
|
||||
data-waiting={state === State.Waiting || null}
|
||||
disabled={state !== State.Idle}
|
||||
>Create</button>
|
||||
|
||||
<button on:click={() => dispatch('close')} class="text">
|
||||
Cancel
|
||||
</button>
|
||||
<span slot="actions">
|
||||
{#if !org}
|
||||
<button
|
||||
on:click={createOrg}
|
||||
class="primary"
|
||||
data-waiting={[State.Signing, State.Pending].includes(state) || null}
|
||||
disabled={state !== State.Idle}
|
||||
>
|
||||
{#if state === State.Pending}
|
||||
Creating...
|
||||
{:else}
|
||||
Create
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<button on:click={() => dispatch('close')} class="text">
|
||||
Cancel
|
||||
</button>
|
||||
{:else}
|
||||
<button on:click={() => dispatch('close')}>
|
||||
Done
|
||||
</button>
|
||||
{/if}
|
||||
</span>
|
||||
</Modal>
|
||||
|
||||
@@ -37,6 +37,9 @@ export class Org {
|
||||
orgFactoryAbi,
|
||||
config.signer
|
||||
);
|
||||
return await orgFactory.createOrg([owner], 1);
|
||||
|
||||
return orgFactory.createOrg([owner], 1, {
|
||||
gasLimit: config.gasLimits.createOrg
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
}
|
||||
|
||||
export let config;
|
||||
export let query;
|
||||
export const query = {};
|
||||
|
||||
let modal = null;
|
||||
let state = State.Idle;
|
||||
|
||||
$: owner = $session.address;
|
||||
$: owner = $session && $session.address;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<main>
|
||||
<button on:click={() => modal = CreateOrg} disabled={!owner} data-disabled-tooltip="No!" class="secondary">
|
||||
<button on:click={() => modal = CreateOrg} disabled={!owner} class="secondary">
|
||||
Create an Org
|
||||
</button>
|
||||
</main>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script lang="typescript">
|
||||
import { onMount } from 'svelte';
|
||||
import { get } from 'svelte/store';
|
||||
import { Router, Link, Route, navigate } from "svelte-routing";
|
||||
import { navigate } from "svelte-routing";
|
||||
import { ethers } from 'ethers';
|
||||
import { error } from '@app/error';
|
||||
import { session } from '@app/session';
|
||||
import { registrar } from './registrar';
|
||||
|
||||
import Modal from '@app/Modal.svelte';
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import Begin from '@app/base/register/steps/Begin.svelte';
|
||||
import Submit from '@app/base/register/steps/Submit.svelte';
|
||||
|
||||
export let session;
|
||||
export let config;
|
||||
export let query;
|
||||
</script>
|
||||
@@ -17,5 +18,5 @@
|
||||
</Route>
|
||||
|
||||
<Route path="register/:name/submit" let:params>
|
||||
<Submit {config} subdomain={params.name} {query} />
|
||||
<Submit {config} subdomain={params.name} {query} {session} />
|
||||
</Route>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// TODO: Two registration actions with same label
|
||||
import { ethers } from "ethers";
|
||||
import { State, state } from './state';
|
||||
import { approveSpender, updateBalance } from '@app/session';
|
||||
import * as session from '@app/session';
|
||||
import { Failure } from '@app/error';
|
||||
|
||||
const registrarAbi = [
|
||||
@@ -51,7 +51,7 @@ async function approveRegistrar(owner, config) {
|
||||
state.set(State.Approving);
|
||||
|
||||
const amount = await registrationFee(config);
|
||||
await approveSpender(config.registrar.address, amount, config);
|
||||
await session.approveSpender(config.registrar.address, amount, config);
|
||||
}
|
||||
|
||||
async function commitAndRegister(name, owner, config) {
|
||||
@@ -81,7 +81,7 @@ async function commit(commitment, fee, minAge, config) {
|
||||
.catch(e => console.error(e));
|
||||
|
||||
await tx.wait(1);
|
||||
updateBalance(fee.mul(-1));
|
||||
session.state.updateBalance(fee.mul(-1));
|
||||
|
||||
// TODO: Getting "commitment too new"
|
||||
state.set(State.WaitingToRegister);
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import { navigate } from 'svelte-routing';
|
||||
import { error } from '@app/error';
|
||||
import { formatAddress } from '@app/utils';
|
||||
import { session } from '@app/session';
|
||||
import { registrar } from '../registrar';
|
||||
import { session } from '@app/session';
|
||||
|
||||
import Connect from '@app/Connect.svelte';
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
export let query;
|
||||
|
||||
let state = State.Initial;
|
||||
$: registrationOwner = query.get("owner") || $session.address;
|
||||
$: registrationOwner = query.get("owner") || ($session && $session.address);
|
||||
|
||||
async function begin() {
|
||||
state = State.CheckingAvailability;
|
||||
@@ -64,12 +64,12 @@
|
||||
Back
|
||||
</button>
|
||||
{:else}
|
||||
{#if $session.address}
|
||||
{#if $session}
|
||||
<button on:click={begin} class="primary register">
|
||||
Begin registration →
|
||||
</button>
|
||||
{:else}
|
||||
<Connect caption="Connect to register" className="primary" />
|
||||
<Connect caption="Connect to register" className="primary" {config} />
|
||||
{/if}
|
||||
|
||||
<button on:click={() => navigate("/register")} class="text">
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
import { get } from 'svelte/store';
|
||||
import { navigate } from 'svelte-routing';
|
||||
import { ethers } from 'ethers';
|
||||
import { session } from '@app/session';
|
||||
import { registrar, registerName, registrationFee } from '../registrar';
|
||||
import { State, state } from '../state';
|
||||
import type { Session } from '@app/session';
|
||||
|
||||
export let config;
|
||||
export let subdomain;
|
||||
export let query;
|
||||
export let session: Session;
|
||||
|
||||
let error = null;
|
||||
let s = get(session);
|
||||
let registrationOwner = query.get("owner") || s.address;
|
||||
let registrationOwner = query.get("owner") || session.address;
|
||||
|
||||
async function getFee(cfg) {
|
||||
let fee = await registrationFee(cfg);
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
{:else if $state === State.Registered}
|
||||
<div class="modal-body">
|
||||
The name <span class="domain">{subdomain}</span> has been successfully registered to {$session.address}.
|
||||
The name <span class="domain">{subdomain}</span> has been successfully registered to {session.address}.
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button on:click={() => state.set(State.Idle)} class="primary register">
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="typescript">
|
||||
import { onMount } from 'svelte';
|
||||
import { get, derived, writable } from 'svelte/store';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import { ethers } from 'ethers';
|
||||
import { session } from '@app/session';
|
||||
import { formatAddress } from '@app/utils';
|
||||
import { State, state } from './state';
|
||||
import { getInfo, withdrawVested } from './vesting';
|
||||
import type { Session } from '@app/session';
|
||||
|
||||
let input;
|
||||
|
||||
@@ -14,24 +15,23 @@
|
||||
});
|
||||
|
||||
export let config;
|
||||
export let session: Session;
|
||||
|
||||
let contractAddress = "";
|
||||
const info = writable(null);
|
||||
let info = null;
|
||||
|
||||
async function loadContract(config) {
|
||||
state.set(State.Loading);
|
||||
info.set(await getInfo(contractAddress, config));
|
||||
info = await getInfo(contractAddress, config);
|
||||
state.set(State.Idle);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
$info = null;
|
||||
info = null;
|
||||
state.set(State.Idle);
|
||||
}
|
||||
|
||||
let isBeneficiary = derived([session, info], ([$s, $i]) => {
|
||||
return $i && ($i.beneficiary === $s.address);
|
||||
});
|
||||
$: isBeneficiary = info && session && (info.beneficiary === session.address);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -53,25 +53,25 @@
|
||||
</style>
|
||||
|
||||
<main>
|
||||
{#if $info}
|
||||
{#if info}
|
||||
<div class="modal">
|
||||
<div class="modal-title">
|
||||
{contractAddress}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{#if $state === State.Withdrawn}
|
||||
Tokens successfully withdrawn to {formatAddress($info.beneficiary)}.
|
||||
Tokens successfully withdrawn to {formatAddress(info.beneficiary)}.
|
||||
{:else}
|
||||
<table>
|
||||
<tr><td class="label">Beneficiary</td><td>{$info.beneficiary}</td></tr>
|
||||
<tr><td class="label">Allocation</td><td>{$info.totalVesting} <strong>{$info.symbol}</strong></td></tr>
|
||||
<tr><td class="label">Withdrawn</td><td>{$info.withdrawn} <strong>{$info.symbol}</strong></td></tr>
|
||||
<tr><td class="label">Withdrawable</td><td>{$info.withdrawableBalance} <strong>{$info.symbol}</strong></td></tr>
|
||||
<tr><td class="label">Beneficiary</td><td>{info.beneficiary}</td></tr>
|
||||
<tr><td class="label">Allocation</td><td>{info.totalVesting} <strong>{info.symbol}</strong></td></tr>
|
||||
<tr><td class="label">Withdrawn</td><td>{info.withdrawn} <strong>{info.symbol}</strong></td></tr>
|
||||
<tr><td class="label">Withdrawable</td><td>{info.withdrawableBalance} <strong>{info.symbol}</strong></td></tr>
|
||||
</table>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
{#if $isBeneficiary}
|
||||
{#if isBeneficiary}
|
||||
{#if $state === State.WithdrawingSign}
|
||||
<button disabled data-waiting class="primary small">
|
||||
Waiting for signature...
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -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<Config> {
|
||||
orgFactory: cfg.orgFactory,
|
||||
provider: provider,
|
||||
signer: provider.getSigner(),
|
||||
gasLimits: gasLimits,
|
||||
};
|
||||
} else {
|
||||
throw `Wrong network: ${network.name}`;
|
||||
|
||||
@@ -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}'`);
|
||||
}
|
||||
}
|
||||
|
||||
+140
-65
@@ -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<State>(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<Session> = 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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user