mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-30 16:50:47 +08:00
Improve vesting discovery and error messages
When loading a ETH address as vesting contract we show a cryptic error message, which non tech-savy end users don't understand, this commit changes it to something more human readable. We allow users to enter and load a contract that probably isn't a vesting contract. The `getInfo` function in the `vesting` folder is very prone to possible errors, and benefits from some parallelization between all the method calls. We allow hereby to enter a contract address in search and once on the contract profile page we provide a link to the detailed vesting information if available. Signed-off-by: Sebastian Martinez <me@sebastinez.dev>
This commit is contained in:
+1
-1
@@ -105,7 +105,7 @@
|
||||
session={$session}
|
||||
activeRoute={$activeRouteStore} />
|
||||
{:else if $activeRouteStore.resource === "vesting"}
|
||||
<Vesting {wallet} session={$session} activeRoute={$activeRouteStore} />
|
||||
<Vesting {wallet} activeRoute={$activeRouteStore} />
|
||||
{:else if $activeRouteStore.resource === "projects"}
|
||||
<Projects {wallet} activeRoute={$activeRouteStore} />
|
||||
{:else if $activeRouteStore.resource === "profile"}
|
||||
|
||||
@@ -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}
|
||||
<div class="wrapper">
|
||||
{#if addressType === AddressType.Org}
|
||||
<Link
|
||||
route={{
|
||||
resource: "profile",
|
||||
params: { addressOrName: addressOrName },
|
||||
}}>
|
||||
{addressLabel}
|
||||
</Link>
|
||||
{#if !noBadge}
|
||||
<Link route={{ resource: "profile", params: { addressOrName } }}>
|
||||
{addressLabel}
|
||||
</Link>
|
||||
|
||||
{#if !noBadge}
|
||||
{#if addressType === AddressType.Org}
|
||||
<Badge variant="foreground">org</Badge>
|
||||
{/if}
|
||||
{:else if addressType === AddressType.Contract}
|
||||
<Link route={{ resource: "profile", params: { addressOrName: address } }}>
|
||||
{addressLabel}
|
||||
</Link>
|
||||
{#if !noBadge}
|
||||
{:else if addressType === AddressType.Vesting}
|
||||
<Badge variant="foreground">vesting contract</Badge>
|
||||
{:else if addressType === AddressType.Contract}
|
||||
<Badge variant="foreground">contract</Badge>
|
||||
{/if}
|
||||
{:else if addressType === AddressType.EOA}
|
||||
<Link
|
||||
route={{
|
||||
resource: "profile",
|
||||
params: { addressOrName: addressOrName },
|
||||
}}>
|
||||
{addressLabel}
|
||||
</Link>
|
||||
{:else}
|
||||
<!-- While we're waiting to find out what address type it is -->
|
||||
<a href={explorerLink(address, wallet)} target="_blank" rel="noreferrer">
|
||||
{addressLabel}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
line-height: 1.6;
|
||||
height: var(--button-tiny-height);
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.foreground {
|
||||
color: var(--color-foreground-6);
|
||||
|
||||
+116
-48
@@ -15,6 +15,7 @@
|
||||
| "gear"
|
||||
| "chevron-down"
|
||||
| "chevron-up"
|
||||
| "etherscan"
|
||||
| "url";
|
||||
</script>
|
||||
|
||||
@@ -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"}
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m12.001 4.8414c-3.9536 0-7.1586 3.205-7.1586 7.1586 0 1.4947
|
||||
0.45765 2.8814 1.2405 4.029 0.14846 0.0072 0.29643 0.01103 0.44383
|
||||
0.01179v-4.8829c0-0.23253 0.18853-0.42109 0.42109-0.42109h1.6844c0.23256 0
|
||||
0.42109 0.18857 0.42109 0.42109v4.6036c0.28408-0.06325 0.56493-0.13618
|
||||
0.84219-0.21771v-6.0703c0-0.23256 0.18853-0.42109
|
||||
0.42109-0.42109h1.6844c0.23253 0 0.42109 0.18853 0.42109
|
||||
0.42109v5.066c0.28752-0.14351 0.56839-0.29359
|
||||
0.84219-0.44855v-6.3019c0-0.23256 0.18857-0.42109
|
||||
0.4211-0.42109h1.6844c0.23253 0 0.42109 0.18853 0.42109
|
||||
0.42109v4.6166c1.5372-1.1835 2.6045-2.3046
|
||||
2.9851-2.7241-0.96346-2.8161-3.6334-4.8402-6.775-4.8402zm7.3852
|
||||
5.4209c-0.47314 0.51981-1.8267 1.9326-3.7716 3.3228-0.27337
|
||||
0.19539-0.666-0.0066-0.666-0.34252v-5.0324h-0.84219v6.1245c0 0.14982-0.07967
|
||||
0.28837-0.20912 0.36383-0.54498 0.3175-1.1208 0.61909-1.723 0.8912-0.27388
|
||||
0.12372-0.5945-0.08329-0.5945-0.38379v-5.3114h-0.84219v5.9606c0
|
||||
0.18334-0.11866 0.34563-0.29334 0.4013-0.56266 0.17913-1.1429 0.32668-1.7377
|
||||
0.43356-0.24549
|
||||
0.04413-0.49555-0.16507-0.49555-0.41444v-4.6966h-0.84219v4.877c0
|
||||
0.22714-0.18015 0.41335-0.40718 0.42084-0.32874
|
||||
0.01086-0.58124-0.01095-0.90874-0.01668-0.42767
|
||||
0-0.63194-0.28744-0.85625-0.64916-0.75855-1.2233-1.1966-2.6665-1.1966-4.2109
|
||||
0-4.4187 3.5821-8.0008 8.0008-8.0008 3.4905 0 6.458 2.2349 7.5514 5.351
|
||||
0.13374 0.38113 0.09955 0.62011-0.16616 0.91215zm0.31068 0.92928c0.24558
|
||||
0.07765 0.29216 0.3165 0.29932 0.54043 0.14132 4.4581-3.5249 8.2688-7.9952
|
||||
8.2688-1.6352
|
||||
0-3.157-0.491-4.4245-1.3338-0.15447-0.10266-0.22354-0.29434-0.17006-0.47205
|
||||
0.053471-0.17762 0.2169-0.29931 0.4024-0.29965 3.1395-0.0061 5.924-1.539
|
||||
7.9792-3.1492 1.8644-1.46063.0905-2.9551 3.3867-3.3309 0.13046-0.16541
|
||||
0.29544-0.29544 0.52216-0.22369zm-10.399 7.4391c0.83386 0.34033 1.7464
|
||||
0.52788 2.7033 0.52788 3.689 0 6.7262-2.7903 7.1162-6.3755-0.62086
|
||||
0.68883-1.5821 1.6643-2.8098 2.626-1.8078 1.4163-4.2267 2.8293-7.0097
|
||||
3.2216z" />
|
||||
{:else if name === "clipboard"}
|
||||
<path
|
||||
d="M9 5H14.7071L18 8.29289V17H9V5ZM10 6V16H17V9H14V6H10ZM15
|
||||
@@ -198,22 +235,41 @@
|
||||
17.25Z" />
|
||||
{:else if name === "github"}
|
||||
<path
|
||||
d="M12 4C7.58 4 4 7.67295 4 12.2031C4 15.8282 6.292 18.9023 9.47
|
||||
19.9858C9.87 20.0631 10.0167 19.8095 10.0167 19.5914C10.0167 19.3966 10.01
|
||||
18.8805 10.0067 18.1969C7.78133 18.6918 7.312 17.0963 7.312 17.0963C6.948
|
||||
16.1495 6.422 15.8966 6.422 15.8966C5.69733 15.388 6.478 15.3982 6.478
|
||||
15.3982C7.28133 15.4557 7.70333 16.2432 7.70333 16.2432C8.41667 17.4975
|
||||
9.576 17.1352 10.0333 16.9254C10.1053 16.3949 10.3113 16.0333 10.54
|
||||
15.8282C8.76333 15.6231 6.896 14.9177 6.896 11.7745C6.896 10.879 7.206
|
||||
10.1476 7.71933 9.57334C7.62933 9.36621 7.35933 8.53222 7.78933
|
||||
7.40224C7.78933 7.40224 8.45933 7.18213 9.98933 8.24306C10.6293 8.06054
|
||||
11.3093 7.97031 11.9893 7.96621C12.6693 7.97031 13.3493 8.06054 13.9893
|
||||
8.24306C15.5093 7.18213 16.1793 7.40224 16.1793 7.40224C16.6093 8.53222
|
||||
16.3393 9.36621 16.2593 9.57334C16.7693 10.1476 17.0793 10.879 17.0793
|
||||
11.7745C17.0793 14.9259 15.2093 15.6197 13.4293 15.8214C13.7093 16.0675
|
||||
13.9693 16.5706 13.9693 17.339C13.9693 18.4368 13.9593 19.3186 13.9593
|
||||
19.5852C13.9593 19.8006 14.0993 20.0569 14.5093 19.9749C17.71 18.8989 20
|
||||
15.8227 20 12.2031C20 7.67295 16.418 4 12 4" />
|
||||
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"}
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
@@ -238,23 +294,25 @@
|
||||
17.5858 7.5 18 7.5H19.5V6C19.5 5.58579 19.8358 5.25 20.25 5.25Z" />
|
||||
{:else if name === "url"}
|
||||
<path
|
||||
d="M18.7566 11.2493L15.7531 14.2518C14.0953 15.9107 11.4059 15.9107
|
||||
9.74803 14.2518C9.48676 13.9916 9.28252 13.6982 9.10313 13.3954L10.4987
|
||||
11.9999C10.565 11.933 10.6469 11.8947 10.7252 11.8496C10.8216 12.1793
|
||||
10.9901 12.4914 11.2493 12.7505C12.0772 13.5789 13.4245 13.5779 14.2518
|
||||
12.7505L17.2543 9.74802C18.0827 8.91963 18.0827 7.57285 17.2543
|
||||
6.74499C16.427 5.91713 15.0802 5.91713 14.2518 6.74499L13.1839
|
||||
7.81391C12.3177 7.47644 11.3841 7.38573 10.4753 7.51894L12.7505
|
||||
5.24373C14.4094 3.58542 17.0977 3.58542 18.7566 5.24373C20.4145 6.90211
|
||||
20.4145 9.591 18.7566 11.2493ZM10.8164 16.1865L9.74803 17.2554C8.92016
|
||||
18.0828 7.57284 18.0828 6.74497 17.2554C5.9171 16.427 5.9171 15.0802
|
||||
6.74497 14.2519L9.74803 11.2493C10.5764 10.421 11.9227 10.421 12.7506
|
||||
11.2493C13.0092 11.508 13.1777 11.8201 13.2752 12.1493C13.354 12.1036
|
||||
13.4349 12.0663 13.5012 12L14.8967 10.605C14.7184 10.3012 14.5131 10.0088
|
||||
14.2518 9.74809C12.594 8.08978 9.90462 8.08978 8.24627 9.74809L5.24374
|
||||
12.7506C3.58542 14.4094 3.58542 17.0978 5.24374 18.7566C6.90207 20.4145
|
||||
9.59097 20.4145 11.2493 18.7566L13.5251 16.4809C12.6158 16.6146 11.6822
|
||||
16.5234 10.8164 16.1865Z" />
|
||||
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"}
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
@@ -300,21 +358,31 @@
|
||||
19.875 12 19.875Z" />
|
||||
{:else if name === "twitter"}
|
||||
<path
|
||||
d="M19.9687 7.54849C19.3697 7.81214 18.7351 7.98617 18.0853
|
||||
8.06498C18.7694 7.65395 19.2816 7.00936 19.5273 6.25025C18.8933 6.62013
|
||||
18.1907 6.88937 17.4427 7.03932C16.9492 6.51179 16.2952 6.1619 15.5824
|
||||
6.04399C14.8696 5.92608 14.1378 6.04675 13.5006 6.38727C12.8635 6.72778
|
||||
12.3566 7.26908 12.0587 7.92711C11.7608 8.58514 11.6886 9.32307 11.8533
|
||||
10.0263C9.12667 9.8977 6.71133 8.58814 5.09333 6.61013C4.7992 7.10984
|
||||
4.64578 7.67979 4.64933 8.25958C4.64933 9.3992 5.22933 10.4009 6.108
|
||||
10.9893C5.58724 10.9728 5.07798 10.832 4.62267 10.5788V10.6188C4.62237
|
||||
11.3761 4.88418 12.1103 5.36367 12.6966C5.84316 13.283 6.51081 13.6854
|
||||
7.25333 13.8357C6.7722 13.9646 6.26828 13.984 5.77867 13.8924C5.98941
|
||||
14.5442 6.39844 15.1139 6.94868 15.5222C7.49891 15.9304 8.1629 16.1567
|
||||
8.848 16.1696C7.68769 17.0799 6.25498 17.574 4.78 17.5725C4.52 17.5725
|
||||
4.26067 17.5571 4 17.5278C5.50381 18.4904 7.25234 19.0013 9.038 19C15.0733
|
||||
19 18.37 14.0043 18.37 9.67978C18.37 9.53982 18.37 9.39987 18.36
|
||||
9.25992C19.004 8.79665 19.5595 8.22147 20 7.56182L19.9687 7.54849Z" />
|
||||
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}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
});
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === "Enter") {
|
||||
if (event.key === "Enter" && valid) {
|
||||
dispatch("submit");
|
||||
}
|
||||
}
|
||||
|
||||
+16
-4
@@ -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<T>(
|
||||
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;
|
||||
|
||||
+120
-41
@@ -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<VestingState>({ type: "idle" });
|
||||
|
||||
export async function withdrawVested(
|
||||
address: string,
|
||||
wallet: Wallet,
|
||||
): Promise<void> {
|
||||
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<VestingInfo> {
|
||||
const contract = new ethers.Contract(
|
||||
export const getInfo = cache.cached(
|
||||
async (address: string, wallet: Wallet): Promise<VestingInfo | undefined> => {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -169,6 +225,14 @@
|
||||
<Icon name="github" />
|
||||
</a>
|
||||
{/if}
|
||||
{#if utils.isAddress(profile.address)}
|
||||
<a
|
||||
class="url"
|
||||
title="Lookup address on Etherscan"
|
||||
href={utils.explorerLink(profile.address, wallet)}>
|
||||
<Icon name="etherscan" />
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -255,6 +319,64 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if vestingInfo}
|
||||
<div class="txt-highlight">Vesting Beneficiary</div>
|
||||
<div style:display="flex" style:gap="1rem">
|
||||
<Address address={vestingInfo.beneficiary} {wallet} resolve compact />
|
||||
</div>
|
||||
<div class="layout-desktop" />
|
||||
<div class="txt-highlight">Allocation</div>
|
||||
<div>
|
||||
{vestingInfo.totalVesting}
|
||||
<span class="txt-bold">{vestingInfo.symbol}</span>
|
||||
</div>
|
||||
<div class="layout-desktop" />
|
||||
<div class="txt-highlight">Withdrawn</div>
|
||||
<div>
|
||||
{vestingInfo.withdrawn}
|
||||
<span class="txt-bold">{vestingInfo.symbol}</span>
|
||||
</div>
|
||||
<div class="layout-desktop" />
|
||||
<div class="txt-highlight">Withdrawable</div>
|
||||
<div>
|
||||
{vestingInfo.withdrawableBalance}
|
||||
<span class="txt-bold">{vestingInfo.symbol}</span>
|
||||
</div>
|
||||
<div class="layout-desktop">
|
||||
{#if isUserAuthorized(vestingInfo.beneficiary) && parseFloat(vestingInfo.withdrawableBalance) > 0}
|
||||
<Button variant="secondary" size="small" on:click={withdrawVesting}>
|
||||
Withdraw
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="txt-highlight">Start Time</div>
|
||||
<div>
|
||||
<span>
|
||||
{parseVestingPeriods(vestingInfo.vestingStartTime)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="layout-desktop" />
|
||||
<div class="txt-highlight">Cliff Period End</div>
|
||||
<div>
|
||||
<span>
|
||||
{parseVestingPeriods(
|
||||
vestingInfo.vestingStartTime,
|
||||
vestingInfo.cliffPeriod,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="layout-desktop" />
|
||||
<div class="txt-highlight">Vesting Period End</div>
|
||||
<div>
|
||||
<span>
|
||||
{parseVestingPeriods(
|
||||
vestingInfo.vestingStartTime,
|
||||
vestingInfo.vestingPeriod,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="layout-desktop" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if profile.seed?.valid}
|
||||
@@ -268,11 +390,18 @@
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<svelte:component
|
||||
this={withdrawVestingModal}
|
||||
info={vestingInfo}
|
||||
contractAddress={addressOrName}
|
||||
{wallet}
|
||||
on:close={() => (withdrawVestingModal = undefined)} />
|
||||
|
||||
<svelte:component
|
||||
this={setNameForm}
|
||||
entity={new User(profile.address)}
|
||||
{wallet}
|
||||
on:close={() => (setNameForm = null)} />
|
||||
on:close={() => (setNameForm = undefined)} />
|
||||
{:catch err}
|
||||
{#if err instanceof NotFoundError}
|
||||
<NotFound
|
||||
|
||||
@@ -18,24 +18,23 @@
|
||||
: "";
|
||||
|
||||
const loadContract = async () => {
|
||||
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" });
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -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} />
|
||||
|
||||
<Button
|
||||
on:click={loadContract}
|
||||
variant="primary"
|
||||
waiting={$state === "loading"}
|
||||
disabled={!valid || $state === "loading"}>
|
||||
waiting={$state.type === "loading"}
|
||||
disabled={!valid || $state.type === "loading"}>
|
||||
Load
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
<script lang="ts">
|
||||
import type { Wallet } from "@app/lib/wallet";
|
||||
import type { VestingRoute } from "@app/lib/router/definitions";
|
||||
import type { Session } from "@app/lib/session";
|
||||
|
||||
import Form from "./Form.svelte";
|
||||
import View from "./View.svelte";
|
||||
import Form from "@app/views/vesting/Form.svelte";
|
||||
import Profile from "@app/views/profiles/Profile.svelte";
|
||||
|
||||
export let activeRoute: VestingRoute;
|
||||
export let wallet: Wallet;
|
||||
export let session: Session | null;
|
||||
</script>
|
||||
|
||||
{#if activeRoute.params.view.resource === "form"}
|
||||
<Form {wallet} />
|
||||
{:else if activeRoute.params.view.resource === "view"}
|
||||
<View
|
||||
{wallet}
|
||||
{session}
|
||||
info={activeRoute.params.view.params.info}
|
||||
contractAddress={activeRoute.params.view.params.contract} />
|
||||
<Profile {wallet} addressOrName={activeRoute.params.view.params.contract} />
|
||||
{/if}
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { Session } from "@app/lib/session";
|
||||
import type { Wallet } from "@app/lib/wallet";
|
||||
import type { VestingInfo } from "@app/lib/vesting";
|
||||
|
||||
import * as router from "@app/lib/router";
|
||||
import * as utils from "@app/lib/utils";
|
||||
import Address from "@app/components/Address.svelte";
|
||||
import Button from "@app/components/Button.svelte";
|
||||
import Modal from "@app/components/Modal.svelte";
|
||||
import { state, getInfo, withdrawVested } from "@app/lib/vesting";
|
||||
import { onMount } from "svelte";
|
||||
import ErrorModal from "@app/components/ErrorModal.svelte";
|
||||
import Loading from "@app/components/Loading.svelte";
|
||||
|
||||
export let contractAddress: string;
|
||||
export let info: VestingInfo | null = null;
|
||||
export let session: Session | null;
|
||||
export let wallet: Wallet;
|
||||
|
||||
let error: Error | undefined = undefined;
|
||||
|
||||
onMount(async () => {
|
||||
if (!info) {
|
||||
state.set("loading");
|
||||
try {
|
||||
info = await getInfo(contractAddress, wallet);
|
||||
} catch (e) {
|
||||
error = e as Error;
|
||||
}
|
||||
}
|
||||
state.set("idle");
|
||||
});
|
||||
|
||||
const parseVestingPeriods = (input: string[]): string => {
|
||||
const total = input
|
||||
.map(s => parseInt(s))
|
||||
.reduce((prev, curr) => prev + curr, 0);
|
||||
return new Date(total * 1000).toDateString();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
table {
|
||||
table-layout: fixed;
|
||||
border-collapse: separate;
|
||||
border-spacing: 2rem 0;
|
||||
}
|
||||
td {
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<title>Radicle – Vesting</title>
|
||||
</svelte:head>
|
||||
|
||||
{#if error}
|
||||
<ErrorModal
|
||||
title="Failed to obtain contract information"
|
||||
message={error.message}
|
||||
on:close={() => router.pop()} />
|
||||
{:else if $state === "loading"}
|
||||
<Loading center />
|
||||
{:else if info}
|
||||
{@const isBeneficiary =
|
||||
session && utils.isAddressEqual(info.beneficiary, session.address)}
|
||||
<Modal>
|
||||
<span slot="title">
|
||||
{contractAddress}
|
||||
</span>
|
||||
|
||||
<span slot="body">
|
||||
{#if $state === "withdrawn"}
|
||||
Tokens successfully withdrawn to {utils.formatAddress(
|
||||
info.beneficiary,
|
||||
)}.
|
||||
{:else}
|
||||
<table>
|
||||
<tr>
|
||||
<td class="txt-highlight">Beneficiary</td>
|
||||
<td>
|
||||
<Address {wallet} address={info.beneficiary} compact resolve />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="txt-highlight">Allocation</td>
|
||||
<td>
|
||||
{info.totalVesting}
|
||||
<span class="txt-bold">{info.symbol}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="txt-highlight">Withdrawn</td>
|
||||
<td>
|
||||
{info.withdrawn}
|
||||
<span class="txt-bold">{info.symbol}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="txt-highlight">Withdrawable</td>
|
||||
<td>
|
||||
{info.withdrawableBalance}
|
||||
<span class="txt-bold">{info.symbol}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="txt-highlight">Start Time</td>
|
||||
<td>
|
||||
<span class="txt-bold">
|
||||
{parseVestingPeriods([info.vestingStartTime])}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="txt-highlight">Cliff Period End</td>
|
||||
<td>
|
||||
<span class="txt-bold">
|
||||
{parseVestingPeriods([info.vestingStartTime, info.cliffPeriod])}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="txt-highlight">Vesting Period End</td>
|
||||
<td>
|
||||
<span class="txt-bold">
|
||||
{parseVestingPeriods([
|
||||
info.vestingStartTime,
|
||||
info.vestingPeriod,
|
||||
])}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<span slot="actions">
|
||||
{#if isBeneficiary}
|
||||
{#if $state === "withdrawingSign"}
|
||||
<Button disabled waiting={true} variant="primary">
|
||||
Waiting for signature…
|
||||
</Button>
|
||||
{:else if $state === "withdrawing"}
|
||||
<Button disabled waiting={true} variant="primary">
|
||||
Withdrawing…
|
||||
</Button>
|
||||
{:else if $state === "idle"}
|
||||
<Button
|
||||
on:click={() => withdrawVested(contractAddress, wallet)}
|
||||
variant="primary">
|
||||
Withdraw
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
<Button on:click={() => router.pop()} variant="primary">Back</Button>
|
||||
</span>
|
||||
</Modal>
|
||||
{/if}
|
||||
@@ -0,0 +1,72 @@
|
||||
<script lang="ts" strictEvents>
|
||||
import type { VestingInfo } from "@app/lib/vesting";
|
||||
import type { Wallet } from "@app/lib/wallet";
|
||||
|
||||
import * as utils from "@app/lib/utils";
|
||||
import Button from "@app/components/Button.svelte";
|
||||
import ErrorModal from "@app/components/ErrorModal.svelte";
|
||||
import Loading from "@app/components/Loading.svelte";
|
||||
import Modal from "@app/components/Modal.svelte";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { state } from "@app/lib/vesting";
|
||||
import { withdrawVested } from "@app/lib/vesting";
|
||||
|
||||
export let contractAddress: string;
|
||||
export let info: VestingInfo;
|
||||
export let wallet: Wallet;
|
||||
|
||||
const dispatch = createEventDispatcher<{ close: never }>();
|
||||
|
||||
onMount(async () => {
|
||||
await withdrawVested(contractAddress, wallet);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
{#if $state.type === "error"}
|
||||
<ErrorModal
|
||||
floating
|
||||
title="Withdraw failed"
|
||||
message={$state.error}
|
||||
on:close={() => dispatch("close")} />
|
||||
{:else}
|
||||
<Modal on:close floating>
|
||||
<span slot="title">
|
||||
{utils.formatAddress(contractAddress)}
|
||||
</span>
|
||||
|
||||
<span slot="subtitle">
|
||||
{#if $state.type === "withdrawingSign"}
|
||||
<span class="txt-missing">Waiting for a signature…</span>
|
||||
{:else if $state.type === "withdrawing"}
|
||||
<span class="txt-missing">Waiting for confirmation…</span>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<span slot="body">
|
||||
{#if $state.type === "withdrawn"}
|
||||
<span>
|
||||
Tokens have been withdrawn to <span class="txt-highlight">
|
||||
{utils.formatAddress(info.beneficiary)}
|
||||
</span>
|
||||
</span>
|
||||
{:else}
|
||||
<Loading small center />
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<span class="actions" slot="actions">
|
||||
<Button variant="foreground" on:click={() => dispatch("close")}>
|
||||
Close
|
||||
</Button>
|
||||
</span>
|
||||
</Modal>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user