diff --git a/test/balancer.ts b/test/balancer.ts index 7228a6e7..ad34a3cc 100644 --- a/test/balancer.ts +++ b/test/balancer.ts @@ -4,8 +4,3 @@ export enum UserBalanceOpKind { TRANSFER_INTERNAL = 2, TRANSFER_EXTERNAL = 3, } - -export enum BalancerErrors { - SWAP_LIMIT = "BAL#507", - SWAP_DEADLINE = "BAL#508", -} diff --git a/test/bytecode.ts b/test/bytecode.ts deleted file mode 100644 index 65062e93..00000000 --- a/test/bytecode.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { artifacts, ethers } from "hardhat"; - -export async function builtAndDeployedMetadataCoincide( - contractAddress: string, - contractName: string, -): Promise { - const contractArtifacts = await artifacts.readArtifact(contractName); - - const code = await ethers.provider.send("eth_getCode", [ - contractAddress, - "latest", - ]); - - // NOTE: The last 53 bytes in a deployed contract's bytecode contains the - // contract metadata. Compare the deployed contract's metadata with the - // compiled contract's metadata. - // - const metadata = (bytecode: string) => bytecode.slice(-106); - - return metadata(code) === metadata(contractArtifacts.deployedBytecode); -} diff --git a/test/decoding.test.ts b/test/decoding.test.ts index d023fd0d..d35cbe20 100644 --- a/test/decoding.test.ts +++ b/test/decoding.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { constants, utils, Wallet } from "ethers"; +import { constants, ethers, utils, Wallet } from "ethers"; import { waffle } from "hardhat"; import { @@ -20,8 +20,6 @@ import { signOrder, } from "../src/ts"; -import { fillDistinctBytes, SAMPLE_ORDER } from "./testHelpers"; - type UnknownArray = unknown[] | readonly unknown[]; // [A, B, C] -> [A, B] type RemoveLast = T extends [...infer U, unknown] @@ -107,6 +105,29 @@ function validFlags(): TradeFlags[] { /* eslint-enable @typescript-eslint/no-explicit-any */ } +function fillDistinctBytes(count: number, start: number): string { + return ethers.utils.hexlify( + [...Array(count)].map((_, i) => (start + i) % 256), + ); +} + +function fillBytes(count: number, byte: number): string { + return ethers.utils.hexlify([...Array(count)].map(() => byte)); +} + +const SAMPLE_ORDER = { + sellToken: fillBytes(20, 0x01), + buyToken: fillBytes(20, 0x02), + receiver: fillBytes(20, 0x03), + sellAmount: ethers.utils.parseEther("42"), + buyAmount: ethers.utils.parseEther("13.37"), + validTo: 0xffffffff, + appData: ethers.constants.HashZero, + feeAmount: ethers.utils.parseEther("1.0"), + kind: OrderKind.SELL, + partiallyFillable: false, +}; + describe("Order flags", () => { it("encodeTradeFlags is the right inverse of decodeTradeFlags", () => { for (const tradeFlags of validFlags()) { diff --git a/test/e2e/balancerSwap.test.ts b/test/e2e/balancerSwap.test.ts index b3e63b08..acfc317b 100644 --- a/test/e2e/balancerSwap.test.ts +++ b/test/e2e/balancerSwap.test.ts @@ -14,10 +14,15 @@ import { domain, grantRequiredRoles, } from "../../src/ts"; -import { UserBalanceOpKind, BalancerErrors } from "../balancer"; +import { UserBalanceOpKind } from "../balancer"; import { deployTestContracts } from "./fixture"; +export enum BalancerErrors { + SWAP_LIMIT = "BAL#507", + SWAP_DEADLINE = "BAL#508", +} + const LOTS = ethers.utils.parseEther("10000.0"); const debug = Debug("e2e:balancerSwap"); diff --git a/test/e2e/deployment.test.ts b/test/e2e/deployment.test.ts index 8a7c1e6a..394c2c53 100644 --- a/test/e2e/deployment.test.ts +++ b/test/e2e/deployment.test.ts @@ -10,7 +10,6 @@ import { implementationAddress, proxyInterface, } from "../../src/ts"; -import { builtAndDeployedMetadataCoincide } from "../bytecode"; import { deployTestContracts } from "./fixture"; @@ -22,6 +21,26 @@ async function contractAddress( return deterministicDeploymentAddress(artifact, deploymentArguments); } +async function builtAndDeployedMetadataCoincide( + contractAddress: string, + contractName: string, +): Promise { + const contractArtifacts = await artifacts.readArtifact(contractName); + + const code = await ethers.provider.send("eth_getCode", [ + contractAddress, + "latest", + ]); + + // NOTE: The last 53 bytes in a deployed contract's bytecode contains the + // contract metadata. Compare the deployed contract's metadata with the + // compiled contract's metadata. + // + const metadata = (bytecode: string) => bytecode.slice(-106); + + return metadata(code) === metadata(contractArtifacts.deployedBytecode); +} + describe("E2E: Deployment", () => { let owner: Wallet; let manager: Wallet; diff --git a/test/e2e/wineOilMarket.test.ts b/test/e2e/wineOilMarket.test.ts index 764c892e..3531903a 100644 --- a/test/e2e/wineOilMarket.test.ts +++ b/test/e2e/wineOilMarket.test.ts @@ -1,6 +1,6 @@ import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; import { expect } from "chai"; -import { BigNumber, Contract, Wallet } from "ethers"; +import { BigNumber, BigNumberish, Contract, Wallet } from "ethers"; import { ethers, waffle } from "hardhat"; import { @@ -11,10 +11,13 @@ import { TypedDataDomain, domain, } from "../../src/ts"; -import { ceilDiv } from "../testHelpers"; import { deployTestContracts } from "./fixture"; +function ceilDiv(p: BigNumberish, q: BigNumberish): BigNumber { + return BigNumber.from(p).add(q).sub(1).div(q); +} + describe("E2E: RetrETH Red Wine and Olive Oil Market", () => { let deployer: Wallet; let solver: Wallet; diff --git a/test/encoding.ts b/test/encoding.ts index d3cb82b8..4e2a0b4f 100644 --- a/test/encoding.ts +++ b/test/encoding.ts @@ -1,17 +1,9 @@ import { BigNumber } from "ethers"; import { ethers } from "hardhat"; -import { - Order, - OrderBalance, - OrderKind, - normalizeOrder, - FLAG_MASKS, - FlagKey, - FlagOptions, -} from "../src/ts"; +import { Order, OrderBalance, OrderKind } from "../src/ts"; -export type AbiOrder = [ +type AbiOrder = [ string, string, string, @@ -26,42 +18,6 @@ export type AbiOrder = [ string, ]; -function optionsForKey(key: K): FlagOptions { - return FLAG_MASKS[key].options; -} -export const allOrderKinds = optionsForKey("kind"); -export const allPartiallyFillable = optionsForKey("partiallyFillable"); -export const allSigningSchemes = optionsForKey("signingScheme"); - -export function allOptions< - K extends FlagKey, - AllOptions extends Record>, ->(): AllOptions { - const result: Record> = {}; - Object.entries(FLAG_MASKS).map( - ([key, value]) => (result[key] = value.options), - ); - return result as AllOptions; -} - -export function encodeOrder(order: Order): AbiOrder { - const o = normalizeOrder(order); - return [ - o.sellToken, - o.buyToken, - o.receiver, - BigNumber.from(o.sellAmount), - BigNumber.from(o.buyAmount), - o.validTo, - o.appData, - BigNumber.from(o.feeAmount), - ethers.utils.id(o.kind), - o.partiallyFillable, - ethers.utils.id(o.sellTokenBalance), - ethers.utils.id(o.buyTokenBalance), - ]; -} - function decodeEnum(hash: string, values: T[]): T { for (const value of values) { if (hash == ethers.utils.id(`${value}`)) { diff --git a/test/hardhatNetwork.ts b/test/hardhatNetwork.ts deleted file mode 100644 index 6de3f2b6..00000000 --- a/test/hardhatNetwork.ts +++ /dev/null @@ -1,12 +0,0 @@ -import hre from "hardhat"; - -export async function setTime(timestamp: number): Promise { - return await hre.ethers.provider.send("evm_setNextBlockTimestamp", [ - timestamp, - ]); -} - -export async function synchronizeBlockchainAndCurrentTime(): Promise { - const now = Math.floor(Date.now() / 1000); - return setTime(now); -} diff --git a/test/libraries/Order.sol b/test/libraries/Order.sol index 393735f8..59c4dbc7 100644 --- a/test/libraries/Order.sol +++ b/test/libraries/Order.sol @@ -2,13 +2,10 @@ pragma solidity ^0.8; import {GPv2Order, IERC20} from "src/contracts/libraries/GPv2Order.sol"; -import {GPv2Trade} from "src/contracts/libraries/GPv2Trade.sol"; -library Order { - using GPv2Order for GPv2Order.Data; - using GPv2Order for bytes; - using GPv2Trade for uint256; +import {Eip712} from "./Eip712.sol"; +library Order { /// Order flags struct Flags { bytes32 kind; @@ -134,10 +131,24 @@ library Order { } } - /// @dev Given a GPv2Trade encoded flags, decode them into a `Flags` struct - function toFlags(uint256 encodedFlags) internal pure returns (Flags memory flags) { - (flags.kind, flags.partiallyFillable, flags.sellTokenBalance, flags.buyTokenBalance,) = - encodedFlags.extractFlags(); + /// @dev Given a GPv2Trade encoded flags, decode them into a `Flags` struct. + /// See GPv2Trade.extractFlags for a complete definition of each flag. + function toFlags(uint256 encodedFlags) internal pure returns (Flags memory) { + bytes32 sellTokenBalance; + if (encodedFlags & 0x08 == 0) { + sellTokenBalance = GPv2Order.BALANCE_ERC20; + } else if (encodedFlags & 0x04 == 0) { + sellTokenBalance = GPv2Order.BALANCE_EXTERNAL; + } else { + sellTokenBalance = GPv2Order.BALANCE_INTERNAL; + } + + return Flags({ + kind: (encodedFlags & 0x01 == 0) ? GPv2Order.KIND_SELL : GPv2Order.KIND_BUY, + partiallyFillable: encodedFlags & 0x02 != 0, + sellTokenBalance: sellTokenBalance, + buyTokenBalance: (encodedFlags & 0x10 == 0) ? GPv2Order.BALANCE_ERC20 : GPv2Order.BALANCE_INTERNAL + }); } /// @dev Computes the order UID for an order and the given owner @@ -146,17 +157,16 @@ library Order { pure returns (bytes memory) { - return computeOrderUid(order.hash(domainSeparator), owner, order.validTo); + return computeOrderUid(hash(order, domainSeparator), owner, order.validTo); } /// @dev Computes the order UID for its base components - function computeOrderUid(bytes32 orderHash, address owner, uint32 validTo) - internal - pure - returns (bytes memory orderUid) - { - orderUid = new bytes(GPv2Order.UID_LENGTH); - orderUid.packOrderUidParams(orderHash, owner, validTo); + function computeOrderUid(bytes32 orderHash, address owner, uint32 validTo) internal pure returns (bytes memory) { + return abi.encodePacked(orderHash, owner, validTo); + } + + function hash(GPv2Order.Data memory order, bytes32 domainSeparator) internal pure returns (bytes32) { + return Eip712.typedDataHash(Eip712.toEip712SignedStruct(order), domainSeparator); } function fuzz(Fuzzed memory params) internal pure returns (GPv2Order.Data memory) { diff --git a/test/libraries/Sign.sol b/test/libraries/Sign.sol index f79c7473..a2d036f0 100644 --- a/test/libraries/Sign.sol +++ b/test/libraries/Sign.sol @@ -3,16 +3,16 @@ pragma solidity ^0.8; import {Vm} from "forge-std/Test.sol"; -import {EIP1271Verifier, GPv2Order, GPv2Signing, GPv2Trade} from "src/contracts/mixins/GPv2Signing.sol"; +import {EIP1271Verifier, GPv2Order, GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; import {Bytes} from "./Bytes.sol"; +import {Order} from "./Order.sol"; type PreSignSignature is address; library Sign { - using GPv2Order for GPv2Order.Data; - using GPv2Trade for uint256; using Bytes for bytes; + using Order for GPv2Order.Data; // Copied from GPv2Signing.sol uint256 internal constant PRE_SIGNED = uint256(keccak256("GPv2Signing.Scheme.PreSign")); @@ -123,8 +123,12 @@ library Sign { } /// @dev Given a GPv2Trade encoded flags, decode them into a `GPv2Signing.Scheme` - function toSigningScheme(uint256 encodedFlags) internal pure returns (GPv2Signing.Scheme signingScheme) { - (,,,, signingScheme) = encodedFlags.extractFlags(); + function toSigningScheme(uint256 encodedFlags) internal pure returns (GPv2Signing.Scheme) { + uint256 encodedScheme = (encodedFlags >> 5); + if (encodedScheme >= 4) { + revert("invalid flag encoding, trying to use invalid signature scheme"); + } + return GPv2Signing.Scheme(encodedScheme); } /// @dev Internal helper function for EthSign signatures (non-EIP-712) diff --git a/test/libraries/Trade.sol b/test/libraries/Trade.sol index 4dc2312d..f7794614 100644 --- a/test/libraries/Trade.sol +++ b/test/libraries/Trade.sol @@ -6,7 +6,6 @@ import {Sign} from "./Sign.sol"; import {GPv2Order, GPv2Signing, GPv2Trade, IERC20} from "src/contracts/mixins/GPv2Signing.sol"; library Trade { - using GPv2Trade for uint256; using Order for Order.Flags; using Order for uint256; using Sign for GPv2Signing.Scheme; diff --git a/test/libraries/encoders/SettlementEncoder.sol b/test/libraries/encoders/SettlementEncoder.sol index de7abd77..9b5ccbfb 100644 --- a/test/libraries/encoders/SettlementEncoder.sol +++ b/test/libraries/encoders/SettlementEncoder.sol @@ -18,7 +18,6 @@ import {Trade} from "../Trade.sol"; import {Registry, TokenRegistry} from "./TokenRegistry.sol"; library SettlementEncoder { - using GPv2Order for GPv2Order.Data; using Trade for GPv2Order.Data; using Sign for Vm; using TokenRegistry for TokenRegistry.State; diff --git a/test/sign.test.ts b/test/sign.test.ts deleted file mode 100644 index def6a990..00000000 --- a/test/sign.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { joinSignature } from "@ethersproject/bytes"; -import { hashMessage } from "@ethersproject/hash"; -import { SigningKey } from "@ethersproject/signing-key"; -import { expect } from "chai"; -import { ethers, waffle } from "hardhat"; - -import { SigningScheme, signOrder } from "../src/ts"; - -import { SAMPLE_ORDER } from "./testHelpers"; - -const patchedSignMessageBuilder = - (key: SigningKey) => - async (message: string): Promise => { - // Reproducing `@ethersproject/wallet/src.ts/index.ts` sign message behavior - const sig = joinSignature(key.signDigest(hashMessage(message))); - - // Unpack the signature - const { r, s, v } = ethers.utils.splitSignature(sig); - // Pack it again - return ethers.utils.solidityPack( - ["bytes32", "bytes32", "uint8"], - // Remove last byte's `27` padding - [r, s, v - 27], - ); - }; - -describe("signOrder", () => { - it("should pad the `v` byte when needed", async () => { - const [signer] = waffle.provider.getWallets(); - // Patch signMessage - signer.signMessage = patchedSignMessageBuilder(signer._signingKey()); - - const domain = { name: "test" }; - - for (const scheme of [ - SigningScheme.EIP712, - SigningScheme.ETHSIGN, - ] as const) { - // Extract `v` from the signature data - const v = ethers.utils.hexDataSlice( - (await signOrder(domain, SAMPLE_ORDER, signer, scheme)).data as string, - 64, - 65, - ); - // Confirm it is either 27 or 28, in hex - expect(v).to.be.oneOf(["0x1b", "0x1c"]); - } - }); -}); diff --git a/test/src/FeeClaimingERC20.sol b/test/src/FeeClaimingERC20.sol deleted file mode 100644 index 73c139e6..00000000 --- a/test/src/FeeClaimingERC20.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -// solhint-disable-next-line compiler-version -pragma solidity ^0.7.6; - -import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol"; - -contract FeeClaimingERC20 is ERC20PresetMinterPauser { - // solhint-disable-next-line no-empty-blocks - constructor() ERC20PresetMinterPauser("FEE", "FEE") {} - - function _transfer(address sender, address recipient, uint256 amount) internal override { - uint256 finalAmount = (amount * 99) / 100; - uint256 burnAmount = amount - finalAmount; - - super._transfer(sender, recipient, finalAmount); - super._burn(sender, burnAmount); - } -} diff --git a/test/src/TestERC20.sol b/test/src/TestERC20.sol deleted file mode 100644 index 8e970fca..00000000 --- a/test/src/TestERC20.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -// solhint-disable-next-line compiler-version -pragma solidity ^0.7.6; - -import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol"; - -contract TestERC20 is ERC20PresetMinterPauser { - constructor(string memory symbol, uint8 decimals) ERC20PresetMinterPauser(symbol, symbol) { - _setupDecimals(decimals); - } -} diff --git a/test/testHelpers.ts b/test/testHelpers.ts deleted file mode 100644 index 4187cc76..00000000 --- a/test/testHelpers.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { BigNumber, BigNumberish } from "ethers"; -import { ethers } from "hardhat"; - -import { OrderKind } from "../src/ts"; - -export function fillBytes(count: number, byte: number): string { - return ethers.utils.hexlify([...Array(count)].map(() => byte)); -} - -export function fillDistinctBytes(count: number, start: number): string { - return ethers.utils.hexlify( - [...Array(count)].map((_, i) => (start + i) % 256), - ); -} - -export function fillUint(bits: number, byte: number): BigNumber { - return BigNumber.from(fillBytes(bits / 8, byte)); -} - -export const SAMPLE_ORDER = { - sellToken: fillBytes(20, 0x01), - buyToken: fillBytes(20, 0x02), - receiver: fillBytes(20, 0x03), - sellAmount: ethers.utils.parseEther("42"), - buyAmount: ethers.utils.parseEther("13.37"), - validTo: 0xffffffff, - appData: ethers.constants.HashZero, - feeAmount: ethers.utils.parseEther("1.0"), - kind: OrderKind.SELL, - partiallyFillable: false, -}; - -export function ceilDiv(p: BigNumberish, q: BigNumberish): BigNumber { - return BigNumber.from(p).add(q).sub(1).div(q); -}