Skip to content
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
139 changes: 14 additions & 125 deletions src/beta.ts
Original file line number Diff line number Diff line change
@@ -1,126 +1,10 @@
import {
Hex,
Signature,
fromHex,
hashMessage,
hashTypedData,
keccak256,
serializeSignature,
serializeTransaction,
} from "viem";
import {
addSignature,
populateTx,
relaySignedTransaction,
toPayload,
} from "./utils/transaction";
import { NearEthTxData, RecoveryData } from "./types";
import { Hex, Signature, serializeSignature } from "viem";
import { addSignature, relaySignedTransaction } from "./utils/transaction";
import { NearEthTxData, signMethods } from "./types";
import { NearEthAdapter } from "./chains/ethereum";
import { Web3WalletTypes } from "@walletconnect/web3wallet";

// Interface for Ethereum transaction parameters
export interface EthTransactionParams {
from: Hex;
to: Hex;
gas?: Hex;
value?: Hex;
data?: Hex;
}

/// Interface for personal sign parameters (message and address)
export type PersonalSignParams = [Hex, Hex];

/// Interface for eth_sign parameters (address and message)
export type EthSignParams = [Hex, Hex];

/// Interface for complex structured parameters like EIP-712
export type TypedDataParams = [Hex, string];

export type SessionRequestParams =
| EthTransactionParams[]
| PersonalSignParams
| EthSignParams
| TypedDataParams;

export async function wcRouter(
method: string,
chainId: string,
params: SessionRequestParams
): Promise<{
evmMessage: string;
payload: number[];
recoveryData: RecoveryData;
}> {
switch (method) {
case "eth_sign": {
const [sender, messageHash] = params as EthSignParams;
return {
evmMessage: fromHex(messageHash, "string"),
payload: toPayload(hashMessage({ raw: messageHash })),
recoveryData: {
type: method,
data: {
address: sender,
message: { raw: messageHash },
},
},
};
}
case "personal_sign": {
const [messageHash, sender] = params as PersonalSignParams;
return {
evmMessage: fromHex(messageHash, "string"),
payload: toPayload(hashMessage({ raw: messageHash })),
recoveryData: {
type: method,
data: {
address: sender,
message: { raw: messageHash },
},
},
};
}
case "eth_sendTransaction": {
const tx = params[0] as EthTransactionParams;
const transaction = await populateTx(
{
to: tx.to,
chainId: parseInt(stripEip155Prefix(chainId)),
value: fromHex(tx.value || "0x0", "bigint"),
data: tx.data || "0x",
...(tx.gas ? { gas: fromHex(tx.gas, "bigint") } : {}),
},
tx.from
);
const txHex = serializeTransaction(transaction);
return {
payload: toPayload(keccak256(serializeTransaction(transaction))),
evmMessage: txHex,
recoveryData: {
type: "eth_sendTransaction",
data: txHex,
},
};
}
case "eth_signTypedData":
case "eth_signTypedData_v4": {
const [sender, dataString] = params as TypedDataParams;
const typedData = JSON.parse(dataString);
return {
evmMessage: dataString,
payload: toPayload(hashTypedData(typedData)),
recoveryData: {
type: "eth_signTypedData",
data: {
address: sender,
...typedData,
},
},
};
}
}
throw new Error(`Unhandled session_request method: ${method}`);
}
import { isSignMethod } from "./guards";
import { requestRouter } from "./utils/request";

