Signing Bytes
Wallet-Provider is deprecated. Use Wallet Kit instead.
You can sign arbitrary bytes with Wallet Provider in a React-based web application. This action is useful for verifying account ownership without having to post a transaction to the chain and is commonly used as a form of simple user authentication.
Not using React? Use the wallet-controller instead.
The Wallet Provider comes with a useConnectedWallet
hook, which lets you trigger actions from a Terra wallet that's connected to the web page. The connectedWallet
object includes a .signBytes()
method, which prompts the user to sign the data and then returns an object of type SignBytesResult
. The returned SignBytesResult
object contains the address of the signer and the signed data.
The verifyBytes
function then compares the original TEST_BYTES
against the signature and public key pairing returned by the SignBytesResult
. If verifyBytes
returns true
, then the account is owned by the connected wallet. Likewise, if verifyBytes
returns false
, then the account is not owned by the connected wallet. In this way, the owner of the associated wallet is verified without having to produce an on-chain action or pay gas fees.
You can see how the verifyBytes
function works
here.
Wallet Provider also supplies useful error types that can be used with a catch
statement to notify the user whether or not the signing was successful:
_96import {_96 SignBytesFailed,_96 SignBytesResult,_96 Timeout,_96 useConnectedWallet,_96 UserDenied,_96 verifyBytes,_96} from '@terra-money/wallet-provider';_96import React, { useCallback, useState } from 'react';_96_96const TEST_BYTES = Buffer.from('hello'); // resolves to <Buffer 68 65 6c 6c 6f>_96_96export function SignBytesSample() {_96 const [txResult, setTxResult] = useState<SignBytesResult | null>(null);_96 const [txError, setTxError] = useState<string | null>(null);_96 const [verifyResult, setVerifyResult] = useState<string | null>(null);_96 const chainID = 'phoenix-1';_96_96 const connectedWallet = useConnectedWallet();_96_96 const signBytes = useCallback(() => {_96 if (!connectedWallet) {_96 return;_96 }_96_96 setTxResult(null);_96 setTxError(null);_96 setVerifyResult(null);_96_96 connectedWallet_96 .signBytes(TEST_BYTES)_96 .then((nextSignBytesResult: SignBytesResult) => {_96 setTxResult(nextSignBytesResult);_96 setTxError(null);_96_96 const result = verifyBytes(TEST_BYTES, nextSignBytesResult.result);_96 setVerifyResult(result ? 'Verify OK' : 'Verify failed');_96 })_96 .catch((error) => {_96 setTxResult(null);_96 setVerifyResult(null);_96_96 if (error instanceof UserDenied) {_96 setTxError('User Denied');_96 } else if (error instanceof Timeout) {_96 setTxError('Timeout');_96 } else if (error instanceof SignBytesFailed) {_96 setTxError('Sign Bytes Failed');_96 } else {_96 setTxError(_96 'Unknown Error: ' +_96 (error instanceof Error ? error.message : String(error)),_96 );_96 }_96 });_96 }, [connectedWallet]);_96_96 return (_96 <div>_96 <h1>Sign Bytes Sample</h1>_96_96 {connectedWallet?.availableSignBytes &&_96 !txResult &&_96 !txError &&_96 !verifyResult && (_96 <button onClick={() => signBytes()}>_96 Sign bytes with {connectedWallet.addresses[chainID]}_96 </button>_96 )}_96_96 {txResult && <pre>{JSON.stringify(txResult, null, 2)}</pre>}_96_96 {txError && <pre>{txError}</pre>}_96_96 {verifyResult && <pre>{verifyResult}</pre>}_96_96 {(!!txResult || !!txError || !!verifyResult) && (_96 <button_96 onClick={() => {_96 setTxResult(null);_96 setTxError(null);_96 setVerifyResult(null);_96 }}_96 >_96 Clear result_96 </button>_96 )}_96_96 {!connectedWallet && <p>Wallet not connected!</p>}_96_96 {connectedWallet && !connectedWallet.availableSignBytes && (_96 <p>This connection does not support signBytes()</p>_96 )}_96 </div>_96 );_96}
You can find this code being used in context on GitHub.
You can view a working sandbox example of bytes signing with Station on codesandbox.io.