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.
102 lines
4.1 KiB
JavaScript
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);
|
|
});
|
|
});
|