Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: migrate GPv2Signing recoverOrderSigner tests to Foundry #207

Merged
merged 30 commits into from
Aug 15, 2024
Merged
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d0173f0
chore: migrate GPv2Signing domain separator tests to Foundry
fedgiac Aug 9, 2024
c7d4149
Fix unused import
fedgiac Aug 9, 2024
bc7f235
Remove duplicated import
fedgiac Aug 12, 2024
0721892
chore: migrate GPv2Signing set pre-signature tests to Foundry
fedgiac Aug 12, 2024
56ff598
chore: migrate GPv2Signing recoverOrderFromTrade tests to Foundry
fedgiac Aug 12, 2024
f71e28d
Merge branch 'main' into migrate-test-signing-domain-separator
fedgiac Aug 12, 2024
2816827
Add explanatory comment on domain separator struct
fedgiac Aug 12, 2024
585703d
Reuse existing PRE_SIGNED variable from library
fedgiac Aug 12, 2024
d51fced
Merge branch 'main' into migrate-test-signing-domain-separator
fedgiac Aug 12, 2024
e233612
Merge branch 'migrate-test-signing-domain-separator' into migrate-tes…
fedgiac Aug 12, 2024
84eb7fc
Fix missing setting of pre-signature at start of test
fedgiac Aug 12, 2024
6e618dc
Clean up imports
fedgiac Aug 14, 2024
a8c8b45
chore: migrate GPv2Signing calldata manipulation tests to Foundry
fedgiac Aug 14, 2024
ac6bcae
chore: migrate GPv2Signing recoverOrderSigner tests to Foundry
fedgiac Aug 14, 2024
483e8f2
Fix fuzz test case where buy and sell tokens are the same
fedgiac Aug 14, 2024
61a0da9
Merge branch 'main' into migrate-test-signing-set-pre-signature
fedgiac Aug 15, 2024
a16ab67
Merge branch 'main' into migrate-test-signing-set-pre-signature
fedgiac Aug 15, 2024
2ebcc44
Merge branch 'migrate-test-signing-set-pre-signature' into migrate-te…
fedgiac Aug 15, 2024
8dd66e7
Merge fuzzed order library into order library
fedgiac Aug 15, 2024
21e12c1
Merge branch 'migrate-test-signing-recover-order-from-trade' into mig…
fedgiac Aug 15, 2024
46fcb81
Update fuzz library usage
fedgiac Aug 15, 2024
db2a861
Merge branch 'migrate-test-signing-calldata-manipulation' into migrat…
fedgiac Aug 15, 2024
16d5d65
Fix invalid signing scheme test to involve a call
fedgiac Aug 15, 2024
6e97dce
Prefer EIP1271Verifier.isValidSignature.selector to static constant
fedgiac Aug 15, 2024
cc09214
Merge branch 'main' into migrate-test-signing-recover-order-from-trade
fedgiac Aug 15, 2024
a34bc31
Merge branch 'migrate-test-signing-recover-order-from-trade' into mig…
fedgiac Aug 15, 2024
53ef88b
Merge branch 'main' into migrate-test-signing-calldata-manipulation
fedgiac Aug 15, 2024
6d96620
Merge branch 'migrate-test-signing-calldata-manipulation' into migrat…
fedgiac Aug 15, 2024
a467ea6
Remove test of Solidity enum parsing
fedgiac Aug 15, 2024
94bf104
Remove unused import
fedgiac Aug 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 2 additions & 299 deletions test/GPv2Signing.test.ts
Original file line number Diff line number Diff line change
@@ -1,299 +1,2 @@
import { expect } from "chai";
import { Contract } from "ethers";
import { artifacts, ethers, waffle } from "hardhat";

import {
EIP1271_MAGICVALUE,
SigningScheme,
TypedDataDomain,
computeOrderUid,
domain,
encodeEip1271SignatureData,
hashOrder,
signOrder,
} from "../src/ts";

import { encodeOrder } from "./encoding";
import { SAMPLE_ORDER } from "./testHelpers";

