diff --git a/.env.example b/.env.example index 15b446f..521b463 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,3 @@ NEAR_ACCOUNT_ID= NEAR_ACCOUNT_PRIVATE_KEY= NEAR_MULTICHAIN_CONTRACT=multichain-testnet-2.testnet - -NODE_URL=https://rpc.sepolia.org -SCAN_URL=https://sepolia.etherscan.io -GAS_STATION_URL=https://sepolia.beaconcha.in/api/v1/execution/gasnow diff --git a/README.md b/README.md index de42c9e..4545072 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **DISCLAIMER: This should only be used for educational purposes.** -NEAR-CA is a TypeScript library that provides an abstraction layer for interacting with the NEAR blockchain. It simplifies the process of performing transactions and managing accounts on NEAR and Ethereum chains. +NEAR-CA is a TypeScript library that provides an abstraction layer for interacting with the NEAR blockchain. It simplifies the process of performing transactions and managing accounts on NEAR and Ethereum chains. Intended to be used on server-side applications only. @@ -41,39 +41,35 @@ For Ethereum, you can derive addresses, create payloads for transactions, and se ```typescript import dotenv from "dotenv"; -import { MultichainContract, NearEthAdapter, nearAccountFromEnv } from "near-ca"; +import { + MultichainContract, + NearEthAdapter, + nearAccountFromEnv, +} from "near-ca"; dotenv.config(); // Could also import and use nearAccountFromKeyPair here ;) const account = await nearAccountFromEnv(); -// Near Config -const near = { - mpcContract: new MultichainContract( - account, - process.env.NEAR_MULTICHAIN_CONTRACT! - ), - derivationPath: "ethereum,1", -}; - -// EVM Config -const evm = { - providerUrl: process.env.NODE_URL!, - scanUrl: process.env.SCAN_URL!, - gasStationUrl: process.env.GAS_STATION_URL!, -}; - -const neareth = await NearEthAdapter.fromConfig({ near, evm }); - -await neareth.signAndSendTransaction({ - receiver: "0xdeADBeeF0000000000000000000000000b00B1e5", - amount: 0.00000001, - // Optional Set nearGas (default is 200 TGAS - which still sometimes doesn't work!) + +const adapter = await NearEthAdapter.fromConfig({ + mpcContract: new MultichainContract( + account, + process.env.NEAR_MULTICHAIN_CONTRACT! + ), + derivationPath: "ethereum,1", +}); + +await adapter.signAndSendTransaction({ + receiver: "0xdeADBeeF0000000000000000000000000b00B1e5", + amount: 0.00000001, + chainId: 11_155_111, + // Optional Set nearGas (default is 200 TGAS - which still sometimes doesn't work!) }); ``` ### Other Examples -Each of the following scripts can be run with +Each of the following scripts can be run with ```bash npx ts-node examples/*.ts @@ -81,8 +77,8 @@ npx ts-node examples/*.ts 1. [(Basic) Send ETH](./examples/send-eth.ts) 2. **WETH** - - [Deposit (aka wrap-ETH)](./examples/weth/wrap.ts) - - [Withdraw (aka unwrap-ETH)](./examples/weth/wrap.ts) + - [Deposit (aka wrap-ETH)](./examples/weth/wrap.ts) + - [Withdraw (aka unwrap-ETH)](./examples/weth/wrap.ts) 3. [Transfer ERC721](./examples/nft/erc721/transfer.ts) 4. [(Advanced) Buy NFT On Opensea](./examples/opensea.ts) @@ -93,8 +89,5 @@ Before using NEAR-CA, ensure you have the following environment variables set: - `NEAR_ACCOUNT_ID`: Your NEAR account identifier. - `NEAR_ACCOUNT_PRIVATE_KEY`: Your NEAR account private key. - `NEAR_MULTICHAIN_CONTRACT`: The NEAR contract that handles multichain operations. -- `NODE_URL=https://rpc.sepolia.org` -- `SCAN_URL=https://sepolia.etherscan.io` -- `GAS_STATION_URL=https://sepolia.beaconcha.in/api/v1/execution/gasnow` Copy the `.env.example` file and place these values in `.env` diff --git a/examples/nft/erc1155/transfer.ts b/examples/nft/erc1155/transfer.ts index cf93ca8..8b86736 100644 --- a/examples/nft/erc1155/transfer.ts +++ b/examples/nft/erc1155/transfer.ts @@ -1,5 +1,5 @@ import erc1155Abi from "../../abis/ERC1155.json"; -import { setupNearEthAdapter } from "../../setup"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter } from "../../setup"; import { encodeFunctionData } from "viem"; const run = async (): Promise => { @@ -12,12 +12,13 @@ const run = async (): Promise => { const callData = encodeFunctionData({ abi: erc1155Abi, functionName: "safeTransferFrom(address,address,uint256,uint256,bytes)", - args: [evm.ethPublicKey(), to, tokenId, 1, "0x"], + args: [evm.address, to, tokenId, 1, "0x"], }); await evm.signAndSendTransaction({ to: tokenAddress, data: callData, + chainId: SEPOLIA_CHAIN_ID, }); }; diff --git a/examples/nft/erc721/mint.ts b/examples/nft/erc721/mint.ts index 7873e96..f844110 100644 --- a/examples/nft/erc721/mint.ts +++ b/examples/nft/erc721/mint.ts @@ -1,5 +1,5 @@ import { encodeFunctionData } from "viem"; -import { setupNearEthAdapter } from "../../setup"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter } from "../../setup"; const run = async (): Promise => { const adapter = await setupNearEthAdapter(); @@ -11,6 +11,7 @@ const run = async (): Promise => { functionName: "safeMint", args: ["0xAA5FcF171dDf9FE59c985A28747e650C2e9069cA"], }), + chainId: SEPOLIA_CHAIN_ID, }); }; diff --git a/examples/nft/erc721/transfer.ts b/examples/nft/erc721/transfer.ts index 3fae946..89cf0d4 100644 --- a/examples/nft/erc721/transfer.ts +++ b/examples/nft/erc721/transfer.ts @@ -1,6 +1,6 @@ import erc721ABI from "../../abis/ERC721.json"; import { encodeFunctionData } from "viem"; -import { setupNearEthAdapter } from "../../setup"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter } from "../../setup"; const run = async (): Promise => { const neareth = await setupNearEthAdapter(); @@ -14,8 +14,9 @@ const run = async (): Promise => { data: encodeFunctionData({ abi: erc721ABI, functionName: "safeTransferFrom(address,address,uint256)", - args: [neareth.ethPublicKey(), to, tokenId], + args: [neareth.address, to, tokenId], }), + chainId: SEPOLIA_CHAIN_ID, }); }; diff --git a/examples/nft/setApprovalForAll.ts b/examples/nft/setApprovalForAll.ts index 73e4d5f..877d18d 100644 --- a/examples/nft/setApprovalForAll.ts +++ b/examples/nft/setApprovalForAll.ts @@ -1,6 +1,6 @@ import erc721ABI from "../abis/ERC721.json"; import { encodeFunctionData } from "viem"; -import { setupNearEthAdapter } from "../setup"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter } from "../setup"; const run = async (): Promise => { const neareth = await setupNearEthAdapter(); @@ -14,6 +14,7 @@ const run = async (): Promise => { functionName: "setApprovalForAll", args: [operator, true], }), + chainId: SEPOLIA_CHAIN_ID, }); }; diff --git a/examples/opensea.ts b/examples/opensea.ts index 0d89c01..e2df201 100644 --- a/examples/opensea.ts +++ b/examples/opensea.ts @@ -1,5 +1,5 @@ import { OpenSeaSDK, Chain, OrderSide } from "opensea-js"; -import { setupNearEthAdapter, sleep } from "./setup"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter, sleep } from "./setup"; import * as readline from "readline"; import { ethers } from "ethers"; import { Address, Hex, encodeFunctionData } from "viem"; @@ -39,7 +39,7 @@ const run = async (slug: string): Promise => { // This sleep is due to free-tier testnet rate limiting. await sleep(1000); const data = await openseaSDK.api.generateFulfillmentData( - evm.ethPublicKey(), + evm.address, cheapestAvailable.order_hash, cheapestAvailable.protocol_address, OrderSide.ASK @@ -74,6 +74,7 @@ const run = async (slug: string): Promise => { to: tx.to as Address, value: BigInt(tx.value), data: callData as Hex, + chainId: SEPOLIA_CHAIN_ID, }); }; diff --git a/examples/send-eth.ts b/examples/send-eth.ts index 6b990da..00cc0ab 100644 --- a/examples/send-eth.ts +++ b/examples/send-eth.ts @@ -1,5 +1,5 @@ import dotenv from "dotenv"; -import { setupNearEthAdapter } from "./setup"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter } from "./setup"; dotenv.config(); const run = async (): Promise => { @@ -8,6 +8,7 @@ const run = async (): Promise => { to: "0xdeADBeeF0000000000000000000000000b00B1e5", // THIS IS ONE WEI! value: 1n, + chainId: SEPOLIA_CHAIN_ID, }); }; diff --git a/examples/setup.ts b/examples/setup.ts index 295b50f..1b50e3e 100644 --- a/examples/setup.ts +++ b/examples/setup.ts @@ -1,22 +1,17 @@ import dotenv from "dotenv"; import { MultichainContract, NearEthAdapter, nearAccountFromEnv } from "../src"; +export const SEPOLIA_CHAIN_ID = 11_155_111; + export async function setupNearEthAdapter(): Promise { dotenv.config(); const account = await nearAccountFromEnv(); return NearEthAdapter.fromConfig({ - evm: { - providerUrl: process.env.NODE_URL!, - scanUrl: process.env.SCAN_URL!, - gasStationUrl: process.env.GAS_STATION_URL!, - }, - near: { - mpcContract: new MultichainContract( - account, - process.env.NEAR_MULTICHAIN_CONTRACT! - ), - derivationPath: "ethereum,1", - }, + mpcContract: new MultichainContract( + account, + process.env.NEAR_MULTICHAIN_CONTRACT! + ), + derivationPath: "ethereum,1", }); } diff --git a/examples/sign-message.ts b/examples/sign-message.ts index 601358d..57cf772 100644 --- a/examples/sign-message.ts +++ b/examples/sign-message.ts @@ -5,7 +5,7 @@ dotenv.config(); const run = async (): Promise => { const evm = await setupNearEthAdapter(); const message = "Hello World"; - console.log(`Signing "${message}" with ${evm.ethPublicKey()}`); + console.log(`Signing "${message}" with ${evm.address}`); const signature = await evm.signMessage(message); console.log("Got Validated Signature", signature); diff --git a/examples/weth/unwrap.ts b/examples/weth/unwrap.ts index a445fde..24a224c 100644 --- a/examples/weth/unwrap.ts +++ b/examples/weth/unwrap.ts @@ -1,6 +1,6 @@ import wethABI from "../abis/WETH.json"; import { encodeFunctionData, parseEther } from "viem"; -import { setupNearEthAdapter } from "../setup"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter } from "../setup"; const run = async (): Promise => { const neareth = await setupNearEthAdapter(); @@ -15,6 +15,7 @@ const run = async (): Promise => { functionName: "withdraw", args: [parseEther(withdrawAmount.toString())], }), + chainId: SEPOLIA_CHAIN_ID, }); }; diff --git a/examples/weth/wrap.ts b/examples/weth/wrap.ts index 604155c..e8f7431 100644 --- a/examples/weth/wrap.ts +++ b/examples/weth/wrap.ts @@ -1,5 +1,5 @@ import { parseEther } from "viem"; -import { setupNearEthAdapter } from "../setup"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter } from "../setup"; const run = async (): Promise => { const neareth = await setupNearEthAdapter(); @@ -11,6 +11,7 @@ const run = async (): Promise => { to: sepoliaWETH, value: ethAmount, data: deposit, + chainId: SEPOLIA_CHAIN_ID, }); }; diff --git a/src/chains/ethereum.ts b/src/chains/ethereum.ts index 749d02b..3eb0f6f 100644 --- a/src/chains/ethereum.ts +++ b/src/chains/ethereum.ts @@ -1,9 +1,6 @@ import { Address, Hex, - PublicClient, - createPublicClient, - http, Hash, serializeTransaction, hashMessage, @@ -16,6 +13,7 @@ import { hashTypedData, TypedData, TypedDataDefinition, + parseTransaction, } from "viem"; import { BaseTx, @@ -28,41 +26,23 @@ import { MultichainContract } from "../mpcContract"; import BN from "bn.js"; import { queryGasPrice } from "../utils/gasPrice"; import { buildTxPayload, addSignature } from "../utils/transaction"; -// import { ethers } from "ethers"; +import { Network } from "../network"; export class NearEthAdapter { - ethClient: PublicClient; - private scanUrl: string; - private gasStationUrl: string; - - mpcContract: MultichainContract; - private derivationPath: string; - private sender: Address; + readonly mpcContract: MultichainContract; + readonly address: Address; + readonly derivationPath: string; private constructor(config: { - providerUrl: string; - scanUrl: string; - gasStationUrl: string; mpcContract: MultichainContract; derivationPath: string; sender: Address; }) { - this.ethClient = createPublicClient({ - transport: http(config.providerUrl), - }); - this.scanUrl = config.scanUrl; this.mpcContract = config.mpcContract; - this.gasStationUrl = config.gasStationUrl; this.derivationPath = config.derivationPath; - this.sender = config.sender; + this.address = config.sender; } - /** - * @returns ETH address derived by Near account via `derivationPath`. - */ - ethPublicKey(): Address { - return this.sender; - } /** * @returns Near accountId linked to derived ETH. */ @@ -76,13 +56,12 @@ export class NearEthAdapter { */ static async fromConfig(args: NearEthAdapterParams): Promise { // Sender is uniquely determined by the derivation path! - const mpcContract = args.near.mpcContract; - const derivationPath = args.near.derivationPath || "ethereum,1"; + const mpcContract = args.mpcContract; + const derivationPath = args.derivationPath || "ethereum,1"; return new NearEthAdapter({ sender: await mpcContract.deriveEthAddress(derivationPath), derivationPath, mpcContract, - ...args.evm, }); } @@ -96,7 +75,7 @@ export class NearEthAdapter { * Note that the signature request is a recursive function. */ async signAndSendTransaction(txData: BaseTx, nearGas?: BN): Promise { - console.log("Creating Payload for sender:", this.sender); + console.log("Creating Payload for sender:", this.address); const { transaction, signArgs } = await this.createTxPayload(txData); console.log("Requesting signature from Near..."); const { big_r, big_s } = await this.mpcContract.requestSignature( @@ -123,7 +102,7 @@ export class NearEthAdapter { transaction: Hex; requestPayload: NearContractFunctionPayload; }> { - console.log("Creating Payload for sender:", this.sender); + console.log("Creating Payload for sender:", this.address); const { transaction, signArgs } = await this.createTxPayload(txData); console.log("Requesting signature from Near..."); return { @@ -154,8 +133,8 @@ export class NearEthAdapter { * @param {number?} nonce - Optional transaction nonce. * @returns Transaction and its bytes (the payload to be signed on Near). */ - async createTxPayload(tx: BaseTx, nonce?: number): Promise { - const transaction = await this.buildTransaction(tx, nonce); + async createTxPayload(tx: BaseTx): Promise { + const transaction = await this.buildTransaction(tx); console.log("Built (unsigned) Transaction", transaction); const signArgs = { payload: buildTxPayload(transaction), @@ -165,38 +144,42 @@ export class NearEthAdapter { return { transaction, signArgs }; } - async buildTransaction(tx: BaseTx, nonce?: number): Promise { + /** + * Transforms minimal transaction request data into a fully populated EVM transaction. + * @param {BaseTx} tx - Minimal transaction request data + * @returns {Hex} serialized (aka RLP encoded) transaction. + */ + async buildTransaction(tx: BaseTx): Promise { + const network = Network.fromChainId(tx.chainId); const transactionData = { nonce: - nonce || - (await this.ethClient.getTransactionCount({ - address: this.sender, + tx.nonce || + (await network.client.getTransactionCount({ + address: this.address, })), - account: this.sender, + account: this.address, to: tx.to, value: tx.value ?? 0n, data: tx.data ?? "0x", }; - const [estimatedGas, { maxFeePerGas, maxPriorityFeePerGas }, chainId] = + const [estimatedGas, { maxFeePerGas, maxPriorityFeePerGas }] = await Promise.all([ - this.ethClient.estimateGas(transactionData), - queryGasPrice(this.gasStationUrl), - this.ethClient.getChainId(), + network.client.estimateGas(transactionData), + queryGasPrice(network.gasStationUrl), ]); const transactionDataWithGasLimit = { ...transactionData, gas: BigInt(estimatedGas.toString()), maxFeePerGas, maxPriorityFeePerGas, - chainId, + chainId: network.chainId, }; - console.log("Gas Estimation:", estimatedGas); console.log("Transaction Request", transactionDataWithGasLimit); return serializeTransaction(transactionDataWithGasLimit); } reconstructSignature(tx: TransactionWithSignature): Hex { - return addSignature(tx, this.sender); + return addSignature(tx, this.address); } /** @@ -207,11 +190,13 @@ export class NearEthAdapter { private async relaySignedTransaction( serializedTransaction: Hex ): Promise { - const txHash = await this.ethClient.sendRawTransaction({ + const tx = parseTransaction(serializedTransaction); + const network = Network.fromChainId(tx.chainId!); + const hash = await network.client.sendRawTransaction({ serializedTransaction, }); - console.log(`Transaction Confirmed: ${this.scanUrl}/tx/${txHash}`); - return txHash; + console.log(`Transaction Confirmed: ${network.scanUrl}/tx/${hash}`); + return hash; } // Below code is inspired by https://github.com/Connor-ETHSeoul/near-viem @@ -222,7 +207,7 @@ export class NearEthAdapter { const sigs = await this.sign(hashTypedData(typedData)); const common = { - address: this.ethPublicKey(), + address: this.address, types: typedData.types, /* eslint-disable @typescript-eslint/no-explicit-any */ primaryType: typedData.primaryType as any, @@ -246,7 +231,7 @@ export class NearEthAdapter { async signMessage(message: SignableMessage): Promise { const sigs = await this.sign(hashMessage(message)); const common = { - address: this.ethPublicKey(), + address: this.address, message, }; const validity = await Promise.all([ @@ -262,6 +247,11 @@ export class NearEthAdapter { return this.pickValidSignature(validity, sigs); } + /** + * Requests signature from Near MPC Contract. + * @param msgHash - Message Hash to be signed. + * @returns Two different potential signatures for the hash (one of which is valid). + */ async sign(msgHash: `0x${string}` | Uint8Array): Promise<[Hex, Hex]> { const hashToSign = isBytes(msgHash) ? msgHash : toBytes(msgHash); diff --git a/src/index.ts b/src/index.ts index 8fdf104..74b4ab4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from "./chains/ethereum"; export * from "./mpcContract"; export * from "./chains/near"; export * from "./types"; +export * from "./network"; diff --git a/src/network.ts b/src/network.ts new file mode 100644 index 0000000..2069e8c --- /dev/null +++ b/src/network.ts @@ -0,0 +1,84 @@ +import { Chain, createPublicClient, http, PublicClient } from "viem"; +import { sepolia, mainnet, gnosis, holesky } from "viem/chains"; + +// All supported networks +const SUPPORTED_NETWORKS = createNetworkMap([ + mainnet, + gnosis, + sepolia, + holesky, +]); + +interface NetworkFields { + name: string; + rpcUrl: string; + chainId: number; + scanUrl: string; + gasStationUrl: string; +} +/** + * Leveraging Network Data provided from through viem + * This class makes all relevant network fields accessible dynamically by chain ID. + */ +export class Network implements NetworkFields { + name: string; + rpcUrl: string; + chainId: number; + scanUrl: string; + gasStationUrl: string; + client: PublicClient; + + constructor({ + name, + rpcUrl, + chainId, + scanUrl, + gasStationUrl, + }: NetworkFields) { + const network = SUPPORTED_NETWORKS[chainId]; + + this.name = name; + this.rpcUrl = rpcUrl; + this.chainId = chainId; + this.scanUrl = scanUrl; + this.gasStationUrl = gasStationUrl; + this.client = createPublicClient({ + transport: http(network.rpcUrl), + }); + } + + /// Returns Network by ChainId + static fromChainId(chainId: number): Network { + const networkFields = SUPPORTED_NETWORKS[chainId]; + return new Network(networkFields); + } +} + +type NetworkMap = { [key: number]: NetworkFields }; + +/** + * This function is currently limited to networks supported by: + * https://status.beaconcha.in/ + */ +function gasStationUrl(network: Chain): string { + if (network.id === 1) { + return "https://beaconcha.in/api/v1/execution/gasnow"; + } + return `https://${network.name.toLowerCase()}.beaconcha.in/api/v1/execution/gasnow`; +} + +/// Dynamically generate network map accessible by chainId. +function createNetworkMap(supportedNetworks: Chain[]): NetworkMap { + const networkMap: NetworkMap = {}; + supportedNetworks.forEach((network) => { + networkMap[network.id] = { + name: network.name, + rpcUrl: network.rpcUrls.default.http[0], + chainId: network.id, + scanUrl: network.blockExplorers?.default.url || "", + gasStationUrl: gasStationUrl(network), + }; + }); + + return networkMap; +} diff --git a/src/types.ts b/src/types.ts index 28f2b8d..a1fe3e9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,25 +10,13 @@ export interface BaseTx { value?: bigint; /// Call Data of the transaction data?: `0x${string}`; + /// integer ID of the network for the transaction. + chainId: number; + /// Specified transaction nonce + nonce?: number; } export interface NearEthAdapterParams { - /// Near configuration. - near: NearParams; - /// EVM configuration. - evm: EvmParams; -} - -export interface EvmParams { - /// The URL of the Ethereum JSON RPC provider. - providerUrl: string; - /// The base URL of the blockchain explorer. - scanUrl: string; - /// The base URL of the blockchain gas station. - gasStationUrl: string; -} - -export interface NearParams { /// An instance of the NearMPC contract connected to the associated near account. mpcContract: MultichainContract; /// path used to generate ETH account from Near account (e.g. "ethereum,1") diff --git a/src/utils/gasPrice.ts b/src/utils/gasPrice.ts index 3646eb6..8526e42 100644 --- a/src/utils/gasPrice.ts +++ b/src/utils/gasPrice.ts @@ -14,7 +14,6 @@ interface GasPriceResponse { } export async function queryGasPrice(gasStationUrl: string): Promise { - console.log("Querying gas station:", gasStationUrl); const res = await fetch(gasStationUrl); const gasPrices = (await res.json()) as GasPriceResponse; const maxPriorityFeePerGas = BigInt(getFirstNonZeroGasPrice(gasPrices)!); diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index da3e29c..e47a382 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -1,14 +1,15 @@ -import { setupNearEthAdapter } from "../examples/setup"; -import { NearEthAdapter } from "../src"; +import { SEPOLIA_CHAIN_ID, setupNearEthAdapter } from "../examples/setup"; +import { NearEthAdapter, Network } from "../src"; import { getBalance } from "viem/actions"; describe("End To End", () => { - let evm: NearEthAdapter; + let adapter: NearEthAdapter; const to = "0xdeADBeeF0000000000000000000000000b00B1e5"; const ONE_WEI = 1n; + const chainId = SEPOLIA_CHAIN_ID; beforeAll(async () => { - evm = await setupNearEthAdapter(); + adapter = await setupNearEthAdapter(); }); afterAll(async () => { @@ -17,21 +18,43 @@ describe("End To End", () => { it("signAndSendTransaction", async () => { await expect( - evm.signAndSendTransaction({ to, value: ONE_WEI }) + adapter.signAndSendTransaction({ + // Sending 1 WEI to self (so we never run out of funds) + to: adapter.address, + value: ONE_WEI, + chainId, + }) + ).resolves.not.toThrow(); + }); + + it.skip("signAndSendTransaction - Gnosis Chain", async () => { + await expect( + adapter.signAndSendTransaction({ + // Sending 1 WEI to self (so we never run out of funds) + to: adapter.address, + value: ONE_WEI, + // Gnosis Chain! + chainId: 100, + }) ).resolves.not.toThrow(); }); it("Fails to sign and send", async () => { - const senderBalance = await getBalance(evm.ethClient, { - address: evm.ethPublicKey(), + const network = Network.fromChainId(chainId); + const senderBalance = await getBalance(network.client, { + address: adapter.address, }); await expect( - evm.signAndSendTransaction({ to, value: senderBalance + ONE_WEI }) + adapter.signAndSendTransaction({ + to, + value: senderBalance + ONE_WEI, + chainId, + }) ).rejects.toThrow(); }); it("signMessage", async () => { - await expect(evm.signMessage("NearEth")).resolves.not.toThrow(); + await expect(adapter.signMessage("NearEth")).resolves.not.toThrow(); }); it("signTypedData", async () => { @@ -67,7 +90,7 @@ describe("End To End", () => { } as const; await expect( - evm.signTypedData({ + adapter.signTypedData({ types, primaryType: "Mail", message,