Skip to content

Commit

Permalink
PRO-2222 - SignTypedData_v4 (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
vignesha22 authored May 14, 2024
1 parent 53d419b commit be9442d
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 15 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Changelog
## [1.8.1] - 2024-05-14
### New
- Added signTypedData method to all providers supported
### Bug Fix
- Sign message rpc parameters should be in the correct order according to the rpc documentation msg params should always be first

## [1.8.0] - 2024-04-29
### Breaking changes
- Removed (deprecated) networks Goerli, Op Goerli, Arb Goerli, Mumbai, Klaytn and Mantle Testnet (Goerli)
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@etherspot/prime-sdk",
"version": "1.8.0",
"version": "1.8.1",
"description": "Etherspot Prime (Account Abstraction) SDK",
"keywords": [
"ether",
Expand Down
6 changes: 5 additions & 1 deletion src/sdk/base/BaseAccountAPI.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ethers, BigNumber, BigNumberish } from 'ethers';
import { ethers, BigNumber, BigNumberish, TypedDataField } from 'ethers';
import { BehaviorSubject } from 'rxjs';
import { Provider } from '@ethersproject/providers';
import { EntryPoint, EntryPoint__factory, INonceManager, INonceManager__factory } from '../contracts';
Expand Down Expand Up @@ -508,4 +508,8 @@ export abstract class BaseAccountAPI {
}
return null;
}

async signTypedData(types: TypedDataField[], message: any) {
return this.services.walletService.signTypedData(types, message, this.accountAddress);
}
}
9 changes: 8 additions & 1 deletion src/sdk/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { Factory, PaymasterApi, SdkOptions } from './interfaces';
import { Network } from "./network";
import { BatchUserOpsRequest, Exception, getGasFee, onRampApiKey, openUrl, UserOpsRequest } from "./common";
import { BigNumber, BigNumberish, ethers, providers } from 'ethers';
import { BigNumber, BigNumberish, ethers, providers, TypedDataField } from 'ethers';
import { Networks, onRamperAllNetworks } from './network/constants';
import { UserOperationStruct } from './contracts/account-abstraction/contracts/core/BaseAccount';
import { EtherspotWalletAPI, HttpRpcClient, VerifyingPaymasterAPI } from './base';
Expand Down Expand Up @@ -244,6 +244,13 @@ export class PrimeSdk {
return this.bundler.sendUserOpToBundler(signedUserOp);
}

async signTypedData(
DataFields: TypedDataField[],
message: any
) {
return this.etherspotWallet.signTypedData(DataFields, message);
}

async getNativeBalance() {
if (!this.etherspotWallet.accountAddress) {
await this.getCounterFactualAddress();
Expand Down
3 changes: 3 additions & 0 deletions src/sdk/wallet/providers/dynamic.wallet-provider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NetworkNames, prepareNetworkName } from '../../network';
import { prepareAddress, UniqueSubject } from '../../common';
import { WalletProvider } from './interfaces';
import { TypedDataField } from 'ethers';

export abstract class DynamicWalletProvider implements WalletProvider {
readonly address$ = new UniqueSubject<string>();
Expand All @@ -20,6 +21,8 @@ export abstract class DynamicWalletProvider implements WalletProvider {

abstract signMessage(message: any): Promise<string>;

abstract signTypedData(typedData: TypedDataField[], message: any, accountAddress: string): Promise<string>;

protected setAddress(address: string): void {
this.address$.next(prepareAddress(address));
}
Expand Down
3 changes: 2 additions & 1 deletion src/sdk/wallet/providers/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BytesLike, Wallet } from 'ethers';
import { BytesLike, TypedDataField, Wallet } from 'ethers';
import type UniversalProvider from '@walletconnect/universal-provider';
import { UniqueSubject } from '../../common';
import { NetworkNames } from '../../network';
Expand All @@ -12,6 +12,7 @@ export interface WalletProvider {
readonly networkName$?: UniqueSubject<NetworkNames>;

signMessage(message: BytesLike): Promise<string>;
signTypedData(typedData: TypedDataField[], message: any, accountAddress: string): Promise<string>;
}

export interface Web3Provider {
Expand Down
6 changes: 5 additions & 1 deletion src/sdk/wallet/providers/key.wallet-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Wallet, BytesLike } from 'ethers';
import { Wallet, BytesLike, TypedDataField } from 'ethers';
import { WalletProvider } from './interfaces';

export class KeyWalletProvider implements WalletProvider {
Expand All @@ -18,4 +18,8 @@ export class KeyWalletProvider implements WalletProvider {
async signMessage(message: BytesLike): Promise<string> {
return this.wallet.signMessage(message);
}

async signTypedData(typedData: TypedDataField[], message: any, accountAddress: string): Promise<string> {
throw new Error('Not supported in this connectedProvider');
}
}
44 changes: 42 additions & 2 deletions src/sdk/wallet/providers/meta-mask.wallet-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BytesLike } from 'ethers';
import { BytesLike, TypedDataField } from 'ethers';
import { toHex } from '../../common';
import { DynamicWalletProvider } from './dynamic.wallet-provider';

Expand Down Expand Up @@ -52,11 +52,51 @@ export class MetaMaskWalletProvider extends DynamicWalletProvider {

async signMessage(message: BytesLike): Promise<string> {
return this.sendRequest('personal_sign', [
this.address, //
toHex(message),
this.address, //
]);
}

async signTypedData(typedData: TypedDataField[], message: any, accountAddress: string): Promise<string> {
const chainId = await this.sendRequest<string>('eth_chainId');
const domainSeparator = {
name: "EtherspotWallet",
version: "2.0.0",
chainId: chainId,
verifyingContract: accountAddress
};
let signature = await this.sendRequest('eth_signTypedData_v4', [
this.address,
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"message": typedData
},
"primaryType": "message",
"domain": domainSeparator,
"message": message
}
])
return signature;
}

protected async connect(): Promise<void> {
const { ethereum } = window;

Expand Down
45 changes: 44 additions & 1 deletion src/sdk/wallet/providers/wallet-connect-2.wallet-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BytesLike } from 'ethers';
import { BytesLike, TypedDataField } from 'ethers';
import { toHex } from '../../common';
import { DynamicWalletProvider } from './dynamic.wallet-provider';
import { EthereumProvider } from './interfaces';
Expand Down Expand Up @@ -38,6 +38,49 @@ export class WalletConnect2WalletProvider extends DynamicWalletProvider {
return typeof response === 'string' ? response : null;
}

async signTypedData(typedData: TypedDataField[], message: any, accountAddress: string): Promise<string> {

const domainSeparator = {
name: "EtherspotWallet",
version: "2.0.0",
chainId: this.provider.chainId,
verifyingContract: accountAddress
};
const signature = await this.provider.signer.request({
method: 'eth_signTypedData_v4',
params: [
this.address,
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"message": typedData
},
"primaryType": "message",
"domain": domainSeparator,
"message": message
}
]
})
return typeof signature === 'string' ? signature : null;
}

