Scaffolding for orgs

This commit is contained in:
Alexis Sellier
2021-04-30 19:30:38 +02:00
parent 1f73869c39
commit 8819c8c6f7
10 changed files with 228 additions and 26 deletions
+25 -8
View File
@@ -13,6 +13,7 @@
--color-primary-6: #ffd4ff;
--color-primary-faded: #ff55ff77;
--color-secondary: #5555ff;
--color-secondary-faded: #5555ff77;
--color-secondary-1: #212847;
--color-secondary-2: #2c326d;
--color-secondary-6: #e3e3ff;
@@ -91,7 +92,7 @@ button {
cursor: pointer;
min-width: 8rem;
}
button:hover {
button:not([disabled]):hover {
color: var(--color-background) !important;
background-color: var(--color-foreground);
}
@@ -102,7 +103,11 @@ button.secondary {
color: var(--color-secondary);
border-color: var(--color-secondary);
}
button.secondary:hover {
button.secondary[disabled] {
color: var(--color-secondary-faded);
border-color: var(--color-secondary-faded);
}
button.secondary:not([disabled]):hover {
background-color: var(--color-secondary);
}
button.primary {
@@ -175,7 +180,7 @@ input.wide {
background: var(--color-background);
box-shadow: 8px 8px 64px var(--box-shadow-color);
min-width: 480px;
max-width: 720px;
max-width: 760px;
text-align: center;
}
.modal-title {
@@ -185,6 +190,8 @@ input.wide {
text-align: center;
}
.modal-body {
overflow-x: hidden;
text-overflow: ellipsis;
margin-top: 2em;
margin-bottom: 3em;
}
@@ -192,12 +199,22 @@ input.wide {
margin-top: 2rem;
text-align: center;
}
.modal-actions > button {
.modal-actions button {
margin-right: 1rem;
}
.modal-actions > button:last-child {
.modal-actions button:last-child {
margin-right: 0;
}
.modal.error .modal-title {
color: var(--color-negative);
}
.modal.error .modal-actions button {
color: var(--color-negative);
border-color: var(--color-negative);
}
.modal.error .modal-actions button:hover {
background-color: var(--color-negative);
}
.error {
color: var(--color-negative);
@@ -214,12 +231,12 @@ button.error:hover {
table {
table-layout: fixed;
border-collapse: collapse;
width: 100%;
max-width: 480px;
border-collapse: separate;
border-spacing: 2rem 0;
}
td {
text-align: left;
text-overflow: ellipsis;
}
td.label {
color: var(--color-secondary);
+2
View File
@@ -8,6 +8,7 @@
import Vesting from '@app/base/vesting/Vesting.svelte';
import Register from '@app/base/register/Routes.svelte';
import Orgs from '@app/base/orgs/Routes.svelte';
import Header from '@app/Header.svelte';
const defaultPath = "register";
@@ -44,6 +45,7 @@
<Vesting {config} />
</Route>
<Register {config} {query} />
<Orgs {config} {query} />
</Router>
</div>
</div>
+1
View File
@@ -78,6 +78,7 @@
<div>
<span class="nav">
<a use:link href="/register/">Register</a>
<a use:link href="/orgs/">Orgs</a>
<a use:link href="/vesting/">Vesting</a>
</span>
+32 -7
View File
@@ -1,7 +1,14 @@
<script lang="typescript">
export let floating = false;
import { createEventDispatcher } from 'svelte';
let className = floating ? "modal-floating" : "";
export let floating = false;
export let error = null;
let dispatch = createEventDispatcher();
const onClose = () => {
dispatch('close');
};
</script>
<style>
@@ -29,16 +36,34 @@
<div class="modal-overlay"></div>
{/if}
<div class={className}>
<div class="modal">
<div class:modal-floating={floating}>
<div class="modal" class:error={error}>
<div class="modal-title">
<slot name="title"></slot>
{#if error}
Error
{:else}
<slot name="title"></slot>
{/if}
</div>
<div class="modal-body">
<slot name="body"></slot>
{#if error}
{#if error === Object(error) && error.message}
<strong>Error:</strong> {error.message}
{:else}
{error}
{/if}
{:else}
<slot name="body"></slot>
{/if}
</div>
<div class="modal-actions">
<slot name="actions"></slot>
{#if error}
<button on:click={onClose}>
Dismiss
</button>
{:else}
<slot name="actions"></slot>
{/if}
</div>
</div>
</div>
+60
View File
@@ -0,0 +1,60 @@
<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';
export let config;
export let owner;
enum State {
Idle,
Waiting,
Success,
}
let state = State.Idle;
let error = null;
const dispatch = createEventDispatcher();
const createOrg = async () => {
state = State.Waiting;
console.log("creating org");
try {
let tx = await Org.create(owner, config);
let receipt = await tx.wait();
console.log(receipt);
let org = Org.fromReceipt(receipt);
console.log(org);
state = State.Success;
} catch (e) {
state = State.Idle;
console.error(e);
error = e;
}
};
</script>
<Modal floating {error} on:close>
<span slot="title">
Create an Org
</span>
<span slot="body">
<table>
<tr><td class="label">Owner</td><td>{owner}</td></tr>
</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>
</Modal>
+42
View File
@@ -0,0 +1,42 @@
import * as ethers from 'ethers';
import type { TransactionReceipt, TransactionResponse } from '@ethersproject/providers';
import type { ContractReceipt } from '@ethersproject/contracts';
import type { Config } from '@app/config';
const orgFactoryAbi = [
"function createOrg(address[], uint256) returns (address)",
"event OrgCreated(address, address)",
];
const orgAbi = ["function owner() view returns (address)"];
export class Org {
address: string
safe: string
constructor(address: string, safe: string) {
this.address = address;
this.safe = safe;
}
static fromReceipt(receipt: ContractReceipt): Org {
let event = receipt.events.find(e => e.event === 'OrgCreated');
let address = event.args[0];
let safe = event.args[1];
return new Org(address, safe);
}
static async create(
owner: string,
config: Config,
): Promise<TransactionResponse> {
const orgFactory = new ethers.Contract(
config.orgFactory.address,
orgFactoryAbi,
config.signer
);
return await orgFactory.createOrg([owner], 1);
}
}
+32
View File
@@ -0,0 +1,32 @@
<script lang="typescript">
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import { Router, Link, Route, navigate } from "svelte-routing";
import { ethers } from 'ethers';
import { error } from '@app/error';
import { session } from '@app/session';
import CreateOrg from '@app/base/orgs/CreateOrg.svelte';
enum State {
Idle,
}
export let config;
export let query;
let modal = null;
let state = State.Idle;
$: owner = $session.address;
</script>
<style>
</style>
<main>
<button on:click={() => modal = CreateOrg} disabled={!owner} data-disabled-tooltip="No!" class="secondary">
Create an Org
</button>
</main>
<svelte:component this="{modal}" {owner} {config} on:close={() => modal = null} />
+11
View File
@@ -0,0 +1,11 @@
<script lang="typescript">
import { Route } from "svelte-routing";
import Orgs from '@app/base/orgs/Orgs.svelte';
export let config;
export let query;
</script>
<Route path="/orgs">
<Orgs {config} />
</Route>
+1 -5
View File
@@ -35,16 +35,12 @@
</script>
<style>
.available {
line-height: 1.75em;
padding: 2rem;
}
.domain {
color: var(--color-secondary);
}
</style>
<div class="modal {error ? 'error' : ''}">
<div class="modal" class:error={error}>
{#if error}
<div class="modal-title error">
Transaction failed
+22 -6
View File
@@ -6,14 +6,25 @@ declare global {
}
}
const config = {
export type Config = {
registrar: { address: string },
radToken: { address: string },
orgFactory: { address: string },
provider: ethers.providers.JsonRpcProvider,
signer: ethers.Signer,
};
const addresses = {
homestead: {
registrar: {
address: "0x37723287Ae6F34866d82EE623401f92Ec9013154",
},
radToken: {
address: "0x31c8EAcBFFdD875c74b94b077895Bd78CF1E64A3",
}
},
orgFactory: {
address: "0x0000000000000000000000000000000000000000",
},
},
ropsten: {
registrar: {
@@ -21,27 +32,32 @@ const config = {
},
radToken: {
address: "0x59b5eee36f5fa52400A136Fd4630Ee2bF126a4C0",
}
},
orgFactory: {
address: "0xe30aA5594FFB52B6bF5bbB21eB7e71Ac525bB028",
},
}
};
function isMetamaskInstalled() {
function isMetamaskInstalled(): boolean {
const { ethereum } = window;
return Boolean(ethereum && ethereum.isMetaMask);
}
export async function getConfig() {
export async function getConfig(): Promise<Config> {
if (isMetamaskInstalled()) {
const provider = new ethers.providers.Web3Provider(window.ethereum);
let network = await provider.ready;
let cfg = config[network.name];
let cfg = addresses[network.name];
if (cfg) {
return {
registrar: cfg.registrar,
radToken: cfg.radToken,
orgFactory: cfg.orgFactory,
provider: provider,
signer: provider.getSigner(),
};
} else {
throw `Wrong network: ${network.name}`;