Signing Bytes
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:
_76 import {_76 SignBytesFailed,_76 SignBytesResult,_76 Timeout,_76 useConnectedWallet,_76 UserDenied,_76 verifyBytes,_76} from '@terra-money/wallet-provider';_76import React, { useCallback, useState } from 'react';_76_76const TEST_BYTES = Buffer.from('hello'); // resolves to <Buffer 68 65 6c 6c 6f>_76_76export function SignBytesSample() {_76 const [txResult, setTxResult] = useState<SignBytesResult | null>(null);_76 const [txError, setTxError] = useState<string | null>(null);_76 const [verifyResult, setVerifyResult] = useState<string | null>(null);_76_76 const connectedWallet = useConnectedWallet();_76_76 const signBytes = useCallback(async () => {_76 if (!connectedWallet) {_76 return;_76 }_76_76 try {_76 const signedBytes: SignBytesResult = await connectedWallet.signBytes(TEST_BYTES);_76 setTxResult(signedBytes);_76 setTxError(null);_76 const result = verifyBytes(TEST_BYTES, signedBytes.result);_76 setVerifyResult(result ? 'Verified' : 'Verification failed');_76_76 } catch (error) {_76 setTxResult(null);_76 setVerifyResult(null);_76 if (error instanceof UserDenied) {_76 setTxError('User Denied');_76 } else if (error instanceof Timeout) {_76 setTxError('Timeout');_76 } else if (error instanceof SignBytesFailed) {_76 setTxError('Sign Bytes Failed');_76 } else {_76 setTxError(_76 'Unknown Error: ' +_76 (error instanceof Error ? error.message : String(error)),_76 );_76 }_76 }_76 }, [connectedWallet]);_76_76 return (_76 <div>_76 <h1>Sign Bytes Sample</h1>_76_76 {connectedWallet?.availableSignBytes &&_76 !txResult &&_76 !txError &&_76 !verifyResult && (_76 <button onClick={() => signBytes()}>_76 Sign bytes with {connectedWallet.walletAddress}_76 </button>_76 )}_76_76 {txResult && <pre>{JSON.stringify(txResult, null, 2)}</pre>}_76_76 {txError && <pre>{txError}</pre>}_76_76 {verifyResult && <pre>{verifyResult}</pre>}_76_76 {!connectedWallet && <p>Wallet not connected!</p>}_76_76 {connectedWallet && !connectedWallet.availableSignBytes && (_76 <p>This connection does not support signBytes()</p>_76 )}_76 </div>_76 );_76}
You can find this code being used in context on GitHub.
You can view a working sandbox example of bytes signing with Terra Station on codesandbox.io.