Ethers

Integrate your dapp with the JoyID wallet using the JoyID provider API, which enables your dapp to interact with its users' EVM accounts. Axon is a Proof-of-Stake (PoS) and 100% EVM compatible framework that enables developers to build app-chains as Layer 2 of CKB network. We recommend using @joyid/ethers SDK to easily enable your users to connect to their JoyID wallet with Axon app-chains.

To connect, sign and send the transaction with the user's JoyID, we need to do the following steps:

Step 1: Connect JoyID

React
import * as React from "react";
import {JoyIDProvider} from "@joyid/ethers";
import "./style.css";

const JOY_ID_URL = "https://app.joyid.dev";
const AXON_RPC_URL = "https://axon-rpc.internal.joyid.dev";

export default function App() {
  const provider = new JoyIDProvider({
    name: "JoyID EVM Demo",
    logo: "https://fav.farm/๐Ÿ†”",
    joyidAppURL: JOY_ID_URL,
    rpcURL: AXON_RPC_URL,
  });

  const onConnect = async () => {
    try {
      const authData = await provider.connect();
      console.log(`JoyID user info:`, authData);
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div>
      <h1>Hello JoyID!</h1>
      <button onClick={onConnect}>Connect JoyID</button>
    </div>
  );
}
Vue
<template>
  <div id="app">
    <h1>Hello JoyID!</h1>
    <button @click="connect">Connect JoyID</button>
  </div>
</template>

<script>
import { JoyIDProvider } from '@joyid/ethers';

const JOY_ID_URL = 'https://app.joyid.dev'
const AXON_RPC_URL = 'https://axon-rpc.internal.joyid.dev'

export default {
  name: 'App',
  methods: {
      async connect() {
          const provider = new JoyIDProvider({
            name: 'JoyID EVM Demo',
            logo: 'https://fav.farm/๐Ÿ†”',
            joyidAppURL: JOY_ID_URL,
            rpcURL: AXON_RPC_URL,
          }),
          try {
            const authData = await provider.connect();
            console.log(`JoyID user info:`, authData);
          } catch (error) {
            console.log(error);
          }
      },
  },
};
</script>
To learn more about the connect function, please check the API Reference.

Step 2: Sign a challenge

After the connection is complete, we need to add a button element and listen to the click event. When the user clicks the button, we will call the signChallenge function to sign a challenge with the user's JoyID.

Verify the credential before signing a challenge

React
import * as React from "react";
import {JoyIDProvider} from "@joyid/ethers";
import {verifyCredential} from "@joyid/core";
import "./style.css";

const JOY_ID_URL = "https://app.joyid.dev";
const AXON_RPC_URL = "https://axon-rpc.internal.joyid.dev";

export default function App() {
  const [joyidInfo, setJoyidInfo] = React.useState(null);
  const [challenge, setChallenge] = React.useState("Sign this for me");
  const provider = new JoyIDProvider({
    name: "JoyID EVM Demo",
    logo: "https://fav.farm/๐Ÿ†”",
    joyidAppURL: JOY_ID_URL,
    rpcURL: AXON_RPC_URL,
  });

  const onConnect = async () => {
    try {
      const authData = await provider.connect();
      setJoyidInfo(authData);
    } catch (error) {
      console.log(error);
    }
  };
  const onSign = async () => {
    const {keyType, address, pubkey, alg} = joyidInfo;
    if (keyType === "main_session_key" || keyType === "sub_session_key") {
      const isValid = await verifyCredential(pubkey, address, keyType, alg);
      if (!isValid) {
        alert("Your key is expired, please re-authenticate with JoyID");
        return;
      }
    }
    const signer = provider.getSigner(authData.ethAddress);
    const res = await signer.signChallenge(challenge());
    if (res) {
      console.log(`Sign message result: ${res}`);
    }
  };
  return (
    <div>
      <h1>Hello JoyID!</h1>
      {joyidInfo ? null : <button onClick={onConnect}>Connect JoyID</button>}
      {joyidInfo ? (
        <div>
          <textarea value={challenge} onChange={(e) => setChallenge(e.target.value)} />
          <button onClick={onSign}>Sign With JoyID</button>
        </div>
      ) : null}
    </div>
  );
}
Vue
<template>
  <div id="app">
    <h1>Hello JoyID!</h1>
    <button @click="connect" v-if="!joyidInfo">Connect JoyID</button>
    <div v-else>
      <textarea v-model="challenge" />
      <button @click="sign">Sign With JoyID</button>
    </div>
  </div>
</template>

<script>
import {JoyIDProvider} from "@joyid/ethers";
import {verifyCredential} from "@joyid/core";

const JOY_ID_URL = "https://app.joyid.dev";
const AXON_RPC_URL = "https://axon-rpc.internal.joyid.dev";

export default {
  name: "App",
  data() {
    return {
      joyidInfo: null,
      challenge: "Sign this for me",
    };
  },
  methods: {
    getProvider() {
      return new JoyIDProvider({
        name: "JoyID EVM Demo",
        logo: "https://fav.farm/๐Ÿ†”",
        joyidAppURL: JOY_ID_URL,
        rpcURL: AXON_RPC_URL,
      });
    },
    async connect() {
      try {
        const provider = this.getProvider();
        const authData = await provider.connect();
        this.joyidInfo = authData;
      } catch (error) {
        console.log(error);
      }
    },
    async sign() {
      const {keyType, address, pubkey, alg} = this.joyidInfo;
      if (keyType === "main_session_key" || keyType === "sub_session_key") {
        const isValid = await verifyCredential(pubkey, address, keyType, alg);
        if (!isValid) {
          alert("Your key is expired, please re-authenticate with JoyID");
          return;
        }
      }
      const provider = this.getProvider();
      const signer = provider.getSigner(this.joyidInfo.ethAddress);
      const res = await signer.signChallenge(this.challenge);
      if (res) {
        console.log(`Sign message result: ${res}`);
      }
    },
  },
};
</script>
To learn more about the signChallenge function, please check the API Reference.

challenge vs. message

Step 3: Sign and Send a Transaction

After the connection is complete, we need to add a button element and listen to the click event. When the user clicks the button, we will call the signTransaction and eth_sendRawTransaction to sign and send a transaction or call the sendTransaction function to send a transaction directly with the user's JoyID.

React
import * as React from "react";
import {JoyIDProvider} from "@joyid/ethers";
import {parseEther} from "ethers/lib/utils";
import "./style.css";

const JOY_ID_URL = "https://app.joyid.dev";
const AXON_RPC_URL = "https://axon-rpc.internal.joyid.dev";

export default function App() {
  const [joyidInfo, setJoyidInfo] = React.useState(null);
  const [toAddress, setToAddress] = React.useState("0xA6eBeCE9938C3e1757bE3024D2296666d6F8Fc49");
  const [amount, setAmount] = React.useState("0.01");
  const provider = new JoyIDProvider({
    name: "JoyID EVM Demo",
    logo: "https://fav.farm/๐Ÿ†”",
    joyidAppURL: JOY_ID_URL,
    rpcURL: AXON_RPC_URL,
  });

  const onConnect = async () => {
    try {
      const authData = await provider.connect();
      setJoyidInfo(authData);
    } catch (error) {
      console.log(error);
    }
  };

  const onSend = async () => {
    const signer = provider.getSigner(joyidInfo.ethAddress);
    // There are two ways to implement the signing and sending of transactions, the following is way one:
    const signedTx = await signer.signTransaction({
      to: toAddress,
      from: joyidInfo.ethAddress,
      value: parseEther(amount).toString(),
    });
    try {
      const txHash = await provider.send("eth_sendRawTransaction", [signedTx]);
      console.log(`txHash: ${txHash}`);
    } catch (e) {
      console.error(e);
    }
    // The following is way two:
    // try {
    //   const tx = await signer.sendTransaction({
    //     to: toAddress,
    //     from: joyidInfo.ethAddress,
    //     value: parseEther(amount).toString(),
    //   })
    //   console.log(`txHash: ${txHash}`)
    // } catch (e) {
    //   console.error(e)
    // }
  };
  return (
    <div>
      <h1>Hello JoyID!</h1>
      {joyidInfo ? null : <button onClick={onConnect}>Connect JoyID</button>}
      {joyidInfo ? (
        <div>
          <textarea value={toAddress} onChange={(e) => setToAddress(e.target.value)} />
          <textarea value={amount} onChange={(e) => setAmount(e.target.value)} />
          <button onClick={onSend}>Send</button>
        </div>
      ) : null}
    </div>
  );
}
Vue
<template>
  <div id="app">
    <h1>Hello JoyID!</h1>
    <button @click="connect" v-if="!joyidInfo">Connect JoyID</button>
    <div v-else>
      <textarea v-model="toAddress" />
      <textarea v-model="amount" />
      <button @click="send">Send</button>
    </div>
  </div>
</template>

<script>
import {JoyIDProvider} from "@joyid/ethers";
import {parseEther} from "ethers/lib/utils";

const JOY_ID_URL = "https://app.joyid.dev";
const AXON_RPC_URL = "https://axon-rpc.internal.joyid.dev";

export default {
  name: "App",
  data() {
    return {
      joyidInfo: null,
      toAddress: "0xA6eBeCE9938C3e1757bE3024D2296666d6F8Fc49",
      amount: "0.01",
    };
  },
  methods: {
    getProvider() {
      return new JoyIDProvider({
        name: "JoyID EVM Demo",
        logo: "https://fav.farm/๐Ÿ†”",
        joyidAppURL: JOY_ID_URL,
        rpcURL: AXON_RPC_URL,
      });
    },
    async connect() {
      try {
        const provider = this.getProvider();
        const authData = await provider.connect();
        this.joyidInfo = authData;
      } catch (error) {
        console.log(error);
      }
    },
    async send() {
      const provider = this.getProvider();
      const signer = provider.getSigner(this.joyidInfo.ethAddress);
      // There are two ways to implement the signing and sending of transactions, the following is way one:
      const signedTx = await signer.signTransaction({
        to: this.toAddress,
        from: this.joyidInfo.ethAddress,
        value: parseEther(this.amount).toString(),
      });
      try {
        const txHash = await provider.send("eth_sendRawTransaction", [signedTx]);
        console.log(`txHash: ${txHash}`);
      } catch (e) {
        console.error(e);
      }

      // The following is way two:
      // try {
      //   const tx = await signer.sendTransaction({
      //     to: this.toAddress,
      //     from: this.joyidInfo.ethAddress,
      //     value: parseEther(this.amount).toString(),
      //   })
      //   console.log(`txHash: ${txHash}`)
      // } catch (e) {
      //   console.error(e)
      // }
    },
  },
};
</script>

signTransaction vs. sendTransaction

To learn more about the signTransaction function, please check the API Reference.

Try it out

To learn more about the ethers demo, please check the react-ethers and vue-ethers