166 lines
6.2 KiB
TypeScript
166 lines
6.2 KiB
TypeScript
import hre from "hardhat";
|
|
import { expect } from "chai";
|
|
// import { parseEther } from "ethers";
|
|
import { DiceGame, RiggedRoll, RiggedRoll__factory } from "../typechain-types";
|
|
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
|
|
const { ethers } = hre;
|
|
|
|
describe("🚩 Challenge: 🎲 Dice Game", function () {
|
|
let diceGame: DiceGame;
|
|
let riggedRoll: RiggedRoll;
|
|
let deployer: HardhatEthersSigner;
|
|
const { provider } = ethers;
|
|
|
|
const rollAmountString = "0.002";
|
|
const rollAmount = ethers.parseEther(rollAmountString);
|
|
|
|
async function deployContracts() {
|
|
[deployer] = await ethers.getSigners();
|
|
const DiceGame = await ethers.getContractFactory("DiceGame");
|
|
diceGame = await DiceGame.deploy();
|
|
|
|
const contractAddress = process.env.CONTRACT_ADDRESS;
|
|
let contractArtifact;
|
|
if (contractAddress) {
|
|
contractArtifact = `contracts/download-${contractAddress}.sol:RiggedRoll`;
|
|
} else {
|
|
contractArtifact = "contracts/RiggedRoll.sol:RiggedRoll";
|
|
}
|
|
|
|
const diceGameAddress = await diceGame.getAddress();
|
|
const RiggedRoll = (await ethers.getContractFactory(contractArtifact)) as RiggedRoll__factory;
|
|
riggedRoll = await RiggedRoll.deploy(diceGameAddress);
|
|
}
|
|
|
|
async function fundRiggedContract() {
|
|
const riggedRollAddress = await riggedRoll.getAddress();
|
|
return deployer.sendTransaction({
|
|
to: riggedRollAddress,
|
|
value: rollAmount,
|
|
});
|
|
}
|
|
|
|
async function getRoll(getRollLessThanFive: boolean) {
|
|
let expectedRoll;
|
|
while (true) {
|
|
const latestBlockNumber = await provider.getBlockNumber();
|
|
const block = await provider.getBlock(latestBlockNumber);
|
|
if (!block) {
|
|
return;
|
|
}
|
|
const prevHash = block.hash;
|
|
const nonce = await diceGame.nonce();
|
|
|
|
const diceGameAddress = await diceGame.getAddress();
|
|
const hash = ethers.solidityPackedKeccak256(
|
|
["bytes32", "address", "uint256"],
|
|
[prevHash, diceGameAddress, nonce],
|
|
);
|
|
|
|
const bigInt = BigInt(hash);
|
|
expectedRoll = bigInt % 16n;
|
|
if (expectedRoll < 5n == getRollLessThanFive) {
|
|
break;
|
|
}
|
|
|
|
const options = { value: rollAmount };
|
|
await diceGame.rollTheDice(options);
|
|
}
|
|
return expectedRoll;
|
|
}
|
|
|
|
describe("Checkpoint2: 🔑 Rigged Contract", function () {
|
|
beforeEach(async function () {
|
|
await deployContracts();
|
|
});
|
|
|
|
it("Checkpoint2: Should deploy contracts", async function () {
|
|
const diceGameAddress = await diceGame.getAddress();
|
|
expect(await riggedRoll.diceGame()).to.equal(diceGameAddress);
|
|
});
|
|
|
|
it(`Checkpoint2: Should revert if balance is less than ${rollAmountString} ethers`, async function () {
|
|
await expect(riggedRoll.riggedRoll())
|
|
.to.be.revertedWithCustomError(riggedRoll, "NotEnoughETH")
|
|
.withArgs(rollAmount, 0);
|
|
});
|
|
|
|
it("Checkpoint2: Should transfer sufficient eth to RiggedRoll", async function () {
|
|
console.log("\t", "💸 Funding RiggedRoll contract");
|
|
await fundRiggedContract();
|
|
const riggedRollAddress = await riggedRoll.getAddress();
|
|
const balance = await provider.getBalance(riggedRollAddress);
|
|
console.log("\t", "💲 RiggedRoll balance: ", ethers.formatEther(balance));
|
|
expect(balance).to.gte(rollAmount, `Error when expecting DiceGame contract to have >= ${rollAmount} eth`);
|
|
});
|
|
|
|
it("Checkpoint2: Should call diceGame.rollTheDice for a roll <= 5", async () => {
|
|
console.log("\t", "💸 Funding RiggedRoll contract");
|
|
await fundRiggedContract();
|
|
|
|
const getRollLessThanFive = true;
|
|
const expectedRoll = await getRoll(getRollLessThanFive);
|
|
console.log("\t", "🎲 Expect roll to be less than or equal to 5. Dice Game Roll:", Number(expectedRoll));
|
|
|
|
const tx = await riggedRoll.riggedRoll();
|
|
const riggedRollAddress = await riggedRoll.getAddress();
|
|
|
|
await expect(tx).to.emit(diceGame, "Roll").withArgs(riggedRollAddress, rollAmount, expectedRoll);
|
|
await expect(tx).to.emit(diceGame, "Winner");
|
|
});
|
|
|
|
it("Checkpoint2: Should not call diceGame.rollTheDice for a roll > 5", async () => {
|
|
console.log("\t", "💸 Funding RiggedRoll contract");
|
|
await fundRiggedContract();
|
|
|
|
const getRollLessThanFive = false;
|
|
const expectedRoll = await getRoll(getRollLessThanFive);
|
|
console.log("\t", "🎲 Expect roll to be greater than 5. Dice Game Roll:", Number(expectedRoll));
|
|
console.log("\t", "◀ Expect riggedRoll to be reverted");
|
|
|
|
await expect(riggedRoll.riggedRoll())
|
|
.to.be.revertedWithCustomError(riggedRoll, "NotWinningRoll")
|
|
.withArgs(expectedRoll);
|
|
});
|
|
});
|
|
|
|
describe("Checkpoint3: 💵 Where's my money?!?", function () {
|
|
beforeEach(async function () {
|
|
await deployContracts();
|
|
});
|
|
|
|
it("Checkpoint3: Should withdraw funds", async () => {
|
|
console.log("\t", "💸 Funding RiggedRoll contract");
|
|
await fundRiggedContract();
|
|
|
|
const deployerPrevBalance = await provider.getBalance(deployer.address);
|
|
console.log("\t", "💲 Current RiggedRoll balance: ", ethers.formatEther(deployerPrevBalance));
|
|
const riggedRollAddress = await riggedRoll.getAddress();
|
|
const riggedRollBalance = await provider.getBalance(riggedRollAddress);
|
|
await riggedRoll.withdraw(deployer.address, riggedRollBalance);
|
|
|
|
const deployerCurrentBalance = await provider.getBalance(deployer.address);
|
|
console.log("\t", "💲 New RiggedRoll balance: ", ethers.formatEther(deployerCurrentBalance));
|
|
|
|
expect(
|
|
deployerPrevBalance < deployerCurrentBalance,
|
|
"Error when expecting RiggedRoll balance to increase when calling withdraw",
|
|
).to.true;
|
|
});
|
|
|
|
it("Checkpoint3: Should revert withdraw when amount exceeds contract balance", async () => {
|
|
console.log("\t", "💸 Funding RiggedRoll contract");
|
|
await fundRiggedContract();
|
|
|
|
const riggedRollAddress = await riggedRoll.getAddress();
|
|
const riggedRollBalance = await provider.getBalance(riggedRollAddress);
|
|
const tooMuch = riggedRollBalance + rollAmount;
|
|
|
|
await expect(riggedRoll.withdraw(deployer.address, tooMuch))
|
|
.to.be.revertedWithCustomError(riggedRoll, "InsufficientBalance")
|
|
.withArgs(tooMuch, riggedRollBalance);
|
|
});
|
|
});
|
|
});
|