/** * Synor Provider * * Custom JSON-RPC provider for Synor blockchain. */ import { HardhatRuntimeEnvironment } from "hardhat/types"; import { TransactionRequest, TransactionResponse, TransactionReceipt, Log } from "./type-extensions"; /** * Synor-specific JSON-RPC provider */ export class SynorProvider { private hre: HardhatRuntimeEnvironment; private rpcUrl: string; constructor(hre: HardhatRuntimeEnvironment) { this.hre = hre; this.rpcUrl = this.getNetworkUrl(); } /** * Gets the RPC URL for the current network */ private getNetworkUrl(): string { const network = this.hre.network.config; if ("url" in network) { return network.url; } return "http://localhost:8545"; } /** * Makes an RPC call */ async rpc(method: string, params: any[] = []): Promise { const response = await fetch(this.rpcUrl, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ jsonrpc: "2.0", id: Date.now(), method, params, }), }); const data = await response.json(); if (data.error) { throw new Error(`RPC Error: ${data.error.message}`); } return data.result; } /** * Gets account balance */ async getBalance(address: string, blockTag: string = "latest"): Promise { const result = await this.rpc("eth_getBalance", [address, blockTag]); return BigInt(result); } /** * Gets current block number */ async getBlockNumber(): Promise { const result = await this.rpc("eth_blockNumber"); return parseInt(result, 16); } /** * Gets a block by number or hash */ async getBlock(blockHashOrNumber: string | number, includeTransactions: boolean = false): Promise { const method = typeof blockHashOrNumber === "number" ? "eth_getBlockByNumber" : "eth_getBlockByHash"; const param = typeof blockHashOrNumber === "number" ? `0x${blockHashOrNumber.toString(16)}` : blockHashOrNumber; return this.rpc(method, [param, includeTransactions]); } /** * Gets transaction by hash */ async getTransaction(hash: string): Promise { const tx = await this.rpc("eth_getTransactionByHash", [hash]); if (!tx) return null; return { hash: tx.hash, from: tx.from, to: tx.to, value: BigInt(tx.value || "0"), gasLimit: BigInt(tx.gas || "0"), gasPrice: BigInt(tx.gasPrice || "0"), nonce: parseInt(tx.nonce, 16), data: tx.input || "0x", blockNumber: tx.blockNumber ? parseInt(tx.blockNumber, 16) : undefined, blockHash: tx.blockHash, timestamp: undefined, // Would need to fetch block for timestamp confirmations: 0, // Would need current block to calculate wait: async (confirmations = 1) => this.waitForTransaction(hash, confirmations), }; } /** * Gets transaction receipt */ async getTransactionReceipt(hash: string): Promise { const receipt = await this.rpc("eth_getTransactionReceipt", [hash]); if (!receipt) return null; return { hash: receipt.transactionHash, blockNumber: parseInt(receipt.blockNumber, 16), blockHash: receipt.blockHash, transactionIndex: parseInt(receipt.transactionIndex, 16), from: receipt.from, to: receipt.to, contractAddress: receipt.contractAddress, gasUsed: BigInt(receipt.gasUsed), status: parseInt(receipt.status, 16), logs: receipt.logs.map((log: any) => this.parseLog(log)), events: [], // Would need ABI to parse events }; } /** * Waits for transaction confirmation */ async waitForTransaction(hash: string, confirmations: number = 1): Promise { const timeout = this.hre.config.synor.timeout; const startTime = Date.now(); while (Date.now() - startTime < timeout) { const receipt = await this.getTransactionReceipt(hash); if (receipt) { const currentBlock = await this.getBlockNumber(); const txConfirmations = currentBlock - receipt.blockNumber + 1; if (txConfirmations >= confirmations) { return receipt; } } // Wait before next check await new Promise(resolve => setTimeout(resolve, 1000)); } throw new Error(`Transaction ${hash} not confirmed within ${timeout}ms`); } /** * Sends a transaction */ async sendTransaction(tx: TransactionRequest): Promise { const txData: any = { from: tx.from, to: tx.to, value: tx.value ? `0x${BigInt(tx.value).toString(16)}` : "0x0", data: tx.data || "0x", }; if (tx.gasLimit) { txData.gas = `0x${tx.gasLimit.toString(16)}`; } if (tx.gasPrice) { txData.gasPrice = `0x${tx.gasPrice.toString(16)}`; } if (tx.nonce !== undefined) { txData.nonce = `0x${tx.nonce.toString(16)}`; } const hash = await this.rpc("eth_sendTransaction", [txData]); return { hash, from: tx.from || "", to: tx.to, value: BigInt(tx.value || 0), gasLimit: BigInt(tx.gasLimit || 0), gasPrice: tx.gasPrice || BigInt(0), nonce: tx.nonce || 0, data: tx.data || "0x", confirmations: 0, wait: async (confirmations = 1) => this.waitForTransaction(hash, confirmations), }; } /** * Sends a raw transaction */ async sendRawTransaction(signedTx: string): Promise { return this.rpc("eth_sendRawTransaction", [signedTx]); } /** * Calls a contract (read-only) */ async call(tx: TransactionRequest, blockTag: string = "latest"): Promise { const txData = { from: tx.from, to: tx.to, data: tx.data, value: tx.value ? `0x${BigInt(tx.value).toString(16)}` : undefined, }; return this.rpc("eth_call", [txData, blockTag]); } /** * Estimates gas for a transaction */ async estimateGas(tx: TransactionRequest): Promise { const txData = { from: tx.from, to: tx.to, data: tx.data, value: tx.value ? `0x${BigInt(tx.value).toString(16)}` : undefined, }; const result = await this.rpc("eth_estimateGas", [txData]); return BigInt(result); } /** * Gets current gas price */ async getGasPrice(): Promise { const result = await this.rpc("eth_gasPrice"); return BigInt(result); } /** * Gets account nonce */ async getTransactionCount(address: string, blockTag: string = "pending"): Promise { const result = await this.rpc("eth_getTransactionCount", [address, blockTag]); return parseInt(result, 16); } /** * Gets contract code */ async getCode(address: string, blockTag: string = "latest"): Promise { return this.rpc("eth_getCode", [address, blockTag]); } /** * Gets storage at position */ async getStorageAt(address: string, position: string, blockTag: string = "latest"): Promise { return this.rpc("eth_getStorageAt", [address, position, blockTag]); } /** * Gets logs matching filter */ async getLogs(filter: { fromBlock?: string | number; toBlock?: string | number; address?: string | string[]; topics?: (string | string[] | null)[]; }): Promise { const filterData: any = {}; if (filter.fromBlock !== undefined) { filterData.fromBlock = typeof filter.fromBlock === "number" ? `0x${filter.fromBlock.toString(16)}` : filter.fromBlock; } if (filter.toBlock !== undefined) { filterData.toBlock = typeof filter.toBlock === "number" ? `0x${filter.toBlock.toString(16)}` : filter.toBlock; } if (filter.address) { filterData.address = filter.address; } if (filter.topics) { filterData.topics = filter.topics; } const logs = await this.rpc("eth_getLogs", [filterData]); return logs.map(log => this.parseLog(log)); } /** * Parses a raw log */ private parseLog(log: any): Log { return { blockNumber: parseInt(log.blockNumber, 16), blockHash: log.blockHash, transactionIndex: parseInt(log.transactionIndex, 16), transactionHash: log.transactionHash, logIndex: parseInt(log.logIndex, 16), address: log.address, topics: log.topics, data: log.data, }; } // Synor-specific methods /** * Gets quantum signature status */ async getQuantumStatus(): Promise<{ enabled: boolean; algorithm: string }> { return this.rpc("synor_getQuantumStatus"); } /** * Gets shard information */ async getShardInfo(): Promise<{ shardId: number; totalShards: number }> { return this.rpc("synor_getShardInfo"); } /** * Gets DAG block information */ async getDagBlock(hash: string): Promise { return this.rpc("synor_getDagBlock", [hash]); } /** * Gets pending cross-shard messages */ async getPendingCrossShardMessages(address: string): Promise { return this.rpc("synor_getPendingCrossShardMessages", [address]); } }