near-ca
Version:
An SDK for controlling Ethereum Accounts from a Near Account.
127 lines (126 loc) • 4.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MpcContract = void 0;
const near_api_js_1 = require("near-api-js");
const utils_1 = require("./utils");
const chains_1 = require("./chains");
/**
* High-level interface for the Near MPC-Recovery Contract
* located in: https://github.com/near/mpc-recovery
*/
class MpcContract {
/**
* Creates a new MPC Contract instance
*
* @param account - The NEAR account to use
* @param contractId - The contract ID
* @param rootPublicKey - Optional root public key
*/
constructor(account, contractId, rootPublicKey) {
/**
* Derives an Ethereum address from a derivation path
*
* @param derivationPath - The path to derive the address from
* @returns The derived Ethereum address
*/
this.deriveEthAddress = async (derivationPath) => {
if (!this.rootPublicKey) {
this.rootPublicKey = await this.contract.public_key();
}
const publicKey = (0, utils_1.deriveChildPublicKey)((0, utils_1.najPublicKeyStrToUncompressedHexPoint)(this.rootPublicKey), this.connectedAccount.accountId, derivationPath);
return (0, utils_1.uncompressedHexPointToEvmAddress)(publicKey);
};
/**
* Gets the required deposit for the signature
*
* @returns The required deposit amount as a string
*/
this.getDeposit = async () => {
try {
const deposit = await this.contract.experimental_signature_deposit();
return BigInt(deposit.toLocaleString("fullwide", { useGrouping: false })).toString();
}
catch {
// They are phasing out experimental_signature_deposit.
// required deposit is 1 yocto (see v1.signer-prod.testnet).
return "1";
}
};
/**
* Requests a signature from the MPC contract
*
* @param signArgs - The arguments for the signature request
* @param gas - Optional gas limit
* @returns The signature
*/
this.requestSignature = async (signArgs, gas) => {
const transaction = await this.encodeSignatureRequestTx(signArgs, gas);
const outcome = await this.signAndSendSignRequest(transaction);
return (0, utils_1.signatureFromOutcome)(outcome);
};
this.connectedAccount = account;
this.rootPublicKey = rootPublicKey;
this.contract = new near_api_js_1.Contract(account.getConnection(), contractId, {
changeMethods: ["sign"],
viewMethods: ["public_key", "experimental_signature_deposit"],
useLocalViewExecution: false,
});
}
/**
* Gets the contract ID
*
* @returns The contract ID
*/
accountId() {
return this.contract.contractId;
}
/**
* Encodes a signature request into a transaction
*
* @param signArgs - The arguments for the signature request
* @param gas - Optional gas limit
* @returns The encoded transaction
*/
async encodeSignatureRequestTx(signArgs, gas) {
return {
signerId: this.connectedAccount.accountId,
receiverId: this.contract.contractId,
actions: [
{
type: "FunctionCall",
params: {
methodName: "sign",
args: { request: signArgs },
gas: gasOrDefault(gas),
deposit: await this.getDeposit(),
},
},
],
};
}
/**
* Signs and sends a signature request
*
* @param transaction - The transaction to sign and send
* @param blockTimeout - Optional timeout in blocks
* @returns The execution outcome
*/
async signAndSendSignRequest(transaction) {
const account = this.connectedAccount;
const signedTx = await account.createSignedTransaction(this.contract.contractId, transaction.actions.map(({ params: { args, gas, deposit } }) => near_api_js_1.transactions.functionCall("sign", args, BigInt(gas), BigInt(deposit))));
return account.provider.sendTransactionUntil(signedTx, "EXECUTED");
}
}
exports.MpcContract = MpcContract;
/**
* Returns the gas value or a default if not provided
*
* @param gas - Optional gas value
* @returns The gas value as a string
*/
function gasOrDefault(gas) {
if (gas !== undefined) {
return gas.toString();
}
return (chains_1.TGAS * 250n).toString();
}