near-ca
Version:
An SDK for controlling Ethereum Accounts from a Near Account.
173 lines (172 loc) • 6.57 kB
JavaScript
import { serializeTransaction, hashMessage, serializeSignature, hashTypedData, } from "viem";
import { Network, buildTxPayload, populateTx, toPayload, broadcastSignedTransaction, } from "..";
import { Beta } from "../beta";
import { requestRouter } from "../utils";
/**
* Adapter class for interacting with Ethereum through NEAR MPC contract
*/
export class NearEthAdapter {
mpcContract;
address;
derivationPath;
beta;
constructor(config) {
this.mpcContract = config.mpcContract;
this.derivationPath = config.derivationPath;
this.address = config.sender;
this.beta = new Beta(this);
}
/**
* Gets the NEAR account ID linked to derived EVM account
*
* @returns The connected NEAR account ID
*/
nearAccountId() {
return this.mpcContract.connectedAccount.accountId;
}
/**
* Retrieves the balance of the Ethereum address associated with this adapter
*
* @param chainId - The chain ID of the Ethereum network to query
* @returns The balance of the address in wei
*/
async getBalance(chainId) {
const network = Network.fromChainId(chainId);
return network.client.getBalance({ address: this.address });
}
/**
* Constructs an EVM instance with the provided configuration
*
* @param args - The configuration object for the Adapter instance
* @returns A new NearEthAdapter instance
*/
static async fromConfig(args) {
const mpcContract = args.mpcContract;
const derivationPath = args.derivationPath || "ethereum,1";
return new NearEthAdapter({
sender: await mpcContract.deriveEthAddress(derivationPath),
derivationPath,
mpcContract,
});
}
/**
* Takes a minimally declared Ethereum Transaction,
* builds the full transaction payload (with gas estimates, prices etc...),
* acquires signature from Near MPC Contract and submits transaction to public mempool
*
* @param txData - Minimal transaction data to be signed by Near MPC and executed on EVM
* @param nearGas - Manually specified gas to be sent with signature request
* @returns The ethereum transaction hash
*/
async signAndSendTransaction(txData, nearGas) {
const { transaction, signArgs } = await this.createTxPayload(txData);
const signature = await this.mpcContract.requestSignature(signArgs, nearGas);
return broadcastSignedTransaction({ transaction, signature });
}
/**
* Takes a minimally declared Ethereum Transaction,
* builds the full transaction payload (with gas estimates, prices etc...),
* and prepares the signature request payload for the Near MPC Contract
*
* @param txData - Minimal transaction data to be signed by Near MPC and executed on EVM
* @param nearGas - Manually specified gas to be sent with signature request
* @returns The transaction and request payload
*/
async getSignatureRequestPayload(txData, nearGas) {
const { transaction, signArgs } = await this.createTxPayload(txData);
return {
transaction,
requestPayload: await this.mpcContract.encodeSignatureRequestTx(signArgs, nearGas),
};
}
/**
* Builds a Near Transaction Payload for Signing serialized EVM Transaction
*
* @param transaction - RLP encoded (i.e. serialized) Ethereum Transaction
* @param nearGas - Optional gas parameter
* @returns Prepared Near Transaction with signerId as this.address
*/
async mpcSignRequest(transaction, nearGas) {
return this.mpcContract.encodeSignatureRequestTx({
payload: buildTxPayload(transaction),
path: this.derivationPath,
key_version: 0,
}, nearGas);
}
/**
* Builds a complete, unsigned transaction (with nonce, gas estimates, current prices)
* and payload bytes in preparation to be relayed to Near MPC contract
*
* @param tx - Minimal transaction data to be signed by Near MPC and executed on EVM
* @returns Transaction and its bytes (the payload to be signed on Near)
*/
async createTxPayload(tx) {
const transaction = await this.buildTransaction(tx);
const signArgs = {
payload: buildTxPayload(transaction),
path: this.derivationPath,
key_version: 0,
};
return { transaction, signArgs };
}
/**
* Transforms minimal transaction request data into a fully populated EVM transaction
*
* @param tx - Minimal transaction request data
* @returns Serialized (aka RLP encoded) transaction
*/
async buildTransaction(tx) {
const transaction = await populateTx(tx, this.address);
return serializeTransaction(transaction);
}
/**
* Signs typed data according to EIP-712
*
* @param typedData - The typed data to sign
* @returns The signature hash
*/
async signTypedData(typedData) {
return this.sign(hashTypedData(typedData));
}
/**
* Signs a message according to personal_sign
*
* @param message - The message to sign
* @returns The signature hash
*/
async signMessage(message) {
return this.sign(hashMessage(message));
}
/**
* 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) {
const signature = await this.mpcContract.requestSignature({
path: this.derivationPath,
payload: toPayload(msgHash),
key_version: 0,
});
return serializeSignature(signature);
}
/**
* Encodes a signature request for submission to the Near-Ethereum transaction MPC contract
*
* @param signRequest - The signature request data containing method, chain ID, and parameters
* @returns The encoded Near-Ethereum transaction data, original EVM message, and recovery data
*/
async encodeSignRequest(signRequest) {
const { evmMessage, hashToSign } = await requestRouter(signRequest);
return {
nearPayload: await this.mpcContract.encodeSignatureRequestTx({
path: this.derivationPath,
payload: toPayload(hashToSign),
key_version: 0,
}),
evmMessage,
hashToSign,
};
}
}