synor/apps/hardhat-plugin/src/provider.ts
Gulshan Yadav 8b152a5a23 feat(tooling): add Phase 14 M4 - Developer Tooling
Adds formal verification DSL, multi-sig contract, and Hardhat plugin:

synor-verifier crate:
- Verification DSL for contract invariants and properties
- SMT solver integration (Z3 backend optional)
- Symbolic execution engine for path exploration
- Automatic vulnerability detection (reentrancy, overflow, etc.)
- 29 tests passing

contracts/multi-sig:
- M-of-N multi-signature wallet contract
- Transaction proposals with timelock
- Owner management (add/remove)
- Emergency pause functionality
- Native token and contract call support

apps/hardhat-plugin (@synor/hardhat-plugin):
- Network configuration for mainnet/testnet/devnet
- Contract deployment with gas estimation
- Contract verification on explorer
- WASM compilation support
- TypeScript type generation
- Testing utilities (fork, impersonate, time manipulation)
- Synor-specific RPC methods (quantum status, shard info, DAG)
2026-01-19 20:55:56 +05:30

349 lines
9.1 KiB
TypeScript

/**
* 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<T>(method: string, params: any[] = []): Promise<T> {
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<bigint> {
const result = await this.rpc<string>("eth_getBalance", [address, blockTag]);
return BigInt(result);
}
/**
* Gets current block number
*/
async getBlockNumber(): Promise<number> {
const result = await this.rpc<string>("eth_blockNumber");
return parseInt(result, 16);
}
/**
* Gets a block by number or hash
*/
async getBlock(blockHashOrNumber: string | number, includeTransactions: boolean = false): Promise<any> {
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<TransactionResponse | null> {
const tx = await this.rpc<any>("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<TransactionReceipt | null> {
const receipt = await this.rpc<any>("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<TransactionReceipt> {
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<TransactionResponse> {
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<string>("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<string> {
return this.rpc<string>("eth_sendRawTransaction", [signedTx]);
}
/**
* Calls a contract (read-only)
*/
async call(tx: TransactionRequest, blockTag: string = "latest"): Promise<string> {
const txData = {
from: tx.from,
to: tx.to,
data: tx.data,
value: tx.value ? `0x${BigInt(tx.value).toString(16)}` : undefined,
};
return this.rpc<string>("eth_call", [txData, blockTag]);
}
/**
* Estimates gas for a transaction
*/
async estimateGas(tx: TransactionRequest): Promise<bigint> {
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<string>("eth_estimateGas", [txData]);
return BigInt(result);
}
/**
* Gets current gas price
*/
async getGasPrice(): Promise<bigint> {
const result = await this.rpc<string>("eth_gasPrice");
return BigInt(result);
}
/**
* Gets account nonce
*/
async getTransactionCount(address: string, blockTag: string = "pending"): Promise<number> {
const result = await this.rpc<string>("eth_getTransactionCount", [address, blockTag]);
return parseInt(result, 16);
}
/**
* Gets contract code
*/
async getCode(address: string, blockTag: string = "latest"): Promise<string> {
return this.rpc<string>("eth_getCode", [address, blockTag]);
}
/**
* Gets storage at position
*/
async getStorageAt(address: string, position: string, blockTag: string = "latest"): Promise<string> {
return this.rpc<string>("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<Log[]> {
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<any[]>("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<any> {
return this.rpc("synor_getDagBlock", [hash]);
}
/**
* Gets pending cross-shard messages
*/
async getPendingCrossShardMessages(address: string): Promise<any[]> {
return this.rpc("synor_getPendingCrossShardMessages", [address]);
}
}