Skip to content

Commit

Permalink
Refactor: Preparing for WC Integration (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
bh2smith authored May 8, 2024
1 parent f9aaa17 commit 0b1a6a0
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 72 deletions.
24 changes: 11 additions & 13 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,25 @@
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"plugins": ["@typescript-eslint"],
"rules": {
"indent": ["error", 2],
"indent": ["error", 2, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error","always"],
"semi": ["error", "always"],
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/explicit-function-return-type": ["error", {
"allowExpressions": true,
"allowTypedFunctionExpressions": true
}]
"@typescript-eslint/explicit-function-return-type": [
"error",
{
"allowExpressions": true,
"allowTypedFunctionExpressions": true
}
]
}
}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Lint & Unit Test
run: |
yarn lint
yarn test utils
yarn test unit
# These are only relevant for e2e tests
# env:
# NEAR_MULTICHAIN_CONTRACT: multichain-testnet-2.testnet
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.eslintrc
50 changes: 7 additions & 43 deletions src/chains/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import {
} from "../types/types";
import { MultichainContract } from "../mpcContract";
import BN from "bn.js";
import { queryGasPrice } from "../utils/gasPrice";
import { buildTxPayload, addSignature } from "../utils/transaction";
import { buildTxPayload, addSignature, populateTx } from "../utils/transaction";
import { Network } from "../network";
import { pickValidSignature } from "../utils/signature";

export class NearEthAdapter {
readonly mpcContract: MultichainContract;
Expand Down Expand Up @@ -150,32 +150,9 @@ export class NearEthAdapter {
* @returns {Hex} serialized (aka RLP encoded) transaction.
*/
async buildTransaction(tx: BaseTx): Promise<Hex> {
const network = Network.fromChainId(tx.chainId);
const transactionData = {
nonce:
tx.nonce ||
(await network.client.getTransactionCount({
address: this.address,
})),
account: this.address,
to: tx.to,
value: tx.value ?? 0n,
data: tx.data ?? "0x",
};
const [estimatedGas, { maxFeePerGas, maxPriorityFeePerGas }] =
await Promise.all([
network.client.estimateGas(transactionData),
queryGasPrice(network.gasStationUrl),
]);
const transactionDataWithGasLimit = {
...transactionData,
gas: BigInt(estimatedGas.toString()),
maxFeePerGas,
maxPriorityFeePerGas,
chainId: network.chainId,
};
console.log("Transaction Request", transactionDataWithGasLimit);
return serializeTransaction(transactionDataWithGasLimit);
const transaction = await populateTx(tx, this.address);
console.log("Transaction Request", transaction);
return serializeTransaction(transaction);
}

reconstructSignature(tx: TransactionWithSignature): Hex {
Expand Down Expand Up @@ -225,7 +202,7 @@ export class NearEthAdapter {
...common,
}),
]);
return this.pickValidSignature(validity, sigs);
return pickValidSignature(validity, sigs);
}

async signMessage(message: SignableMessage): Promise<Hash> {
Expand All @@ -244,7 +221,7 @@ export class NearEthAdapter {
...common,
}),
]);
return this.pickValidSignature(validity, sigs);
return pickValidSignature(validity, sigs);
}