protected updateSessionHandler(error: Error, payload: { params: { accounts: string[]; chainId: number } }): void {
let address: string = null;
let chainId: number = null;
Expand Down
6 changes: 5 additions & 1 deletion src/sdk/wallet/providers/wallet-connect.wallet-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BytesLike } from 'ethers';
import { BytesLike, TypedDataField } from 'ethers';
import { toHex } from '../../common';
import { DynamicWalletProvider } from './dynamic.wallet-provider';
import { WalletConnectConnector } from './interfaces';
Expand Down Expand Up @@ -42,6 +42,10 @@ export class WalletConnectWalletProvider extends DynamicWalletProvider {
return response || null;
}

async signTypedData(typedData: TypedDataField[], message: any, accountAddress: string): Promise<string> {
throw new Error('Not supported on this provider')
}

protected updateSessionHandler(error: Error, payload: { params: { accounts: string[]; chainId: number } }): void {
let address: string = null;
let chainId: number = null;
Expand Down
44 changes: 42 additions & 2 deletions src/sdk/wallet/providers/web3.wallet-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BytesLike } from 'ethers';
import { BytesLike, TypedDataField } from 'ethers';
import { prepareAddress, toHex } from '../../common';
import { NetworkNames, prepareNetworkName } from '../../network';
import { Web3Provider } from './interfaces';
Expand Down Expand Up @@ -51,13 +51,53 @@ export class Web3WalletProvider extends DynamicWalletProvider {
return this.sendRequest(
'personal_sign',
[
this.address, //
toHex(message),
this.address, //
],
this.address,
);
}

async signTypedData(typedData: TypedDataField[], message: any, accountAddress: string): Promise<string> {
const chainId = await this.sendRequest<string>('eth_chainId');
const domainSeparator = {
name: "EtherspotWallet",
version: "2.0.0",
chainId: chainId,
verifyingContract: accountAddress
};
let signature = await this.sendRequest('eth_signTypedData_v4', [
this.address,
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"message": typedData
},
"primaryType": "message",
"domain": domainSeparator,
"message": message
}
])
return signature;
}

protected async sendRequest<T = any>(method: string, params: any[] = [], from?: string): Promise<T> {
return new Promise<T>((resolve, reject) => {
const id = Date.now();
Expand Down
42 changes: 41 additions & 1 deletion src/sdk/wallet/providers/web3eip1193.wallet-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BytesLike } from 'ethers';
import { BytesLike, TypedDataField } from 'ethers';
import { prepareAddress, toHex } from '../../common';
import { NetworkNames, prepareNetworkName } from '../../network';
import { Web3eip1193Provider } from './interfaces';
Expand Down Expand Up @@ -50,6 +50,46 @@ export class Web3eip1193WalletProvider extends DynamicWalletProvider {
return this.sendRequest('personal_sign', [toHex(message), this.address]);
}

async signTypedData(typedData: TypedDataField[], message: any, accountAddress: string): Promise<string> {
const chainId = await this.sendRequest<string>('eth_chainId');
const domainSeparator = {
name: "EtherspotWallet",
version: "2.0.0",
chainId: chainId,
verifyingContract: accountAddress
};
let signature = await this.sendRequest('eth_signTypedData_v4', [
this.address,
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"message": typedData
},
"primaryType": "message",
"domain": domainSeparator,
"message": message
}
])
return signature;
}

protected async sendRequest<T = any>(method: string, params: any[] = []): Promise<T> {
try {
const result = await this.web3.request({
Expand Down
6 changes: 5 additions & 1 deletion src/sdk/wallet/wallet.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { BytesLike, ethers, providers, Wallet as EtherWallet } from 'ethers';
import { BytesLike, ethers, providers, Wallet as EtherWallet, TypedDataField } from 'ethers';
import { Service, ObjectSubject } from '../common';
import { WalletProvider, WalletProviderLike, KeyWalletProvider, WalletLike } from './providers';
import { Wallet, WalletOptions } from './interfaces';
Expand Down Expand Up @@ -45,6 +45,10 @@ export class WalletService extends Service {
return this.provider ? this.provider.signMessage(message) : null;
}

async signTypedData(types: TypedDataField[], message: any, accountAddress: string): Promise<string> {
return this.provider ? this.provider.signTypedData(types, message, accountAddress) : null;
}

protected switchWalletProvider(providerLike: WalletProviderLike): void {
let provider: WalletProvider = null;
if (providerLike) {
Expand Down

0 comments on commit be9442d

Please sign in to comment.