257 lines
7.1 KiB
JavaScript
257 lines
7.1 KiB
JavaScript
const {app, dialog, ipcMain} = require("electron");
|
|
const path = require("path");
|
|
const fs = require("fs");
|
|
const fetch = require("node-fetch");
|
|
|
|
class PaperclipRPC {
|
|
constructor() {
|
|
this.isConnected = false;
|
|
this.rpcUrl = "http://localhost:26657";
|
|
this.logEvents = false;
|
|
|
|
// create the user data dir (needed for MacOS)
|
|
if (!fs.existsSync(app.getPath("userData"))) {
|
|
fs.mkdirSync(app.getPath("userData"));
|
|
}
|
|
|
|
if (this.logEvents) {
|
|
this.logStream = fs.createWriteStream(path.join(app.getPath("userData"), "paperclip-rpc.log"), {flags: "a"});
|
|
}
|
|
}
|
|
|
|
_writeLog(text) {
|
|
if (this.logEvents) {
|
|
this.logStream.write(`${new Date().toISOString()}: ${text}\n`);
|
|
}
|
|
// Suppress console logging in production
|
|
if (process.env.NODE_ENV === 'development') {
|
|
console.log("PaperclipRPC:", text);
|
|
}
|
|
}
|
|
|
|
async _makeRPCCall(method, params = {}) {
|
|
try {
|
|
const url = `${this.rpcUrl}/${method}`;
|
|
const queryParams = new URLSearchParams();
|
|
|
|
Object.keys(params).forEach(key => {
|
|
queryParams.append(key, params[key]);
|
|
});
|
|
|
|
const fullUrl = queryParams.toString() ? `${url}?${queryParams}` : url;
|
|
|
|
this._writeLog(`Making RPC call to: ${fullUrl}`);
|
|
|
|
const response = await fetch(fullUrl);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
return data;
|
|
} catch (error) {
|
|
this._writeLog(`RPC call failed: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getStatus() {
|
|
try {
|
|
const result = await this._makeRPCCall("status");
|
|
this.isConnected = true;
|
|
return result;
|
|
} catch (error) {
|
|
this.isConnected = false;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getBalance(address) {
|
|
try {
|
|
const result = await this._makeRPCCall("abci_query", {
|
|
path: `"balance:${address}"`
|
|
});
|
|
|
|
if (result.result && result.result.response && result.result.response.value) {
|
|
// Decode base64 response
|
|
const balance = Buffer.from(result.result.response.value, 'base64').toString();
|
|
return parseInt(balance) || 0;
|
|
}
|
|
return 0;
|
|
} catch (error) {
|
|
this._writeLog(`Failed to get balance for ${address}: ${error.message}`);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
async getNonce(address) {
|
|
try {
|
|
const result = await this._makeRPCCall("abci_query", {
|
|
path: `"nonce:${address}"`
|
|
});
|
|
|
|
if (result.result && result.result.response && result.result.response.value) {
|
|
const nonce = Buffer.from(result.result.response.value, 'base64').toString();
|
|
return parseInt(nonce) || 0;
|
|
}
|
|
return 0;
|
|
} catch (error) {
|
|
this._writeLog(`Failed to get nonce for ${address}: ${error.message}`);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
async broadcastTransaction(txHex) {
|
|
try {
|
|
const result = await this._makeRPCCall("broadcast_tx_commit", {
|
|
tx: txHex
|
|
});
|
|
|
|
if (result.result && result.result.check_tx && result.result.check_tx.code === 0) {
|
|
this._writeLog(`Transaction broadcast successful: ${result.result.hash}`);
|
|
return {
|
|
success: true,
|
|
hash: result.result.hash,
|
|
height: result.result.height
|
|
};
|
|
} else {
|
|
const error = result.result?.check_tx?.log || "Transaction failed";
|
|
throw new Error(error);
|
|
}
|
|
} catch (error) {
|
|
this._writeLog(`Failed to broadcast transaction: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getTransaction(hash) {
|
|
try {
|
|
const result = await this._makeRPCCall("tx", {
|
|
hash: hash,
|
|
prove: "false"
|
|
});
|
|
return result;
|
|
} catch (error) {
|
|
this._writeLog(`Failed to get transaction ${hash}: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getBlock(height = null) {
|
|
try {
|
|
const params = height ? { height: height.toString() } : {};
|
|
const result = await this._makeRPCCall("block", params);
|
|
return result;
|
|
} catch (error) {
|
|
this._writeLog(`Failed to get block: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getContractInfo(contractAddress) {
|
|
try {
|
|
const result = await this._makeRPCCall("abci_query", {
|
|
path: `"contract:${contractAddress}"`
|
|
});
|
|
|
|
if (result.result && result.result.response && result.result.response.value) {
|
|
const contractInfo = Buffer.from(result.result.response.value, 'base64').toString();
|
|
return JSON.parse(contractInfo);
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
this._writeLog(`Failed to get contract info for ${contractAddress}: ${error.message}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async getMultisigInfo(multisigAddress) {
|
|
try {
|
|
const result = await this._makeRPCCall("abci_query", {
|
|
path: `"multisig:${multisigAddress}"`
|
|
});
|
|
|
|
if (result.result && result.result.response && result.result.response.value) {
|
|
const multisigInfo = Buffer.from(result.result.response.value, 'base64').toString();
|
|
return JSON.parse(multisigInfo);
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
this._writeLog(`Failed to get multisig info for ${multisigAddress}: ${error.message}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async getFeePool() {
|
|
try {
|
|
const result = await this._makeRPCCall("abci_query", {
|
|
path: `"fee_pool"`
|
|
});
|
|
|
|
if (result.result && result.result.response && result.result.response.value) {
|
|
const feePool = Buffer.from(result.result.response.value, 'base64').toString();
|
|
return parseInt(feePool) || 0;
|
|
}
|
|
return 0;
|
|
} catch (error) {
|
|
this._writeLog(`Failed to get fee pool: ${error.message}`);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
setRPCUrl(url) {
|
|
this.rpcUrl = url;
|
|
this._writeLog(`RPC URL updated to: ${url}`);
|
|
}
|
|
|
|
startConnection() {
|
|
this._writeLog("Starting PaperclipChain RPC connection");
|
|
|
|
// Test connection
|
|
this.getStatus()
|
|
.then(() => {
|
|
this._writeLog("Connected to PaperclipChain node");
|
|
})
|
|
.catch((error) => {
|
|
this._writeLog(`Connection failed: ${error.message}`);
|
|
});
|
|
}
|
|
|
|
stopConnection() {
|
|
this._writeLog("Stopping PaperclipChain RPC connection");
|
|
this.isConnected = false;
|
|
|
|
if (this.logStream) {
|
|
this.logStream.end();
|
|
}
|
|
}
|
|
}
|
|
|
|
const PaperclipNode = new PaperclipRPC();
|
|
|
|
// IPC handlers for renderer process
|
|
ipcMain.handle("paperclip-get-status", async () => {
|
|
return await PaperclipNode.getStatus();
|
|
});
|
|
|
|
ipcMain.handle("paperclip-get-balance", async (event, address) => {
|
|
return await PaperclipNode.getBalance(address);
|
|
});
|
|
|
|
ipcMain.handle("paperclip-get-nonce", async (event, address) => {
|
|
return await PaperclipNode.getNonce(address);
|
|
});
|
|
|
|
ipcMain.handle("paperclip-broadcast-tx", async (event, txHex) => {
|
|
return await PaperclipNode.broadcastTransaction(txHex);
|
|
});
|
|
|
|
ipcMain.handle("paperclip-get-transaction", async (event, hash) => {
|
|
return await PaperclipNode.getTransaction(hash);
|
|
});
|
|
|
|
ipcMain.handle("paperclip-get-block", async (event, height) => {
|
|
return await PaperclipNode.getBlock(height);
|
|
});
|
|
|
|
module.exports = PaperclipNode; |