describe("GPv2Signing", () => {
const [deployer, ...traders] = waffle.provider.getWallets();

let signing: Contract;
let testDomain: TypedDataDomain;

beforeEach(async () => {
const GPv2Signing = await ethers.getContractFactory(
"GPv2SigningTestInterface",
);

signing = await GPv2Signing.deploy();

const { chainId } = await ethers.provider.getNetwork();
testDomain = domain(chainId, signing.address);
});

describe("recoverOrderSigner", () => {
it("should recover signing address for all supported ECDSA-based schemes", async () => {
for (const scheme of [
SigningScheme.EIP712,
SigningScheme.ETHSIGN,
] as const) {
const { data: signature } = await signOrder(
testDomain,
SAMPLE_ORDER,
traders[0],
scheme,
);
expect(
await signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
scheme,
signature,
),
).to.equal(traders[0].address);
}
});

it("should revert for invalid signing schemes", async () => {
await expect(
signing.recoverOrderSignerTest(encodeOrder(SAMPLE_ORDER), 42, "0x"),
).to.be.reverted;
});

it("should revert for malformed ECDSA signatures", async () => {
for (const scheme of [SigningScheme.EIP712, SigningScheme.ETHSIGN]) {
await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
scheme,
"0x",
),
).to.be.revertedWith("malformed ecdsa signature");
}
});

it("should revert for invalid eip-712 order signatures", async () => {
const { data: signature } = await signOrder(
testDomain,
SAMPLE_ORDER,
traders[0],
SigningScheme.EIP712,
);

// NOTE: `v` must be either `27` or `28`, so just set it to something else
// to generate an invalid signature.
const invalidSignature = ethers.utils.arrayify(
ethers.utils.joinSignature(signature),
);
invalidSignature[64] = 42;

await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.EIP712,
invalidSignature,
),
).to.be.revertedWith("invalid ecdsa signature");
});

it("should revert for invalid ethsign order signatures", async () => {
const { data: signature } = await signOrder(
testDomain,
SAMPLE_ORDER,
traders[0],
SigningScheme.ETHSIGN,
);

// NOTE: `v` must be either `27` or `28`, so just set it to something else
// to generate an invalid signature.
const invalidSignature = ethers.utils.arrayify(
ethers.utils.joinSignature(signature),
);
invalidSignature[64] = 42;

await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.ETHSIGN,
invalidSignature,
),
).to.be.revertedWith("invalid ecdsa signature");
});

it("should verify EIP-1271 contract signatures by returning owner", async () => {
const artifact = await artifacts.readArtifact("EIP1271Verifier");
const verifier = await waffle.deployMockContract(deployer, artifact.abi);

const message = hashOrder(testDomain, SAMPLE_ORDER);
const eip1271Signature = "0x031337";
await verifier.mock.isValidSignature
.withArgs(message, eip1271Signature)
.returns(EIP1271_MAGICVALUE);

expect(
await signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.EIP1271,
encodeEip1271SignatureData({
verifier: verifier.address,
signature: eip1271Signature,
}),
),
).to.equal(verifier.address);
});

it("should revert on an invalid EIP-1271 signature", async () => {
const message = hashOrder(testDomain, SAMPLE_ORDER);
const eip1271Signature = "0x031337";

const artifact = await artifacts.readArtifact("EIP1271Verifier");
const verifier1 = await waffle.deployMockContract(deployer, artifact.abi);

await verifier1.mock.isValidSignature
.withArgs(message, eip1271Signature)
.returns("0xbaadc0d3");
await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.EIP1271,
encodeEip1271SignatureData({
verifier: verifier1.address,
signature: eip1271Signature,
}),
),
).to.be.revertedWith("invalid eip1271 signature");
});

