@abstraxn/relayer
Version:
Abstraxn Relayer package for handling gas-less transactions, facilitating smart contract interactions, and relaying user transactions efficiently.
222 lines • 9.01 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Relayer = void 0;
const ethers_1 = require("ethers");
const httpRequests_1 = require("./lib/httpRequests");
const HelperFunction_1 = require("./utils/HelperFunction");
const WebSocketManager_1 = require("./lib/WebSocketManager");
class Relayer {
constructor(relayerConfig) {
var _a;
this.relayerConfig = relayerConfig;
if (!relayerConfig.provider || !relayerConfig.signer) {
throw new Error("RelayerConfig must include a valid provider and signer.");
}
this.relayerUrl = relayerConfig.relayerUrl;
this.chainId = relayerConfig.chainId;
this.signer = relayerConfig.signer;
this.provider = relayerConfig.provider;
// Initialize WebSocket manager
this.webSocketManager = new WebSocketManager_1.WebSocketManager(this.relayerUrl, relayerConfig.webSocket);
// Auto-connect if enabled
if (((_a = relayerConfig.webSocket) === null || _a === void 0 ? void 0 : _a.autoConnect) !== false) {
this.webSocketManager.connect();
}
}
getRelayerUrl() {
return `${this.relayerUrl}`;
}
async buildRelayerTx(params) {
try {
const { abi, contractAddress, method, args } = params;
if (!abi || !contractAddress || !method) {
throw new Error("Invalid parameters: 'abi', 'contractAddress', and 'method' are required.");
}
// Create contract instance
const contract = new ethers_1.Contract(contractAddress, abi, this.provider);
// Encode function signature
const functionSignature = contract.interface.encodeFunctionData(method, args);
// Get user address
const userAddress = await this.signer.getAddress();
// Fetch nonce from contract
const nonce = await contract.getNonce(userAddress);
// Generate meta-transaction hash
const metaTxHash = ethers_1.ethers.solidityPackedKeccak256(["uint256", "address", "uint256", "bytes"], [nonce, contractAddress, this.chainId, functionSignature]);
// Sign the transaction hash
const signature = await this.signer.signMessage(ethers_1.ethers.getBytes(metaTxHash));
const sig = ethers_1.ethers.Signature.from(signature);
const { r, s, v } = sig;
const executeMetaData = contract.interface.encodeFunctionData("executeMetaTransaction", [userAddress, functionSignature, r, s, v]);
return {
userAddress,
functionSignature,
signature,
data: executeMetaData,
chainId: this.chainId,
contractAddress,
};
}
catch (error) {
console.error("Error in buildRelayerTx:", error);
throw new Error(`Failed to build relayer transaction: ${error instanceof Error ? error.message : error}`);
}
}
async buildRelayerTxEIP712(params) {
try {
const { abi, contractAddress, method, args } = params;
if (!abi || !contractAddress || !method) {
throw new Error("Invalid parameters: 'abi', 'contractAddress', and 'method' are required.");
}
const contract = new ethers_1.Contract(contractAddress, abi, this.provider);
const userAddress = await this.signer.getAddress();
const functionSignature = contract.interface.encodeFunctionData(method, args);
const nonce = await contract.getNonce(userAddress);
const domain = {
name: typeof contract.name === "function" ? await contract.name() : "MetaTx",
version: "1",
verifyingContract: contractAddress,
salt: ethers_1.ethers.zeroPadValue(ethers_1.ethers.toBeHex(this.chainId), 32),
};
const types = {
MetaTransaction: [
{ name: "nonce", type: "uint256" },
{ name: "from", type: "address" },
{ name: "functionSignature", type: "bytes" },
],
};
const message = {
nonce: nonce.toString(),
from: userAddress,
functionSignature,
};
let signature;
let r, s;
let v;
if (typeof this.signer.signTypedData === "function") {
signature = await this.signer.signTypedData(domain, types, message);
const sig = ethers_1.ethers.Signature.from(signature);
({ r, s, v } = sig);
}
else if (typeof this.signer._signTypedData === "function") {
signature = await this.signer._signTypedData(domain, types, message);
const sig = ethers_1.ethers.Signature.from(signature);
({ r, s, v } = sig);
}
else {
throw new Error("Signer does not support EIP-712 signing.");
}
const executeMetaData = contract.interface.encodeFunctionData("executeMetaTransaction", [
userAddress,
functionSignature,
r,
s,
v,
]);
return {
userAddress,
functionSignature,
signature,
data: executeMetaData,
chainId: this.chainId,
contractAddress,
};
}
catch (error) {
console.error("Error in buildRelayerTxEIP712:", error);
throw new Error(`Failed to build EIP-712 relayer transaction: ${error instanceof Error ? error.message : error}`);
}
}
async sendRelayerTx(txParams) {
const relayerUrl = this.getRelayerUrl();
const sendRelayerResponse = await (0, httpRequests_1.sendRequest)({
url: relayerUrl,
method: httpRequests_1.HttpMethod.Post,
body: {
method: "eth_sendTransactionToRelayer",
params: txParams,
id: (0, HelperFunction_1.getTimestampInSeconds)(),
jsonrpc: "2.0",
},
});
const response = {
message: sendRelayerResponse.message,
transactionId: sendRelayerResponse.result,
};
return response;
}
async getRelayerTxStatus(transactionId) {
const relayerUrl = this.getRelayerUrl();
const response = await (0, httpRequests_1.sendRequest)({
url: relayerUrl,
method: httpRequests_1.HttpMethod.Post,
body: {
method: "abstraxn_getRelayerTxStatus",
params: [transactionId],
id: (0, HelperFunction_1.getTimestampInSeconds)(),
jsonrpc: "2.0",
},
});
const txStatus = response.result;
return txStatus;
}
// WebSocket Methods
/**
* Send relayer transaction with real-time updates via WebSocket
*/
async sendRelayerTxWithRealTimeUpdates(params) {
// Send the transaction first
const response = await this.sendRelayerTx(params);
// If real-time updates are enabled, subscribe to transaction updates
if (params.enableRealTimeUpdates && response.transactionId) {
try {
await this.subscribeToTransaction(response.transactionId, params.webSocketEvents);
}
catch (error) {
console.warn('Failed to subscribe to transaction updates:', error);
// Don't throw error here as the transaction was still sent successfully
}
}
return response;
}
/**
* Subscribe to real-time transaction updates
*/
async subscribeToTransaction(transactionId, events) {
if (!this.webSocketManager.connected()) {
throw new Error('WebSocket not connected. Call connectWebSocket() first.');
}
return this.webSocketManager.subscribeToTransaction(transactionId, events || {});
}
/**
* Unsubscribe from transaction updates
*/
unsubscribeFromTransaction(transactionId) {
this.webSocketManager.unsubscribeFromTransaction(transactionId);
}
/**
* Connect to WebSocket server
*/
connectWebSocket() {
this.webSocketManager.connect();
}
/**
* Disconnect from WebSocket server
*/
disconnectWebSocket() {
this.webSocketManager.disconnect();
}
/**
* Check if WebSocket is connected
*/
isWebSocketConnected() {
return this.webSocketManager.connected();
}
/**
* Get WebSocket connection information
*/
getWebSocketInfo() {
return this.webSocketManager.getConnectionInfo();
}
}
exports.Relayer = Relayer;
//# sourceMappingURL=Relayer.js.map