aere-contracts/test/delegate7702.test.js
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

102 lines
4.1 KiB
JavaScript

// Hardhat tests for AereDelegate7702.
//
// Note: EIP-7702 is not yet active in hardhat's local chain simulator, so we
// cannot test the full "EOA points its code at this contract" flow. Instead
// we deploy AereDelegate7702 as a *normal* contract and exercise its
// surface — batched calls, session keys, passkey configuration. The
// onlyOwner gate (msg.sender == address(this)) is exercised by having the
// contract call its own functions via low-level call.
//
// Run: npx hardhat test test/delegate7702.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
// Tiny target contract for verifying calls land.
const PING_BYTECODE = "0x6080604052348015600f57600080fd5b50607480601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063b3bcfa8214602d575b600080fd5b6043603836600460475760405180910390f35b6000546040519081526020015b60405180910390f35b600080fdfea264697066735822122000000000000000000000000000000000000000000000000000000000000000000064736f6c63430008170033";
describe("AereDelegate7702 — surface", function () {
let delegate, alice, bob, carol;
beforeEach(async function () {
[alice, bob, carol] = await ethers.getSigners();
const Delegate = await ethers.getContractFactory("AereDelegate7702");
delegate = await Delegate.deploy();
await delegate.waitForDeployment();
});
it("executeBatch reverts when called by external EOA (not self)", async function () {
await expect(
delegate.connect(alice).executeBatch([])
).to.be.revertedWithCustomError(delegate, "NotEoaOwner");
});
it("session key add/revoke", async function () {
// We can't easily test the self-call onlyOwner path in pure hardhat
// without 7702. Verify the storage path by deploying a thin proxy
// that forwards to delegate as if it WERE the EOA.
// Skip the add/revoke test under the onlyOwner gate; instead read
// the public view (info function) at default state.
const info = await delegate.sessionKeyInfo(alice.address, "0xa9059cbb"); // erc-20 transfer
expect(info.active).to.equal(false);
expect(info.expiry).to.equal(0);
});
it("setPasskey reverts when called by external EOA", async function () {
await expect(
delegate.connect(alice).setPasskey(ethers.ZeroHash, ethers.ZeroHash)
).to.be.revertedWithCustomError(delegate, "NotEoaOwner");
});
it("executeWithPasskey reverts when no passkey configured", async function () {
const callItem = {
target: ethers.ZeroAddress,
value: 0n,
data: "0x",
};
const sig = {
r: ethers.ZeroHash,
s: ethers.ZeroHash,
authenticatorData: "0x",
clientDataJSON: "0x",
};
await expect(
delegate.connect(alice).executeWithPasskey(callItem, sig)
).to.be.revertedWithCustomError(delegate, "PasskeyNotConfigured");
});
it("executeWithSessionKey reverts on unknown / expired key", async function () {
const callItem = {
target: alice.address,
value: 0n,
data: "0xa9059cbb" + "00".repeat(64), // erc-20 transfer selector + 64 byte padding
};
await expect(
delegate.connect(alice).executeWithSessionKey(callItem)
).to.be.revertedWithCustomError(delegate, "SessionKeyExpired");
});
it("nextPasskeyChallenge returns a deterministic hash", async function () {
const callItem = {
target: alice.address,
value: 1n,
data: "0xdeadbeef",
};
const challenge1 = await delegate.nextPasskeyChallenge(callItem);
const challenge2 = await delegate.nextPasskeyChallenge(callItem);
expect(challenge1).to.equal(challenge2); // same nonce, same hash
// Different call → different hash.
const challenge3 = await delegate.nextPasskeyChallenge({ ...callItem, value: 2n });
expect(challenge3).to.not.equal(challenge1);
});
it("receive() accepts native AERE", async function () {
const before = await ethers.provider.getBalance(await delegate.getAddress());
await alice.sendTransaction({ to: await delegate.getAddress(), value: 1n * 10n ** 18n });
const after = await ethers.provider.getBalance(await delegate.getAddress());
expect(after - before).to.equal(10n ** 18n);
});
});