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.