mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-08 21:20:45 +08:00
Scaffolding for orgs
This commit is contained in:
+25
-8
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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} />
|
||||
@@ -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>
|
||||
@@ -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
@@ -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}`;
|
||||
|
||||
Reference in New Issue
Block a user