@imtbl/wallet 2.12.3 → 2.12.4-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.js +24 -32
- package/dist/node/index.cjs +47 -55
- package/dist/node/index.js +26 -34
- package/dist/types/guardian/index.d.ts +1 -2
- package/dist/types/magic/magicTEESigner.d.ts +8 -7
- package/dist/types/types.d.ts +15 -6
- package/dist/types/utils/crypto.d.ts +12 -0
- package/dist/types/zkEvm/personalSign.d.ts +4 -3
- package/dist/types/zkEvm/relayerClient.d.ts +6 -6
- package/dist/types/zkEvm/sequenceCompat.d.ts +60 -30
- package/dist/types/zkEvm/signTypedDataV4.d.ts +6 -3
- package/dist/types/zkEvm/transactionHelpers.d.ts +15 -4
- package/dist/types/zkEvm/types.d.ts +5 -6
- package/dist/types/zkEvm/user/registerZkEvmUser.d.ts +4 -3
- package/dist/types/zkEvm/walletHelpers.d.ts +15 -14
- package/dist/types/zkEvm/zkEvmProvider.d.ts +2 -3
- package/package.json +5 -6
- package/src/guardian/index.ts +3 -3
- package/src/magic/magicTEESigner.ts +11 -24
- package/src/types.ts +16 -6
- package/src/utils/crypto.ts +85 -0
- package/src/utils/string.ts +30 -2
- package/src/zkEvm/personalSign.ts +6 -5
- package/src/zkEvm/relayerClient.ts +11 -11
- package/src/zkEvm/sendDeployTransactionAndPersonalSign.ts +1 -1
- package/src/zkEvm/sequenceCompat.ts +26 -24
- package/src/zkEvm/sessionActivity/sessionActivity.ts +7 -3
- package/src/zkEvm/signEjectionTransaction.ts +1 -1
- package/src/zkEvm/signTypedDataV4.test.ts +125 -0
- package/src/zkEvm/signTypedDataV4.ts +16 -10
- package/src/zkEvm/transactionHelpers.ts +20 -11
- package/src/zkEvm/types.ts +5 -6
- package/src/zkEvm/user/registerZkEvmUser.ts +9 -8
- package/src/zkEvm/walletHelpers.ts +112 -63
- package/src/zkEvm/zkEvmProvider.ts +32 -32
|
@@ -1,35 +1,65 @@
|
|
|
1
1
|
export declare const walletContracts: {
|
|
2
2
|
mainModule: {
|
|
3
|
-
abi:
|
|
4
|
-
type:
|
|
5
|
-
name:
|
|
6
|
-
constant:
|
|
7
|
-
inputs:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
3
|
+
abi: readonly [{
|
|
4
|
+
readonly type: "function";
|
|
5
|
+
readonly name: "nonce";
|
|
6
|
+
readonly constant: true;
|
|
7
|
+
readonly inputs: readonly [];
|
|
8
|
+
readonly outputs: readonly [{
|
|
9
|
+
readonly type: "uint256";
|
|
10
|
+
}];
|
|
11
|
+
readonly payable: false;
|
|
12
|
+
readonly stateMutability: "view";
|
|
13
|
+
}, {
|
|
14
|
+
readonly type: "function";
|
|
15
|
+
readonly name: "readNonce";
|
|
16
|
+
readonly constant: true;
|
|
17
|
+
readonly inputs: readonly [{
|
|
18
|
+
readonly type: "uint256";
|
|
19
|
+
readonly name: "_space";
|
|
20
|
+
}];
|
|
21
|
+
readonly outputs: readonly [{
|
|
22
|
+
readonly type: "uint256";
|
|
23
|
+
}];
|
|
24
|
+
readonly payable: false;
|
|
25
|
+
readonly stateMutability: "view";
|
|
26
|
+
}, {
|
|
27
|
+
readonly type: "function";
|
|
28
|
+
readonly name: "execute";
|
|
29
|
+
readonly constant: false;
|
|
30
|
+
readonly inputs: readonly [{
|
|
31
|
+
readonly components: readonly [{
|
|
32
|
+
readonly type: "bool";
|
|
33
|
+
readonly name: "delegateCall";
|
|
34
|
+
}, {
|
|
35
|
+
readonly type: "bool";
|
|
36
|
+
readonly name: "revertOnError";
|
|
37
|
+
}, {
|
|
38
|
+
readonly type: "uint256";
|
|
39
|
+
readonly name: "gasLimit";
|
|
40
|
+
}, {
|
|
41
|
+
readonly type: "address";
|
|
42
|
+
readonly name: "target";
|
|
43
|
+
}, {
|
|
44
|
+
readonly type: "uint256";
|
|
45
|
+
readonly name: "value";
|
|
46
|
+
}, {
|
|
47
|
+
readonly type: "bytes";
|
|
48
|
+
readonly name: "data";
|
|
49
|
+
}];
|
|
50
|
+
readonly name: "_txs";
|
|
51
|
+
readonly type: "tuple[]";
|
|
52
|
+
}, {
|
|
53
|
+
readonly type: "uint256";
|
|
54
|
+
readonly name: "_nonce";
|
|
55
|
+
}, {
|
|
56
|
+
readonly type: "bytes";
|
|
57
|
+
readonly name: "_signature";
|
|
58
|
+
}];
|
|
59
|
+
readonly outputs: readonly [];
|
|
60
|
+
readonly payable: false;
|
|
61
|
+
readonly stateMutability: "nonpayable";
|
|
62
|
+
}];
|
|
33
63
|
};
|
|
34
64
|
};
|
|
35
65
|
export type SequenceSigner = {
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { Flow } from '@imtbl/metrics';
|
|
2
|
-
import {
|
|
2
|
+
import type { PublicClient } from 'viem';
|
|
3
3
|
import GuardianClient from '../guardian';
|
|
4
|
+
import { TypedDataPayload } from './types';
|
|
4
5
|
import { RelayerClient } from './relayerClient';
|
|
6
|
+
import type { WalletSigner } from '../types';
|
|
5
7
|
export type SignTypedDataV4Params = {
|
|
6
|
-
ethSigner:
|
|
7
|
-
rpcProvider:
|
|
8
|
+
ethSigner: WalletSigner;
|
|
9
|
+
rpcProvider: PublicClient;
|
|
8
10
|
relayerClient: RelayerClient;
|
|
9
11
|
method: string;
|
|
10
12
|
params: Array<any>;
|
|
11
13
|
guardianClient: GuardianClient;
|
|
12
14
|
flow: Flow;
|
|
13
15
|
};
|
|
16
|
+
export declare const transformTypedData: (typedData: string | object, chainId: bigint) => TypedDataPayload;
|
|
14
17
|
export declare const signTypedDataV4: ({ params, method, ethSigner, rpcProvider, relayerClient, guardianClient, flow, }: SignTypedDataV4Params) => Promise<string>;
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { Flow } from '@imtbl/metrics';
|
|
2
|
-
import {
|
|
2
|
+
import type { PublicClient, Hex } from 'viem';
|
|
3
3
|
import { RelayerClient } from './relayerClient';
|
|
4
4
|
import GuardianClient from '../guardian';
|
|
5
|
+
import type { WalletSigner } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Transaction request type compatible with eth_sendTransaction
|
|
8
|
+
*/
|
|
9
|
+
export interface TransactionRequest {
|
|
10
|
+
to?: string;
|
|
11
|
+
data?: Hex | string | null;
|
|
12
|
+
value?: bigint;
|
|
13
|
+
nonce?: bigint;
|
|
14
|
+
chainId?: bigint | number;
|
|
15
|
+
}
|
|
5
16
|
export type TransactionParams = {
|
|
6
|
-
ethSigner:
|
|
7
|
-
rpcProvider:
|
|
17
|
+
ethSigner: WalletSigner;
|
|
18
|
+
rpcProvider: PublicClient;
|
|
8
19
|
guardianClient: GuardianClient;
|
|
9
20
|
relayerClient: RelayerClient;
|
|
10
21
|
zkEvmAddress: string;
|
|
@@ -24,7 +35,7 @@ export declare const prepareAndSignTransaction: ({ transactionRequest, ethSigner
|
|
|
24
35
|
}) => Promise<{
|
|
25
36
|
signedTransactions: string;
|
|
26
37
|
relayerId: string;
|
|
27
|
-
nonce:
|
|
38
|
+
nonce: bigint;
|
|
28
39
|
}>;
|
|
29
40
|
export declare const prepareAndSignEjectionTransaction: ({ transactionRequest, ethSigner, zkEvmAddress, flow, }: EjectionTransactionParams & {
|
|
30
41
|
transactionRequest: TransactionRequest;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { BigNumberish } from 'ethers';
|
|
2
1
|
import { JsonRpcError } from './JsonRpcError';
|
|
3
2
|
export declare enum RelayerTransactionStatus {
|
|
4
3
|
PENDING = "PENDING",
|
|
@@ -24,19 +23,19 @@ export interface FeeOption {
|
|
|
24
23
|
}
|
|
25
24
|
export interface MetaTransaction {
|
|
26
25
|
to: string;
|
|
27
|
-
value?:
|
|
26
|
+
value?: bigint | null;
|
|
28
27
|
data?: string | null;
|
|
29
|
-
nonce?:
|
|
30
|
-
gasLimit?:
|
|
28
|
+
nonce?: bigint;
|
|
29
|
+
gasLimit?: bigint;
|
|
31
30
|
delegateCall?: boolean;
|
|
32
31
|
revertOnError?: boolean;
|
|
33
32
|
}
|
|
34
33
|
export interface MetaTransactionNormalised {
|
|
35
34
|
delegateCall: boolean;
|
|
36
35
|
revertOnError: boolean;
|
|
37
|
-
gasLimit:
|
|
36
|
+
gasLimit: bigint;
|
|
38
37
|
target: string;
|
|
39
|
-
value:
|
|
38
|
+
value: bigint;
|
|
40
39
|
data: string;
|
|
41
40
|
}
|
|
42
41
|
export interface TypedDataPayload {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { MultiRollupApiClients } from '@imtbl/generated-clients';
|
|
2
2
|
import { Flow } from '@imtbl/metrics';
|
|
3
|
-
import {
|
|
3
|
+
import type { PublicClient } from 'viem';
|
|
4
4
|
import { Auth } from '@imtbl/auth';
|
|
5
|
+
import type { WalletSigner } from '../../types';
|
|
5
6
|
export type RegisterZkEvmUserInput = {
|
|
6
7
|
auth: Auth;
|
|
7
|
-
ethSigner:
|
|
8
|
+
ethSigner: WalletSigner;
|
|
8
9
|
multiRollupApiClients: MultiRollupApiClients;
|
|
9
10
|
accessToken: string;
|
|
10
|
-
rpcProvider:
|
|
11
|
+
rpcProvider: PublicClient;
|
|
11
12
|
flow: Flow;
|
|
12
13
|
};
|
|
13
14
|
export declare function registerZkEvmUser({ auth, ethSigner, multiRollupApiClients, accessToken, rpcProvider, flow, }: RegisterZkEvmUserInput): Promise<string>;
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type PublicClient, type Hex } from 'viem';
|
|
2
2
|
import { MetaTransaction, MetaTransactionNormalised, TypedDataPayload } from './types';
|
|
3
|
+
import type { WalletSigner } from '../types';
|
|
3
4
|
export declare const getNormalisedTransactions: (txs: MetaTransaction[]) => MetaTransactionNormalised[];
|
|
4
|
-
export declare const digestOfTransactionsAndNonce: (nonce:
|
|
5
|
-
export declare const encodedTransactions: (normalisedTransactions: MetaTransactionNormalised[]) =>
|
|
5
|
+
export declare const digestOfTransactionsAndNonce: (nonce: bigint, normalisedTransactions: MetaTransactionNormalised[]) => Hex;
|
|
6
|
+
export declare const encodedTransactions: (normalisedTransactions: MetaTransactionNormalised[]) => Hex;
|
|
6
7
|
/**
|
|
7
|
-
* This helper function is used to coerce the type <
|
|
8
|
+
* This helper function is used to coerce the type <bigint | undefined> to bigint for the
|
|
8
9
|
* getNonce function above.
|
|
9
|
-
* @param
|
|
10
|
-
* @returns
|
|
10
|
+
* @param nonceSpace - An unsigned 256 bit value that can be used to encode a nonce into a distinct space.
|
|
11
|
+
* @returns The passed in nonceSpace or instead initialises the nonce to 0.
|
|
11
12
|
*/
|
|
12
13
|
export declare const coerceNonceSpace: (nonceSpace?: bigint) => bigint;
|
|
13
14
|
/**
|
|
14
15
|
* This helper function is used to encode the nonce into a 256 bit value where the space is encoded into
|
|
15
16
|
* the first 160 bits, and the nonce the remaining 96 bits.
|
|
16
|
-
* @param
|
|
17
|
-
* @param nonce
|
|
18
|
-
* @returns
|
|
17
|
+
* @param nonceSpace - An unsigned 256 bit value that can be used to encode a nonce into a distinct space.
|
|
18
|
+
* @param nonce - Sequential number starting at 0, and incrementing in single steps e.g. 0,1,2,...
|
|
19
|
+
* @returns The encoded value where the space is left shifted 96 bits, and the nonce is in the first 96 bits.
|
|
19
20
|
*/
|
|
20
21
|
export declare const encodeNonce: (nonceSpace: bigint, nonce: bigint) => bigint;
|
|
21
22
|
/**
|
|
@@ -24,10 +25,10 @@ export declare const encodeNonce: (nonceSpace: bigint, nonce: bigint) => bigint;
|
|
|
24
25
|
* wallet address do not. This function overload can be used to invoke transactions in parallel per smart
|
|
25
26
|
* contract wallet if required.
|
|
26
27
|
*/
|
|
27
|
-
export declare const getNonce: (rpcProvider:
|
|
28
|
-
export declare const encodeMessageSubDigest: (chainId: bigint, walletAddress: string, digest: string) =>
|
|
29
|
-
export declare const signMetaTransactions: (metaTransactions: MetaTransaction[], nonce:
|
|
28
|
+
export declare const getNonce: (rpcProvider: PublicClient, smartContractWalletAddress: string, nonceSpace?: bigint) => Promise<bigint>;
|
|
29
|
+
export declare const encodeMessageSubDigest: (chainId: bigint, walletAddress: string, digest: string) => Hex;
|
|
30
|
+
export declare const signMetaTransactions: (metaTransactions: MetaTransaction[], nonce: bigint, chainId: bigint, walletAddress: string, signer: WalletSigner) => Promise<string>;
|
|
30
31
|
export declare const packSignatures: (EOASignature: string, EOAAddress: string, relayerSignature: string) => string;
|
|
31
|
-
export declare const signAndPackTypedData: (typedData: TypedDataPayload, relayerSignature: string, chainId: bigint, walletAddress: string, signer:
|
|
32
|
-
export declare const signERC191Message: (chainId: bigint, payload: string, signer:
|
|
32
|
+
export declare const signAndPackTypedData: (typedData: TypedDataPayload, relayerSignature: string, chainId: bigint, walletAddress: string, signer: WalletSigner) => Promise<string>;
|
|
33
|
+
export declare const signERC191Message: (chainId: bigint, payload: string, signer: WalletSigner, walletAddress: string) => Promise<string>;
|
|
33
34
|
export declare const getEip155ChainId: (chainId: number) => string;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { MultiRollupApiClients } from '@imtbl/generated-clients';
|
|
2
|
-
import { Signer } from 'ethers';
|
|
3
2
|
import { Provider, RequestArguments } from './types';
|
|
4
3
|
import { Auth, TypedEventEmitter } from '@imtbl/auth';
|
|
5
4
|
import { WalletConfiguration } from '../config';
|
|
6
|
-
import { PassportEventMap, User } from '../types';
|
|
5
|
+
import { PassportEventMap, User, WalletSigner } from '../types';
|
|
7
6
|
import GuardianClient from '../guardian';
|
|
8
7
|
export type ZkEvmProviderInput = {
|
|
9
8
|
auth: Auth;
|
|
@@ -11,7 +10,7 @@ export type ZkEvmProviderInput = {
|
|
|
11
10
|
multiRollupApiClients: MultiRollupApiClients;
|
|
12
11
|
passportEventEmitter: TypedEventEmitter<PassportEventMap>;
|
|
13
12
|
guardianClient: GuardianClient;
|
|
14
|
-
ethSigner:
|
|
13
|
+
ethSigner: WalletSigner;
|
|
15
14
|
user: User | null;
|
|
16
15
|
sessionActivityApiUrl: string | null;
|
|
17
16
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imtbl/wallet",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.4-alpha.1",
|
|
4
4
|
"description": "Wallet SDK for Immutable",
|
|
5
5
|
"author": "Immutable",
|
|
6
6
|
"bugs": "https://github.com/immutable/ts-immutable-sdk/issues",
|
|
@@ -25,11 +25,10 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@imtbl/auth": "2.12.
|
|
29
|
-
"@imtbl/generated-clients": "2.12.
|
|
30
|
-
"@imtbl/metrics": "2.12.
|
|
31
|
-
"
|
|
32
|
-
"ethers": "^6.13.4"
|
|
28
|
+
"@imtbl/auth": "2.12.4-alpha.1",
|
|
29
|
+
"@imtbl/generated-clients": "2.12.4-alpha.1",
|
|
30
|
+
"@imtbl/metrics": "2.12.4-alpha.1",
|
|
31
|
+
"viem": "~2.18.0"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"@swc/core": "^1.3.36",
|
package/src/guardian/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as GeneratedClients from '@imtbl/generated-clients';
|
|
2
|
-
import {
|
|
2
|
+
import { zeroAddress } from 'viem';
|
|
3
3
|
import { Auth, IAuthConfiguration } from '@imtbl/auth';
|
|
4
4
|
import ConfirmationScreen from '../confirmation/confirmation';
|
|
5
5
|
import { JsonRpcError, ProviderErrorCode, RpcErrorCode } from '../zkEvm/JsonRpcError';
|
|
@@ -37,7 +37,7 @@ const transactionRejectedCrossSdkBridgeError = 'Transaction requires confirmatio
|
|
|
37
37
|
+ ' supported in this environment. Please contact Immutable support if you need to enable this feature.';
|
|
38
38
|
|
|
39
39
|
export const convertBigNumberishToString = (
|
|
40
|
-
value:
|
|
40
|
+
value: bigint,
|
|
41
41
|
): string => BigInt(value).toString();
|
|
42
42
|
|
|
43
43
|
const transformGuardianTransactions = (
|
|
@@ -48,7 +48,7 @@ const transformGuardianTransactions = (
|
|
|
48
48
|
delegateCall: t.delegateCall === true,
|
|
49
49
|
revertOnError: t.revertOnError === true,
|
|
50
50
|
gasLimit: t.gasLimit ? convertBigNumberishToString(t.gasLimit) : '0',
|
|
51
|
-
target: t.to ??
|
|
51
|
+
target: t.to ?? zeroAddress,
|
|
52
52
|
value: t.value ? convertBigNumberishToString(t.value) : '0',
|
|
53
53
|
data: t.data ? t.data.toString() : '0x',
|
|
54
54
|
}));
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/* eslint-disable no-bitwise */
|
|
2
|
-
import { AbstractSigner, Signer } from 'ethers';
|
|
3
2
|
import { MagicTeeApiClients } from '@imtbl/generated-clients';
|
|
4
3
|
import { Flow, trackDuration } from '@imtbl/metrics';
|
|
5
4
|
import { WalletError, WalletErrorType } from '../errors';
|
|
6
5
|
import { Auth } from '@imtbl/auth';
|
|
7
6
|
import { withMetricsAsync } from '../utils/metrics';
|
|
8
|
-
import { isUserZkEvm, User } from '../types';
|
|
7
|
+
import { isUserZkEvm, User, WalletSigner } from '../types';
|
|
9
8
|
import { isAxiosError } from '../utils/http';
|
|
10
9
|
|
|
11
10
|
const CHAIN_IDENTIFIER = 'ETH';
|
|
@@ -58,7 +57,11 @@ const toBase64 = (value: string): string => {
|
|
|
58
57
|
return output;
|
|
59
58
|
};
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
/**
|
|
61
|
+
* MagicTEESigner implements the WalletSigner interface for Magic TEE-based signing.
|
|
62
|
+
* This signer delegates cryptographic operations to the Magic TEE service.
|
|
63
|
+
*/
|
|
64
|
+
export default class MagicTEESigner implements WalletSigner {
|
|
62
65
|
private readonly auth: Auth;
|
|
63
66
|
|
|
64
67
|
private readonly magicTeeApiClient: MagicTeeApiClients;
|
|
@@ -68,7 +71,6 @@ export default class MagicTEESigner extends AbstractSigner {
|
|
|
68
71
|
private createWalletPromise: Promise<UserWallet> | null = null;
|
|
69
72
|
|
|
70
73
|
constructor(auth: Auth, magicTeeApiClient: MagicTeeApiClients) {
|
|
71
|
-
super();
|
|
72
74
|
this.auth = auth;
|
|
73
75
|
this.magicTeeApiClient = magicTeeApiClient;
|
|
74
76
|
}
|
|
@@ -184,19 +186,19 @@ export default class MagicTEESigner extends AbstractSigner {
|
|
|
184
186
|
};
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
public async getAddress(): Promise
|
|
189
|
+
public async getAddress(): Promise<`0x${string}`> {
|
|
188
190
|
const userWallet = await this.getUserWallet();
|
|
189
|
-
return userWallet.walletAddress
|
|
191
|
+
return userWallet.walletAddress as `0x${string}`;
|
|
190
192
|
}
|
|
191
193
|
|
|
192
|
-
public async signMessage(message: string | Uint8Array): Promise
|
|
194
|
+
public async signMessage(message: string | Uint8Array): Promise<`0x${string}`> {
|
|
193
195
|
// Call getUserWallet to ensure that the createWallet endpoint has been called at least once,
|
|
194
196
|
// as this is a prerequisite for signing messages.
|
|
195
197
|
await this.getUserWallet();
|
|
196
198
|
|
|
197
199
|
const messageToSign = message instanceof Uint8Array ? `0x${toHex(message)}` : message;
|
|
198
200
|
const user = await this.getUserOrThrow();
|
|
199
|
-
const headers =
|
|
201
|
+
const headers = MagicTEESigner.getHeaders(user);
|
|
200
202
|
|
|
201
203
|
return withMetricsAsync(async (flow: Flow) => {
|
|
202
204
|
try {
|
|
@@ -217,7 +219,7 @@ export default class MagicTEESigner extends AbstractSigner {
|
|
|
217
219
|
Math.round(performance.now() - startTime),
|
|
218
220
|
);
|
|
219
221
|
|
|
220
|
-
return response.data.signature
|
|
222
|
+
return response.data.signature as `0x${string}`;
|
|
221
223
|
} catch (error) {
|
|
222
224
|
let errorMessage: string = 'MagicTEE: Failed to sign message using EOA';
|
|
223
225
|
|
|
@@ -235,19 +237,4 @@ export default class MagicTEESigner extends AbstractSigner {
|
|
|
235
237
|
}
|
|
236
238
|
}, 'magicSignMessage');
|
|
237
239
|
}
|
|
238
|
-
|
|
239
|
-
// eslint-disable-next-line class-methods-use-this
|
|
240
|
-
connect(): Signer {
|
|
241
|
-
throw new Error('Method not implemented.');
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// eslint-disable-next-line class-methods-use-this
|
|
245
|
-
signTransaction(): Promise<string> {
|
|
246
|
-
throw new Error('Method not implemented.');
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// eslint-disable-next-line class-methods-use-this
|
|
250
|
-
signTypedData(): Promise<string> {
|
|
251
|
-
throw new Error('Method not implemented.');
|
|
252
|
-
}
|
|
253
240
|
}
|
package/src/types.ts
CHANGED
|
@@ -2,9 +2,19 @@ import { Flow } from '@imtbl/metrics';
|
|
|
2
2
|
import {
|
|
3
3
|
Auth, TypedEventEmitter, type AuthEventMap,
|
|
4
4
|
} from '@imtbl/auth';
|
|
5
|
-
import { BigNumberish } from 'ethers';
|
|
6
5
|
import { JsonRpcError } from './zkEvm/JsonRpcError';
|
|
7
6
|
|
|
7
|
+
/**
|
|
8
|
+
* A viem-compatible signer interface for wallet operations.
|
|
9
|
+
* This replaces ethers' AbstractSigner/Signer.
|
|
10
|
+
*/
|
|
11
|
+
export interface WalletSigner {
|
|
12
|
+
/** Get the wallet address */
|
|
13
|
+
getAddress(): Promise<`0x${string}`>;
|
|
14
|
+
/** Sign a message (EIP-191 personal_sign) */
|
|
15
|
+
signMessage(message: string | Uint8Array): Promise<`0x${string}`>;
|
|
16
|
+
}
|
|
17
|
+
|
|
8
18
|
// Re-export auth types for convenience
|
|
9
19
|
export type {
|
|
10
20
|
User, UserProfile, UserZkEvm, DirectLoginMethod, AuthEventMap,
|
|
@@ -83,10 +93,10 @@ export interface TypedDataPayload {
|
|
|
83
93
|
|
|
84
94
|
export interface MetaTransaction {
|
|
85
95
|
to: string;
|
|
86
|
-
value?:
|
|
96
|
+
value?: bigint | null;
|
|
87
97
|
data?: string | null;
|
|
88
|
-
nonce?:
|
|
89
|
-
gasLimit?:
|
|
98
|
+
nonce?: bigint;
|
|
99
|
+
gasLimit?: bigint;
|
|
90
100
|
delegateCall?: boolean;
|
|
91
101
|
revertOnError?: boolean;
|
|
92
102
|
}
|
|
@@ -94,9 +104,9 @@ export interface MetaTransaction {
|
|
|
94
104
|
export interface MetaTransactionNormalised {
|
|
95
105
|
delegateCall: boolean;
|
|
96
106
|
revertOnError: boolean;
|
|
97
|
-
gasLimit:
|
|
107
|
+
gasLimit: bigint;
|
|
98
108
|
target: string;
|
|
99
|
-
value:
|
|
109
|
+
value: bigint;
|
|
100
110
|
data: string;
|
|
101
111
|
}
|
|
102
112
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { WalletSigner } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Signature components for Ethereum signatures
|
|
5
|
+
*/
|
|
6
|
+
type SignatureOptions = {
|
|
7
|
+
r: bigint;
|
|
8
|
+
s: bigint;
|
|
9
|
+
recoveryParam: number | null | undefined;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Adds '0x' prefix to a hex string if not present
|
|
14
|
+
*/
|
|
15
|
+
function addHexPrefix(hex: string): string {
|
|
16
|
+
return hex.startsWith('0x') ? hex : `0x${hex}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Removes '0x' prefix from a hex string if present
|
|
21
|
+
*/
|
|
22
|
+
function removeHexPrefix(hex: string): string {
|
|
23
|
+
return hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Pads a hex string to a specified length with leading zeros
|
|
28
|
+
*/
|
|
29
|
+
function padLeft(str: string, length: number): string {
|
|
30
|
+
return str.padStart(length, '0');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Serializes Ethereum signature components into a hex string.
|
|
35
|
+
* This format is used for IMX registration with golang backend.
|
|
36
|
+
* @see https://github.com/ethers-io/ethers.js/issues/823
|
|
37
|
+
*/
|
|
38
|
+
function serializeEthSignature(sig: SignatureOptions): string {
|
|
39
|
+
const rHex = padLeft(sig.r.toString(16), 64);
|
|
40
|
+
const sHex = padLeft(sig.s.toString(16), 64);
|
|
41
|
+
const vHex = padLeft(sig.recoveryParam?.toString(16) || '', 2);
|
|
42
|
+
return addHexPrefix(rHex + sHex + vHex);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Imports recovery parameter from hex string, normalizing v value
|
|
47
|
+
*/
|
|
48
|
+
function importRecoveryParam(v: string): number | undefined {
|
|
49
|
+
if (!v.trim()) return undefined;
|
|
50
|
+
|
|
51
|
+
const vValue = parseInt(v, 16);
|
|
52
|
+
// If v >= 27, subtract 27 to get recovery param (0 or 1)
|
|
53
|
+
return vValue >= 27 ? vValue - 27 : vValue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Deserializes a signature hex string into its components (r, s, v)
|
|
58
|
+
*/
|
|
59
|
+
function deserializeSignature(sig: string, size = 64): SignatureOptions {
|
|
60
|
+
const cleanSig = removeHexPrefix(sig);
|
|
61
|
+
return {
|
|
62
|
+
r: BigInt(`0x${cleanSig.substring(0, size)}`),
|
|
63
|
+
s: BigInt(`0x${cleanSig.substring(size, size * 2)}`),
|
|
64
|
+
recoveryParam: importRecoveryParam(cleanSig.substring(size * 2, size * 2 + 2)),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Signs a message with the provided signer and returns a serialized signature
|
|
70
|
+
* suitable for IMX registration and authorization.
|
|
71
|
+
*
|
|
72
|
+
* This is inlined from @imtbl/toolkit to avoid ethers dependency.
|
|
73
|
+
*
|
|
74
|
+
* @param payload - The message to sign
|
|
75
|
+
* @param signer - A WalletSigner implementation
|
|
76
|
+
* @returns The serialized signature as a hex string
|
|
77
|
+
*/
|
|
78
|
+
export async function signRaw(
|
|
79
|
+
payload: string,
|
|
80
|
+
signer: WalletSigner,
|
|
81
|
+
): Promise<string> {
|
|
82
|
+
const rawSignature = await signer.signMessage(payload);
|
|
83
|
+
const signature = deserializeSignature(rawSignature);
|
|
84
|
+
return serializeEthSignature(signature);
|
|
85
|
+
}
|
package/src/utils/string.ts
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toBytes, type Hex } from 'viem';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Strip leading zero bytes from a Uint8Array
|
|
5
|
+
*/
|
|
6
|
+
const stripZerosLeft = (bytes: Uint8Array): Uint8Array => {
|
|
7
|
+
let start = 0;
|
|
8
|
+
while (start < bytes.length && bytes[start] === 0) {
|
|
9
|
+
start++;
|
|
10
|
+
}
|
|
11
|
+
return bytes.slice(start);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Convert UTF-8 bytes to string
|
|
16
|
+
*/
|
|
17
|
+
const toUtf8String = (bytes: Uint8Array): string => {
|
|
18
|
+
if (typeof TextDecoder !== 'undefined') {
|
|
19
|
+
return new TextDecoder('utf-8').decode(bytes);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Fallback for environments without TextDecoder
|
|
23
|
+
let result = '';
|
|
24
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
25
|
+
result += String.fromCharCode(bytes[i]);
|
|
26
|
+
}
|
|
27
|
+
return decodeURIComponent(escape(result));
|
|
28
|
+
};
|
|
2
29
|
|
|
3
30
|
export const hexToString = (hex: string) => {
|
|
4
31
|
if (!hex) return hex;
|
|
5
32
|
|
|
6
33
|
try {
|
|
7
|
-
const
|
|
34
|
+
const bytes = toBytes(hex as Hex);
|
|
35
|
+
const stripped = stripZerosLeft(bytes);
|
|
8
36
|
return toUtf8String(stripped);
|
|
9
37
|
} catch (e) {
|
|
10
38
|
return hex;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { Flow } from '@imtbl/metrics';
|
|
2
|
-
import {
|
|
2
|
+
import type { PublicClient } from 'viem';
|
|
3
3
|
import { JsonRpcError, RpcErrorCode } from './JsonRpcError';
|
|
4
4
|
import { hexToString } from '../utils/string';
|
|
5
5
|
import GuardianClient from '../guardian';
|
|
6
6
|
import { RelayerClient } from './relayerClient';
|
|
7
7
|
import { packSignatures, signERC191Message } from './walletHelpers';
|
|
8
|
+
import type { WalletSigner } from '../types';
|
|
8
9
|
|
|
9
10
|
interface PersonalSignParams {
|
|
10
|
-
ethSigner:
|
|
11
|
-
rpcProvider:
|
|
11
|
+
ethSigner: WalletSigner;
|
|
12
|
+
rpcProvider: PublicClient;
|
|
12
13
|
params: any[];
|
|
13
14
|
zkEvmAddress: string;
|
|
14
15
|
guardianClient: GuardianClient;
|
|
@@ -38,7 +39,7 @@ export const personalSign = async ({
|
|
|
38
39
|
|
|
39
40
|
// Convert message into a string if it's a hex
|
|
40
41
|
const payload = hexToString(message);
|
|
41
|
-
const
|
|
42
|
+
const chainId = await rpcProvider.getChainId();
|
|
42
43
|
flow.addEvent('endDetectNetwork');
|
|
43
44
|
const chainIdBigNumber = BigInt(chainId);
|
|
44
45
|
|
|
@@ -46,7 +47,7 @@ export const personalSign = async ({
|
|
|
46
47
|
const eoaSignaturePromise = signERC191Message(chainIdBigNumber, payload, ethSigner, fromAddress);
|
|
47
48
|
eoaSignaturePromise.then(() => flow.addEvent('endEOASignature'));
|
|
48
49
|
|
|
49
|
-
await guardianClient.evaluateERC191Message({ chainID:
|
|
50
|
+
await guardianClient.evaluateERC191Message({ chainID: chainIdBigNumber, payload });
|
|
50
51
|
flow.addEvent('endEvaluateERC191Message');
|
|
51
52
|
|
|
52
53
|
const [eoaSignature, relayerSignature] = await Promise.all([
|