Skip to content
This repository has been archived by the owner on Apr 27, 2022. It is now read-only.

Added TypedDataV3Signer #686

Merged
merged 1 commit into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export * from "./proxy";
export * from "./reader";
export * from "./settlement";
export * from "./sign";
export * from "./signers";
export * from "./swap";
export * from "./vault";
export * from "./types/ethers";
129 changes: 129 additions & 0 deletions src/ts/signers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { _TypedDataEncoder } from "@ethersproject/hash";
import type { JsonRpcProvider } from "@ethersproject/providers";
import { ethers, Signer } from "ethers";

import {
isJsonRpcProvider,
TypedDataDomain,
TypedDataSigner,
TypedDataTypes,
} from "./types/ethers";

/**
* Wrapper around a TypedDataSigner Signer object that implements `_signTypedData` using
* `eth_signTypedData_v3` instead of `eth_signTypedData_v4`.
*
* Takes a Signer instance on creation.
* All other Signer methods are proxied to initial instance.
*/
export class TypedDataV3Signer implements TypedDataSigner {
signer: Signer;
provider: JsonRpcProvider;
_isSigner = true;

constructor(signer: Signer) {
this.signer = signer;

if (!signer.provider) {
throw new Error("Signer does not have a provider set");
}
if (!isJsonRpcProvider(signer.provider)) {
throw new Error("Provider must be of type JsonRpcProvider");
}

this.provider = signer.provider;
}

async _signTypedData(
domain: TypedDataDomain,
types: TypedDataTypes,
data: Record<string, unknown>,
): Promise<string> {
const populated = await _TypedDataEncoder.resolveNames(
domain,
types,
data,
(name: string) => {
return this.provider.resolveName(name);
},
);

const payload = _TypedDataEncoder.getPayload(
populated.domain,
types,
populated.value,
);
const msg = JSON.stringify(payload);

const address = await this.getAddress();

// Actual signing
return (await this.provider.send("eth_signTypedData_v3", [
address.toLowerCase(),
msg,
])) as string;
}

// --- start boilerplate proxy methods ---

getAddress(): Promise<string> {
return this.signer.getAddress();
}
signMessage(message: string | ethers.utils.Bytes): Promise<string> {
return this.signer.signMessage(message);
}
signTransaction(
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
): Promise<string> {
return this.signer.signTransaction(transaction);
}
connect(provider: ethers.providers.Provider): ethers.Signer {
return this.signer.connect(provider);
}
getBalance(blockTag?: ethers.providers.BlockTag): Promise<ethers.BigNumber> {
return this.signer.getBalance(blockTag);
}
getTransactionCount(blockTag?: ethers.providers.BlockTag): Promise<number> {
return this.signer.getTransactionCount(blockTag);
}
estimateGas(
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
): Promise<ethers.BigNumber> {
return this.signer.estimateGas(transaction);
}
call(
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
blockTag?: ethers.providers.BlockTag,
): Promise<string> {
return this.signer.call(transaction, blockTag);
}
sendTransaction(
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
): Promise<ethers.providers.TransactionResponse> {
return this.signer.sendTransaction(transaction);
}
getChainId(): Promise<number> {
return this.signer.getChainId();
}
getGasPrice(): Promise<ethers.BigNumber> {
return this.signer.getGasPrice();
}
resolveName(name: string): Promise<string> {
return this.signer.resolveName(name);
}
checkTransaction(
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
): ethers.utils.Deferrable<ethers.providers.TransactionRequest> {
return this.signer.checkTransaction(transaction);
}
populateTransaction(
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
): Promise<ethers.providers.TransactionRequest> {
return this.signer.populateTransaction(transaction);
}
_checkProvider(operation?: string): void {
return this.signer._checkProvider(operation);
}

// --- end boilerplate proxy methods ---
}
10 changes: 10 additions & 0 deletions src/ts/types/ethers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { JsonRpcProvider, Provider } from "@ethersproject/providers";
import type { ethers, Signer } from "ethers";

/**
Expand Down Expand Up @@ -36,3 +37,12 @@ export interface TypedDataSigner extends Signer {
export function isTypedDataSigner(signer: Signer): signer is TypedDataSigner {
return "_signTypedData" in signer;
}

/**
* Checks whether the specified provider is a JSON RPC provider.
*/
export function isJsonRpcProvider(
provider: Provider,
): provider is JsonRpcProvider {
return "send" in provider;
}