Skip to content

Commit

Permalink
productionize request router
Browse files Browse the repository at this point in the history
  • Loading branch information
bh2smith committed Sep 3, 2024
1 parent 8ef54cb commit 87d66a7
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 184 deletions.
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

0 comments on commit 87d66a7

Please sign in to comment.