Ethers

Note that @joyid/ethers only supports Ethers.js v5.

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