🛠️ “Het testen van knooppunt- en netwerkgedrag in slimme contracten met behulp van Hardhat met Typescript & Mocha”
Een blockchain -netwerk bestaat uit meerdere knooppunten die met elkaar communiceren. Elk knooppunt neemt deel aan mijnblokken om een kettingdie vereist gaskosten. Elk blok bevat:
- Transactiegegevens (bijv. Records van deposito’s of overdracht)
- Tijdstempel (toen het blok is gemaakt)
- Een link naar het vorige blok
- Technische informatie over de netwerkstatus
Als u bijvoorbeeld stort 10 ethHet blok zou opnemen:
- Uw portemonnee -adres (die het heeft verzonden)
- Het contractadres (die het heeft ontvangen)
- Het bedrag (10 ETH)
- De transactietijdstempel
- Andere technische details over de transactie
In deze blog zullen we een slim contract voor tokenleningenwaar je kunt Deposit Eth naar leen weth tokensZoals weergegeven in de onderstaande soliditeitscode:
- Netwerksimulatiemogelijkheden
// In Hardhat, we can control block mining
await network.provider.send("evm_mine", []); // Mine a new block// We can control block timestamps
await network.provider.send("evm_increaseTime", [3600]); // Move time forward 1 hour
// We can even control gas prices
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", [
ethers.utils.hexValue(ethers.utils.parseUnits("100", "gwei"))
]);
U kunt de hele blockchain -status opslaan en herstellen voor herstel van testfout:
describe("Error Recovery Testing", () => {
it("should recover from failed operations", async function() {
// Take snapshot before risky operation
const snapshotId = await network.provider.send("evm_snapshot", []);try {
// Attempt potentially failing operation
await lendingProtocol.withdraw(ethers.utils.parseEther("100")); // More than deposited
} catch (error) {
// Revert to clean state
await network.provider.send("evm_revert", [snapshotId]);
// Verify state is clean
const balance = await lendingProtocol.getUserDeposit(signer.address);
expect(balance).to.equal(0);
}
});
});
Met Hardhat kunnen we op enig moment een kopie van het echte Ethereum -mainnet maken voor testen.
// We can fork mainnet at a specific block
await network.provider.request({
method: "hardhat_reset",
params: [{
forking: {
jsonRpcUrl: MAINNET_RPC_URL,
blockNumber: 15000000 // Specific block number
}
}]
});// Now we can interact with real mainnet contracts
const USDC = await ethers.getContractAt("IERC20", "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
We kunnen stapelsporen krijgen voor mislukte transacties
it("should maintain consistent performance under sustained load", async function() {
const testUser = await setupTestUser(0, "1000");
const userContract = lendingProtocol.connect(testUser);
const depositAmount = ethers.utils.parseEther("0.1");
const iterations = 3;
const results = [];try {
// Get initial balance
const initialBalance = await userContract.getUserDeposit(testUser.address);
console.log("Initial balance:", ethers.utils.formatEther(initialBalance));
} catch (error) {
console.error("Load test failed:", error);
}
U kunt ook HARTHAT gebruiken om een ​​slimme contract in de console te debuggen:
npx hardhat console
Met Hardhat hebben we uitgebreide controle over de testomgeving.
// We can manipulate account balances directly
await network.provider.send("hardhat_setBalance", [
userAddress,
ethers.utils.hexValue(ethers.utils.parseEther("100"))
]);// We can impersonate any Ethereum address
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [whaleAddress],
});
Voorafgaande hardhat -functie:
We kunnen testen hoe ons protocol omgaat in afwachting van transacties en mempoolgedrag zoals de onderstaande code.
describe("Transaction Pool Behavior", () => {
it("should handle multiple pending transactions correctly", async function() {
// Enable auto-mining
await network.provider.send("evm_setAutomine", [false]);// Submit multiple transactions that will sit in the mempool
const tx1 = lendingProtocol.deposit({ value: ethers.utils.parseEther("1") });
const tx2 = lendingProtocol.deposit({ value: ethers.utils.parseEther("2") });
const tx3 = lendingProtocol.deposit({ value: ethers.utils.parseEther("3") });
// Get pending transactions
const pendingTxs = await network.provider.send("eth_pendingTransactions");
console.log("Pending transactions:", pendingTxs.length);
// Mine them all at once
await network.provider.send("evm_mine");
// Verify final state
const totalDeposits = await lendingProtocol.totalDeposits();
expect(totalDeposits).to.equal(ethers.utils.parseEther("6"));
});
});
Om een ​​testscript uit te voeren met hardhat op lokaal:
npx hardhat test
Methode 1: hebben volledig toegang tot de Dev -code -repository
// Need access to contract code and typechain
import { TestLendingProtocol } from "../../typechain/contracts/TestLendingProtocol";// Full testing capabilities with type safety
describe("Full Testing Suite", () => {
let lendingProtocol: TestLendingProtocol;
beforeEach(async () => {
lendingProtocol = await ethers.getContractAt(
"TestLendingProtocol",
addresses.lendingProtocol,
signer
);
});
});
Voordat u vanuit de map Typechain importeert, moet u mogelijk deze opdracht uitvoeren om het Solidity -bestand samen te stellen om ABI (Toepassing Binaire Interface) en TYPECHAIN ​​-typen te genereren.
npx hardhat compile
Dit creëert:
artifacts/map met abi’stypechain/map met typescript -typen
Methode 2: Testen van geĂŻmplementeerde contracten (geen broncodetoegang)
// QA only has contract address and ABI
const contractAddress = "0x123..."; // Deployed contract address
const abi = [
"function deposit() external payable",
"function withdraw(uint256 amount) external"
];describe("Limited Testing Suite", () => {
it("can test basic functionality", async () => {
const contract = new ethers.Contract(contractAddress, abi, signer);
// Can test basic functions
await contract.deposit({ value: ethers.utils.parseEther("1") });
// Can't simulate network conditions as effectively
// Limited to actual network behavior
});
});
Over het testen van geĂŻmplementeerde contracten, kunt u nog steeds Hardhat gebruiken om het Ethereum -mainnet of een testnet te vorken, afhankelijk van aan welk netwerk de ontwikkelaar heeft ingezet.
Als u Ethereum mainnet vork:
// CORRECT: Using Mainnet contract address
const MAINNET_USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const MAINNET_WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";await network.provider.request({
method: "hardhat_reset",
params: [{
forking: {
jsonRpcUrl: MAINNET_RPC_URL, // Mainnet RPC URL
blockNumber: 15000000
}
}]
});
const usdc = new ethers.Contract(MAINNET_USDC, USDC_ABI, signer);
const weth = new ethers.Contract(MAINNET_WETH, WETH_ABI, signer);
Als u testnets vork (bijv. Sepolia):
// CORRECT: Using Sepolia contract address
const SEPOLIA_USDC = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"; // Example addressawait network.provider.request({
method: "hardhat_reset",
params: [{
forking: {
jsonRpcUrl: SEPOLIA_RPC_URL, // Sepolia RPC URL
blockNumber: 3000000
}
}]
});
const usdc = new ethers.Contract(SEPOLIA_USDC, USDC_ABI, signer);