function stripEip155Prefix(eip155Address: string): string {
return eip155Address.split(":").pop() ?? "";
Expand All @@ -145,11 +29,16 @@ export class Beta {
request: { method, params },
} = request.params!;
console.log(`Session Request of type ${method} for chainId ${chainId}`);
const { evmMessage, payload, recoveryData } = await wcRouter(
if (!isSignMethod(method)) {
throw new Error(
`Unsupported sign method ${method}: Available sign methods ${signMethods}`
);
}
const { evmMessage, payload, recoveryData } = await requestRouter({
method,
chainId,
params
);
chainId: parseInt(stripEip155Prefix(chainId)),
params,
});
console.log("Parsed Request:", payload, recoveryData);
return {
nearPayload: await this.adapter.mpcContract.encodeSignatureRequestTx({
Expand Down
29 changes: 29 additions & 0 deletions src/chains/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ import {
populateTx,
toPayload,
broadcastSignedTransaction,
SignRequestData,
NearEthTxData,
} from "..";
import { Beta } from "../beta";
import { requestRouter } from "../utils/request";

export class NearEthAdapter {
readonly mpcContract: MpcContract;
Expand Down Expand Up @@ -202,4 +205,30 @@ export class NearEthAdapter {
});
return serializeSignature(signature);
}

/**
* Encodes a signature request for submission to the Near-Ethereum transaction MPC contract.
*
* @async
* @function encodeSignRequest
* @param {SignRequestData} signRequest - The signature request data containing method, chain ID, and parameters.
* @returns {Promise<NearEthTxData>}
* - Returns a promise that resolves to an object containing the encoded Near-Ethereum transaction data,
* the original EVM message, and recovery data necessary for verifying or reconstructing the signature.
*/
async encodeSignRequest(
signRequest: SignRequestData
): Promise<NearEthTxData> {
const { evmMessage, payload, recoveryData } =
await requestRouter(signRequest);
return {
nearPayload: await this.mpcContract.encodeSignatureRequestTx({
path: this.derivationPath,
payload,
key_version: 0,
}),
evmMessage,
recoveryData,
};
}
}
11 changes: 11 additions & 0 deletions src/guards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { SignMethod } from "./types";

export function isSignMethod(method: string): method is SignMethod {
return [
"eth_sign",
"personal_sign",
"eth_sendTransaction",
"eth_signTypedData",
"eth_signTypedData_v4",
].includes(method);
}
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import {

export * from "./chains/ethereum";
export * from "./chains/near";
export * from "./guards";
export * from "./mpcContract";
export * from "./types";
export * from "./utils/signature";
export * from "./network";
export * from "./utils/transaction";
export * from "./types";
export * from "./utils";
/// Beta features
export * from "./beta";

Expand Down
99 changes: 98 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { MpcContract } from "./mpcContract";
import { Hex, SignableMessage, Signature, TransactionSerializable } from "viem";
import {
Address,
Hex,
SignableMessage,
Signature,
TransactionSerializable,
} from "viem";

/**
* Borrowed from @near-wallet-selector/core
Expand Down Expand Up @@ -179,3 +185,94 @@ export interface TransactionWithSignature {
transaction: Hex;
signature: Signature;
}

/// Below is hand-crafted types losely related to wallet connect

/**
* Interface representing the parameters required for an Ethereum transaction.
*
* @property {Hex} from - The sender's Ethereum address in hexadecimal format.
* @property {Hex} to - The recipient's Ethereum address in hexadecimal format.
* @property {Hex} [gas] - Optional gas limit for the transaction in hexadecimal format.
* @property {Hex} [value] - Optional amount of Ether to send in hexadecimal format.
* @property {Hex} [data] - Optional data payload for the transaction in hexadecimal format, often used for contract interactions. */
export interface EthTransactionParams {
from: Hex;
to: Hex;
gas?: Hex;
value?: Hex;
data?: Hex;
}

/**
* Type representing the parameters for a personal_sign request.
*
* @type {[Hex, Address]}
* @property {Hex} 0 - The message to be signed in hexadecimal format.
* @property {Address} 1 - The address of the signer in hexadecimal format.
*/
export type PersonalSignParams = [Hex, Address];

/**
* Type representing the parameters for an eth_sign request.
*
* @type {[Address, Hex]}
* @property {Address} 0 - The address of the signer in hexadecimal format.
* @property {Hex} 1 - The message to be signed in hexadecimal format.
*/
export type EthSignParams = [Address, Hex];

/**
* Type representing the parameters for signing complex structured data (like EIP-712).
*
* @type {[Hex, string]}
* @property {Hex} 0 - The address of the signer in hexadecimal format.
* @property {string} 1 - The structured data in JSON string format to be signed.
*/
export type TypedDataParams = [Hex, string];

/**
* Type representing the possible request parameters for a signing session.
*
* @type {EthTransactionParams[] | Hex | PersonalSignParams | EthSignParams | TypedDataParams}
* @property {EthTransactionParams[]} - An array of Ethereum transaction parameters.
* @property {Hex} - A simple hexadecimal value representing RLP Encoded Ethereum Transaction.
* @property {PersonalSignParams} - Parameters for a personal sign request.
* @property {EthSignParams} - Parameters for an eth_sign request.
* @property {TypedDataParams} - Parameters for signing structured data.
*/
export type SessionRequestParams =
| EthTransactionParams[]
| Hex
| PersonalSignParams
| EthSignParams
| TypedDataParams;

/**
* An array of supported signing methods.
*/
export const signMethods = [
"eth_sign",
"personal_sign",
"eth_sendTransaction",
"eth_signTypedData",
"eth_signTypedData_v4",
] as const;

/**
* Type representing one of the supported signing methods.
*/
export type SignMethod = (typeof signMethods)[number];

/**
* Interface representing the data required for a signature request.
*
* @property {SignMethods} method - The signing method to be used.
* @property {number} chainId - The ID of the Ethereum chain where the transaction or signing is taking place.
* @property {SessionRequestParams} params - The parameters required for the signing request, which vary depending on the method.
*/
export type SignRequestData = {
method: SignMethod;
chainId: number;
params: SessionRequestParams;
};
3 changes: 3 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./request";
export * from "./signature";
export * from "./transaction";
Loading