import { expect } from "chai"; import { getAddress, Wallet } from "ethers"; import { ethers, network } from "hardhat"; import { resolveWorkflowSigner } from "../scripts/workflow/lib/signer"; describe("Workflow signer", function () { const originalPrivateKey = process.env.PRIVATE_KEY; const originalWalletCredential = process.env.WALLET_CREDENTIAL; const originalCustodyApiBaseUrl = process.env.CUSTODY_API_BASE_URL; const selectedAddress = "0x1000000000000000000000000000000000000001"; const otherAddress = "0x2000000000000000000000000000000000000002"; const expectRejectedWith = async ( operation: Promise, message: string, ): Promise => { let caught: unknown; try { await operation; } catch (error) { caught = error; } expect(caught).to.be.instanceOf(Error); expect((caught as Error).message).to.contain(message); }; afterEach(function () { if (originalPrivateKey === undefined) { delete process.env.PRIVATE_KEY; } else { process.env.PRIVATE_KEY = originalPrivateKey; } if (originalWalletCredential === undefined) { delete process.env.WALLET_CREDENTIAL; } else { process.env.WALLET_CREDENTIAL = originalWalletCredential; } if (originalCustodyApiBaseUrl === undefined) { delete process.env.CUSTODY_API_BASE_URL; } else { process.env.CUSTODY_API_BASE_URL = originalCustodyApiBaseUrl; } }); it("impersonates the matching explicit simulation signer", async function () { const signer = await resolveWorkflowSigner("simulate", { explicitAddress: selectedAddress, taskAddress: selectedAddress, }); expect(await signer.getAddress()).to.equal(getAddress(selectedAddress)); if (network.name === "hardhat") { expect(await ethers.provider.getBalance(selectedAddress)).to.be.greaterThan(0n); } }); it("uses task metadata only for a no-lock simulation", async function () { const signer = await resolveWorkflowSigner("simulate", { taskAddress: selectedAddress, useLock: false, }); expect(await signer.getAddress()).to.equal(getAddress(selectedAddress)); await expectRejectedWith( resolveWorkflowSigner("simulate", { taskAddress: selectedAddress, useLock: true, }), "Simulation requires an explicit signer address", ); }); it("rejects missing, invalid, zero, and conflicting simulation signer sources", async function () { await expectRejectedWith( resolveWorkflowSigner("simulate"), "Simulation requires an explicit signer address", ); await expectRejectedWith( resolveWorkflowSigner("simulate", { explicitAddress: "not-an-address" }), "simulation signer must be a valid non-zero EVM address", ); await expectRejectedWith( resolveWorkflowSigner("simulate", { explicitAddress: "0x0000000000000000000000000000000000000000", }), "simulation signer must be a valid non-zero EVM address", ); await expectRejectedWith( resolveWorkflowSigner("simulate", { explicitAddress: selectedAddress, taskAddress: otherAddress, }), "simulation signer sources do not match", ); }); it("never uses PRIVATE_KEY as an implicit simulation identity", async function () { const privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123"; process.env.PRIVATE_KEY = privateKey; await expectRejectedWith( resolveWorkflowSigner("simulate"), "Simulation requires an explicit signer address", ); }); it("continues to derive execute mode from PRIVATE_KEY", async function () { const privateKey = "0x0123456789012345678901234567890123456789012345678901234567890123"; process.env.PRIVATE_KEY = privateKey; const signer = await resolveWorkflowSigner("execute"); const expectedAddress = new Wallet(privateKey).address; expect(await signer.getAddress()).to.equal(expectedAddress); }); it("derives execute mode from a custody wallet credential", async function () { const projectSecret = `sk-test-WORKFLOW_SIGNER.${"c".repeat(32)}`; process.env.WALLET_CREDENTIAL = `custody:${projectSecret}:${selectedAddress}`; delete process.env.PRIVATE_KEY; process.env.CUSTODY_API_BASE_URL = "https://custody.example"; const signer = await resolveWorkflowSigner("execute", { taskAddress: selectedAddress, }); expect(await signer.getAddress()).to.equal(getAddress(selectedAddress)); await expectRejectedWith( resolveWorkflowSigner("execute", { taskAddress: otherAddress }), "execution signer does not match deployment signer address", ); }); });