///
///
import * as Web3Core from "web3-core";
import { SignedTransaction } from "web3-core";
import * as Web3Eth from "web3-eth";
import { TransactionConfig, TransactionReceipt } from "web3-eth";
import { MessageTypes, TypedMessage } from "@metamask/eth-sig-util";
import * as Eulith from "./index";
export declare module Signing {
/**
* The R/S/V signature used throughout Ethereum.
*
* This is roughly analogous to the web3js interfaces/classes: SignedTransaction, RLPEncodedTransaction, SignatureObject
* This is roughly analogous to the ethereum-js-tx interfaces/classes: BaseTransaction
*
* This class makes it easy to construct the signature from various representations, and to see either the independent R/S/V, or
* the combined RSV (used in some APIs).
*
* SEE https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm (R/S)
* SEE https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages#ecdsa-public-key-recovery-from-signature (V)
*/
class ECDSASignature {
/**
* Validate and convert arguments to a valid ECDSASignature object (with valid hex strings as r/s/v values)
*/
constructor({ r, s, v, rsv }: {
r?: string | Buffer;
s?: string | Buffer;
v?: string | number;
rsv?: string;
});
r: string;
s: string;
v: string;
/**
* Compute the concatenation of the R/S/V values, in a standardized format, that can be compared with string equality.
*/
get rsv(): string;
}
/**
* The most basic signing interface. Can only sign a hash, no other higher level signing APIs (like transactions or typed data)
*/
interface ICryptographicSigner {
/**
* signer address guaranteed a valid checksum address (len=66, hex characters, etc) - 0x3341....
*/
readonly address: string;
/**
* Eulith.Signing.ISigner.prototype.signHash
*
* Take a hash, and sign it, returning the ECDSASignature (r,s,v)
*
* Note: this handles CRYPTOGRAPHIC signing, and doesn't know about the chainId, so the caller needs
* to handle any adjustments to the 'v' value.
*
* Note: Unlike web3.eth.accounts.sign - this takes the digest/hash, as argument and just signs that.
* The caller will typically want to first compute a message digest (aka hash), and hand that to this method.
*/
signHash(hash: Buffer): Promise;
}
/**
* Signer for transaction configs
*/
interface ITransactionSigner {
/**
* signer address guaranteed a valid checksum address (len=66, hex characters, etc) - 0x3341....
*/
readonly address: string;
signTransaction(transactionConfig: TransactionConfig): Promise;
}
/**
* Combines signing and sending for compatibility with some browser based wallets
*/
interface ITransactionSigningSender {
/**
* signer address guaranteed a valid checksum address (len=66, hex characters, etc) - 0x3341....
*/
readonly address: string;
/**
* This has a builtin provider and sends the signed transaction through that provider
*/
sendTransaction(transactionConfig: TransactionConfig): Promise;
}
/**
* EIP-712 Typed Data
*/
type TypedData = TypedMessage;
/**
* Supports EIP-712: Typed structured data hashing and signing - https://eips.ethereum.org/EIPS/eip-712
*/
interface ITypedDataSigner {
/**
* signer address guaranteed a valid checksum address (len=66, hex characters, etc) - 0x3341....
*/
readonly address: string;
signTypedData(typedDataMessage: TypedData): Promise;
}
/**
* Eulith.Signing.SigningService is a high level wrapper on all sorts of Ethereum signing - transaction signing, typed data, hashes, etc.
* NOT ALL facilities will be available for ALL SigningService objects.
*
* There are several kinds of signing in the Ethereum world. At the core of all of them are two basic processes:
* > message digest (creating a canonical standardized summary of a thing to be signed)
* > cryptographic signing
* there are more subtleties here, but this is the gist.
*
* Eulith provides abstract APIs for each kind of signing:
* > ICryptographicSigner
* > ITransactionSigner
* > ITypedDataSigner
*
* Some services support one or more of these. For example, Metamask (generally) supports ITransactionSigner
* and ITypedDataSigner (so ALL but ICryptographicSigner).
*
* But different 'wallets' might support any subset of them.
*
* SigningService is an abstraction that allows you to easily peform whatever KIND of signing you might want to
* do, given whatever kind of building block signing functionality you might start with. SOMETIMES, there may
* be a kind of signing you might want to do with it a SigningService instance configured to not support it.
* You can check properties to see if the kind of signing you want to do will work, or just try to sign, and see
* if you get an exception.
*
* Generally, if you start with a ICryptographicSigner (and a provider) - you can do any kind of signing. If you start
* with a user-interface wallet service, it will depend on that service what sorts of signing it handles.
*
*
* Example Usage:
* With a LocalSigner, and a provider, you can do any sort of signing
*
* const signingService = new SigningService ({cryptographicSigner: new LocalSigner({privateKey: "0x...."}), provider})
* signingService.address; // no problem - ALL signing service objects support this
* signingService.signTransaction(tx); // again - no problem
* signingService.signTypedData(tx); // ''
*
* Example Usage:
* // From react/WAGMI
* const signingService = new SigningService ({typedDataSigner_: {address: "0x333....", signTypedData: signTypedDataFuncFromWagmi}});
* signingService.address; // no problem - ALL signing service objects support this
* signingService.signTypedData(tx); // no problem - calls your signTypedDataFuncFromWagmi
* signingService.signTransaction(tx); // WHOOPS! - NOT gona work - raises exception - because you only passed in a signTypedData handler)
*
* Example Usage:
* // From react/WAGMI
* const signingService = new SigningService ({transactionSigner_: {address: "0x333....", signTransaction: signTransactionFromWAGMI}});
* signingService.address; // no problem - ALL signing service objects support this
* signingService.signTransaction(tx); // Works fine, using argument transaction signer
* signingService.signTypedData(tx); // fails cuz provided transactionSigner
*
* Example Usage:
* const signingService = new SigningService ({cryptographicSigner: kmsSigner, provider})
* signingService.signTransaction(tx); // no problem
* signingService.signTypedData(tx); // anything goes - all the APIs will work
*
* Example Usage:
* const signingService = new SigningService ({cryptographicSigner: kmsSigner, })
* signingService.signTypedData(tx); // no problem
* signingService.signTransaction(tx); // WHOOPSIE - wont work cuz you need a provider to canonicalize your transaction
*
* Historical Note:
*
* The Eulith API used to have a ISigner interface, that amounts to the same thing as the new ICryptographicSigner
* and then we implemented the various sorts of signing in Eulith.Provider and Eulith.Signing.UnsignedTransaction
* using this.
*
* However, for reasons I don't completley follow, direct use of signHash (not really called that in web3js) is frowned
* upon, and instead, APIs like wagmi, metamask etc, provide the derivitive APIs like signTypedData,
* signTransaction and so on.
*
* We wish to be able to levarge metamask signing from the Eulith library, in some cases. But also, wish to be able
* to easily access these other kinds of signing from APIs that only provide cryptogrpahic signing.
*
* SigningService bridges that gap. It provides an API which gives access to these various high level signing services
* in a way that can be used through the Eulith client library framework. It carries with it, the low level peices its
* composed of, and can easily compose the higher levle signing services from the crytpograhic service.
*
* So serves as a lingua-franca within the Eulith client library framework for signing services (thus the name).
*/
class SigningService {
private readonly address_;
private transactionSigner_?;
private transactionSigningSender_?;
private readonly cryptographicSigner_?;
private typedDataSigner_?;
private readonly provider_;
/**
* Thin wrapper around constructor, for backwards compatibility.
*/
static assure(signer: ICryptographicSigner, provider?: Eulith.Provider | Eulith.Web3): SigningService;
/**
* At least one of transactionSigner, cryptographicSigner, and typedDataSigner MUST be provided.
* More MAY be provided, but if so, they must agree on the 'address' property.
*
* Provider argument is optional. But - if provided, along with cryptographicSigner, and missing other signers, it can be used
* to implement transactionSigner functionality.
*/
constructor({ transactionSigner, transactionSigningSender, cryptographicSigner, typedDataSigner, provider }: {
transactionSigner?: ITransactionSigner;
transactionSigningSender?: ITransactionSigningSender;
cryptographicSigner?: ICryptographicSigner;
typedDataSigner?: ITypedDataSigner;
provider?: Eulith.Provider | Eulith.Web3;
});
get address(): string;
get transactionSigner(): ITransactionSigner;
get transactionSigningSender(): ITransactionSigningSender;
get cryptographicSigner(): ICryptographicSigner;
get typedDataSigner(): ITypedDataSigner;
signHash(hash: Buffer): Promise;
signTransaction(transactionConfig: TransactionConfig): Promise;
/**
* Takes the argument transaction, and signs it (which may involve canonicalization and filling in defaults).
* and then sends the message on the associated provider with this signer
*/
sendTransaction(transactionConfig: TransactionConfig): Promise;
/**
* Signs and sends the transaction, then waits for confirmation and returns a receipt
*/
sendTransactionAndWait(transactionConfig: TransactionConfig, timeoutInMS?: number): Promise;
/**
* Signs EIP-712 TypedData
*/
signTypedData(typedDataMessage: TypedData): Promise;
}
/**
* Uses plaintext private key to perform signatures locally.
*
* Note: this is a terrible idea in production.
*/
class LocalSigner implements ICryptographicSigner {
constructor({ privateKey }: {
privateKey?: string;
});
/**
* Returns signer's address
*/
get address(): string;
/**
* Signs hash of data
*/
signHash(hash: Buffer): Promise;
private privateKey_;
private account_;
}
/**
* Eulith.Signing.hashMessage
*
* Take any string (interpreted as a BLOB made up of UTF-8 bytes) or Buffer (BLOB) as argument, and produce a hash, suitable for use with Ethereum signing.
*
* Roughly this consists of prepending 'Ethereum Signed Message', and running the keccak256 digest algorithm on the result.
*/
function hashMessage(message: string | Buffer): Buffer;
/**
* Recovers the signer's address of the provided raw transaction
*/
function recoverTransactionSigner(rawTransaction: string): string;
/**
* Recovers the signer's address of the provided message and signature string
*/
function recoverSignerAddress(message: string, rsvSignature: string): string;
/**
* Recovers the signer's address of the provided EIP-712 TypedData and signature string
*/
function recoverTypedDataSignature(typedDataMessage: TypedData, rsvSignature: string): string;
class UnsignedTransaction implements Web3Eth.TransactionConfig {
from?: string | number;
to?: string;
value?: number | string;
gas?: number | string;
gasPrice?: number | string;
maxPriorityFeePerGas?: number | string;
maxFeePerGas?: number | string;
data?: string;
nonce?: number;
chainId?: number;
common?: Web3Eth.Common;
chain?: string;
hardfork?: string;
constructor(from: Partial);
/**
* Shorthand for Eulith.Signing.SigningService.assure(signer, provider).sendTransactionAndWait(this, timeoutInMS);
*/
signAndSendAndWait(signer: Eulith.Signing.SigningService | Eulith.Signing.ICryptographicSigner, provider: Eulith.Provider, timeoutInMS?: number): Promise;
/**
* Takes this UnsignedTransaction object, a signer, and provider (to canonicalize), and produces a new, signed transaction object
*/
signTransaction({ signer, provider }: {
signer: Eulith.Signing.ICryptographicSigner;
provider: Eulith.Provider;
}): Promise;
/**
* Take this unsigned, and potentially incomplete transaction object, and fill in missing details
* as needed (e.g. gas). This validates, and can throw if there are critical details missing, or that
* cannot be computed (again, such as gas).
*
* This requires a signer as argument, only to provide certain defaults.
*
* This requires a provider as argument, because it (typically) will make a few RPC calls to gather the
* data needed.
*/
canonicalize(signerAddress: string, provider: Eulith.Provider): Promise;
/**
* Hashes and rlp encodes transaction
*/
serialize(): Buffer;
private get asEthereumJSTxTransaction_();
/**
* Take this (possibly incomplete but valid) unsigned message, and compute the hash of the unsigned message (to be signed)
*/
private computeUnsignedHash;
}
}