it("should revert with non-standard EIP-1271 verifiers", async () => {
const message = hashOrder(testDomain, SAMPLE_ORDER);
const eip1271Signature = "0x031337";

const NON_STANDARD_EIP1271_VERIFIER = [
"function isValidSignature(bytes32 _hash, bytes memory _signature)",
]; // no return value
const verifier = await waffle.deployMockContract(
deployer,
NON_STANDARD_EIP1271_VERIFIER,
);

await verifier.mock.isValidSignature
.withArgs(message, eip1271Signature)
.returns();
await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.EIP1271,
encodeEip1271SignatureData({
verifier: verifier.address,
signature: eip1271Signature,
}),
),
).to.be.reverted;
});

it("should revert for EIP-1271 signatures from externally owned accounts", async () => {
// Transaction reverted: function call to a non-contract account
await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.EIP1271,
encodeEip1271SignatureData({
verifier: traders[0].address,
signature: "0x00",
}),
),
).to.be.reverted;
});

it("should revert if the EIP-1271 verification function changes the state", async () => {
const StateChangingEIP1271 = await ethers.getContractFactory(
"StateChangingEIP1271",
);

const evilVerifier = await StateChangingEIP1271.deploy();
const message = hashOrder(testDomain, SAMPLE_ORDER);
const eip1271Signature = "0x";

expect(await evilVerifier.state()).to.equal(ethers.constants.Zero);
await evilVerifier.isValidSignature(message, eip1271Signature);
expect(await evilVerifier.state()).to.equal(ethers.constants.One);
expect(
await evilVerifier.callStatic.isValidSignature(
message,
eip1271Signature,
),
).to.equal(EIP1271_MAGICVALUE);

// Transaction reverted and Hardhat couldn't infer the reason.
await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.EIP1271,
encodeEip1271SignatureData({
verifier: evilVerifier.address,
signature: eip1271Signature,
}),
),
).to.be.reverted;
expect(await evilVerifier.state()).to.equal(ethers.constants.One);
});

it("should verify pre-signed order", async () => {
const orderUid = computeOrderUid(
testDomain,
SAMPLE_ORDER,
traders[0].address,
);

await signing.connect(traders[0]).setPreSignature(orderUid, true);
expect(
await signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.PRESIGN,
traders[0].address,
),
).to.equal(traders[0].address);
});

it("should revert if order doesn't have pre-signature set", async () => {
await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.PRESIGN,
traders[0].address,
),
).to.be.revertedWith("order not presigned");
});

it("should revert if pre-signed order is modified", async () => {
await signing
.connect(traders[0])
.setPreSignature(
computeOrderUid(testDomain, SAMPLE_ORDER, traders[0].address),
true,
);

await expect(
signing.recoverOrderSignerTest(
encodeOrder({
...SAMPLE_ORDER,
buyAmount: ethers.constants.Zero,
}),
SigningScheme.PRESIGN,
traders[0].address,
),
).to.be.revertedWith("order not presigned");
});

it("should revert for malformed pre-sign order UID", async () => {
await expect(
signing.recoverOrderSignerTest(
encodeOrder(SAMPLE_ORDER),
SigningScheme.PRESIGN,
"0x",
),
).to.be.revertedWith("malformed presignature");
});
});
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
describe("GPv2Signing", () => {});
13 changes: 12 additions & 1 deletion test/GPv2Signing/Helper.sol
Original file line number Diff line number Diff line change
@@ -3,12 +3,23 @@ pragma solidity ^0.8;

import {Test} from "forge-std/Test.sol";

import {GPv2Order} from "src/contracts/mixins/GPv2Signing.sol";

import {Sign} from "test/libraries/Sign.sol";
import {GPv2SigningTestInterface} from "test/src/GPv2SigningTestInterface.sol";

// TODO: move the content of `GPv2SigningTestInterface` here once all tests have
// been removed.
// solhint-disable-next-line no-empty-blocks
contract Harness is GPv2SigningTestInterface {}
contract Harness is GPv2SigningTestInterface {
function recoverOrderSignerTest(GPv2Order.Data memory order, Sign.Signature calldata signature)
public
view
returns (address owner)
{
(, owner) = recoverOrderSigner(order, signature.scheme, signature.data);
}
}

contract Helper is Test {
Harness internal executor;
Loading