/**
Expand All @@ -268,17 +245,4 @@ export class NearEthAdapter {
signatureToHex({ r, s, yParity: 1 }),
];
}

private pickValidSignature(
[valid0, valid1]: [boolean, boolean],
[sig0, sig1]: [Hash, Hash]
): Hash {
if (!valid0 && !valid1) {
throw new Error("Invalid signature");
} else if (valid0) {
return sig0;
} else {
return sig1;
}
}
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./chains/ethereum";
export * from "./mpcContract";
export * from "./chains/near";
export * from "./mpcContract";
export * from "./types/types";
export * from "./utils/signature";
export * from "./network";
6 changes: 3 additions & 3 deletions src/mpcContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ export class MultichainContract {
return { big_r, big_s };
};

encodeSignatureRequestTx = async (
encodeSignatureRequestTx(
signArgs: SignArgs,
gas?: BN
): Promise<NearContractFunctionPayload> => {
): NearContractFunctionPayload {
return {
signerId: this.contract.account.accountId,
receiverId: this.contract.contractId,
Expand All @@ -89,5 +89,5 @@ export class MultichainContract {
},
],
};
};
}
}
4 changes: 3 additions & 1 deletion src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface BaseTx {
chainId: number;
/// Specified transaction nonce
nonce?: number;
/// optional gasLimit
gas?: bigint;
}

export interface NearEthAdapterParams {
Expand Down Expand Up @@ -52,7 +54,7 @@ export interface SignArgs {
}

export interface TxPayload {
/// Deserialized Ethereum Transaction.
/// Serialized Ethereum Transaction.
transaction: Hex;
/// Arguments required by Near MPC Contract signature request.
signArgs: SignArgs;
Expand Down
14 changes: 14 additions & 0 deletions src/utils/getSignature.ts → src/utils/signature.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Hash } from "viem";
import { JSONRPCResponse } from "../types/rpc";

export async function signatureFromTxHash(
Expand Down Expand Up @@ -35,3 +36,16 @@ export async function signatureFromTxHash(
throw new Error("No valid values found in the array.");
}
}

export function pickValidSignature(
[valid0, valid1]: [boolean, boolean],
[sig0, sig1]: [Hash, Hash]
): Hash {
if (!valid0 && !valid1) {
throw new Error("Invalid signature");
} else if (valid0) {
return sig0;
} else {
return sig1;
}
}
50 changes: 44 additions & 6 deletions src/utils/transaction.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,60 @@
import {
Address,
Hex,
hexToBytes,
TransactionSerializable,
hexToNumber,
keccak256,
parseTransaction,
serializeTransaction,
signatureToHex,
toBytes,
} from "viem";
import { TransactionWithSignature } from "../types/types";
import { BaseTx, TransactionWithSignature } from "../types/types";
import { secp256k1 } from "@noble/curves/secp256k1";

import { publicKeyToAddress } from "viem/utils";
import { queryGasPrice } from "./gasPrice";
import { Network } from "../network";

export function toPayload(hexString: Hex): number[] {
if (hexString.slice(2).length !== 32 * 2) {
throw new Error(`Payload Hex must have 32 bytes: ${hexString}`);
}
return Array.from(toBytes(hexString).reverse());
}

export function buildTxPayload(unsignedTxHash: `0x${string}`): number[] {
// Compute the Transaction Message Hash.
const messageHash = keccak256(unsignedTxHash);
return Array.from(hexToBytes(messageHash).slice().reverse());
return toPayload(keccak256(unsignedTxHash));
}

export async function populateTx(
tx: BaseTx,
from: Hex
): Promise<TransactionSerializable> {
const network = Network.fromChainId(tx.chainId);
const transactionData = {
nonce:
tx.nonce ||
(await network.client.getTransactionCount({
address: from,
})),
account: from,
to: tx.to,
value: tx.value ?? 0n,
data: tx.data ?? "0x",
};
const [estimatedGas, { maxFeePerGas, maxPriorityFeePerGas }] =
await Promise.all([
// Only estimate gas if not provided.
tx.gas || network.client.estimateGas(transactionData),
queryGasPrice(network.gasStationUrl),
]);
return {
...transactionData,
gas: estimatedGas,
maxFeePerGas,
maxPriorityFeePerGas,
chainId: network.chainId,
};
}

export function addSignature(
Expand Down
2 changes: 1 addition & 1 deletion tests/utils.kdf.test.ts → tests/unit/utils.kdf.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
najPublicKeyStrToUncompressedHexPoint,
deriveChildPublicKey,
uncompressedHexPointToEvmAddress,
} from "../src/utils/kdf";
} from "../../src/utils/kdf";

const ROOT_PK =
"ecp256k1:4HFcTSodRLVCGNVcGc4Mf2fwBBBxv9jxkGdiW2S2CA1y6UpVVRWKj6RX7d7TDt65k2Bj3w9FU4BGtt43ZvuhCnNt";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { signatureFromTxHash } from "../src/utils/getSignature";
import { signatureFromTxHash } from "../../src/utils/signature";

describe("utility: get Signature", () => {
const url: string = "https://archival-rpc.testnet.near.org";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TransactionWithSignature } from "../src";
import { buildTxPayload, addSignature } from "../src/utils/transaction";
import { TransactionWithSignature } from "../../src";
import { buildTxPayload, addSignature } from "../../src/utils/transaction";

describe("Transaction Builder Functions", () => {
it("buildTxPayload", async () => {
Expand Down

0 comments on commit 0b1a6a0

Please sign in to comment.