This repository has been archived by the owner on Aug 1, 2022. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: integrate org ENS name registration
Co-Authored-By: Jason Efstathiou <j@jason-e.dev> Co-Authored-By: Rūdolfs Ošiņš <rudolfs@osins.org> Co-Authored-By: Thomas Scholtes <geigerzaehler@axiom.fm> Signed-off-by: Jason Efstathiou <j@jason-e.dev> Signed-off-by: Rūdolfs Ošiņš <rudolfs@osins.org> Signed-off-by: Thomas Scholtes <geigerzaehler@axiom.fm>
- Loading branch information
Showing
28 changed files
with
1,876 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
<!-- | ||
Copyright © 2021 The Radicle Upstream Contributors | ||
This file is part of radicle-upstream, distributed under the GPLv3 | ||
with Radicle Linking Exception. For full terms see the included | ||
LICENSE file. | ||
--> | ||
<script lang="typescript"> | ||
import type * as ethers from "ethers"; | ||
import * as ensResolver from "ui/src/org/ensResolver"; | ||
import * as modal from "ui/src/modal"; | ||
import * as error from "ui/src/error"; | ||
import { unreachable } from "ui/src/unreachable"; | ||
import Intro from "./ConfigureEns/Intro.svelte"; | ||
import LinkOrgToName from "./ConfigureEns/LinkOrgToName.svelte"; | ||
import RegisterName from "./ConfigureEns/RegisterName.svelte"; | ||
import type * as registerName from "./ConfigureEns/RegisterName.svelte"; | ||
import UpdateMetadata from "./ConfigureEns/UpdateMetadata.svelte"; | ||
export let orgAddress: string; | ||
export let registration: ensResolver.Registration | undefined = undefined; | ||
export let safeAddress: string | undefined = undefined; | ||
export let fee: ethers.BigNumber; | ||
type State = | ||
| { type: "intro" } | ||
| { type: "registerName"; currentName: string | undefined } | ||
| { | ||
type: "updateMetadata"; | ||
registration: ensResolver.Registration; | ||
} | ||
| { | ||
type: "linkOrgToName"; | ||
domain: string; | ||
}; | ||
let state: State = ((): State => { | ||
if (registration) { | ||
const currentName = registration.domain.replace( | ||
`.${ensResolver.DOMAIN}`, | ||
"" | ||
); | ||
return { type: "registerName", currentName }; | ||
} else { | ||
return { type: "intro" }; | ||
} | ||
})(); | ||
async function registrationDone(result: registerName.Result) { | ||
let registration: ensResolver.Registration | undefined; | ||
if (result.registration) { | ||
registration = result.registration; | ||
} else { | ||
const domain = `${result.name}.${ensResolver.DOMAIN}`; | ||
// TODO(thomas): handle exception | ||
registration = await ensResolver.getRegistration(domain); | ||
if (!registration) { | ||
throw new error.Error({ | ||
message: "Domain not registered", | ||
details: { domain }, | ||
}); | ||
} | ||
} | ||
state = { | ||
type: "updateMetadata", | ||
registration, | ||
}; | ||
} | ||
function introDone() { | ||
const currentName = registration?.domain.replace( | ||
`.${ensResolver.DOMAIN}`, | ||
"" | ||
); | ||
state = { | ||
type: "registerName", | ||
currentName, | ||
}; | ||
} | ||
function bindUpdateMetadataDone(domain: string) { | ||
return () => { | ||
// Don't show linkOrgToName step if it is already linked. | ||
if (registration?.address?.toLowerCase() === orgAddress.toLowerCase()) { | ||
modal.hide(); | ||
return; | ||
} | ||
state = { | ||
type: "linkOrgToName", | ||
domain, | ||
}; | ||
}; | ||
} | ||
function linkOrgToNameDone() { | ||
modal.hide(); | ||
} | ||
</script> | ||
|
||
{#if state.type === "intro"} | ||
<Intro onSubmit={introDone} {fee} /> | ||
{:else if state.type === "registerName"} | ||
<RegisterName currentName={state.currentName} {fee} {registrationDone} /> | ||
{:else if state.type === "updateMetadata"} | ||
<UpdateMetadata | ||
{orgAddress} | ||
registration={state.registration} | ||
onSubmit={bindUpdateMetadataDone(state.registration.domain)} /> | ||
{:else if state.type === "linkOrgToName"} | ||
<LinkOrgToName | ||
domain={state.domain} | ||
{orgAddress} | ||
{safeAddress} | ||
onSubmit={linkOrgToNameDone} /> | ||
{:else} | ||
{unreachable(state)} | ||
{/if} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<!-- | ||
Copyright © 2021 The Radicle Upstream Contributors | ||
This file is part of radicle-upstream, distributed under the GPLv3 | ||
with Radicle Linking Exception. For full terms see the included | ||
LICENSE file. | ||
--> | ||
<script lang="typescript"> | ||
import { onDestroy } from "svelte"; | ||
import * as svelteStore from "ui/src/svelteStore"; | ||
import * as wallet from "ui/src/wallet"; | ||
export let commitmentBlock: number; | ||
export let minimumCommitmentAge: number; | ||
export let onFinish: () => void; | ||
const walletStore = svelteStore.get(wallet.store); | ||
// There seems to be an off-by-one error in the contract, because if we don't | ||
// wait for that one extra block we get an error saying that the commitment | ||
// isn't old enough. | ||
const requiredBlockCount = minimumCommitmentAge + 1; | ||
let confirmedBlockCount: number = 0; | ||
const onBlock = (currentBlock: number) => { | ||
confirmedBlockCount = currentBlock - commitmentBlock; | ||
if (confirmedBlockCount >= requiredBlockCount) { | ||
onFinish(); | ||
walletStore.provider.off("block", onBlock); | ||
// When we resume a saved commitment, it can happen that more blocks have | ||
// been included than the minimum required amount in the mean time. | ||
// We don't want to overflow the counter that we show to the user. | ||
confirmedBlockCount = requiredBlockCount; | ||
} | ||
}; | ||
walletStore.provider.on("block", onBlock); | ||
onDestroy(() => { | ||
walletStore.provider.off("block", onBlock); | ||
}); | ||
</script> | ||
|
||
<style> | ||
p { | ||
color: var(--color-foreground-level-6); | ||
} | ||
</style> | ||
|
||
<p class="typo-text-bold"> | ||
Confirmed {confirmedBlockCount} out of {requiredBlockCount} blocks. | ||
</p> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<!-- | ||
Copyright © 2021 The Radicle Upstream Contributors | ||
This file is part of radicle-upstream, distributed under the GPLv3 | ||
with Radicle Linking Exception. For full terms see the included | ||
LICENSE file. | ||
--> | ||
<script lang="typescript"> | ||
import type * as registerName from "./RegisterName.svelte"; | ||
import * as ensRegistrar from "ui/src/org/ensRegistrar"; | ||
import * as ensResolver from "ui/src/org/ensResolver"; | ||
import * as error from "ui/src/error"; | ||
import * as modal from "ui/src/modal"; | ||
import * as notification from "ui/src/notification"; | ||
import * as transaction from "ui/src/transaction"; | ||
import { Button, Modal } from "ui/DesignSystem"; | ||
import BlockTimer from "./BlockTimer.svelte"; | ||
let confirmButtonDisabled = true; | ||
export let commitment: ensRegistrar.Commitment; | ||
export let commitmentBlock: number; | ||
export let registrationDone: (result: registerName.Result) => void; | ||
async function register() { | ||
confirmButtonDisabled = true; | ||
const registrationNotification = notification.info({ | ||
message: | ||
"Waiting for you to confirm the registration transaction in your connected wallet", | ||
showIcon: true, | ||
persist: true, | ||
}); | ||
let registrationTx: transaction.ContractTransaction; | ||
try { | ||
registrationTx = await ensRegistrar.register( | ||
commitment.name, | ||
commitment.salt | ||
); | ||
} catch (err) { | ||
confirmButtonDisabled = false; | ||
error.show( | ||
new error.Error({ | ||
message: err.message, | ||
source: err, | ||
}) | ||
); | ||
// Don't advance flow if the user rejected the tx. | ||
return; | ||
} finally { | ||
registrationNotification.remove(); | ||
} | ||
ensRegistrar.clearCommitment(); | ||
transaction.add(transaction.registerEnsName(registrationTx)); | ||
const txNotification = notification.info({ | ||
message: "Waiting for the transaction to be included", | ||
showIcon: true, | ||
persist: true, | ||
}); | ||
try { | ||
await registrationTx.wait(1); | ||
} catch (err) { | ||
confirmButtonDisabled = false; | ||
error.show( | ||
new error.Error({ | ||
message: err.message, | ||
source: err, | ||
}) | ||
); | ||
// Don't advance flow unless we have the tx receipt. | ||
return; | ||
} finally { | ||
txNotification.remove(); | ||
} | ||
notification.info({ | ||
message: `${commitment.name}.${ensResolver.DOMAIN} has been registered with your wallet`, | ||
showIcon: true, | ||
}); | ||
registrationDone({ name: commitment.name, registration: undefined }); | ||
} | ||
</script> | ||
|
||
<Modal emoji="📇" title="Awaiting registration commitment"> | ||
<svelte:fragment slot="description"> | ||
The waiting period is required to ensure another person hasn’t tried to | ||
register the same name. | ||
</svelte:fragment> | ||
|
||
<div style="display: flex; justify-content: center;"> | ||
<BlockTimer | ||
onFinish={() => { | ||
confirmButtonDisabled = false; | ||
}} | ||
{commitmentBlock} | ||
minimumCommitmentAge={commitment.minimumCommitmentAge} /> | ||
</div> | ||
|
||
<svelte:fragment slot="buttons"> | ||
<Button | ||
variant="transparent" | ||
on:click={() => { | ||
modal.hide(); | ||
}}>Cancel</Button> | ||
<Button on:click={register} disabled={confirmButtonDisabled} | ||
>Confirm registration</Button> | ||
</svelte:fragment> | ||
</Modal> |
Oops, something went wrong.