diff --git a/src/App.svelte b/src/App.svelte
index e51f0dbfb..0bdaf9b5e 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -105,7 +105,7 @@
session={$session}
activeRoute={$activeRouteStore} />
{:else if $activeRouteStore.resource === "vesting"}
-
+
{:else if $activeRouteStore.resource === "projects"}
{:else if $activeRouteStore.resource === "profile"}
diff --git a/src/components/Address.svelte b/src/components/Address.svelte
index ac7f4cb3b..8db8c5676 100644
--- a/src/components/Address.svelte
+++ b/src/components/Address.svelte
@@ -9,7 +9,6 @@
import Link from "@app/components/Link.svelte";
import {
AddressType,
- explorerLink,
formatAddress,
identifyAddress,
parseEnsLabel,
@@ -65,12 +64,6 @@
align-items: center;
height: 100%;
}
- .address a {
- color: var(--color-foreground-6);
- }
- .address a:hover {
- color: var(--color-foreground);
- }
.highlight {
color: var(--color-foreground-6);
font-weight: var(--font-weight-bold);
@@ -96,37 +89,18 @@
{/if}
{/if}
- {#if addressType === AddressType.Org}
-
- {addressLabel}
-
- {#if !noBadge}
+
+ {addressLabel}
+
+
+ {#if !noBadge}
+ {#if addressType === AddressType.Org}
org
- {/if}
- {:else if addressType === AddressType.Contract}
-
- {addressLabel}
-
- {#if !noBadge}
+ {:else if addressType === AddressType.Vesting}
+
vesting contract
+ {:else if addressType === AddressType.Contract}
contract
{/if}
- {:else if addressType === AddressType.EOA}
-
- {addressLabel}
-
- {:else}
-
-
- {addressLabel}
-
{/if}
diff --git a/src/components/Badge.svelte b/src/components/Badge.svelte
index ea0cad990..975ec64c9 100644
--- a/src/components/Badge.svelte
+++ b/src/components/Badge.svelte
@@ -16,6 +16,7 @@
line-height: 1.6;
height: var(--button-tiny-height);
display: flex;
+ white-space: nowrap;
}
.foreground {
color: var(--color-foreground-6);
diff --git a/src/components/Icon.svelte b/src/components/Icon.svelte
index 376e8ec07..e43a57c9d 100644
--- a/src/components/Icon.svelte
+++ b/src/components/Icon.svelte
@@ -15,6 +15,7 @@
| "gear"
| "chevron-down"
| "chevron-up"
+ | "etherscan"
| "url";
@@ -59,6 +60,42 @@
14.7859 16.9725C14.8779 16.9315 14.9607 16.8724 15.0293 16.7987L19.2793
12.5487C19.4198 12.4081 19.4987 12.2175 19.4987 12.0187C19.4987 11.82
19.4198 11.6293 19.2793 11.4887L15.0293 7.23871V7.23871Z" />
+ {:else if name === "etherscan"}
+
{:else if name === "clipboard"}
{:else if name === "github"}
+ clip-rule="evenodd"
+ fill-rule="evenodd"
+ d="m7.3295 4.5202c-0.26315-0.090455-0.55523 0.13567-0.52819
+ 0.41659l0.22799 3.0462c-0.34577 0.3331-1.0298 1.2253-1.0298 2.873 0 1.7391
+ 0.87721 2.9043 2.1775 3.6113 0.30568 0.16625 0.63459 0.3073 0.98108
+ 0.42571l-0.49322
+ 0.9844-1.2994-0.10729c-0.4524-0.037363-0.87865-0.22746-1.2087-0.53908l-1.0828-1.0224c-0.16064-0.15169-0.41382-0.14441-0.5655
+ 0.01616-0.15168 0.16065-0.14442 0.41387 0.016209 0.56548l1.0828 1.0225c0.46207
+ 0.43628 1.0588 0.70246 1.6922 0.7547l0.97925 0.08089c-0.18373 0.37987-0.27927
+ 0.79662-0.27927 1.219v0.63245c0 0.2209-0.1791 0.40003-0.40003
+ 0.40003h-0.40003c-0.22093 0-0.40003 0.17905-0.40003 0.40003 0 0.2209 0.1791
+ 0.40003 0.40003 0.40003h0.40003c0.6628 0 1.2001-0.53732
+ 1.2001-1.2001v-0.63245c0-0.31106 0.072566-0.61789
+ 0.21192-0.89599l0.92927-1.8546c0.40628 0.09553 0.82889 0.16585 1.2603
+ 0.21426l-0.3999 4.7353c-0.01859 0.2201 0.1448 0.41363 0.36495 0.43228 0.22016
+ 0.01856 0.4137-0.14481 0.43226-0.36499l0.40003-4.7369 8e-5 -9.6e-4c0.53228
+ 0.02768 1.0706 0.02768 1.6028 0l8e-5 9.6e-4 0.40003 4.7369c0.01856 0.22018
+ 0.2121 0.38355 0.43228 0.36499 0.2201-0.01864 0.38355-0.21218
+ 0.36491-0.43228l-0.39987-4.7353c0.39443-0.04424 0.7815-0.10681
+ 1.1555-0.19026l0.99776 1.8113c0.16289 0.29554 0.24826 0.62757 0.24826
+ 0.96504v0.58277c0 0.66277 0.53732 1.2001 1.2001 1.2001h0.40003c0.2209 0
+ 0.40003-0.17914 0.40003-0.40003
+ 0-0.22098-0.17913-0.40003-0.40003-0.40003h-0.40003c-0.2209
+ 0-0.40003-0.17913-0.40003-0.40003v-0.58277c0-0.47244-0.11953-0.93728-0.34755-1.3511l-0.90431-1.6416c0.38083-0.12465
+ 0.74142-0.27586 1.0745-0.45692 1.3003-0.70702 2.1775-1.8722 2.1775-3.6113
+ 0-1.6477-0.68398-2.5399-1.0298-2.873l0.22794-3.0454c0.027042-0.27529-0.26266-0.50894-0.52844-0.41737l-3.0828
+ 0.98573c-0.74062-0.18142-1.5924-0.27064-2.3875-0.27064-0.79502 0-1.6469
+ 0.089215-2.3874 0.27064zm0.5121 3.5965c0.012201 0.12941-0.037451
+ 0.25373-0.13439 0.3386-0.1738 0.1528-0.32207 0.31325-0.44444 0.51351-0.22285
+ 0.36466-0.46318 0.96791-0.46318 1.8872 0 3.1088 3.5475 3.7601 6.0005 3.7601
+ 2.4529 0 6.0005-0.65125 6.0005-3.7601
+ 0-1.7223-0.81855-2.3326-0.87087-2.372-0.11889-0.082175-0.18474-0.22163-0.17121-0.36685l0.1989-2.6568-2.6353
+ 0.8426c-0.07233 0.023138-0.14977 0.025194-0.2233
+ 0.00593-0.68854-0.1805-1.5179-0.27343-2.2987-0.27343s-1.6102 0.092928-2.2987
+ 0.27343c-0.07348 0.019266-0.15092 0.017209-0.22327-0.00593l-2.6353-0.8426z" />
{:else if name === "moon"}
{:else if name === "url"}
+ clip-rule="evenodd"
+ fill-rule="evenodd"
+ d="m12.671 11.04c-0.67865-0.74752-1.0922-1.7401-1.0922-2.8293 0-2.3254
+ 1.8851-4.2105 4.2105-4.2105 2.3254 0 4.2105 1.8851 4.2105 4.2105 0
+ 2.3254-1.8851 4.2105-4.2105 4.2105-0.93263
+ 0-1.7944-0.30324-2.4921-0.81642l-1.6927 1.6927c0.51318 0.69777 0.81642 1.5595
+ 0.81642 2.4921 0 2.3254-1.8851 4.2105-4.2105 4.2105-2.3254
+ 0-4.2105-1.8851-4.2105-4.2105 0-2.3254 1.8851-4.2105 4.2105-4.2105 1.0892 0
+ 2.0818 0.41356 2.8293 1.0922zm-0.2501-2.8293c0-1.8603 1.5081-3.3684
+ 3.3684-3.3684 1.8603 0 3.3684 1.5081 3.3684 3.3684 0 1.8603-1.5081
+ 3.3684-3.3684 3.3684-0.69962
+ 0-1.3495-0.2133-1.888-0.57844l1.2291-1.2291c0.16446-0.16443 0.16446-0.43102
+ 0-0.59545-0.16438-0.16444-0.43099-0.16444-0.59545 0l-1.2675
+ 1.2676c-0.52674-0.59443-0.84648-1.3764-0.84648-2.233zm-3.2451 6.3245
+ 1.2676-1.2675c-0.59443-0.52674-1.3764-0.84648-2.233-0.84648-1.8603 0-3.3684
+ 1.5081-3.3684 3.3684 0 1.8603 1.5081 3.3684 3.3684 3.3684 1.8603 0
+ 3.3684-1.5081 3.3684-3.3684 0-0.69962-0.2133-1.3495-0.57844-1.888l-1.2291
+ 1.2291c-0.16443 0.16446-0.43102 0.16446-0.59545
+ 0-0.16444-0.16438-0.16444-0.43099 0-0.59545z" />
{:else if name === "checkmark"}
{:else if name === "twitter"}
+ clip-rule="evenodd"
+ fill-rule="evenodd"
+ d="m5.5228 5.4809c0.13886 0.00675 0.26343 0.087463 0.32634 0.21144 1.08
+ 2.1284 2.6203 3.5913 4.862 4.2088v-0.47702c0-1.8169 1.4729-3.2898
+ 3.2898-3.2898 1.1961 0 2.2689 0.56288 2.8469 1.5482h2.7652c0.16263 0 0.30793
+ 0.1017 0.36366 0.25453 0.05566 0.15283 0.0098 0.32417-0.11464 0.42881l-2.5734
+ 2.1625c-0.04079 1.6914-0.6455 3.5129-1.7259 4.9881-1.2652 1.7277-3.2027
+ 3.0031-5.6713 3.0031-2.4028
+ 0-3.8426-0.92007-4.6769-1.8739-0.41221-0.47133-0.66961-0.94259-0.82448-1.2974-0.077555-0.17765-0.12983-0.3272-0.16319-0.43441-0.016689-0.05364-0.028687-0.09676-0.036769-0.12772-0.00405-0.01548-0.00711-0.02794-0.0093-0.03716l-0.00265-0.0113-8.515e-4
+ -0.0038-3.096e-4 -0.0014-1.316e-4 -6.19e-4c-5.42e-5 -2.33e-4 -1.084e-4
+ -4.65e-4 0.3781-0.08267l-0.37821 0.08221c-0.024863-0.11441 0.00341-0.23393
+ 0.076873-0.32511 0.073475-0.09111 0.18426-0.14413
+ 0.30134-0.14413h2.3072c-0.87844-0.64588-1.7455-1.5204-2.2917-2.612-0.80618-1.6109-0.88662-3.6435
+ 0.60706-5.9904 0.074644-0.11729 0.20646-0.18553 0.34532-0.17877zm-0.42433
+ 9.5552c3.561e-4 7.74e-4 7.044e-4 0.0016 0.00106 0.0024 0.13017 0.29833 0.34822
+ 0.69791 0.69767 1.0974 0.68907 0.78785 1.9174 1.6095 4.0942 1.6095 2.1745 0
+ 3.8969-1.1162 5.0467-2.6864 1.0242-1.3984 1.5785-3.1389 1.5785-4.713 0-0.11431
+ 0.05055-0.22277 0.13802-0.29631l1.8961-1.5933h-1.9347c-0.14847
+ 0-0.28385-0.084955-0.34849-0.21865-0.39749-0.82249-1.2464-1.3295-2.2666-1.3295-1.3894
+ 0-2.5157 1.1263-2.5157 2.5157v0.96759c0 0.21372-0.17332 0.38704-0.38704
+ 0.38704-0.01664
+ 0-0.03298-0.0011-0.04908-0.0031-0.01757-0.0013-0.03522-0.0039-0.05295-0.0077-2.5186-0.54599-4.2771-2.0072-5.5042-4.1128-0.9927
+ 1.8701-0.84393 3.4204-0.22929 4.6486 0.6925 1.3838 2.004 2.4102 3.1211 3.0036
+ 0.15696 0.08337 0.23697 0.26295 0.19401 0.43534-0.042953 0.17246-0.19784
+ 0.29353-0.37557 0.29353z" />
{:else}
{unreachable(name)}
{/if}
diff --git a/src/components/TextInput.svelte b/src/components/TextInput.svelte
index f0af1d5fe..0db7ce397 100644
--- a/src/components/TextInput.svelte
+++ b/src/components/TextInput.svelte
@@ -31,7 +31,7 @@
});
function handleKeydown(event: KeyboardEvent) {
- if (event.key === "Enter") {
+ if (event.key === "Enter" && valid) {
dispatch("submit");
}
}
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index ffc9d62c7..dae6d88b7 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -16,10 +16,12 @@ import { base } from "@app/lib/router";
import { config } from "@app/lib/config";
import { getAddress, getResolver } from "@app/lib/registrar";
import { getAvatar, getSeed, getRegistration } from "@app/lib/registrar";
+import { getInfo } from "@app/lib/vesting";
export enum AddressType {
Contract,
Org,
+ Vesting,
EOA,
}
@@ -127,6 +129,11 @@ export function formatAddress(input: string): string {
);
}
+// Returns a shortened Ethereum transaction hash
+export function formatTx(input: string): string {
+ return input.substring(0, 20) + "…";
+}
+
export function formatCommit(oid: string): string {
return oid.substring(0, 7);
}
@@ -288,12 +295,13 @@ export function isFulfilled(
return input.status === "fulfilled";
}
-// Get the explorer link of an address, eg. Etherscan.
-export function explorerLink(addr: string, wallet: Wallet): string {
+// Get the explorer link of an address or tx, eg. Etherscan.
+export function explorerLink(addrOrTx: string, wallet: Wallet): string {
+ const type = isAddress(addrOrTx) ? "address" : "tx";
if (wallet.network.name === "goerli") {
- return `https://goerli.etherscan.io/address/${addr}`;
+ return `https://goerli.etherscan.io/${type}/${addrOrTx}`;
}
- return `https://etherscan.io/address/${addr}`;
+ return `https://etherscan.io/${type}/${addrOrTx}`;
}
// Format a name.
@@ -349,6 +357,10 @@ export async function identifyAddress(
const bytes = ethers.utils.arrayify(code);
if (bytes.length > 0) {
+ const info = await getInfo(address, wallet);
+ if (info) {
+ return AddressType.Vesting;
+ }
return AddressType.Contract;
}
return AddressType.EOA;
diff --git a/src/lib/vesting.ts b/src/lib/vesting.ts
index d0115142d..792e8efe9 100644
--- a/src/lib/vesting.ts
+++ b/src/lib/vesting.ts
@@ -1,12 +1,13 @@
import type { Wallet } from "@app/lib/wallet";
+import type { TransactionResponse } from "@ethersproject/abstract-provider";
-import { ethers } from "ethers";
+import { BigNumber, ethers } from "ethers";
import { writable } from "svelte/store";
+import * as cache from "@app/lib/cache";
import * as session from "@app/lib/session";
import * as utils from "@app/lib/utils";
import ethereumContractAbis from "@app/lib/ethereum/contractAbis.json";
-import { assert } from "@app/lib/error";
export interface VestingInfo {
token: string;
@@ -20,15 +21,24 @@ export interface VestingInfo {
vestingPeriod: string;
}
-export const state = writable<
- "idle" | "loading" | "withdrawingSign" | "withdrawing" | "withdrawn"
->("idle");
+export type VestingState =
+ | { type: "idle" }
+ | { type: "loading" }
+ | { type: "error"; error: string }
+ | { type: "withdrawingSign" }
+ | { type: "withdrawing"; tx: TransactionResponse }
+ | { type: "withdrawn" };
+
+export const state = writable({ type: "idle" });
export async function withdrawVested(
address: string,
wallet: Wallet,
): Promise {
- assert(wallet.signer);
+ if (!wallet.signer) {
+ state.set({ type: "error", error: "No signer available" });
+ return;
+ }
const contract = new ethers.Contract(
address,
@@ -37,50 +47,119 @@ export async function withdrawVested(
);
const signer = wallet.signer;
- state.set("withdrawingSign");
+ state.set({ type: "withdrawingSign" });
- const tx = await contract.connect(signer).withdrawVested();
+ try {
+ const tx: TransactionResponse = await contract
+ .connect(signer)
+ .withdrawVested();
- state.set("withdrawing");
- await tx.wait();
+ state.set({ type: "withdrawing", tx });
+ await tx.wait();
+ } catch (e) {
+ handleEtherErrorState(e, "Unknown error, check the dev console");
+ return;
+ }
session.state.refreshBalance(wallet);
- state.set("withdrawn");
+ state.set({ type: "withdrawn" });
}
-export async function getInfo(
- address: string,
- wallet: Wallet,
-): Promise {
- const contract = new ethers.Contract(
+export const getInfo = cache.cached(
+ async (address: string, wallet: Wallet): Promise => {
+ const contract = getVestingContract(address, wallet);
+
+ let vestingInfo:
+ | [
+ string,
+ string,
+ BigNumber,
+ BigNumber,
+ BigNumber,
+ string,
+ string,
+ string,
+ ]
+ | undefined = undefined;
+ let token: string | undefined = undefined;
+
+ try {
+ token = await contract.token();
+ if (!token) {
+ return undefined;
+ }
+
+ const tokenContract = new ethers.Contract(
+ token,
+ ethereumContractAbis.token,
+ wallet.provider,
+ );
+
+ vestingInfo = await Promise.all([
+ tokenContract.symbol(),
+ contract.beneficiary(),
+ contract.withdrawableBalance(),
+ contract.withdrawn(),
+ contract.totalVestingAmount(),
+ contract.vestingStartTime(),
+ contract.vestingPeriod(),
+ contract.cliffPeriod(),
+ ]);
+ } catch (e) {
+ console.warn(e);
+ return undefined;
+ }
+
+ const [
+ symbol,
+ beneficiary,
+ withdrawable,
+ withdrawn,
+ total,
+ vestingStartTime,
+ vestingPeriod,
+ cliffPeriod,
+ ] = vestingInfo;
+
+ return {
+ token,
+ symbol,
+ beneficiary,
+ withdrawableBalance: utils.formatBalance(withdrawable),
+ withdrawn: utils.formatBalance(withdrawn),
+ totalVesting: utils.formatBalance(total),
+ vestingStartTime,
+ vestingPeriod,
+ cliffPeriod,
+ };
+ },
+ address => address,
+ { max: 1000 },
+);
+
+export function parseVestingPeriods(...timestamps: string[]): string {
+ const sum = timestamps
+ .map(timestamp => parseInt(timestamp))
+ .reduce((prev, curr) => prev + curr, 0);
+ return new Date(sum * 1000).toDateString();
+}
+
+export function getVestingContract(address: string, wallet: Wallet) {
+ return new ethers.Contract(
address,
ethereumContractAbis.vesting,
wallet.provider,
);
- const token = await contract.token();
- const beneficiary = await contract.beneficiary();
- const withdrawable = await contract.withdrawableBalance();
- const withdrawn = await contract.withdrawn();
- const total = await contract.totalVestingAmount();
- const vestingStartTime = await contract.vestingStartTime();
- const vestingPeriod = await contract.vestingPeriod();
- const cliffPeriod = await contract.cliffPeriod();
+}
- const tokenContract = new ethers.Contract(
- token,
- ethereumContractAbis.token,
- wallet.provider,
- );
- const symbol = await tokenContract.symbol();
+export function handleEtherErrorState(e: unknown, message: string): void {
+ const error =
+ typeof e === "object" &&
+ e !== null &&
+ "reason" in e &&
+ typeof e.reason === "string"
+ ? e.reason
+ : message;
- return {
- token,
- symbol,
- beneficiary,
- totalVesting: utils.formatBalance(total),
- withdrawableBalance: utils.formatBalance(withdrawable),
- withdrawn: utils.formatBalance(withdrawn),
- vestingStartTime,
- vestingPeriod,
- cliffPeriod,
- };
+ state.set({ type: "error", error });
+ console.warn(e);
}
diff --git a/src/views/profiles/Profile.svelte b/src/views/profiles/Profile.svelte
index 8aaea4672..68cc5a256 100644
--- a/src/views/profiles/Profile.svelte
+++ b/src/views/profiles/Profile.svelte
@@ -3,6 +3,8 @@
import type { Wallet } from "@app/lib/wallet";
import type { Seed, Stats } from "@app/lib/seed";
import type { ProjectInfo } from "@app/lib/project";
+ import type { VestingInfo } from "@app/lib/vesting";
+
import * as utils from "@app/lib/utils";
import Address from "@app/components/Address.svelte";
import Async from "@app/components/Async.svelte";
@@ -18,20 +20,31 @@
import RadicleId from "@app/components/RadicleId.svelte";
import SeedAddress from "@app/components/SeedAddress.svelte";
import SetName from "./SetName.svelte";
+ import Withdraw from "@app/views/vesting/Withdraw.svelte";
import { MissingReverseRecord, NotFoundError } from "@app/lib/error";
import { User, Profile, ProfileType } from "@app/lib/profile";
import { defaultNodePort } from "@app/lib/seed";
+ import {
+ getInfo,
+ getVestingContract,
+ handleEtherErrorState,
+ parseVestingPeriods,
+ } from "@app/lib/vesting";
+ import { onMount } from "svelte";
import { session } from "@app/lib/session";
export let wallet: Wallet;
export let addressOrName: string;
- export let action: string | null = null;
- let setNameForm: typeof SvelteComponent | null =
- action === "setName" ? SetName : null;
+ let vestingInfo: VestingInfo | undefined = undefined;
+ let setNameForm: typeof SvelteComponent | undefined = undefined;
+ let withdrawVestingModal: typeof SvelteComponent | undefined = undefined;
const setName = () => {
setNameForm = SetName;
};
+ const withdrawVesting = () => {
+ withdrawVestingModal = Withdraw;
+ };
const getProjectsAndStats = async (
seed: Seed,
@@ -45,6 +58,49 @@
return { stats, projects };
};
+ // Refresh vestingInfo and close modal if addressOrName changes
+ $: {
+ vestingInfo = undefined;
+ withdrawVestingModal = undefined;
+ getInfo(addressOrName, wallet)
+ .then(info => {
+ vestingInfo = info;
+ })
+ .catch(() => {
+ console.warn("Not able to get vesting contract info");
+ });
+ }
+
+ onMount(async () => {
+ const addressType = await utils.identifyAddress(addressOrName, wallet);
+ if (addressType === utils.AddressType.Contract) {
+ try {
+ vestingInfo = await getInfo(addressOrName, wallet);
+ } catch (e) {
+ handleEtherErrorState(e, "Not able to get vesting contract info");
+ }
+ }
+ });
+
+ const vestingContract = getVestingContract(addressOrName, wallet);
+
+ wallet.provider.on("block", async () => {
+ if (vestingInfo) {
+ try {
+ const updatedAmounts = await Promise.all([
+ vestingContract.withdrawableBalance(),
+ vestingContract.withdrawn(),
+ ]);
+ vestingInfo.withdrawableBalance = utils.formatBalance(
+ updatedAmounts[0],
+ );
+ vestingInfo.withdrawn = utils.formatBalance(updatedAmounts[1]);
+ } catch (e) {
+ handleEtherErrorState(e, "Not able to update the balance");
+ }
+ }
+ });
+
$: isUserAuthorized = (address: string): boolean | null => {
return $session && utils.isAddressEqual(address, $session.address);
};
@@ -75,7 +131,7 @@
}
.fields {
display: grid;
- grid-template-columns: 5rem 4fr 2fr;
+ grid-template-columns: max-content 4fr 2fr;
gap: 1rem 2rem;
margin-bottom: 1rem;
}
@@ -116,7 +172,7 @@
padding: 1.5rem;
}
.fields {
- grid-template-columns: 5rem auto;
+ grid-template-columns: max-content auto;
}
}
@@ -169,6 +225,14 @@
{/if}
+ {#if utils.isAddress(profile.address)}
+
+
+
+ {/if}
@@ -255,6 +319,64 @@
{/if}
{/if}
+ {#if vestingInfo}
+ Vesting Beneficiary
+
+
+ Allocation
+
+ {vestingInfo.totalVesting}
+ {vestingInfo.symbol}
+
+
+ Withdrawn
+
+ {vestingInfo.withdrawn}
+ {vestingInfo.symbol}
+
+
+ Withdrawable
+
+ {vestingInfo.withdrawableBalance}
+ {vestingInfo.symbol}
+
+
+ {#if isUserAuthorized(vestingInfo.beneficiary) && parseFloat(vestingInfo.withdrawableBalance) > 0}
+
+ {/if}
+
+ Start Time
+
+
+ {parseVestingPeriods(vestingInfo.vestingStartTime)}
+
+
+
+ Cliff Period End
+
+
+ {parseVestingPeriods(
+ vestingInfo.vestingStartTime,
+ vestingInfo.cliffPeriod,
+ )}
+
+
+
+ Vesting Period End
+
+
+ {parseVestingPeriods(
+ vestingInfo.vestingStartTime,
+ vestingInfo.vestingPeriod,
+ )}
+
+
+
+ {/if}
{#if profile.seed?.valid}
@@ -268,11 +390,18 @@
{/if}
+ (withdrawVestingModal = undefined)} />
+
(setNameForm = null)} />
+ on:close={() => (setNameForm = undefined)} />
{:catch err}
{#if err instanceof NotFoundError}
{
- state.set("loading");
+ state.set({ type: "loading" });
try {
const info = await getInfo(contractAddress, wallet);
- router.push({
- resource: "vesting",
- params: {
- view: {
- resource: "view",
- params: { contract: contractAddress, info },
- },
- },
- });
+ if (info) {
+ router.push({
+ resource: "profile",
+ params: { addressOrName: contractAddress },
+ });
+ } else {
+ validationMessage = "No vesting account found under this address.";
+ }
} catch (error) {
validationMessage =
"Couldn't load contract, check dev console for details.";
- console.error(error);
+ console.warn(error);
}
- state.set("idle");
+ state.set({ type: "idle" });
};
@@ -75,16 +74,16 @@
placeholder="Enter vesting contract address"
{valid}
{validationMessage}
- loading={$state === "loading"}
- disabled={$state === "loading"}
+ loading={$state.type === "loading"}
+ disabled={$state.type === "loading"}
on:submit={loadContract}
bind:value={contractAddress} />
diff --git a/src/views/vesting/Routes.svelte b/src/views/vesting/Routes.svelte
index 0735f133c..664a8057d 100644
--- a/src/views/vesting/Routes.svelte
+++ b/src/views/vesting/Routes.svelte
@@ -1,22 +1,16 @@
{#if activeRoute.params.view.resource === "form"}
{:else if activeRoute.params.view.resource === "view"}
-
+
{/if}
diff --git a/src/views/vesting/View.svelte b/src/views/vesting/View.svelte
deleted file mode 100644
index 75979112e..000000000
--- a/src/views/vesting/View.svelte
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-
-
-
- Radicle – Vesting
-
-
-{#if error}
- router.pop()} />
-{:else if $state === "loading"}
-
-{:else if info}
- {@const isBeneficiary =
- session && utils.isAddressEqual(info.beneficiary, session.address)}
-
-
- {contractAddress}
-
-
-
- {#if $state === "withdrawn"}
- Tokens successfully withdrawn to {utils.formatAddress(
- info.beneficiary,
- )}.
- {:else}
-
-
- | Beneficiary |
-
-
- |
-
-
- | Allocation |
-
- {info.totalVesting}
- {info.symbol}
- |
-
-
- | Withdrawn |
-
- {info.withdrawn}
- {info.symbol}
- |
-
-
- | Withdrawable |
-
- {info.withdrawableBalance}
- {info.symbol}
- |
-
-
- | Start Time |
-
-
- {parseVestingPeriods([info.vestingStartTime])}
-
- |
-
-
- | Cliff Period End |
-
-
- {parseVestingPeriods([info.vestingStartTime, info.cliffPeriod])}
-
- |
-
-
- | Vesting Period End |
-
-
- {parseVestingPeriods([
- info.vestingStartTime,
- info.vestingPeriod,
- ])}
-
- |
-
-
- {/if}
-
-
-
- {#if isBeneficiary}
- {#if $state === "withdrawingSign"}
-
- {:else if $state === "withdrawing"}
-
- {:else if $state === "idle"}
-
- {/if}
- {/if}
-
-
-
-{/if}
diff --git a/src/views/vesting/Withdraw.svelte b/src/views/vesting/Withdraw.svelte
new file mode 100644
index 000000000..0da7a4790
--- /dev/null
+++ b/src/views/vesting/Withdraw.svelte
@@ -0,0 +1,72 @@
+
+
+
+
+{#if $state.type === "error"}
+ dispatch("close")} />
+{:else}
+
+
+ {utils.formatAddress(contractAddress)}
+
+
+
+ {#if $state.type === "withdrawingSign"}
+ Waiting for a signature…
+ {:else if $state.type === "withdrawing"}
+ Waiting for confirmation…
+ {/if}
+
+
+
+ {#if $state.type === "withdrawn"}
+
+ Tokens have been withdrawn to
+ {utils.formatAddress(info.beneficiary)}
+
+
+ {:else}
+
+ {/if}
+
+
+
+
+
+
+{/if}