Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Set records functionality
Alexis Sellier committed 5 years ago
commit c2dc2c597b7ea8c4bd325a3fcca87119c2d516bf
parent 9eed1a047b0761c7a4885cff0759b0d78eb833ba
5 files changed +102 -17
modified src/App.svelte
@@ -27,7 +27,8 @@
    align-items: center;
    flex-direction: column;
    justify-content: center;
-
    height: 24rem;
+
    min-height: 24rem;
+
    padding-top: 6rem;
  }
</style>

modified src/Form.svelte
@@ -1,8 +1,8 @@
<script context="module" lang="typescript">
  export interface Field {
-
    name: string,
-
    value: string | null,
-
    placeholder?: string,
+
    name: string
+
    value: string | null
+
    placeholder?: string
    editable: boolean
  }
</script>
modified src/base/register/Registration.svelte
@@ -1,5 +1,7 @@
<script lang="typescript">
  import { getRegistration } from './registrar';
+
  import { setRecords } from './resolver';
+
  import type { EnsRecord } from './resolver';
  import type { Registration } from './registrar';
  import type { Config } from '@app/config';
  import { session } from '@app/session';
@@ -8,28 +10,49 @@
  import Modal from '@app/Modal.svelte';
  import Form from '@app/Form.svelte';
  import type { Field } from '@app/Form.svelte';
+
  import { assert } from '@app/error';

  export let subdomain: string;
  export let config: Config;

  let editable = false;
  let fields: Field[] = [];
+
  let registration: Registration | null = null;

  const loadRegistration = getRegistration(subdomain, config)
-
    .then(registration => {
-
      if (registration) {
+
    .then(r => {
+
      if (r) {
        fields = [
-
          { name: "address", placeholder: "Not set",
-
            value: registration.address, editable: true },
          { name: "owner", placeholder: "",
-
            value: registration.owner, editable: false },
+
            value: r.owner, editable: false },
+
          { name: "address", placeholder: "Not set",
+
            value: r.address, editable: true },
+
          { name: "avatar", placeholder: "Not set",
+
            value: r.avatar, editable: true },
+
          { name: "twitter", placeholder: "Not set",
+
            value: r.twitter, editable: true },
+
          { name: "github", placeholder: "Not set",
+
            value: r.github, editable: true },
        ];
+
        registration = r;
      }
-
      return registration;
+
      return r;
    });

-
  const save = (event: { detail: Field[] }) => {
-
    console.log("Save", event.detail);
+
  const save = async (event: { detail: Field[] }) => {
+
    assert(registration, "registration was found");
+

+
    const recs: EnsRecord[] = event.detail
+
      .filter(r => r.editable && r.value !== null)
+
      .map(f => {
+
        assert(f.value !== null);
+
        return { name: f.name, value: f.value }
+
      });
+

+
    const tx = await setRecords(subdomain, recs, registration.resolver, config);
+
    // TODO: Disable button and fields
+
    await tx.wait();
+
    // TODO: Reload registration
  };

  $: isOwner = (registration: Registration): boolean => {
modified src/base/register/registrar.ts
@@ -2,6 +2,7 @@
// TODO: Two registration actions with same label
import { ethers } from 'ethers';
import type { BigNumber } from 'ethers';
+
import type { EnsResolver } from '@ethersproject/providers';
import { State, state } from './state';
import * as session from '@app/session';
import { Failure } from '@app/error';
@@ -26,21 +27,36 @@ export interface Registration {
  name: string
  owner: string
  address: string | null
+
  url: string | null
+
  avatar: string | null
+
  twitter: string | null
+
  github: string | null
+
  resolver: EnsResolver
}

export async function getRegistration(label: string, config: Config): Promise<Registration | null> {
-
  if (await registrar(config).available(label)) {
-
    // If the name is available, ie. not registered, we don't return anything.
+
  const name =`${label}.${config.registrar.domain}`;
+
  const resolver = await config.provider.getResolver(name);
+
  if (! resolver) {
    return null;
  }
-
  const name =`${label}.${config.registrar.domain}`;
-
  const address = await config.provider.resolveName(name);
+

  const owner = await getOwner(name, config);
+
  const address = await resolver.getAddress();
+
  const avatar = await resolver.getText('avatar');
+
  const url = await resolver.getText('url');
+
  const twitter = await resolver.getText('vnd.twitter');
+
  const github = await resolver.getText('vnd.github');

  return {
    name,
+
    url,
+
    avatar,
    owner,
-
    address
+
    address,
+
    twitter,
+
    github,
+
    resolver,
  };
}

added src/base/register/resolver.ts
@@ -0,0 +1,45 @@
+
import type { TransactionResponse } from '@ethersproject/providers';
+
import type { EnsResolver } from '@ethersproject/providers';
+
import { ethers } from 'ethers';
+
import type { Config } from '@app/config';
+

+
const resolverAbi = [
+
  "function multicall(bytes[] calldata data) returns(bytes[] memory results)",
+
  "function setAddr(bytes32 node, address addr)",
+
  "function setText(bytes32 node, string calldata key, string calldata value)",
+
];
+

+
export type EnsRecord = { name: string, value: string };
+

+
export async function setRecords(name: string, records: EnsRecord[], resolver: EnsResolver, config: Config): Promise<TransactionResponse> {
+
  const resolverContract = new ethers.Contract(resolver.address, resolverAbi, config.signer);
+
  const node = ethers.utils.namehash(`${name}.${config.registrar.domain}`);
+

+
  let calls = [];
+
  const iface = new ethers.utils.Interface(resolverAbi);
+

+
  for (let r of records) {
+
    switch (r.name) {
+
      case "address":
+
        calls.push(
+
          iface.encodeFunctionData("setAddr", [node, r.value])
+
        );
+
        break;
+
      case "url":
+
      case "avatar":
+
        calls.push(
+
          iface.encodeFunctionData("setText", [node, r.name, r.value])
+
        );
+
        break;
+
      case "github":
+
      case "twitter":
+
        calls.push(
+
          iface.encodeFunctionData("setText", [node, "vnd." + r.name, r.value])
+
        );
+
        break;
+
      default:
+
        console.error(`unknown field "${r.name}"`);
+
    }
+
  }
+
  return resolverContract.multicall(calls);
+
}