diff --git a/accounts/accounts.go b/accounts/accounts.go
index bf5190ad98f7a157a12e7613a2df38306c4626b7..7a14e4e3e5d5da5d6fb0452bf88122f7b9f38e14 100644
--- a/accounts/accounts.go
+++ b/accounts/accounts.go
@@ -129,6 +129,8 @@ type Wallet interface {
 	// about which fields or actions are needed. The user may retry by providing
 	// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
 	// the account in a keystore).
+	//
+	// This method should return the signature in 'canonical' format, with v 0 or 1
 	SignText(account Account, text []byte) ([]byte, error)
 
 	// SignTextWithPassphrase is identical to Signtext, but also takes a password
diff --git a/accounts/external/backend.go b/accounts/external/backend.go
index 175458e8076eeac56b598884c462bf534ec23abc..12c1d97fa013c273fe5e7c870213812f31c9eacc 100644
--- a/accounts/external/backend.go
+++ b/accounts/external/backend.go
@@ -175,15 +175,20 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
 }
 
 func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
-	var res hexutil.Bytes
+	var signature hexutil.Bytes
 	var signAddress = common.NewMixedcaseAddress(account.Address)
-	if err := api.client.Call(&res, "account_signData",
+	if err := api.client.Call(&signature, "account_signData",
 		accounts.MimetypeTextPlain,
 		&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
 		hexutil.Encode(text)); err != nil {
 		return nil, err
 	}
-	return res, nil
+	if signature[64] == 27 || signature[64] == 28 {
+		// If clef is used as a backend, it may already have transformed
+		// the signature to ethereum-type signature.
+		signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1
+	}
+	return signature, nil
 }
 
 func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {