diff --git a/contracts/dance/SeedDance.sol b/contracts/dance/SeedDance.sol index 36fa318d4..0e40c3666 100644 --- a/contracts/dance/SeedDance.sol +++ b/contracts/dance/SeedDance.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "../math/LibFixedPointMath.sol"; - /// Represents a minimum and maximum duration for the dance in a structure that /// can fit in a single slot. uint32 representation for timestamps is inline /// with the rest of the rain protocol. @@ -187,7 +185,6 @@ library LibCommitment { /// would be very likely to be hashed into something else that the miner cannot /// control or predict. contract SeedDance { - using LibFixedPointMath for uint256; using LibCommitment for Commitment; /// The dance has started. diff --git a/contracts/factory/CloneFactory.sol b/contracts/factory/CloneFactory.sol index 9201bf737..1780de12e 100644 --- a/contracts/factory/CloneFactory.sol +++ b/contracts/factory/CloneFactory.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.19; import "rain.interface.factory/ICloneableV1.sol"; import "rain.interface.factory/ICloneFactoryV1.sol"; -import "../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; +import "rain.interface.interpreter/deployerDiscoverable/DeployerDiscoverableMetaV1.sol"; import {ClonesUpgradeable as Clones} from "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol"; /// Thrown when an implementation is the zero address which is always a mistake. diff --git a/contracts/flow/FlowCommon.sol b/contracts/flow/FlowCommon.sol index 9091812f4..a9a61fd05 100644 --- a/contracts/flow/FlowCommon.sol +++ b/contracts/flow/FlowCommon.sol @@ -8,7 +8,7 @@ import "rain.interface.interpreter/IInterpreterV1.sol"; import "rain.interface.interpreter/LibEncodedDispatch.sol"; import "rain.interface.interpreter/LibContext.sol"; import "rain.lib.interpreter/LibInterpreterState.sol"; -import "../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; +import "rain.interface.interpreter/deployerDiscoverable/DeployerDiscoverableMetaV1.sol"; import "rain.interface.interpreter/LibEvaluable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; diff --git a/contracts/flow/erc20/FlowERC20.sol b/contracts/flow/erc20/FlowERC20.sol index cb8d8f1a9..3892a2e4d 100644 --- a/contracts/flow/erc20/FlowERC20.sol +++ b/contracts/flow/erc20/FlowERC20.sol @@ -11,10 +11,10 @@ import "rain.interface.interpreter/LibEncodedDispatch.sol"; import "rain.interface.factory/ICloneableV1.sol"; import "rain.interface.flow/IFlowERC20V3.sol"; import "sol.lib.memory/LibStackSentinel.sol"; +import "rain.math.fixedpoint/FixedPointDecimalArithmeticOpenZeppelin.sol"; import {AllStandardOps} from "../../interpreter/ops/AllStandardOps.sol"; import "../libraries/LibFlow.sol"; -import "../../math/LibFixedPointMath.sol"; import "../FlowCommon.sol"; bytes32 constant CALLER_META_HASH = bytes32( @@ -43,7 +43,7 @@ contract FlowERC20 is using LibUint256Array for uint256; using LibUint256Array for uint256[]; using LibUint256Matrix for uint256[]; - using LibFixedPointMath for uint256; + using FixedPointDecimalArithmeticOpenZeppelin for uint256; bool private evalHandleTransfer; Evaluable internal evaluable; diff --git a/contracts/flow/erc721/FlowERC721.sol b/contracts/flow/erc721/FlowERC721.sol index 4f501eee5..e55f9f895 100644 --- a/contracts/flow/erc721/FlowERC721.sol +++ b/contracts/flow/erc721/FlowERC721.sol @@ -12,10 +12,10 @@ import "sol.lib.memory/LibStackSentinel.sol"; import "rain.interface.interpreter/LibEncodedDispatch.sol"; import "rain.interface.factory/ICloneableV1.sol"; import "rain.interface.flow/IFlowERC721V3.sol"; +import "rain.math.fixedpoint/FixedPointDecimalArithmeticOpenZeppelin.sol"; import {AllStandardOps} from "../../interpreter/ops/AllStandardOps.sol"; import "../libraries/LibFlow.sol"; -import "../../math/LibFixedPointMath.sol"; import "../FlowCommon.sol"; /// Thrown when burner of tokens is not the owner of tokens. @@ -49,7 +49,7 @@ contract FlowERC721 is using LibUint256Array for uint256; using LibUint256Array for uint256[]; using LibUint256Matrix for uint256[]; - using LibFixedPointMath for uint256; + using FixedPointDecimalArithmeticOpenZeppelin for uint256; using LibStackSentinel for Pointer; bool private evalHandleTransfer; diff --git a/contracts/interpreter/deploy/DeployerDiscoverableMetaV1.sol b/contracts/interpreter/deploy/DeployerDiscoverableMetaV1.sol deleted file mode 100644 index 05c67b900..000000000 --- a/contracts/interpreter/deploy/DeployerDiscoverableMetaV1.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity =0.8.19; - -import "sol.metadata/IMetaV1.sol"; -import "sol.metadata/LibMeta.sol"; -import "./LibDeployerDiscoverable.sol"; - -struct DeployerDiscoverableMetaV1ConstructionConfig { - address deployer; - bytes meta; -} - -/// @title DeployerDiscoverableMetaV1 -/// @notice Checks metadata against a known hash, emits it then touches the -/// deployer (deploy an empty expression). This allows indexers to discover the -/// metadata of the `DeployerDiscoverableMetaV1` contract by indexing the -/// deployer. In this way the deployer acts as a pseudo-registry by virtue of it -/// being a natural hub for interactions. -abstract contract DeployerDiscoverableMetaV1 is IMetaV1 { - constructor( - bytes32 metaHash_, - DeployerDiscoverableMetaV1ConstructionConfig memory config_ - ) { - LibMeta.checkMetaHashed(metaHash_, config_.meta); - emit MetaV1(msg.sender, uint256(uint160(address(this))), config_.meta); - LibDeployerDiscoverable.touchDeployer(config_.deployer); - } -} diff --git a/contracts/interpreter/deploy/LibDeployerDiscoverable.sol b/contracts/interpreter/deploy/LibDeployerDiscoverable.sol deleted file mode 100644 index 6b8dbf92c..000000000 --- a/contracts/interpreter/deploy/LibDeployerDiscoverable.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import "rain.interface.interpreter/IExpressionDeployerV1.sol"; - -library LibDeployerDiscoverable { - /// Hack so that some deployer will emit an event with the sender as the - /// caller of `touchDeployer`. This MAY be needed by indexers such as - /// subgraph that can only index events from the first moment they are aware - /// of some contract. The deployer MUST be registered in ERC1820 registry - /// before it is touched, THEN the caller meta MUST be emitted after the - /// deployer is touched. This allows indexers such as subgraph to index the - /// deployer, then see the caller, then see the caller's meta emitted in the - /// same transaction. - /// This is NOT required if ANY other expression is deployed in the same - /// transaction as the caller meta, there only needs to be one expression on - /// ANY deployer known to ERC1820. - function touchDeployer(address deployer_) internal { - IExpressionDeployerV1(deployer_).deployExpression( - new bytes[](0), - new uint256[](0), - new uint256[](0) - ); - } -} diff --git a/contracts/lobby/Lobby.sol b/contracts/lobby/Lobby.sol index 96c9fa45c..b769eebd5 100644 --- a/contracts/lobby/Lobby.sol +++ b/contracts/lobby/Lobby.sol @@ -9,10 +9,10 @@ import "rain.interface.interpreter/LibEncodedDispatch.sol"; import "sol.lib.memory/LibStackPointer.sol"; import "rain.interface.interpreter/LibContext.sol"; import "rain.interface.interpreter/IInterpreterCallerV2.sol"; -import "../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; +import "rain.interface.interpreter/deployerDiscoverable/DeployerDiscoverableMetaV1.sol"; import "rain.interface.interpreter/LibEvaluable.sol"; import "rain.math.saturating/SaturatingMath.sol"; -import "../math/LibFixedPointMath.sol"; +import "rain.math.fixedpoint/FixedPointDecimalArithmeticOpenZeppelin.sol"; import "rain.interface.factory/ICloneableV1.sol"; import "sol.lib.memory/LibUint256Matrix.sol"; @@ -20,7 +20,6 @@ import "../phased/Phased.sol"; import {ReentrancyGuardUpgradeable as ReentrancyGuard} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import {MathUpgradeable as Math} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; /// Thrown when a result hash already exists but the lobby is attempting to move /// to complete from pending. @@ -143,7 +142,7 @@ contract Lobby is using LibStackPointer for Pointer; using Math for uint256; using SaturatingMath for uint256; - using LibFixedPointMath for uint256; + using FixedPointDecimalArithmeticOpenZeppelin for uint256; event Initialize(address sender, LobbyConfig config); diff --git a/contracts/math/LibFixedPointMath.sol b/contracts/math/LibFixedPointMath.sol deleted file mode 100644 index 72a0bcb13..000000000 --- a/contracts/math/LibFixedPointMath.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import {MathUpgradeable as Math} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; -import "rain.math.fixedpoint/FixedPointDecimalConstants.sol"; - -/// @title FixedPointMath -/// @notice Sometimes we want to do math with decimal values but all we have -/// are integers, typically uint256 integers. Floats are very complex so we -/// don't attempt to simulate them. Instead we provide a standard definition of -/// "one" as 10 ** 18 and scale everything up/down to this as fixed point math. -/// -/// Overflows SATURATE rather than error, e.g. scaling max uint256 up will result -/// in max uint256. The max uint256 as decimal is roughly 1e77 so scaling values -/// comparable to 1e18 is unlikely to ever saturate in practise. For a typical -/// use case involving tokens, the entire supply of a token rescaled up a full -/// 18 decimals would still put it "only" in the region of ~1e40 which has a full -/// 30 orders of magnitude buffer before running into saturation issues. However, -/// there's no theoretical reason that a token or any other use case couldn't use -/// large numbers or extremely precise decimals that would push this library to -/// saturation point, so it MUST be treated with caution around the edge cases. -/// -/// One case where values could come near the saturation/overflow point is phantom -/// overflow. This is where an overflow happens during the internal logic of some -/// operation like "fixed point multiplication" even though the final result fits -/// within uint256. The fixed point multiplication and division functions are -/// thin wrappers around Open Zeppelin's `mulDiv` function, that handles phantom -/// overflow, reducing the problems of rescaling overflow/saturation to the input -/// and output range rather than to the internal implementation details. For this -/// library that gives an additional full 18 orders of magnitude for safe fixed -/// point multiplication operations. -/// -/// Scaling down ANY fixed point decimal also reduces the precision which can -/// lead to dust or in the worst case trapped funds if subsequent subtraction -/// overflows a rounded-down number. Consider using saturating subtraction for -/// safety against previously downscaled values, and whether trapped dust is a -/// significant issue. If you need to retain full/arbitrary precision in the case -/// of downscaling DO NOT use this library. -/// -/// All rescaling and/or division operations in this library require the rounding -/// flag from Open Zeppelin math. This allows and forces the caller to specify -/// where dust sits due to rounding. For example the caller could round up when -/// taking tokens from `msg.sender` and round down when returning them, ensuring -/// that any dust in the round trip accumulates in the contract rather than -/// opening an exploit or reverting and trapping all funds. This is exactly how -/// the ERC4626 vault spec handles dust and is a good reference point in general. -/// Typically the contract holding tokens and non-interactive participants should -/// be favoured by rounding calculations rather than active participants. This is -/// because we assume that an active participant, e.g. `msg.sender`, knowns -/// something we don't and is carefully crafting an attack, so we are most -/// conservative and suspicious of their inputs and actions. -library LibFixedPointMath { - using Math for uint256; - - /// Fixed point multiplication in native scale decimals. - /// Both `a_` and `b_` MUST be `DECIMALS` fixed point decimals. - /// @param a_ First term. - /// @param b_ Second term. - /// @param rounding_ Rounding direction as per Open Zeppelin Math. - /// @return `a_` multiplied by `b_` to `DECIMALS` fixed point decimals. - function fixedPointMul( - uint256 a_, - uint256 b_, - Math.Rounding rounding_ - ) internal pure returns (uint256) { - return a_.mulDiv(b_, FIXED_POINT_ONE, rounding_); - } - - /// Fixed point division in native scale decimals. - /// Both `a_` and `b_` MUST be `DECIMALS` fixed point decimals. - /// @param a_ First term. - /// @param b_ Second term. - /// @param rounding_ Rounding direction as per Open Zeppelin Math. - /// @return `a_` divided by `b_` to `DECIMALS` fixed point decimals. - function fixedPointDiv( - uint256 a_, - uint256 b_, - Math.Rounding rounding_ - ) internal pure returns (uint256) { - return a_.mulDiv(FIXED_POINT_ONE, b_, rounding_); - } -} diff --git a/contracts/orderbook/LibOrder.sol b/contracts/orderbook/LibOrder.sol deleted file mode 100644 index 7ba3d5fd8..000000000 --- a/contracts/orderbook/LibOrder.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity =0.8.19; - -import "rain.interface.orderbook/IOrderBookV2.sol"; - -/// @title LibOrder -/// @notice Consistent handling of `Order` for where it matters w.r.t. -/// determinism and security. -library LibOrder { - /// Hashes `Order` in a secure and deterministic way. Uses abi.encode rather - /// than abi.encodePacked to guard against potential collisions where many - /// inputs encode to the same output bytes. - /// @param order_ The order to hash. - /// @return The hash of `order_` as a `uint256` rather than `bytes32`. - function hash(Order memory order_) internal pure returns (uint256) { - return uint256(keccak256(abi.encode(order_))); - } -} diff --git a/contracts/orderbook/LibOrderBook.sol b/contracts/orderbook/LibOrderBook.sol deleted file mode 100644 index 868ccca50..000000000 --- a/contracts/orderbook/LibOrderBook.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity =0.8.19; - -import "rain.interface.interpreter/IInterpreterStoreV1.sol"; -import "rain.interface.orderbook/IOrderBookV2.sol"; -import "../math/LibFixedPointMath.sol"; -import {MathUpgradeable as Math} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; - -/// All information resulting from an order calculation that allows for vault IO -/// to be calculated and applied, then the handle IO entrypoint to be dispatched. -/// @param outputMax The UNSCALED maximum output calculated by the order -/// expression. WILL BE RESCALED ACCORDING TO TOKEN DECIMALS to an 18 fixed -/// point decimal number for the purpose of calculating actual vault movements. -/// The output max is CAPPED AT THE OUTPUT VAULT BALANCE OF THE ORDER OWNER. -/// The order is guaranteed that the total output of this single clearance cannot -/// exceed this (subject to rescaling). It is up to the order expression to track -/// values over time if the output max is to impose a global limit across many -/// transactions and counterparties. -/// @param IORatio The UNSCALED order ratio as input/output from the perspective -/// of the order. As each counterparty's input is the other's output, the IORatio -/// calculated by each order is inverse of its counterparty. IORatio is SCALED -/// ACCORDING TO TOKEN DECIMALS to allow 18 decimal fixed point math over the -/// vault balances. I.e. `1e18` returned from the expression is ALWAYS "one" as -/// ECONOMIC EQUIVALENCE between two tokens, but this will be rescaled according -/// to the decimals of the token. For example, if DAI and USDT have a ratio of -/// `1e18` then in reality `1e12` DAI will move in the vault for every `1` USDT -/// that moves, because DAI has `1e18` decimals per $1 peg and USDT has `1e6` -/// decimals per $1 peg. THE ORDER DEFINES THE DECIMALS for each token, NOT the -/// token itself, because the token MAY NOT report its decimals as per it being -/// optional in the ERC20 specification. -/// @param context The entire 2D context array, initialized from the context -/// passed into the order calculations and then populated with the order -/// calculations and vault IO before being passed back to handle IO entrypoint. -/// @param namespace The `StateNamespace` to be passed to the store for calculate -/// IO state changes. -/// @param kvs KVs returned from calculate order entrypoint to pass to the store -/// before calling handle IO entrypoint. -struct OrderIOCalculation { - uint256 outputMax; - //solhint-disable-next-line var-name-mixedcase - uint256 IORatio; - uint256[][] context; - StateNamespace namespace; - uint256[] kvs; -} - -library LibOrderBook { - using LibFixedPointMath for uint256; - using Math for uint256; - - /// Calculates the clear state change given both order calculations for order - /// alice and order bob. The input of each is their output multiplied by - /// their IO ratio and the output of each is the smaller of their maximum - /// output and the counterparty IO * max output. - /// @param aliceOrderIOCalculation_ Order calculation A. - /// @param bobOrderIOCalculation_ Order calculation B. - /// @return The clear state change with absolute inputs and outputs for A and - /// B. - function _clearStateChange( - OrderIOCalculation memory aliceOrderIOCalculation_, - OrderIOCalculation memory bobOrderIOCalculation_ - ) internal pure returns (ClearStateChange memory) { - ClearStateChange memory clearStateChange_; - { - clearStateChange_.aliceOutput = aliceOrderIOCalculation_ - .outputMax - .min( - // B's input is A's output. - // A cannot output more than their max. - // B wants input of their IO ratio * their output. - // Always round IO calculations up. - bobOrderIOCalculation_.outputMax.fixedPointMul( - bobOrderIOCalculation_.IORatio, - Math.Rounding.Up - ) - ); - clearStateChange_.bobOutput = bobOrderIOCalculation_.outputMax.min( - // A's input is B's output. - // B cannot output more than their max. - // A wants input of their IO ratio * their output. - // Always round IO calculations up. - aliceOrderIOCalculation_.outputMax.fixedPointMul( - aliceOrderIOCalculation_.IORatio, - Math.Rounding.Up - ) - ); - - // A's input is A's output * their IO ratio. - // Always round IO calculations up. - clearStateChange_.aliceInput = clearStateChange_ - .aliceOutput - .fixedPointMul( - aliceOrderIOCalculation_.IORatio, - Math.Rounding.Up - ); - // B's input is B's output * their IO ratio. - // Always round IO calculations up. - clearStateChange_.bobInput = clearStateChange_ - .bobOutput - .fixedPointMul( - bobOrderIOCalculation_.IORatio, - Math.Rounding.Up - ); - } - return clearStateChange_; - } -} diff --git a/contracts/orderbook/OrderBook.meta.json b/contracts/orderbook/OrderBook.meta.json deleted file mode 100644 index 00180a066..000000000 --- a/contracts/orderbook/OrderBook.meta.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "name": "Order Book", - "abiName": "OrderBook", - "desc": "An orderbook that deploys strategies represented as interpreter expressions rather than individual orders. The order book contract itself behaves similarly to an ERC4626 vault but with much more fine grained control over how tokens are allocated and moved internally by their owners, and without any concept of 'shares'. Token owners MAY deposit and withdraw their tokens under arbitrary vault IDs on a per-token basis, then define orders that specify how tokens move between vaults according to an expression. The expression returns a maximum amount and a token input/output ratio from the perpective of the order. When two expressions intersect, as in their ratios are the inverse of each other, then tokens can move between vaults. \n For example, consider order A with input TKNA and output TKNB with a constant ratio of 100:1. This order in isolation has no ability to move tokens. If an order B appears with input TKNB and output TKNA and a ratio of 1:100 then this is a perfect match with order A. In this case 100 TKNA will move from order B to order A and 1 TKNB will move from order A to order B. \n IO ratios are always specified as input:output and are 18 decimal fixed point values. The maximum amount that can be moved in the current clearance is also set by the order expression as an 18 decimal fixed point value. \n Typically orders will not clear when their match is exactly 1:1 as the clearer needs to pay gas to process the match. Each order will get exactly the ratio it calculates when it does clear so if there is _overlap_ in the ratios then the clearer keeps the difference. In our above example, consider order B asking a ratio of 1:110 instead of 1:100. In this case 100 TKNA will move from order B to order A and 10 TKNA will move to the clearer's vault and 1 TKNB will move from order A to order B. In the case of fixed prices this is not very interesting as order B could more simply take order A directly for cheaper rather than involving a third party. Indeed, Orderbook supports a direct 'take orders' method that works similar to a 'market buy'. In the case of dynamic expression based ratios, it allows both order A and order B to clear non-interactively according to their strategy, trading off active management, dealing with front-running, MEV, etc. for zero-gas and exact-ratio clearance. \n The general invariant for clearing and take orders is: \n ``` \n ratioA = InputA / OutputA \n ratioB = InputB / OutputB \n ratioA * ratioB = ( InputA * InputB ) / ( OutputA * OutputB ) \n OutputA >= InputB \n OutputB >= InputA \n ∴ ratioA * ratioB <= 1 \n ``` \n Orderbook is `IERC3156FlashLender` compliant with a 0 fee flash loan implementation to allow external liquidity from other onchain DEXes to match against orderbook expressions. All deposited tokens across all vaults are available for flashloan, the flashloan MAY BE REPAID BY CALLING TAKE ORDER such that Orderbook's liability to its vaults is decreased by an incoming trade from the flashloan borrower. See `ZeroExOrderBookFlashBorrower` for an example of how this works in practise. \nOrderbook supports many to many input/output token relationship, for example some order can specify an array of stables it would be willing to accept in return for some ETH. This removes the need for a combinatorial explosion of order strategies between like assets but introduces the issue of token decimal handling. End users understand that 'one' USDT is roughly equal to 'one' DAI, but onchain this is incorrect by _12 orders of magnitude_. This is because 'one' DAI is `1e18` tokens and 'one' USDT is `1e6` tokens. The orderbook is allowing orders to deploy expressions that define _economic equivalence_ but this doesn't map 1:1 with numeric equivalence in a many to many setup behind token decimal convensions. The solution is to require that end users who place orders provide the decimals of each token they include in their valid IO lists, and to calculate all amounts and ratios in their expressions _as though they were 18 decimal fixed point values_. Orderbook will then automatically rescale the expression values before applying the final vault movements. If an order provides the 'wrong' decimal values for some token then it will simply calculate its own ratios and amounts incorrectly which will either lead to no matching orders or a very bad trade for the order owner. There is no way that misrepresenting decimals can attack some other order by a counterparty. Orderbook DOES NOT read decimals from tokens onchain because A. this would be gas for an external call to a cold token contract and B. the ERC20 standard specifically states NOT to read decimals from the interface onchain. \nWhen two orders clear there are NO TOKEN MOVEMENTS, only internal vault balances are updated from the input and output vaults. Typically this results in less gas per clear than calling external token transfers and also avoids issues with reentrancy, allowances, external balances etc. This also means that REBASING TOKENS AND TOKENS WITH DYNAMIC BALANCE ARE NOT SUPPORTED. Orderbook ONLY WORKS IF TOKEN BALANCES ARE 1:1 WITH ADDITION/SUBTRACTION PER VAULT MOVEMENT. \nDust due to rounding errors always favours the order. Output max is rounded down and IO ratios are rounded up. Input and output amounts are always converted to absolute values before applying to vault balances such that orderbook always retains fully collateralised inventory of underlying token balances to support withdrawals, with the caveat that dynamic token balanes are not supported. \nWhen an order clears it is NOT removed. Orders remain active until the owner deactivates them. This is gas efficient as order owners MAY deposit more tokens in a vault with an order against it many times and the order strategy will continue to be clearable according to its expression. As vault IDs are `uint256` values there are effectively infinite possible vaults for any token so there is no limit to how many active orders any address can have at one time. This also allows orders to be daisy chained arbitrarily where output vaults for some order are the input vaults for some other order. \nExpression storage is namespaced by order owner, so gets and sets are unique to each onchain address. Order owners MUST TAKE CARE not to override their storage sets globally across all their orders, which they can do most simply by hashing the order hash into their get/set keys inside the expression. This gives maximum flexibility for shared state across orders without allowing order owners to attack and overwrite values stored by orders placed by their counterparty. \nNote that each order specifies its own interpreter and deployer so the owner is responsible for not corrupting their own calculations with bad interpreters. This also means the Orderbook MUST assume the interpreter, and notably the interpreter's store, is malicious and guard against reentrancy etc. \nAs Orderbook supports any expression that can run on any `IInterpreterV1` and counterparties are available to the order, order strategies are free to implement KYC/membership, tracking, distributions, stock, buybacks, etc. etc.", - "alias": "orderbook", - "source": "https://github.com/rainprotocol/rain-protocol", - "methods": [ - { - "name": "addOrder", - "abiName": "addOrder", - "desc": "Adds a new order", - "inputs": [ - { - "name": "Valid inputs", - "abiName": "validInputs", - "desc": "Token address, vault ID and decimals combinations that are valid inputs for this Order.", - "path": "[25].inputs[0].components[0]" - }, - { - "name": "Valid outputs", - "abiName": "validOutputs", - "desc": "Token address, vault ID and decimals combinations that are valid outputs for this Order.", - "path": "[25].inputs[0].components[1]" - } - ], - "expressions": [ - { - "name": "Order", - "abiName": "evaluableConfig", - "desc": "An expression for the order. Orderbook context is actually fairly complex. The calling context column is populated before calculate order, but the remaining columns are only available to handle IO as they depend on the full evaluation of calculuate order, and cross referencing against the same from the counterparty, as well as accounting limits such as current vault balances, etc. The token address and decimals for vault inputs and outputs IS available to the calculate order entrypoint, but not the final vault balances/diff.", - "path": "[25].inputs[0].components[2]", - "signedContext": true, - "callerContext": false, - "contextColumns": [ - { - "name": "Base", - "desc": "Base context column.", - "alias": "base", - "columnIndex": 0, - "cells": [ - { - "name": "Caller", - "desc": "The contract or EOA that called OrderBook.", - "alias": "orderbook-caller-address", - "cellIndex": 0 - }, - { - "name": "OrderBook Contract", - "desc": "The address of the OrderBook contract.", - "alias": "orderbook-contract-address", - "cellIndex": 1 - } - ] - }, - { - "name": "Calling context", - "desc": "Contextual data available to both calculate order and handle IO. The order hash, order owner and order counterparty. IMPORTANT NOTE that the typical base context of an order with the caller will often be an unrelated clearer of the order rather than the owner or counterparty.", - "alias": "calling-context", - "columnIndex": 1, - "cells": [ - { - "name": "Order hash", - "desc": "The hash of the order.", - "alias": "order-hash", - "cellIndex": 0 - }, - { - "name": "Order owner address", - "desc": "The address of the owner of the order.", - "alias": "order-owner-address", - "cellIndex": 1 - }, - { - "name": "Counterparty", - "desc": "The address of the counterparty of the order.", - "alias": "counterparty-address", - "cellIndex": 2 - } - ] - }, - { - "name": "Calculations", - "desc": "Calculations column contains the DECIMAL RESCALED calculations but otherwise provided as-is according to calculate order entrypoint.", - "alias": "calculations", - "columnIndex": 2, - "cells": [ - { - "name": "Order output max", - "desc": "The output max rescaled to 18 decimals and capped by vault balance.", - "alias": "order-output-max", - "cellIndex": 0 - }, - { - "name": "Order IO ratio", - "desc": "The IO ratio rescaled to 18 decimals.", - "alias": "order-io-ratio", - "cellIndex": 1 - } - ] - }, - { - "name": "Vault inputs", - "desc": "Vault inputs are the literal token amounts and vault balances before and after for the input token from the perspective of the order. MAY be significantly different to the calculated amount due to insufficient vault balances from either the owner or counterparty, etc.", - "alias": "vault-inputs", - "columnIndex": 3, - "cells": [ - { - "name": "Vault input token address", - "desc": "The vault input token address.", - "alias": "vault-input-token-address", - "cellIndex": 0 - }, - { - "name": "Vault input token decimals", - "desc": "The vault input token decimals.", - "alias": "vault-input-token-decimals", - "cellIndex": 1 - }, - { - "name": "Vault input ID", - "desc": "The vault input ID", - "alias": "vault-input-id", - "cellIndex": 2 - }, - { - "name": "Vault input balance before", - "desc": "The balance of the vault for this order's input token, before clearing", - "alias": "vault-input-balance-before", - "cellIndex": 3 - }, - { - "name": "Vault input balance increase", - "desc": "The amount the input vault balance increased by.", - "alias": "vault-input-balance-increase", - "cellIndex": 4 - } - ] - }, - { - "name": "Vault outputs", - "desc": "Vault outputs are the same as vault inputs but for the output token from the perspective of the order.", - "alias": "vault-outputs", - "columnIndex": 4, - "cells": [ - { - "name": "Vault output token address", - "desc": "The vault output token address.", - "alias": "vault-output-token-address", - "cellIndex": 0 - }, - { - "name": "Vault output token decimals", - "desc": "The vault output token decimals.", - "alias": "vault-output-token-decimals", - "cellIndex": 1 - }, - { - "name": "Vault output ID", - "desc": "The vault output ID", - "alias": "vault-output-id", - "cellIndex": 2 - }, - { - "name": "Vault output balance before", - "desc": "The balance of the vault for this order's output token, before clearing", - "alias": "vault-output-balance-before", - "cellIndex": 3 - }, - { - "name": "Vault output balance decrease", - "desc": "The amount the output vault balance decreased by.", - "alias": "vault-output-balance-decrease", - "cellIndex": 4 - } - ] - } - ] - } - ] - } - ] -} diff --git a/contracts/orderbook/OrderBook.sol b/contracts/orderbook/OrderBook.sol deleted file mode 100644 index 5a7c6337e..000000000 --- a/contracts/orderbook/OrderBook.sol +++ /dev/null @@ -1,697 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity =0.8.19; - -import "rain.interface.orderbook/IOrderBookV2.sol"; -import "./LibOrder.sol"; -import "../math/LibFixedPointMath.sol"; -import "rain.math.fixedpoint/FixedPointDecimalScale.sol"; -import "./OrderBookFlashLender.sol"; -import "rain.interface.interpreter/LibEncodedDispatch.sol"; -import "rain.interface.interpreter/LibContext.sol"; -import "../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; -import "./LibOrderBook.sol"; - -import {MulticallUpgradeable as Multicall} from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; -import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import {MathUpgradeable as Math} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; -import {ReentrancyGuardUpgradeable as ReentrancyGuard} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; - -/// Thrown when the `msg.sender` modifying an order is not its owner. -/// @param sender `msg.sender` attempting to modify the order. -/// @param owner The owner of the order. -error NotOrderOwner(address sender, address owner); - -/// Thrown when the input and output tokens don't match, in either direction. -/// @param aliceToken The input or output of one order. -/// @param bobToken The input or output of the other order that doesn't match a. -error TokenMismatch(address aliceToken, address bobToken); - -/// Thrown when the minimum input is not met. -/// @param minimumInput The minimum input required. -/// @param input The input that was achieved. -error MinimumInput(uint256 minimumInput, uint256 input); - -/// Thrown when two orders have the same owner during clear. -/// @param owner The owner of both orders. -error SameOwner(address owner); - -/// @dev Hash of the caller contract metadata for construction. -bytes32 constant CALLER_META_HASH = bytes32( - 0x56ffc3fc82109c33f1e1544157a70144fc15e7c6e9ae9c65a636fd165b1bc51c -); - -/// @dev Value that signifies that an order is live in the internal mapping. -/// Anything nonzero is equally useful. -uint256 constant LIVE_ORDER = 1; - -/// @dev Value that signifies that an order is dead in the internal mapping. -uint256 constant DEAD_ORDER = 0; - -/// @dev Entrypoint to a calculate the amount and ratio of an order. -SourceIndex constant CALCULATE_ORDER_ENTRYPOINT = SourceIndex.wrap(0); -/// @dev Entrypoint to handle the final internal vault movements resulting from -/// matching multiple calculated orders. -SourceIndex constant HANDLE_IO_ENTRYPOINT = SourceIndex.wrap(1); - -/// @dev Minimum outputs for calculate order are the amount and ratio. -uint256 constant CALCULATE_ORDER_MIN_OUTPUTS = 2; -/// @dev Maximum outputs for calculate order are the amount and ratio. -uint16 constant CALCULATE_ORDER_MAX_OUTPUTS = 2; - -/// @dev Handle IO has no outputs as it only responds to vault movements. -uint256 constant HANDLE_IO_MIN_OUTPUTS = 0; -/// @dev Handle IO has no outputs as it only response to vault movements. -uint16 constant HANDLE_IO_MAX_OUTPUTS = 0; - -/// @dev Orderbook context is actually fairly complex. The calling context column -/// is populated before calculate order, but the remaining columns are only -/// available to handle IO as they depend on the full evaluation of calculuate -/// order, and cross referencing against the same from the counterparty, as well -/// as accounting limits such as current vault balances, etc. -/// The token address and decimals for vault inputs and outputs IS available to -/// the calculate order entrypoint, but not the final vault balances/diff. -uint256 constant CALLING_CONTEXT_COLUMNS = 4; -/// @dev Base context from LibContext. -uint256 constant CONTEXT_BASE_COLUMN = 0; - -/// @dev Contextual data available to both calculate order and handle IO. The -/// order hash, order owner and order counterparty. IMPORTANT NOTE that the -/// typical base context of an order with the caller will often be an unrelated -/// clearer of the order rather than the owner or counterparty. -uint256 constant CONTEXT_CALLING_CONTEXT_COLUMN = 1; -/// @dev Calculations column contains the DECIMAL RESCALED calculations but -/// otherwise provided as-is according to calculate order entrypoint -uint256 constant CONTEXT_CALCULATIONS_COLUMN = 2; -/// @dev Vault inputs are the literal token amounts and vault balances before and -/// after for the input token from the perspective of the order. MAY be -/// significantly different to the calculated amount due to insufficient vault -/// balances from either the owner or counterparty, etc. -uint256 constant CONTEXT_VAULT_INPUTS_COLUMN = 3; -/// @dev Vault outputs are the same as vault inputs but for the output token from -/// the perspective of the order. -uint256 constant CONTEXT_VAULT_OUTPUTS_COLUMN = 4; - -/// @dev Row of the token address for vault inputs and outputs columns. -uint256 constant CONTEXT_VAULT_IO_TOKEN = 0; -/// @dev Row of the token decimals for vault inputs and outputs columns. -uint256 constant CONTEXT_VAULT_IO_TOKEN_DECIMALS = 1; -/// @dev Row of the vault ID for vault inputs and outputs columns. -uint256 constant CONTEXT_VAULT_IO_VAULT_ID = 2; -/// @dev Row of the vault balance before the order was cleared for vault inputs -/// and outputs columns. -uint256 constant CONTEXT_VAULT_IO_BALANCE_BEFORE = 3; -/// @dev Row of the vault balance difference after the order was cleared for -/// vault inputs and outputs columns. The diff is ALWAYS POSITIVE as it is a -/// `uint256` so it must be added to input balances and subtraced from output -/// balances. -uint256 constant CONTEXT_VAULT_IO_BALANCE_DIFF = 4; -/// @dev Length of a vault IO column. -uint256 constant CONTEXT_VAULT_IO_ROWS = 5; - -/// @title OrderBook -/// See `IOrderBookV1` for more documentation. -contract OrderBook is - IOrderBookV2, - ReentrancyGuard, - Multicall, - OrderBookFlashLender, - DeployerDiscoverableMetaV1 -{ - using LibUint256Array for uint256[]; - using SafeERC20 for IERC20; - using Math for uint256; - using LibFixedPointMath for uint256; - using FixedPointDecimalScale for uint256; - using LibOrder for Order; - using LibUint256Array for uint256; - - /// All hashes of all active orders. There's nothing interesting in the value - /// it's just nonzero if the order is live. The key is the hash of the order. - /// Removing an order sets the value back to zero so it is identical to the - /// order never existing and gives a gas refund on removal. - /// The order hash includes its owner so there's no need to build a multi - /// level mapping, each order hash MUST uniquely identify the order globally. - /// order hash => order is live - mapping(uint256 => uint256) internal orders; - - /// @inheritdoc IOrderBookV2 - mapping(address => mapping(address => mapping(uint256 => uint256))) - public vaultBalance; - - /// Initializes the orderbook upon construction for compatibility with - /// Open Zeppelin upgradeable contracts. Orderbook itself does NOT support - /// factory deployments as each order is a unique expression deployment - /// rather than needing to wrap up expressions with proxies. - constructor( - DeployerDiscoverableMetaV1ConstructionConfig memory config_ - ) initializer DeployerDiscoverableMetaV1(CALLER_META_HASH, config_) { - __ReentrancyGuard_init(); - __Multicall_init(); - } - - /// @inheritdoc IOrderBookV2 - function deposit(DepositConfig calldata config_) external nonReentrant { - // It is safest with vault deposits to move tokens in to the Orderbook - // before updating internal vault balances although we have a reentrancy - // guard in place anyway. - emit Deposit(msg.sender, config_); - IERC20(config_.token).safeTransferFrom( - msg.sender, - address(this), - config_.amount - ); - vaultBalance[msg.sender][config_.token][config_.vaultId] += config_ - .amount; - } - - /// @inheritdoc IOrderBookV2 - function withdraw(WithdrawConfig calldata config_) external nonReentrant { - uint256 vaultBalance_ = vaultBalance[msg.sender][config_.token][ - config_.vaultId - ]; - uint256 withdrawAmount_ = config_.amount.min(vaultBalance_); - // The overflow check here is redundant with .min above, so technically - // this is overly conservative but we REALLY don't want withdrawals to - // exceed vault balances. - vaultBalance[msg.sender][config_.token][config_.vaultId] = - vaultBalance_ - - withdrawAmount_; - emit Withdraw(msg.sender, config_, withdrawAmount_); - _decreaseFlashDebtThenSendToken( - config_.token, - msg.sender, - withdrawAmount_ - ); - } - - /// @inheritdoc IOrderBookV2 - function addOrder(OrderConfig calldata config_) external nonReentrant { - ( - IInterpreterV1 interpreter_, - IInterpreterStoreV1 store_, - address expression_ - ) = config_.evaluableConfig.deployer.deployExpression( - config_.evaluableConfig.sources, - config_.evaluableConfig.constants, - LibUint256Array.arrayFrom( - CALCULATE_ORDER_MIN_OUTPUTS, - HANDLE_IO_MIN_OUTPUTS - ) - ); - Order memory order_ = Order( - msg.sender, - config_ - .evaluableConfig - .sources[SourceIndex.unwrap(HANDLE_IO_ENTRYPOINT)] - .length > 0, - Evaluable(interpreter_, store_, expression_), - config_.validInputs, - config_.validOutputs - ); - uint256 orderHash_ = order_.hash(); - - orders[orderHash_] = LIVE_ORDER; - emit AddOrder( - msg.sender, - config_.evaluableConfig.deployer, - order_, - orderHash_ - ); - - if (config_.meta.length > 0) { - LibMeta.checkMetaUnhashed(config_.meta); - emit MetaV1(msg.sender, orderHash_, config_.meta); - } - } - - function _calculateOrderDispatch( - address expression_ - ) internal pure returns (EncodedDispatch) { - return - LibEncodedDispatch.encode( - expression_, - CALCULATE_ORDER_ENTRYPOINT, - CALCULATE_ORDER_MAX_OUTPUTS - ); - } - - function _handleIODispatch( - address expression_ - ) internal pure returns (EncodedDispatch) { - return - LibEncodedDispatch.encode( - expression_, - HANDLE_IO_ENTRYPOINT, - HANDLE_IO_MAX_OUTPUTS - ); - } - - /// @inheritdoc IOrderBookV2 - function removeOrder(Order calldata order_) external nonReentrant { - if (msg.sender != order_.owner) { - revert NotOrderOwner(msg.sender, order_.owner); - } - uint256 orderHash_ = order_.hash(); - delete (orders[orderHash_]); - emit RemoveOrder(msg.sender, order_, orderHash_); - } - - /// @inheritdoc IOrderBookV2 - function takeOrders( - TakeOrdersConfig calldata takeOrders_ - ) - external - nonReentrant - returns (uint256 totalInput_, uint256 totalOutput_) - { - uint256 i_ = 0; - TakeOrderConfig memory takeOrder_; - Order memory order_; - uint256 remainingInput_ = takeOrders_.maximumInput; - while (i_ < takeOrders_.orders.length && remainingInput_ > 0) { - takeOrder_ = takeOrders_.orders[i_]; - order_ = takeOrder_.order; - uint256 orderHash_ = order_.hash(); - if (orders[orderHash_] == DEAD_ORDER) { - emit OrderNotFound(msg.sender, order_.owner, orderHash_); - } else { - if ( - order_.validInputs[takeOrder_.inputIOIndex].token != - takeOrders_.output - ) { - revert TokenMismatch( - order_.validInputs[takeOrder_.inputIOIndex].token, - takeOrders_.output - ); - } - if ( - order_.validOutputs[takeOrder_.outputIOIndex].token != - takeOrders_.input - ) { - revert TokenMismatch( - order_.validOutputs[takeOrder_.outputIOIndex].token, - takeOrders_.input - ); - } - - OrderIOCalculation - memory orderIOCalculation_ = _calculateOrderIO( - order_, - takeOrder_.inputIOIndex, - takeOrder_.outputIOIndex, - msg.sender, - takeOrder_.signedContext - ); - - // Skip orders that are too expensive rather than revert as we have - // no way of knowing if a specific order becomes too expensive - // between submitting to mempool and execution, but other orders may - // be valid so we want to take advantage of those if possible. - if (orderIOCalculation_.IORatio > takeOrders_.maximumIORatio) { - emit OrderExceedsMaxRatio( - msg.sender, - order_.owner, - orderHash_ - ); - } else if (orderIOCalculation_.outputMax == 0) { - emit OrderZeroAmount(msg.sender, order_.owner, orderHash_); - } else { - // Don't exceed the maximum total input. - uint256 input_ = remainingInput_.min( - orderIOCalculation_.outputMax - ); - // Always round IO calculations up. - uint256 output_ = input_.fixedPointMul( - orderIOCalculation_.IORatio, - Math.Rounding.Up - ); - - remainingInput_ -= input_; - totalOutput_ += output_; - - _recordVaultIO( - order_, - output_, - input_, - orderIOCalculation_ - ); - emit TakeOrder(msg.sender, takeOrder_, input_, output_); - } - } - - unchecked { - i_++; - } - } - totalInput_ = takeOrders_.maximumInput - remainingInput_; - - if (totalInput_ < takeOrders_.minimumInput) { - revert MinimumInput(takeOrders_.minimumInput, totalInput_); - } - - // We already updated vault balances before we took tokens from - // `msg.sender` which is usually NOT the correct order of operations for - // depositing to a vault. We rely on reentrancy guards to make this safe. - IERC20(takeOrders_.output).safeTransferFrom( - msg.sender, - address(this), - totalOutput_ - ); - // Prioritise paying down any active flash loans before sending any - // tokens to `msg.sender`. - _decreaseFlashDebtThenSendToken( - takeOrders_.input, - msg.sender, - totalInput_ - ); - } - - /// @inheritdoc IOrderBookV2 - function clear( - Order memory alice_, - Order memory bob_, - ClearConfig calldata clearConfig_, - SignedContextV1[] memory aliceSignedContext_, - SignedContextV1[] memory bobSignedContext_ - ) external nonReentrant { - { - if (alice_.owner == bob_.owner) { - revert SameOwner(alice_.owner); - } - if ( - alice_.validOutputs[clearConfig_.aliceOutputIOIndex].token != - bob_.validInputs[clearConfig_.bobInputIOIndex].token - ) { - revert TokenMismatch( - alice_.validOutputs[clearConfig_.aliceOutputIOIndex].token, - bob_.validInputs[clearConfig_.bobInputIOIndex].token - ); - } - - if ( - bob_.validOutputs[clearConfig_.bobOutputIOIndex].token != - alice_.validInputs[clearConfig_.aliceInputIOIndex].token - ) { - revert TokenMismatch( - alice_.validInputs[clearConfig_.aliceInputIOIndex].token, - bob_.validOutputs[clearConfig_.bobOutputIOIndex].token - ); - } - - // If either order is dead the clear is a no-op other than emitting - // `OrderNotFound`. Returning rather than erroring makes it easier to - // bulk clear using `Multicall`. - if (orders[alice_.hash()] == DEAD_ORDER) { - emit OrderNotFound(msg.sender, alice_.owner, alice_.hash()); - return; - } - if (orders[bob_.hash()] == DEAD_ORDER) { - emit OrderNotFound(msg.sender, bob_.owner, bob_.hash()); - return; - } - - // Emit the Clear event before `eval`. - emit Clear(msg.sender, alice_, bob_, clearConfig_); - } - OrderIOCalculation memory aliceOrderIOCalculation_ = _calculateOrderIO( - alice_, - clearConfig_.aliceInputIOIndex, - clearConfig_.aliceOutputIOIndex, - bob_.owner, - bobSignedContext_ - ); - OrderIOCalculation memory bobOrderIOCalculation_ = _calculateOrderIO( - bob_, - clearConfig_.bobInputIOIndex, - clearConfig_.bobOutputIOIndex, - alice_.owner, - aliceSignedContext_ - ); - ClearStateChange memory clearStateChange_ = LibOrderBook - ._clearStateChange( - aliceOrderIOCalculation_, - bobOrderIOCalculation_ - ); - - _recordVaultIO( - alice_, - clearStateChange_.aliceInput, - clearStateChange_.aliceOutput, - aliceOrderIOCalculation_ - ); - _recordVaultIO( - bob_, - clearStateChange_.bobInput, - clearStateChange_.bobOutput, - bobOrderIOCalculation_ - ); - - { - // At least one of these will overflow due to negative bounties if - // there is a spread between the orders. - uint256 aliceBounty_ = clearStateChange_.aliceOutput - - clearStateChange_.bobInput; - uint256 bobBounty_ = clearStateChange_.bobOutput - - clearStateChange_.aliceInput; - if (aliceBounty_ > 0) { - vaultBalance[msg.sender][ - alice_.validOutputs[clearConfig_.aliceOutputIOIndex].token - ][clearConfig_.aliceBountyVaultId] += aliceBounty_; - } - if (bobBounty_ > 0) { - vaultBalance[msg.sender][ - bob_.validOutputs[clearConfig_.bobOutputIOIndex].token - ][clearConfig_.bobBountyVaultId] += bobBounty_; - } - } - - emit AfterClear(msg.sender, clearStateChange_); - } - - /// Main entrypoint into an order calculates the amount and IO ratio. Both - /// are always treated as 18 decimal fixed point values and then rescaled - /// according to the order's definition of each token's actual fixed point - /// decimals. - /// @param order_ The order to evaluate. - /// @param inputIOIndex_ The index of the input token being calculated for. - /// @param outputIOIndex_ The index of the output token being calculated for. - /// @param counterparty_ The counterparty of the order as it is currently - /// being cleared against. - /// @param signedContext_ Any signed context provided by the clearer/taker - /// that the order may need for its calculations. - function _calculateOrderIO( - Order memory order_, - uint256 inputIOIndex_, - uint256 outputIOIndex_, - address counterparty_, - SignedContextV1[] memory signedContext_ - ) internal view virtual returns (OrderIOCalculation memory) { - unchecked { - uint256 orderHash_ = order_.hash(); - - uint256[][] memory context_; - { - uint256[][] memory callingContext_ = new uint256[][]( - CALLING_CONTEXT_COLUMNS - ); - callingContext_[ - CONTEXT_CALLING_CONTEXT_COLUMN - 1 - ] = LibUint256Array.arrayFrom( - orderHash_, - uint256(uint160(order_.owner)), - uint256(uint160(counterparty_)) - ); - - callingContext_[ - CONTEXT_VAULT_INPUTS_COLUMN - 1 - ] = LibUint256Array.arrayFrom( - uint256(uint160(order_.validInputs[inputIOIndex_].token)), - order_.validInputs[inputIOIndex_].decimals, - order_.validInputs[inputIOIndex_].vaultId, - vaultBalance[order_.owner][ - order_.validInputs[inputIOIndex_].token - ][order_.validInputs[inputIOIndex_].vaultId], - // Don't know the balance diff yet! - 0 - ); - - callingContext_[ - CONTEXT_VAULT_OUTPUTS_COLUMN - 1 - ] = LibUint256Array.arrayFrom( - uint256(uint160(order_.validOutputs[outputIOIndex_].token)), - order_.validOutputs[outputIOIndex_].decimals, - order_.validOutputs[outputIOIndex_].vaultId, - vaultBalance[order_.owner][ - order_.validOutputs[outputIOIndex_].token - ][order_.validOutputs[outputIOIndex_].vaultId], - // Don't know the balance diff yet! - 0 - ); - context_ = LibContext.build(callingContext_, signedContext_); - } - - // The state changes produced here are handled in _recordVaultIO so - // that local storage writes happen before writes on the interpreter. - StateNamespace namespace_ = StateNamespace.wrap( - uint256(uint160(order_.owner)) - ); - (uint256[] memory stack_, uint256[] memory kvs_) = order_ - .evaluable - .interpreter - .eval( - order_.evaluable.store, - namespace_, - _calculateOrderDispatch(order_.evaluable.expression), - context_ - ); - - uint256 orderOutputMax_ = stack_[stack_.length - 2]; - uint256 orderIORatio_ = stack_[stack_.length - 1]; - - // Rescale order output max from 18 FP to whatever decimals the - // output token is using. - // Always round order output down. - orderOutputMax_ = orderOutputMax_.scaleN( - order_.validOutputs[outputIOIndex_].decimals, - // Saturate the order max output because if we were willing to - // give more than this on a scale up, we should be comfortable - // giving less. - // Round DOWN to be conservative and give away less if there's - // any loss of precision during scale down. - FLAG_SATURATE - ); - // Rescale the ratio from 18 FP according to the difference in - // decimals between input and output. - // Always round IO ratio up. - orderIORatio_ = orderIORatio_.scaleRatio( - order_.validOutputs[outputIOIndex_].decimals, - order_.validInputs[inputIOIndex_].decimals, - // DO NOT saturate ratios because this would reduce the effective - // IO ratio, which would mean that saturating would make the deal - // worse for the order. Instead we overflow, and round up to get - // the best possible deal. - FLAG_ROUND_UP - ); - - // The order owner can't send more than the smaller of their vault - // balance or their per-order limit. - orderOutputMax_ = orderOutputMax_.min( - vaultBalance[order_.owner][ - order_.validOutputs[outputIOIndex_].token - ][order_.validOutputs[outputIOIndex_].vaultId] - ); - - // Populate the context with the output max rescaled and vault capped - // and the rescaled ratio. - context_[CONTEXT_CALCULATIONS_COLUMN] = LibUint256Array.arrayFrom( - orderOutputMax_, - orderIORatio_ - ); - - return - OrderIOCalculation( - orderOutputMax_, - orderIORatio_, - context_, - namespace_, - kvs_ - ); - } - } - - /// Given an order, final input and output amounts and the IO calculation - /// verbatim from `_calculateOrderIO`, dispatch the handle IO entrypoint if - /// it exists and update the order owner's vault balances. - /// @param order_ The order that is being cleared. - /// @param input_ The exact token input amount to move into the owner's - /// vault. - /// @param output_ The exact token output amount to move out of the owner's - /// vault. - /// @param orderIOCalculation_ The verbatim order IO calculation returned by - /// `_calculateOrderIO`. - function _recordVaultIO( - Order memory order_, - uint256 input_, - uint256 output_, - OrderIOCalculation memory orderIOCalculation_ - ) internal virtual { - orderIOCalculation_.context[CONTEXT_VAULT_INPUTS_COLUMN][ - CONTEXT_VAULT_IO_BALANCE_DIFF - ] = input_; - orderIOCalculation_.context[CONTEXT_VAULT_OUTPUTS_COLUMN][ - CONTEXT_VAULT_IO_BALANCE_DIFF - ] = output_; - - if (input_ > 0) { - // IMPORTANT! THIS MATH MUST BE CHECKED TO AVOID OVERFLOW. - vaultBalance[order_.owner][ - address( - uint160( - orderIOCalculation_.context[ - CONTEXT_VAULT_INPUTS_COLUMN - ][CONTEXT_VAULT_IO_TOKEN] - ) - ) - ][ - orderIOCalculation_.context[CONTEXT_VAULT_INPUTS_COLUMN][ - CONTEXT_VAULT_IO_VAULT_ID - ] - ] += input_; - } - if (output_ > 0) { - // IMPORTANT! THIS MATH MUST BE CHECKED TO AVOID UNDERFLOW. - vaultBalance[order_.owner][ - address( - uint160( - orderIOCalculation_.context[ - CONTEXT_VAULT_OUTPUTS_COLUMN - ][CONTEXT_VAULT_IO_TOKEN] - ) - ) - ][ - orderIOCalculation_.context[CONTEXT_VAULT_OUTPUTS_COLUMN][ - CONTEXT_VAULT_IO_VAULT_ID - ] - ] -= output_; - } - - // Emit the context only once in its fully populated form rather than two - // nearly identical emissions of a partial and full context. - emit Context(msg.sender, orderIOCalculation_.context); - - // Apply state changes to the interpreter store after the vault balances - // are updated, but before we call handle IO. We want handle IO to see - // a consistent view on sets from calculate IO. - if (orderIOCalculation_.kvs.length > 0) { - order_.evaluable.store.set( - orderIOCalculation_.namespace, - orderIOCalculation_.kvs - ); - } - - // Only dispatch handle IO entrypoint if it is defined, otherwise it is - // a waste of gas to hit the interpreter a second time. - if (order_.handleIO) { - // The handle IO eval is run under the same namespace as the - // calculate order entrypoint. - (, uint256[] memory handleIOKVs_) = order_ - .evaluable - .interpreter - .eval( - order_.evaluable.store, - orderIOCalculation_.namespace, - _handleIODispatch(order_.evaluable.expression), - orderIOCalculation_.context - ); - // Apply state changes to the interpreter store from the handle IO - // entrypoint. - if (handleIOKVs_.length > 0) { - order_.evaluable.store.set( - orderIOCalculation_.namespace, - handleIOKVs_ - ); - } - } - } -} diff --git a/contracts/orderbook/OrderBookFlashLender.sol b/contracts/orderbook/OrderBookFlashLender.sol deleted file mode 100644 index c2b704229..000000000 --- a/contracts/orderbook/OrderBookFlashLender.sol +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import {MathUpgradeable as Math} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; - -import "rain.interface.orderbook/ierc3156/IERC3156FlashBorrower.sol"; -import "rain.interface.orderbook/ierc3156/IERC3156FlashLender.sol"; - -/// Thrown when `flashLoan` token is zero address. -error ZeroToken(); - -/// Thrown when `flashLoadn` receiver is zero address. -error ZeroReceiver(); - -/// Thrown when the `onFlashLoan` callback returns anything other than -/// ON_FLASH_LOAN_CALLBACK_SUCCESS. -/// @param result The value that was returned by `onFlashLoan`. -error FlashLenderCallbackFailed(bytes32 result); - -/// Thrown when more than one debt is attempted simultaneously. -/// @param receiver The receiver of the active debt. -/// @param token The token of the active debt. -/// @param amount The amount of the active debt. -error ActiveDebt(address receiver, address token, uint256 amount); - -/// @dev Flash fee is always 0 for orderbook as there's no entity to take -/// revenue for `Orderbook` and its more important anyway that flashloans happen -/// to connect external liquidity to live orders via arbitrage. -uint256 constant FLASH_FEE = 0; - -/// @title OrderBookFlashLender -/// @notice Implements `IERC3156FlashLender` for `OrderBook`. Based on the -/// reference implementation by Alberto Cuesta Cañada found at -/// https://eips.ethereum.org/EIPS/eip-3156 -/// Several features found in the reference implementation are simplified or -/// hardcoded for `Orderbook`. -contract OrderBookFlashLender is IERC3156FlashLender { - using SafeERC20 for IERC20; - using Math for uint256; - - IERC3156FlashBorrower private _receiver = IERC3156FlashBorrower(address(0)); - address private _token = address(0); - uint256 private _amount = 0; - - function _isActiveDebt() internal view returns (bool) { - return (address(_receiver) != address(0) || - _token != address(0) || - _amount != 0); - } - - function _checkActiveDebt() internal view { - if (_isActiveDebt()) { - revert ActiveDebt(address(_receiver), _token, _amount); - } - } - - /// Whenever `Orderbook` sends tokens to any address it MUST first attempt - /// to decrease any outstanding flash loans for that address. Consider the - /// case that Alice deposits 100 TKN and she is the only depositor of TKN - /// then flash borrows 100 TKN. If she attempts to withdraw 100 TKN during - /// her `onFlashLoan` callback then `Orderbook`: - /// - /// - has 0 TKN balance to process the withdrawal - /// - MUST process the withdrawal as Alice has the right to withdraw her - /// balance at any time - /// - Has the 100 TKN debt active under Alice - /// - /// In this case `Orderbook` can simply forgive Alice's 100 TKN debt instead - /// of actually transferring any tokens. The withdrawal can decrease her - /// vault balance by 100 TKN decoupled from needing to know whether a - /// tranfer or forgiveness happened. - /// - /// The same logic applies to withdrawals as sending tokens during - /// `takeOrders` as the reason for sending tokens is irrelevant, all that - /// matters is that `Orderbook` prioritises debt repayments over external - /// transfers. - /// - /// If there is an active debt that only partially eclipses the withdrawal - /// then the debt will be fully repaid and the remainder transferred as a - /// real token transfer. - /// - /// Note that Alice can still contrive a situation that causes `Orderbook` - /// to attempt to send tokens that it does not have. If Alice can write a - /// smart contract to trigger withdrawals she can flash loan 100% of the - /// TKN supply in `Orderbook` and trigger her contract to attempt a - /// withdrawal. For any normal ERC20 token this will fail and revert as the - /// `Orderbook` cannot send tokens it does not have under any circumstances, - /// but the scenario is worth being aware of for more exotic token - /// behaviours that may not be supported. - /// - /// @param token_ The token being sent or for the debt being paid. - /// @param receiver_ The receiver of the token or holder of the debt. - /// @param sendAmount_ The amount to send or repay. - function _decreaseFlashDebtThenSendToken( - address token_, - address receiver_, - uint256 sendAmount_ - ) internal { - // If this token transfer matches the active debt then prioritise - // reducing debt over sending tokens. - if (token_ == _token && receiver_ == address(_receiver)) { - uint256 debtReduction_ = sendAmount_.min(_amount); - sendAmount_ -= debtReduction_; - - // Even if this completely zeros the amount the debt is considered - // active until the `flashLoan` also clears the token and recipient. - _amount -= debtReduction_; - } - - if (sendAmount_ > 0) { - IERC20(token_).safeTransfer(receiver_, sendAmount_); - } - } - - /// @inheritdoc IERC3156FlashLender - function flashLoan( - IERC3156FlashBorrower receiver_, - address token_, - uint256 amount_, - bytes calldata data_ - ) external override returns (bool) { - // This prevents reentrancy, loans can be taken sequentially within a - // transaction but not simultanously. - _checkActiveDebt(); - - // Set the active debt before transferring tokens to prevent reeentrancy. - // The active debt is set beyond the scope of `flashLoan` to facilitate - // early repayment via. `_decreaseFlashDebtThenSendToken`. - { - if (token_ == address(0)) { - revert ZeroToken(); - } - if (address(receiver_) == address(0)) { - revert ZeroReceiver(); - } - _token = token_; - _receiver = receiver_; - _amount = amount_; - if (amount_ > 0) { - IERC20(token_).safeTransfer(address(receiver_), amount_); - } - } - - bytes32 result_ = receiver_.onFlashLoan( - // initiator - msg.sender, - // token - token_, - // amount - amount_, - // fee - 0, - // data - data_ - ); - if (result_ != ON_FLASH_LOAN_CALLBACK_SUCCESS) { - revert FlashLenderCallbackFailed(result_); - } - - // Pull tokens before releasing the active debt to prevent a new loan - // from being taken reentrantly during the repayment of the current loan. - { - // Sync local `amount_` with global `_amount` in case an early - // repayment was made during the loan term via. - // `_decreaseFlashDebtThenSendToken`. - amount_ = _amount; - if (amount_ > 0) { - IERC20(_token).safeTransferFrom( - address(_receiver), - address(this), - amount_ - ); - _amount = 0; - } - - // Both of these are required to fully clear the active debt and - // allow new debts. - _receiver = IERC3156FlashBorrower(address(0)); - _token = address(0); - } - - // Guard against some bad code path that allowed an active debt to remain - // at this point. Should be impossible. - _checkActiveDebt(); - - return true; - } - - /// @inheritdoc IERC3156FlashLender - function flashFee( - address, - uint256 - ) external pure override returns (uint256) { - return FLASH_FEE; - } - - /// There's no limit to the size of a flash loan from `Orderbook` other than - /// the current tokens deposited in `Orderbook`. If there is an active debt - /// then loans are disabled so the max becomes `0` until after repayment. - /// @inheritdoc IERC3156FlashLender - function maxFlashLoan( - address token_ - ) external view override returns (uint256) { - return _isActiveDebt() ? 0 : IERC20(token_).balanceOf(address(this)); - } -} diff --git a/contracts/sale/Sale.sol b/contracts/sale/Sale.sol index 9ee1bbbb1..a131863a4 100644 --- a/contracts/sale/Sale.sol +++ b/contracts/sale/Sale.sol @@ -3,13 +3,12 @@ pragma solidity =0.8.19; import {Cooldown} from "rain.cooldown/Cooldown.sol"; -import "../math/LibFixedPointMath.sol"; +import "rain.math.fixedpoint/FixedPointDecimalArithmeticOpenZeppelin.sol"; import {AllStandardOps} from "../interpreter/ops/AllStandardOps.sol"; import {ERC20Config} from "../erc20/ERC20Config.sol"; import "rain.interface.sale/ISaleV2.sol"; import {RedeemableERC20, RedeemableERC20Config} from "../redeemableERC20/RedeemableERC20.sol"; import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {MathUpgradeable as Math} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import {ReentrancyGuardUpgradeable as ReentrancyGuard} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; @@ -18,7 +17,7 @@ import "rain.interface.interpreter/IInterpreterV1.sol"; import "rain.interface.interpreter/LibEncodedDispatch.sol"; import "rain.interface.interpreter/IInterpreterCallerV2.sol"; import "rain.interface.interpreter/LibContext.sol"; -import "../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; +import "rain.interface.interpreter/deployerDiscoverable/DeployerDiscoverableMetaV1.sol"; import "rain.interface.interpreter/LibEvaluable.sol"; import "rain.interface.factory/ICloneableV1.sol"; import "../factory/CloneFactory.sol"; @@ -176,7 +175,7 @@ contract Sale is DeployerDiscoverableMetaV1 { using Math for uint256; - using LibFixedPointMath for uint256; + using FixedPointDecimalArithmeticOpenZeppelin for uint256; using SafeERC20 for IERC20; using LibUint256Array for uint256; using LibUint256Array for uint256[]; diff --git a/contracts/stake/Stake.sol b/contracts/stake/Stake.sol index 4da260f23..2a3fb2a56 100644 --- a/contracts/stake/Stake.sol +++ b/contracts/stake/Stake.sol @@ -6,7 +6,7 @@ import "rain.interface.interpreter/LibEncodedDispatch.sol"; import "sol.lib.memory/LibStackPointer.sol"; import "rain.interface.interpreter/LibContext.sol"; import "rain.interface.interpreter/IInterpreterCallerV2.sol"; -import "../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; +import "rain.interface.interpreter/deployerDiscoverable/DeployerDiscoverableMetaV1.sol"; import "rain.interface.interpreter/LibEvaluable.sol"; import "sol.lib.memory/LibUint256Array.sol"; import "sol.lib.memory/LibUint256Matrix.sol"; diff --git a/contracts/test/erc3156/ERC3156FlashBorrowerBuyTest.sol b/contracts/test/erc3156/ERC3156FlashBorrowerBuyTest.sol deleted file mode 100644 index 39cce043b..000000000 --- a/contracts/test/erc3156/ERC3156FlashBorrowerBuyTest.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import "rain.interface.orderbook/ierc3156/IERC3156FlashBorrower.sol"; -import {ERC20Upgradeable as ERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import "../../math/LibFixedPointMath.sol"; -import {OrderBook, TakeOrderConfig, TakeOrdersConfig} from "../../orderbook/OrderBook.sol"; - -/// @title ERC3156FlashBorrowerBuyTest -contract ERC3156FlashBorrowerBuyTest is IERC3156FlashBorrower { - using LibFixedPointMath for uint256; - using SafeERC20 for IERC20; - - function onFlashLoan( - address, - address tokenA_, - uint256 amount_, - uint256, - bytes calldata data_ - ) external returns (bytes32) { - TakeOrdersConfig memory takeOrdersConfig = abi.decode( - data_, - (TakeOrdersConfig) - ); - - // simulate 'buy' from external market using loaned tokenA_ - - // 'gives' tokenA_ to market - IERC20(tokenA_).safeTransfer(address(1), amount_); - - // 'receives' tokenB from market. - // make sure this contract has at least a balance of `receiveAmountB` - // before triggering `onFlashLoan` callback. - uint256 receiveAmountB = amount_.fixedPointMul( - 1020000000000000000, - Math.Rounding.Down - ); - require( - IERC20(takeOrdersConfig.output).balanceOf(address(this)) == - receiveAmountB, - "PRE_BUY" - ); - - // approve orderbook transfer - IERC20(takeOrdersConfig.output).approve(msg.sender, receiveAmountB); - - // take orderbook order - OrderBook(msg.sender).takeOrders(takeOrdersConfig); - - return keccak256("ERC3156FlashBorrower.onFlashLoan"); - } -} diff --git a/contracts/test/erc3156/ERC3156FlashBorrowerDepositTest.sol b/contracts/test/erc3156/ERC3156FlashBorrowerDepositTest.sol deleted file mode 100644 index 67c276c23..000000000 --- a/contracts/test/erc3156/ERC3156FlashBorrowerDepositTest.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import "rain.interface.orderbook/ierc3156/IERC3156FlashBorrower.sol"; -import {ERC20Upgradeable as ERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import "../../math/LibFixedPointMath.sol"; -import {OrderBook, DepositConfig} from "../../orderbook/OrderBook.sol"; - -/// @title ERC3156FlashBorrowerDepositTest -contract ERC3156FlashBorrowerDepositTest is IERC3156FlashBorrower { - using LibFixedPointMath for uint256; - using SafeERC20 for IERC20; - - function onFlashLoan( - address, - address token_, - uint256 amount_, - uint256, - bytes calldata data_ - ) external returns (bytes32) { - DepositConfig memory depositConfig = abi.decode(data_, (DepositConfig)); - - // approve orderbook transfer - IERC20(token_).approve(msg.sender, amount_); - - // deposit the flash loan - OrderBook(msg.sender).deposit(depositConfig); - - // 'Approve' debt finalization. - IERC20(token_).approve(msg.sender, amount_); - - return keccak256("ERC3156FlashBorrower.onFlashLoan"); - } -} diff --git a/contracts/test/erc3156/ERC3156FlashBorrowerMaxLoan.sol b/contracts/test/erc3156/ERC3156FlashBorrowerMaxLoan.sol deleted file mode 100644 index 6fa7a738a..000000000 --- a/contracts/test/erc3156/ERC3156FlashBorrowerMaxLoan.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import "rain.interface.orderbook/ierc3156/IERC3156FlashBorrower.sol"; -import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; - -import {OrderBookFlashLender} from "../../orderbook/OrderBookFlashLender.sol"; -import "rain.interface.orderbook/ierc3156/IERC3156FlashLender.sol"; - -/// @title ERC3156FlashBorrowerTest -contract ERC3156FlashBorrowerMaxLoan is IERC3156FlashBorrower { - function onFlashLoan( - address, - address token, - uint256 amount, - uint256 fee, - bytes calldata - ) external returns (bytes32) { - /// If there is an active debt - /// then loans are disabled so the max becomes `0` - require( - IERC3156FlashLender(msg.sender).maxFlashLoan(token) == 0, - "Non-Zero Max Flash Loan" - ); - IERC20(token).approve(msg.sender, amount + fee); - return keccak256("ERC3156FlashBorrower.onFlashLoan"); - } -} diff --git a/contracts/test/erc3156/ERC3156FlashBorrowerTest.sol b/contracts/test/erc3156/ERC3156FlashBorrowerTest.sol deleted file mode 100644 index 392cb9c60..000000000 --- a/contracts/test/erc3156/ERC3156FlashBorrowerTest.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import "rain.interface.orderbook/ierc3156/IERC3156FlashBorrower.sol"; -import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; - -/// @title ERC3156FlashBorrowerTest -contract ERC3156FlashBorrowerTest is IERC3156FlashBorrower { - function onFlashLoan( - address, - address token, - uint256 amount, - uint256 fee, - bytes calldata - ) external returns (bytes32) { - // Approve debt finalization. - IERC20(token).approve(msg.sender, amount + fee); - return keccak256("ERC3156FlashBorrower.onFlashLoan"); - } -} diff --git a/contracts/test/erc3156/ERC3156FlashBorrowerTestReentrancy.sol b/contracts/test/erc3156/ERC3156FlashBorrowerTestReentrancy.sol deleted file mode 100644 index ab86ee04b..000000000 --- a/contracts/test/erc3156/ERC3156FlashBorrowerTestReentrancy.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import "rain.interface.orderbook/ierc3156/IERC3156FlashBorrower.sol"; -import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import "hardhat/console.sol"; - -import {OrderBookFlashLender} from "../../orderbook/OrderBookFlashLender.sol"; -import "rain.interface.orderbook/ierc3156/IERC3156FlashLender.sol"; - -/// @title ERC3156FlashBorrowerTest -contract ERC3156FlashBorrowerTestReentrancy is IERC3156FlashBorrower { - function onFlashLoan( - address, - address token, - uint256 amount, - uint256 fee, - bytes calldata - ) external returns (bytes32) { - if (IERC20(token).balanceOf(address(msg.sender)) > 0) { - // siphon all the token from lender by making reentrant calls. - IERC3156FlashLender(msg.sender).flashLoan( - IERC3156FlashBorrower(address(this)), - token, - amount, - "" - ); - return bytes32(0); - } else { - // Approve debt finalization for only `amount` tokens. - // Insufficent allowance given for `amount` which is less than `debt` - IERC20(token).approve(msg.sender, amount + fee); - return keccak256("ERC3156FlashBorrower.onFlashLoan"); - } - } -} diff --git a/contracts/test/erc3156/ERC3156FlashBorrowerWithdrawTest.sol b/contracts/test/erc3156/ERC3156FlashBorrowerWithdrawTest.sol deleted file mode 100644 index c79dbb934..000000000 --- a/contracts/test/erc3156/ERC3156FlashBorrowerWithdrawTest.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity ^0.8.18; - -import "rain.interface.orderbook/ierc3156/IERC3156FlashBorrower.sol"; -import {ERC20Upgradeable as ERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -import {IERC20Upgradeable as IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import "../../math/LibFixedPointMath.sol"; -import {OrderBook, WithdrawConfig, DepositConfig} from "../../orderbook/OrderBook.sol"; - -/// @title ERC3156FlashBorrowerWithdrawTest -contract ERC3156FlashBorrowerWithdrawTest is IERC3156FlashBorrower { - using LibFixedPointMath for uint256; - using SafeERC20 for IERC20; - - function orderBookDeposit( - address orderbook_, - DepositConfig calldata config_ - ) external { - IERC20(config_.token).approve(orderbook_, config_.amount); - OrderBook(orderbook_).deposit(config_); - } - - function onFlashLoan( - address, - address token_, - uint256 amount_, - uint256, - bytes calldata data_ - ) external returns (bytes32) { - WithdrawConfig memory withdrawConfig = abi.decode( - data_, - (WithdrawConfig) - ); - - OrderBook(msg.sender).withdraw(withdrawConfig); - - // approve orderbook transfer - IERC20(token_).approve(msg.sender, amount_); - - return keccak256("ERC3156FlashBorrower.onFlashLoan"); - } -} diff --git a/contracts/test/interpreter/runtime/IInterpreterCallerConsumer.sol b/contracts/test/interpreter/runtime/IInterpreterCallerConsumer.sol index cbd127a40..15a3ab50b 100644 --- a/contracts/test/interpreter/runtime/IInterpreterCallerConsumer.sol +++ b/contracts/test/interpreter/runtime/IInterpreterCallerConsumer.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "../../../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; -import "../../../interpreter/deploy/LibDeployerDiscoverable.sol"; +import "rain.interface.interpreter/deployerDiscoverable/DeployerDiscoverableMetaV1.sol"; +import "rain.interface.interpreter/deployerDiscoverable/LibDeployerDiscoverable.sol"; contract IInterpreterCallerConsumer is DeployerDiscoverableMetaV1 { constructor( diff --git a/contracts/test/math/LibFixedPointMath/LibFixedPointMathTest.sol b/contracts/test/math/LibFixedPointMath/LibFixedPointMathTest.sol deleted file mode 100644 index 9970b1818..000000000 --- a/contracts/test/math/LibFixedPointMath/LibFixedPointMathTest.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity =0.8.19; - -import {LibFixedPointMath} from "../../../math/LibFixedPointMath.sol"; - -import {MathUpgradeable as Math} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; - -/// @title LibFixedPointMathTest -/// Thin wrapper around the `LibFixedPointMath` library for hardhat unit testing. -contract LibFixedPointMathTest { - using LibFixedPointMath for uint256; - - /// Wraps `FixedPointMath.fixedPointMul`. - /// Fixed point multiplication in native scale decimals. - /// @param a_ First term. - /// @param b_ Second term. - /// @param rounding_ Rounding direction as per Open Zeppelin Math. - /// @return `a_` multiplied by `b_` to `DECIMALS` fixed point decimals. - function fixedPointMul( - uint256 a_, - uint256 b_, - Math.Rounding rounding_ - ) external pure returns (uint256) { - return a_.fixedPointMul(b_, rounding_); - } - - /// Wraps `FixedPointMath.fixedPointDiv`. - /// Fixed point division in native scale decimals. - /// Both `a_` and `b_` MUST be `DECIMALS` fixed point decimals. - /// @param a_ First term. - /// @param b_ Second term. - /// @param rounding_ Rounding direction as per Open Zeppelin Math. - /// @return `a_` divided by `b_` to `DECIMALS` fixed point decimals. - function fixedPointDiv( - uint256 a_, - uint256 b_, - Math.Rounding rounding_ - ) external pure returns (uint256) { - return a_.fixedPointDiv(b_, rounding_); - } -} diff --git a/contracts/tier/CombineTier.sol b/contracts/tier/CombineTier.sol index 63a06cfea..d8851d05d 100644 --- a/contracts/tier/CombineTier.sol +++ b/contracts/tier/CombineTier.sol @@ -10,7 +10,7 @@ import "sol.lib.memory/LibStackPointer.sol"; import "rain.lib.interpreter/LibInterpreterState.sol"; import "rain.interface.interpreter/LibContext.sol"; import "sol.lib.memory/LibUint256Matrix.sol"; -import "../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; +import "rain.interface.interpreter/deployerDiscoverable/DeployerDiscoverableMetaV1.sol"; import "rain.interface.interpreter/LibEvaluable.sol"; import "rain.interface.factory/ICloneableV1.sol"; diff --git a/contracts/verify/auto/AutoApprove.sol b/contracts/verify/auto/AutoApprove.sol index e52ed5f8c..2b315f0f3 100644 --- a/contracts/verify/auto/AutoApprove.sol +++ b/contracts/verify/auto/AutoApprove.sol @@ -11,7 +11,7 @@ import "rain.interface.interpreter/IInterpreterCallerV2.sol"; import "sol.lib.memory/LibStackPointer.sol"; import "rain.interface.interpreter/LibEncodedDispatch.sol"; import "rain.interface.interpreter/LibContext.sol"; -import "../../interpreter/deploy/DeployerDiscoverableMetaV1.sol"; +import "rain.interface.interpreter/deployerDiscoverable/DeployerDiscoverableMetaV1.sol"; import "rain.interface.interpreter/LibEvaluable.sol"; import "rain.interface.factory/ICloneableV1.sol"; diff --git a/deployment/deploy/orderbook.ts b/deployment/deploy/orderbook.ts deleted file mode 100644 index 203116818..000000000 --- a/deployment/deploy/orderbook.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ethers } from "hardhat"; -import { - OrderBook as OrderBookType, - RainterpreterExpressionDeployer, -} from "../../typechain"; -import { DeployerDiscoverableMetaV1ConstructionConfigStruct } from "../../typechain/contracts/factory/CloneFactory"; -import { getRainMetaDocumentFromContract } from "../../utils"; -import { registerContract } from "../utils"; - -export const deployOrderbook = async ( - deployer_: RainterpreterExpressionDeployer -) => { - const orderBookFactory = await ethers.getContractFactory("OrderBook"); - - const config: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), - deployer: deployer_.address, - }; - - const OrderBook = (await orderBookFactory.deploy(config)) as OrderBookType; - - registerContract("OrderBook", OrderBook.address, config); - - return OrderBook; -}; diff --git a/lib/rain.interface.interpreter b/lib/rain.interface.interpreter index 76860103c..6cfc2399e 160000 --- a/lib/rain.interface.interpreter +++ b/lib/rain.interface.interpreter @@ -1 +1 @@ -Subproject commit 76860103c8bca9535e22685881886efc0597b233 +Subproject commit 6cfc2399ed2316957888b1bd7bfcfe0dbe35c942 diff --git a/lib/rain.math.fixedpoint b/lib/rain.math.fixedpoint index 5fa24aca8..84cb2ae98 160000 --- a/lib/rain.math.fixedpoint +++ b/lib/rain.math.fixedpoint @@ -1 +1 @@ -Subproject commit 5fa24aca82a82e45da74d255fc83349e076c2777 +Subproject commit 84cb2ae98305719574aca4cf754aed8b681eb5d9 diff --git a/scripts/getRainContractMeta.ts b/scripts/getRainContractMeta.ts index 001dfc38f..02f07bf67 100755 --- a/scripts/getRainContractMeta.ts +++ b/scripts/getRainContractMeta.ts @@ -89,8 +89,6 @@ const main = async () => { if (item.length !== 1) throw new Error("expected Rain contract name"); if (item[0].toLocaleLowerCase() === "sale") contractMeta = Sale; else if (item[0].toLocaleLowerCase() === "stake") contractMeta = Stake; - else if (item[0].toLocaleLowerCase() === "orderbook") - contractMeta = Orderbook; else if (item[0].toLocaleLowerCase() === "flow20") contractMeta = FlowERC20; else if (item[0].toLocaleLowerCase() === "flow721") diff --git a/test/Flow/Flow/construction.ts b/test/Flow/Flow/construction.ts index 3afb724ba..ac4ccf33e 100644 --- a/test/Flow/Flow/construction.ts +++ b/test/Flow/Flow/construction.ts @@ -134,7 +134,7 @@ describe("Flow construction tests", async function () { const deployerDiscoverableMetaConfig1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), + meta: getRainMetaDocumentFromContract("sale"), deployer: touchDeployer.address, }; diff --git a/test/Flow/FlowERC1155/construction.ts b/test/Flow/FlowERC1155/construction.ts index 472c9a596..95bf9396f 100644 --- a/test/Flow/FlowERC1155/construction.ts +++ b/test/Flow/FlowERC1155/construction.ts @@ -154,7 +154,7 @@ describe("FlowERC1155 construction tests", async function () { const deployerDiscoverableMetaConfig1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), + meta: getRainMetaDocumentFromContract("sale"), deployer: touchDeployer.address, }; diff --git a/test/Flow/FlowERC20/construction.ts b/test/Flow/FlowERC20/construction.ts index ec2251701..2c1ba9e43 100644 --- a/test/Flow/FlowERC20/construction.ts +++ b/test/Flow/FlowERC20/construction.ts @@ -157,7 +157,7 @@ describe("FlowERC20 construction tests", async function () { const deployerDiscoverableMetaConfig1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), + meta: getRainMetaDocumentFromContract("sale"), deployer: touchDeployer.address, }; diff --git a/test/Flow/FlowERC721/construction.ts b/test/Flow/FlowERC721/construction.ts index 32f28a772..df99b06e3 100644 --- a/test/Flow/FlowERC721/construction.ts +++ b/test/Flow/FlowERC721/construction.ts @@ -162,7 +162,7 @@ describe("FlowERC721 construction tests", async function () { const deployerDiscoverableMetaConfig1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), + meta: getRainMetaDocumentFromContract("sale"), deployer: touchDeployer.address, }; diff --git a/test/Interpreter/Caller/caller.ts b/test/Interpreter/Caller/caller.ts index f7756543f..c601c6a45 100644 --- a/test/Interpreter/Caller/caller.ts +++ b/test/Interpreter/Caller/caller.ts @@ -35,7 +35,7 @@ describe("Caller Test", async function () { {} ); - const lobbyContractMeta = getRainMetaDocumentFromContract("orderbook"); + const lobbyContractMeta = getRainMetaDocumentFromContract("sale"); const deployerDiscoverableMetaConfig: DeployerDiscoverableMetaV1ConstructionConfigStruct = { @@ -174,7 +174,7 @@ describe("Caller Test", async function () { await assertError( async () => await caller.checkMeta( - getRainMetaDocumentFromContract("orderbook"), + getRainMetaDocumentFromContract("sale"), getRainMetaDocumentFromContract("lobby") ), "UnexpectedMetaHash", diff --git a/test/Lobby/intialize.ts b/test/Lobby/intialize.ts index 265672e6f..dbd25e034 100644 --- a/test/Lobby/intialize.ts +++ b/test/Lobby/intialize.ts @@ -129,7 +129,7 @@ describe("Lobby Tests Intialize", async function () { const interpreterCallerConfig0: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), // Bad callerMeta passed. + meta: getRainMetaDocumentFromContract("sale"), // Bad callerMeta passed. deployer: touchDeployer.address, }; diff --git a/test/Math/FixedPointMath/scaleArithmetic.ts b/test/Math/FixedPointMath/scaleArithmetic.ts deleted file mode 100644 index 131f4abfb..000000000 --- a/test/Math/FixedPointMath/scaleArithmetic.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { strict as assert } from "assert"; -import { ethers } from "hardhat"; -import { LibFixedPointMathTest } from "../../../typechain/contracts/test/math/LibFixedPointMath/LibFixedPointMathTest"; - -import { eighteenZeros, ONE } from "../../../utils"; -import { fixedPointMathDeploy } from "../../../utils/deploy/math/fixedPointMath/deploy"; - -describe("LibFixedPointMathTest scaling during arithmetic op", async function () { - let fixedPointMathTest: LibFixedPointMathTest; - - const ROUND_DOWN = 0; - const ROUND_UP = 1; - const ROUND_TO_ZERO = 2; - - before(async () => { - fixedPointMathTest = await fixedPointMathDeploy(); - }); - - it("should scale a number by 18 order of magnitude while multiplying", async () => { - const a_ = 5; - const b_ = ethers.BigNumber.from("1000000000000000000").mul(2); - const result = await fixedPointMathTest[ - "fixedPointMul(uint256,uint256,uint8)" - ](a_, b_, ROUND_UP); - - const expectedResult = ethers.BigNumber.from(a_).mul(b_).div(ONE); - assert(result.eq(expectedResult)); - }); - - it("should scale a number by 18 order of magnitude while multiplying and round it to zero", async () => { - const a_ = 5; - const b_ = ethers.BigNumber.from("1382900000000000000").mul(2); - - const result = await fixedPointMathTest[ - "fixedPointMul(uint256,uint256,uint8)" - ](a_, b_, ROUND_TO_ZERO); - const expectedResult = ethers.BigNumber.from(a_).mul(b_).div(ONE); - - assert(result.eq(expectedResult)); - }); - - it("should scale a number by 18 order of magnitude while multiplying and round it down", async () => { - const a_ = 3; - const b_ = ethers.BigNumber.from("1382900000000000000").mul(2); - const result = await fixedPointMathTest[ - "fixedPointMul(uint256,uint256,uint8)" - ](a_, b_, ROUND_DOWN); - const expectedResult = ethers.BigNumber.from(a_).mul(b_).div(ONE); - - assert(result.eq(expectedResult)); - }); - - it("should scale a number by 18 order of magnitude while multiplying and round it up (for mulmod(a_,b_,ONE) > 0 ) ", async () => { - const a_ = 3; - const b_ = ethers.BigNumber.from("1382900000000000000").mul(2); - - const result = await fixedPointMathTest[ - "fixedPointMul(uint256,uint256,uint8)" - ](a_, b_, ROUND_UP); - console.log("result : ", result); - const expectedResult = ethers.BigNumber.from(a_) - .mul(b_) - .div(ONE) - .add(ethers.BigNumber.from(1)); - - assert(result.eq(expectedResult)); - }); - - it("should scale a number by 18 order of magnitude while multiplying and round it up (for mulmod(a_,b_,ONE) == 0 ) ", async () => { - const a_ = 2; - const b_ = ethers.BigNumber.from("500000000000000000"); - - const result = await fixedPointMathTest[ - "fixedPointMul(uint256,uint256,uint8)" - ](a_, b_, ROUND_UP); - - const expectedResult0 = ethers.BigNumber.from(a_) - .mul(b_) - .div(ONE) - .add(ethers.BigNumber.from(1)); - const expectedResult1 = ethers.BigNumber.from(a_).mul(b_).div(ONE); - assert(!result.eq(expectedResult0)); - assert(result.eq(expectedResult1)); - }); - - it("should scale a number by 18 order of magnitude while dividing", async () => { - const a_ = 60; - const b_ = ethers.BigNumber.from("2" + eighteenZeros); - - const result = await fixedPointMathTest[ - "fixedPointDiv(uint256,uint256,uint8)" - ](a_, b_, ROUND_UP); - const expectedResult = ethers.BigNumber.from(a_).mul(ONE).div(b_); - - assert(result.eq(expectedResult)); - }); - - it("should scale a number by 18 order of magnitude while dividing and round it to zero ", async () => { - const a_ = 65; - const b_ = ethers.BigNumber.from("2" + eighteenZeros); - - const result = await fixedPointMathTest[ - "fixedPointDiv(uint256,uint256,uint8)" - ](a_, b_, ROUND_TO_ZERO); - const expectedResult = ethers.BigNumber.from(a_).mul(ONE).div(b_); - - assert(result.eq(expectedResult)); - }); - - it("should scale a number by 18 order of magnitude while dividing and round it down ", async () => { - const a_ = 65; - const b_ = ethers.BigNumber.from("2" + eighteenZeros); - - const result = await fixedPointMathTest[ - "fixedPointDiv(uint256,uint256,uint8)" - ](a_, b_, ROUND_DOWN); - const expectedResult = ethers.BigNumber.from(a_).mul(ONE).div(b_); - - assert(result.eq(expectedResult)); - }); - - it("should scale a number by 18 order of magnitude while dividing and round it up (for mulmod(a_,b_,ONE) > 0 ) ", async () => { - const a_ = 60; - const b_ = ethers.BigNumber.from("2" + eighteenZeros); - - const result = await fixedPointMathTest[ - "fixedPointDiv(uint256,uint256,uint8)" - ](a_, b_, ROUND_UP); - const expectedResult = ethers.BigNumber.from(a_).mul(ONE).div(b_); - - assert(result.eq(expectedResult)); - }); - - it("should scale a number by 18 order of magnitude while dividing and round it up (for mulmod(a_,b_,ONE) > 0 ) ", async () => { - const a_ = 65; - const b_ = ethers.BigNumber.from("2" + eighteenZeros); - - const result = await fixedPointMathTest[ - "fixedPointDiv(uint256,uint256,uint8)" - ](a_, b_, ROUND_UP); - const expectedResult0 = ethers.BigNumber.from(a_).mul(ONE).div(b_); - const expectedResult1 = expectedResult0.add(ethers.BigNumber.from(1)); - - assert(!result.eq(expectedResult0)); - assert(result.eq(expectedResult1)); - }); -}); diff --git a/test/MetaDocument/cborEncodeTypes.ts b/test/MetaDocument/cborEncodeTypes.ts index dac0f3f8d..fce3e6447 100644 --- a/test/MetaDocument/cborEncodeTypes.ts +++ b/test/MetaDocument/cborEncodeTypes.ts @@ -10,7 +10,7 @@ import type { ContractMeta } from "../../utils/types/contractMeta"; describe("CBOR JS Check types", function () { it("should have the correct CBOR type on the payload using ArrayBuffer", async function () { // A given contract - const contractName: ContractMeta = "orderbook"; + const contractName: ContractMeta = "sale"; // Get the ArrayBufferLike from the Uint8Array expression const dataU8A = arrayify(getAbi(contractName)); // Uint8Array @@ -41,7 +41,7 @@ describe("CBOR JS Check types", function () { it("should have the correct CBOR type on the payload using Uint8Array", async function () { // A given contract - const contractName: ContractMeta = "orderbook"; + const contractName: ContractMeta = "sale"; // Get the ArrayBufferLike from the Uint8Array expression const dataU8A = arrayify(getAbi(contractName)); // Uint8Array diff --git a/test/MetaDocument/metaDocument.ts b/test/MetaDocument/metaDocument.ts index d98cf0873..206a86234 100644 --- a/test/MetaDocument/metaDocument.ts +++ b/test/MetaDocument/metaDocument.ts @@ -29,7 +29,7 @@ import type { ContractMeta } from "../../utils/types/contractMeta"; describe("Contract Rain Meta Document", function () { it("should construct a meta document from a contract with the start magic number", async function () { // The contract name to generate the Rain Meta Document - const contractName: ContractMeta = "orderbook"; + const contractName: ContractMeta = "sale"; // Getting the meta document from an arbitrary contract const metaDocument = getRainMetaDocumentFromContract(contractName); diff --git a/test/OrderBook/OpIOrderBookV1/VaultBalance/vaultBalance.ts b/test/OrderBook/OpIOrderBookV1/VaultBalance/vaultBalance.ts index 381296a9b..57827f26c 100644 --- a/test/OrderBook/OpIOrderBookV1/VaultBalance/vaultBalance.ts +++ b/test/OrderBook/OpIOrderBookV1/VaultBalance/vaultBalance.ts @@ -30,7 +30,7 @@ describe("IOrderBookV2 vault balance tests", async function () { const signers = await ethers.getSigners(); await deploy1820(signers[0]); - fakeOrderBook = await smock.fake("OrderBook"); + fakeOrderBook = await smock.fake("IOrderBookV2"); }); before(async () => { diff --git a/test/OrderBook/OrderBookFlashLender/depositFlashLoan.ts b/test/OrderBook/OrderBookFlashLender/depositFlashLoan.ts deleted file mode 100644 index 9da4987ca..000000000 --- a/test/OrderBook/OrderBookFlashLender/depositFlashLoan.ts +++ /dev/null @@ -1,81 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck - -import { ethers } from "hardhat"; -import type { - ERC3156FlashBorrowerDepositTest, - ReserveToken18, -} from "../../../typechain"; -import { DepositConfigStruct } from "../../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../../utils/bytes"; -import { eighteenZeros } from "../../../utils/constants/bigNumber"; -import { basicDeploy } from "../../../utils/deploy/basicDeploy"; -import { assertError } from "../../../utils/test/assertError"; -import { deployOrderBook } from "../../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../../utils/deploy/registry1820/deploy"; - -describe("OrderBook flash loan deposit tests", async function () { - let USDT: ReserveToken18; - let DAI: ReserveToken18; - let erc3156Bot: ERC3156FlashBorrowerDepositTest; - - beforeEach(async () => { - USDT = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - DAI = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await USDT.initialize(); - await DAI.initialize(); - - erc3156Bot = (await basicDeploy( - "ERC3156FlashBorrowerDepositTest", - {} - )) as ERC3156FlashBorrowerDepositTest; - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should not allow depositing flash loan without paying it back", async function () { - const orderBook = await deployOrderBook(); - - const vaultBot = ethers.BigNumber.from(randomUint256()); - - const amountCanLoan = ethers.BigNumber.from("20" + eighteenZeros); - - await DAI.transfer(orderBook.address, amountCanLoan); - - // bot deposits flash loan - const depositConfigStruct: DepositConfigStruct = { - token: DAI.address, - vaultId: vaultBot, - amount: amountCanLoan, - }; - const txFlashLoan = orderBook.flashLoan( - erc3156Bot.address, - DAI.address, - amountCanLoan, - ethers.utils.defaultAbiCoder.encode( - [ - { - type: "tuple", - name: "depositConfig", - components: [ - { name: "token", type: "address" }, - { name: "vaultId", type: "uint256" }, - { name: "amount", type: "uint256" }, - ], - }, - ], - [depositConfigStruct] - ) - ); - - await assertError( - async () => txFlashLoan, - "ERC20: transfer amount exceeds balance", - "avoided paying off flash loan debt" - ); - }); -}); diff --git a/test/OrderBook/OrderBookFlashLender/flashLoan.ts b/test/OrderBook/OrderBookFlashLender/flashLoan.ts deleted file mode 100644 index 0db0e547c..000000000 --- a/test/OrderBook/OrderBookFlashLender/flashLoan.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { ContractFactory } from "ethers"; -import { ethers } from "hardhat"; -import { OrderBookFlashLender } from "../../../typechain/contracts/orderbook/OrderBookFlashLender"; -import { ReserveToken18 } from "../../../typechain/contracts/test/testToken/ReserveToken18"; -import { - ERC3156FlashBorrowerTest, - ERC3156FlashBorrowerTestReentrancy, - IERC3156FlashBorrower, - ERC3156FlashBorrowerMaxLoan, -} from "../../../typechain"; -import { basicDeploy } from "../../../utils/deploy/basicDeploy"; -import { eighteenZeros } from "../../../utils/constants/bigNumber"; -import { strict as assert } from "assert"; -import { FakeContract, smock } from "@defi-wonderland/smock"; -import { keccak256 } from "ethers/lib/utils"; -import { assertError } from "../../../utils/test/assertError"; -import { randomAddress, randomUint256 } from "../../../utils/bytes"; - -const CALLBACK_SUCCESS = keccak256([ - ...Buffer.from("ERC3156FlashBorrower.onFlashLoan"), -]); -const CALLBACK_FOO = keccak256([...Buffer.from("foo")]); - -describe("OrderBookFlashLender flashLoan test", async function () { - let orderBookFlashLenderFactory: ContractFactory; - let erc3156FlashBorrower: ERC3156FlashBorrowerTest; - let erc3156FlashBorrowerReentrancy: ERC3156FlashBorrowerTestReentrancy; - let erc3156FlashBorrowerMaxLoan: ERC3156FlashBorrowerMaxLoan; - let fakeIERC3156FlashBorrower: FakeContract; - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - let lender: OrderBookFlashLender; - - beforeEach(async () => { - erc3156FlashBorrower = (await basicDeploy( - "ERC3156FlashBorrowerTest", - {} - )) as ERC3156FlashBorrowerTest; - - erc3156FlashBorrowerReentrancy = (await basicDeploy( - "ERC3156FlashBorrowerTestReentrancy", - {} - )) as ERC3156FlashBorrowerTestReentrancy; - - erc3156FlashBorrowerMaxLoan = (await basicDeploy( - "ERC3156FlashBorrowerMaxLoan", - {} - )) as ERC3156FlashBorrowerMaxLoan; - - fakeIERC3156FlashBorrower = await smock.fake("IERC3156FlashBorrower"); - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - lender = - (await orderBookFlashLenderFactory.deploy()) as OrderBookFlashLender; - }); - - before(async () => { - orderBookFlashLenderFactory = await ethers.getContractFactory( - "OrderBookFlashLender", - {} - ); - }); - - it("should return maxFlashLoan equal to this contract's current token balance", async function () { - // deposit amount for lending - const amount = ethers.BigNumber.from(123 + eighteenZeros); - await tokenA.transfer(lender.address, amount); - assert((await tokenA.balanceOf(lender.address)).eq(amount)); - - const maxFlashLoanA0_ = await lender.maxFlashLoan(tokenA.address); - assert(maxFlashLoanA0_.eq(amount)); - - // also deposit some tokenB to demonstrate it does not affect maxFlashLoan for tokenA - await tokenB.transfer(lender.address, amount.mul(3)); - - const maxFlashLoanA1_ = await lender.maxFlashLoan(tokenA.address); - assert(maxFlashLoanA1_.eq(amount)); - - const maxFlashLoanB_ = await lender.maxFlashLoan(tokenB.address); - assert(maxFlashLoanB_.eq(amount.mul(3))); - }); - - it("should return flashFee of 0 always", async function () { - const fee0 = await lender.flashFee(randomAddress(), 0); - assert(fee0.isZero()); - const fee1 = await lender.flashFee(randomAddress(), randomUint256()); - assert(fee1.isZero()); - }); - - it("should perform a flash loan with zero fee on the good path", async function () { - // deposit amount for lending - const amount = ethers.BigNumber.from(123 + eighteenZeros); - await tokenA.transfer(lender.address, amount); - assert((await tokenA.balanceOf(lender.address)).eq(amount)); - - await lender.flashLoan( - erc3156FlashBorrower.address, - tokenA.address, - amount, - [] - ); - }); - - it("should roll back flash loan transaction if callback does not return a hash", async function () { - // deposit amount for lending - const amount = ethers.BigNumber.from(123 + eighteenZeros); - await tokenA.transfer(lender.address, amount); - assert((await tokenA.balanceOf(lender.address)).eq(amount)); - - await assertError( - async () => - await lender.flashLoan( - fakeIERC3156FlashBorrower.address, - tokenA.address, - amount, - [] - ), - `FlashLenderCallbackFailed`, - "did not roll back transaction" - ); - }); - - it("should roll back flash loan transaction if callback does not return correct hash", async function () { - fakeIERC3156FlashBorrower.onFlashLoan.returns(CALLBACK_FOO); - - // deposit amount for lending - const amount = ethers.BigNumber.from(123 + eighteenZeros); - await tokenA.transfer(lender.address, amount); - assert((await tokenA.balanceOf(lender.address)).eq(amount)); - - await assertError( - async () => - await lender.flashLoan( - fakeIERC3156FlashBorrower.address, - tokenA.address, - amount, - [] - ), - `FlashLenderCallbackFailed`, - "did not roll back transaction" - ); - }); - - it("should roll back flash loan transaction if debt finalization isn't approved", async function () { - fakeIERC3156FlashBorrower.onFlashLoan.returns(CALLBACK_SUCCESS); - - // deposit amount for lending - const amount = ethers.BigNumber.from(123 + eighteenZeros); - await tokenA.transfer(lender.address, amount); - assert((await tokenA.balanceOf(lender.address)).eq(amount)); - - await assertError( - async () => - await lender.flashLoan( - fakeIERC3156FlashBorrower.address, - tokenA.address, - amount, - [] - ), - "ERC20: insufficient allowance", - "did not roll back transaction" - ); - }); - - it("should ensure flashLoan() does not process active debt", async function () { - // deposit amount for lending - const amount = ethers.BigNumber.from(123 + eighteenZeros); - await tokenA.transfer(lender.address, amount.mul(2)); - assert((await tokenA.balanceOf(lender.address)).eq(amount.mul(2))); - - await assertError( - async () => - await lender.flashLoan( - erc3156FlashBorrowerReentrancy.address, - tokenA.address, - amount, - [] - ), - "ActiveDebt", - "Flash Loan Reentrant" - ); - }); - - it("should ensure maxFlashLoan is zero if there is an active debt", async function () { - // deposit amount for lending - const amount = ethers.BigNumber.from(123 + eighteenZeros); - await tokenA.transfer(lender.address, amount.mul(2)); - assert((await tokenA.balanceOf(lender.address)).eq(amount.mul(2))); - - // should not revert. - await lender.flashLoan( - erc3156FlashBorrowerMaxLoan.address, - tokenA.address, - amount, - [] - ); - - // Max Loan equal balance of lender after repayment - const maxFlashLoanA_ = await lender.maxFlashLoan(tokenA.address); - assert(maxFlashLoanA_.eq(amount.mul(2))); - - const maxFlashLoanB0_ = await lender.maxFlashLoan(tokenB.address); - assert(maxFlashLoanB0_.eq(0)); - - await tokenB.transfer(lender.address, amount.mul(3)); - - await lender.flashLoan( - erc3156FlashBorrowerMaxLoan.address, - tokenB.address, - amount, - [] - ); - - // Max Loan equal balance of lender after repayment - const maxFlashLoanB1_ = await lender.maxFlashLoan(tokenB.address); - assert(maxFlashLoanB1_.eq(amount.mul(3))); - }); -}); diff --git a/test/OrderBook/OrderBookFlashLender/takeOrdersSloshy.ts b/test/OrderBook/OrderBookFlashLender/takeOrdersSloshy.ts deleted file mode 100644 index 15745e6c9..000000000 --- a/test/OrderBook/OrderBookFlashLender/takeOrdersSloshy.ts +++ /dev/null @@ -1,399 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck - -import { strict as assert } from "assert"; -import { concat } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { - ERC3156FlashBorrowerBuyTest, - ReserveToken18, -} from "../../../typechain"; -import { - AddOrderEvent, - DepositConfigStruct, - OrderConfigStruct, - TakeOrderConfigStruct, - TakeOrderEvent, - TakeOrdersConfigStruct, -} from "../../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../../utils/bytes"; -import { - eighteenZeros, - max_uint256, - sixteenZeros, -} from "../../../utils/constants/bigNumber"; -import { basicDeploy } from "../../../utils/deploy/basicDeploy"; -import { getEventArgs } from "../../../utils/events"; -import { - generateEvaluableConfig, - memoryOperand, - MemoryType, - op, -} from "../../../utils/interpreter/interpreter"; -import { AllStandardOps } from "../../../utils/interpreter/ops/allStandardOps"; -import { compareStructs } from "../../../utils/test/compareStructs"; -import { deployOrderBook } from "../../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../../utils/deploy/registry1820/deploy"; -import { encodeMeta } from "../../../utils/orderBook/order"; - -const Opcode = AllStandardOps; - -describe("OrderBook takeOrders sloshy tests", async function () { - let USDT: ReserveToken18; - let DAI: ReserveToken18; - - let erc3156Bot: ERC3156FlashBorrowerBuyTest; - - beforeEach(async () => { - USDT = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - DAI = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await USDT.initialize(); - await DAI.initialize(); - - erc3156Bot = (await basicDeploy( - "ERC3156FlashBorrowerBuyTest", - {} - )) as ERC3156FlashBorrowerBuyTest; - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should complete an e2e slosh with a loan", async function () { - const signers = await ethers.getSigners(); - - const [, alice] = signers; - - const orderBook = await deployOrderBook(); - - const vaultAlice = ethers.BigNumber.from(randomUint256()); - - const threshold = ethers.BigNumber.from(101 + sixteenZeros); // 1% - - const constants = [max_uint256, threshold]; - - const vMaxAmount = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const vThreshold = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - - // prettier-ignore - const source = concat([ - vMaxAmount, - vThreshold, - ]); - - // 1. alice's order says she will give anyone 1 DAI who can give her 1.01 USDT - const evaluableConfig = await generateEvaluableConfig( - [source, []], - constants - ); - - const orderConfig: OrderConfigStruct = { - validInputs: [{ token: USDT.address, decimals: 18, vaultId: vaultAlice }], - validOutputs: [{ token: DAI.address, decimals: 18, vaultId: vaultAlice }], - evaluableConfig, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(orderConfig); - - const { order: askOrder } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // 1.1 Alice deposits DAI into her output vault - const amountDAI = ethers.BigNumber.from("1" + eighteenZeros); - await DAI.transfer(alice.address, amountDAI); - const depositConfigStructAlice: DepositConfigStruct = { - token: DAI.address, - vaultId: vaultAlice, - amount: amountDAI, - }; - await DAI.connect(alice).approve( - orderBook.address, - depositConfigStructAlice.amount - ); - - const _txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - const takeOrderConfigStruct: TakeOrderConfigStruct = { - order: askOrder, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: USDT.address, - input: DAI.address, - minimumInput: amountDAI, - maximumInput: amountDAI, - maximumIORatio: threshold, - orders: [takeOrderConfigStruct], - }; - - // 2. consider the setup where erc3156Bot doesn't have 1 DAI already, erc3156Bot must flash loan alice's 1 DAI - // 3. erc3156Bot 'sells' flash loaned 1 DAI to uni for 1.02 USDT (assuming this happens externally) - - // since we're only mocking interaction with external market, we can just give bot the USDT it would buy from market in advance - const amountUniUSDT = ethers.BigNumber.from(102 + sixteenZeros); // 2% - await USDT.transfer(erc3156Bot.address, amountUniUSDT); - - // 4. erc3156Bot takes alice's order (erc3156Bot sells 1.01 USDT to alice for 1 DAI) - await orderBook.flashLoan( - erc3156Bot.address, - DAI.address, - amountDAI, - ethers.utils.defaultAbiCoder.encode( - [ - { - type: "tuple", - name: "takeOrdersConfig", - components: [ - { name: "output", type: "address" }, - { name: "input", type: "address" }, - { name: "minimumInput", type: "uint256" }, - { name: "maximumInput", type: "uint256" }, - { name: "maximumIORatio", type: "uint256" }, - { - type: "tuple[]", - name: "orders", - components: [ - { - type: "tuple", - name: "order", - components: [ - { name: "owner", type: "address" }, - { name: "handleIO", type: "bool" }, - { - name: "evaluable", - type: "tuple", - components: [ - { name: "interpreter", type: "address" }, - { name: "store", type: "address" }, - { name: "expression", type: "address" }, - ], - }, - { - name: "validInputs", - type: "tuple[]", - components: [ - { name: "token", type: "address" }, - { name: "decimals", type: "uint256" }, - { - name: "vaultId", - type: "uint256", - }, - ], - }, - { - name: "validOutputs", - type: "tuple[]", - components: [ - { name: "token", type: "address" }, - { name: "decimals", type: "uint256" }, - { - name: "vaultId", - type: "uint256", - }, - ], - }, - ], - }, - { name: "inputIOIndex", type: "uint256" }, - { name: "outputIOIndex", type: "uint256" }, - { - type: "tuple[]", - name: "signedContext", - components: [ - { name: "signer", type: "address" }, - { name: "signature", type: "bytes" }, - { name: "context", type: "uint256[]" }, - ], - }, - ], - }, - ], - }, - ], - [takeOrdersConfigStruct] - ) - ); - - // market now has 1 DAI - const marketDAIBalance = await DAI.balanceOf( - "0x0000000000000000000000000000000000000001" // address(1) - ); - assert(marketDAIBalance.eq(amountDAI), "wrong DAI balance"); - - // erc3156Bot now has 0 DAI and 0.01 USDT - const expectedFinalERC3156BotUSDTBalance = ethers.BigNumber.from( - 1 + sixteenZeros - ); // 0.01 USDT - const erc3156BotDAIBalance = await DAI.balanceOf(erc3156Bot.address); - const erc3156BotUSDTBalance = await USDT.balanceOf(erc3156Bot.address); - assert(erc3156BotDAIBalance.isZero(), "wrong DAI balance"); - assert( - erc3156BotUSDTBalance.eq(expectedFinalERC3156BotUSDTBalance), - "wrong USDT balance" - ); - - // alice now has 0 DAI and 1.01 USDT - await orderBook.connect(alice).withdraw({ - token: USDT.address, - vaultId: vaultAlice, - amount: max_uint256, // takes entire vault balance - }); - const aliceUSDTBalance = await USDT.balanceOf(alice.address); - const aliceDAIBalance = await DAI.balanceOf(alice.address); - assert(aliceUSDTBalance.eq(threshold), "wrong USDT balance"); - assert(aliceDAIBalance.eq(0), "wrong DAI balance"); - }); - - it("should complete an e2e slosh without a loan", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, uni] = signers; - - const orderBook = await deployOrderBook(); - - const vaultAlice = ethers.BigNumber.from(randomUint256()); - - const threshold = ethers.BigNumber.from(101 + sixteenZeros); // 1% - - const constants = [max_uint256, threshold]; - - const vMaxAmount = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const vThreshold = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - - // prettier-ignore - const source = concat([ - vMaxAmount, - vThreshold, - ]); - - const evaluableConfig = await generateEvaluableConfig( - [source, []], - constants - ); - - // 1. alice's order says she will give anyone 1 DAI who can give her 1.01 USDT - const orderConfig: OrderConfigStruct = { - validInputs: [{ token: USDT.address, decimals: 18, vaultId: vaultAlice }], - validOutputs: [{ token: DAI.address, decimals: 18, vaultId: vaultAlice }], - evaluableConfig, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(orderConfig); - - const { order: askOrder } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // 1.1 Alice deposits DAI into her output vault - const amountDAI = ethers.BigNumber.from("1" + eighteenZeros); - await DAI.transfer(alice.address, amountDAI); - const depositConfigStructAlice: DepositConfigStruct = { - token: DAI.address, - vaultId: vaultAlice, - amount: amountDAI, - }; - await DAI.connect(alice).approve( - orderBook.address, - depositConfigStructAlice.amount - ); - - const _txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - // 2. consider the setup where bob simply has 1 DAI already - await DAI.transfer(bob.address, amountDAI); - - // 3. bob sells his 1 DAI to uni for 1.02 USDT assuming this (happens externally) - const amountUniUSDT = ethers.BigNumber.from(102 + sixteenZeros); // 2% - await USDT.transfer(uni.address, amountUniUSDT); - - // 3.1 bob transfering 1 DAI to uni - await DAI.connect(bob).transfer(uni.address, amountDAI); - // 3.2 bob receiving 1.02 USDT from uni - await USDT.connect(uni).transfer(bob.address, amountUniUSDT); - - // 4. bob takes alice's order (bob sells his 1.01 USDT to alice for 1 DAI) - const takeOrderConfigStruct: TakeOrderConfigStruct = { - order: askOrder, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: USDT.address, - input: DAI.address, - minimumInput: amountDAI, - maximumInput: amountDAI, - maximumIORatio: threshold, - orders: [takeOrderConfigStruct], - }; - - await USDT.connect(bob).approve(orderBook.address, threshold); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(amountDAI), "wrong input"); - assert(output.eq(threshold), "wrong output"); - compareStructs(config, takeOrderConfigStruct); - - // 4.1 bob now has 1 DAI and 0.01 USDT - const bobUSDTBalance = await USDT.balanceOf(bob.address); - const bobDAIBalance = await DAI.balanceOf(bob.address); - const expectedBalance = amountUniUSDT.sub(threshold); - assert(bobUSDTBalance.eq(expectedBalance), "wrong USDT balance"); - assert(bobDAIBalance.eq(amountDAI), "wrong DAI balance"); - - // 5. alice now has 0 DAI and 1.01 USDT - await orderBook.connect(alice).withdraw({ - token: USDT.address, - vaultId: vaultAlice, - amount: max_uint256, // takes entire vault balance - }); - const aliceUSDTBalance = await USDT.balanceOf(alice.address); - const aliceDAIBalance = await DAI.balanceOf(alice.address); - assert(aliceUSDTBalance.eq(threshold), "wrong USDT balance"); - assert(aliceDAIBalance.eq(0), "wrong DAI balance"); - }); -}); diff --git a/test/OrderBook/OrderBookFlashLender/withdrawFlashLoan.ts b/test/OrderBook/OrderBookFlashLender/withdrawFlashLoan.ts deleted file mode 100644 index 1846e24b0..000000000 --- a/test/OrderBook/OrderBookFlashLender/withdrawFlashLoan.ts +++ /dev/null @@ -1,111 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck - -import { strict as assert } from "assert"; - -import { ethers } from "hardhat"; -import type { - ERC3156FlashBorrowerWithdrawTest, - ReserveToken18, -} from "../../../typechain"; -import { - DepositConfigStruct, - WithdrawConfigStruct, -} from "../../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../../utils/bytes"; -import { eighteenZeros } from "../../../utils/constants/bigNumber"; -import { basicDeploy } from "../../../utils/deploy/basicDeploy"; -import { deployOrderBook } from "../../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../../utils/deploy/registry1820/deploy"; - -describe("OrderBook flash loan withdraw tests", async function () { - let USDT: ReserveToken18; - let DAI: ReserveToken18; - let erc3156Bot: ERC3156FlashBorrowerWithdrawTest; - - beforeEach(async () => { - USDT = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - DAI = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await USDT.initialize(); - await DAI.initialize(); - - erc3156Bot = (await basicDeploy( - "ERC3156FlashBorrowerWithdrawTest", - {} - )) as ERC3156FlashBorrowerWithdrawTest; - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should not allow decreasing flash debt by more than the sender's vault balance", async function () { - const orderBook = await deployOrderBook(); - - const vaultBot = ethers.BigNumber.from(randomUint256()); - - const amountCanLoan = ethers.BigNumber.from("20" + eighteenZeros); - const amountBotDAI = ethers.BigNumber.from("10" + eighteenZeros); - - await DAI.transfer(orderBook.address, amountCanLoan); - - // bot deposits a smaller amount - await DAI.transfer(erc3156Bot.address, amountBotDAI); - const depositConfigStructBot: DepositConfigStruct = { - token: DAI.address, - vaultId: vaultBot, - amount: amountBotDAI, - }; - await erc3156Bot.orderBookDeposit( - orderBook.address, - depositConfigStructBot - ); - - // note vault balance - const botVaultBalance0 = await orderBook.vaultBalance( - erc3156Bot.address, - DAI.address, - vaultBot - ); - assert(botVaultBalance0.eq(amountBotDAI)); - - // bot gets flash loan from orderbook and attempts to decrease debt via `.withdraw()` - // note that bot has a lower vault balance than alice - const withdrawConfigStruct: WithdrawConfigStruct = { - token: DAI.address, - vaultId: vaultBot, - amount: amountCanLoan, // much more than bot's current vault balance - }; - await orderBook.flashLoan( - erc3156Bot.address, - DAI.address, - amountCanLoan, // much more than bot's current vault balance - ethers.utils.defaultAbiCoder.encode( - [ - { - type: "tuple", - name: "withdrawConfig", - components: [ - { name: "token", type: "address" }, - { name: "vaultId", type: "uint256" }, - { name: "amount", type: "uint256" }, - ], - }, - ], - [withdrawConfigStruct] - ) - ); - - const botVaultBalance1 = await orderBook.vaultBalance( - erc3156Bot.address, - DAI.address, - vaultBot - ); - const botBalance1 = await DAI.balanceOf(erc3156Bot.address); - - assert(botVaultBalance1.isZero()); - assert(botBalance1.eq(amountBotDAI)); // bot only gets out what what deposited and paid back the loan in full - }); -}); diff --git a/test/OrderBook/addOrder.ts b/test/OrderBook/addOrder.ts deleted file mode 100644 index c4cafc0fc..000000000 --- a/test/OrderBook/addOrder.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { strict as assert } from "assert"; -import { concat } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { ReserveToken18 } from "../../typechain"; -import { - OrderConfigStruct, - AddOrderEvent, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { - eighteenZeros, - max_uint256, - ONE, -} from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs } from "../../utils/events"; -import { - generateEvaluableConfig, - memoryOperand, - MemoryType, - op, -} from "../../utils/interpreter/interpreter"; -import { AllStandardOps } from "../../utils/interpreter/ops/allStandardOps"; -import { fixedPointDiv } from "../../utils/math"; -import { compareStructs } from "../../utils/test/compareStructs"; -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import { encodeMeta } from "../../utils/orderBook/order"; - -const Opcode = AllStandardOps; - -describe("OrderBook add order", async function () { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should add orders", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - const aliceOrder = encodeMeta("Order_A"); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const constants_A = [max_uint256, ratio_A]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_A = concat([ - aOpMax, - aRatio, - ]); - - const EvaluableConfig_A = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const orderConfig_A: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: aliceInputVault }, - ], - validOutputs: [ - { token: tokenB.address, decimals: 18, vaultId: aliceOutputVault }, - ], - evaluableConfig: EvaluableConfig_A, - meta: aliceOrder, - }; - - const txOrder_A = await orderBook.connect(alice).addOrder(orderConfig_A); - - const { - sender: sender_A, - expressionDeployer: ExpressionDeployer_A, - order: order_A, - } = (await getEventArgs( - txOrder_A, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert( - ExpressionDeployer_A === EvaluableConfig_A.deployer, - "wrong expression deployer" - ); - assert(sender_A === alice.address, "wrong sender"); - compareStructs(order_A, orderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - const constants_B = [max_uint256, ratio_B]; - const bOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const bRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_B = concat([ - bOpMax, - bRatio, - ]); - - const bobOrder = encodeMeta("Order_B"); - - const EvaluableConfig_B = await generateEvaluableConfig( - [source_B, []], - constants_B - ); - - const orderConfig_B: OrderConfigStruct = { - validInputs: [ - { token: tokenB.address, decimals: 18, vaultId: bobInputVault }, - ], - validOutputs: [ - { token: tokenA.address, decimals: 18, vaultId: bobOutputVault }, - ], - evaluableConfig: EvaluableConfig_B, - meta: bobOrder, - }; - - const txOrderB = await orderBook.connect(bob).addOrder(orderConfig_B); - - const { sender: sender_B, order: order_B } = (await getEventArgs( - txOrderB, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(order_B, orderConfig_B); - }); -}); diff --git a/test/OrderBook/bounty.ts b/test/OrderBook/bounty.ts deleted file mode 100644 index e290e229e..000000000 --- a/test/OrderBook/bounty.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { strict as assert } from "assert"; -import { concat } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { ReserveToken18 } from "../../typechain"; -import { - AddOrderEvent, - AfterClearEvent, - ClearConfigStruct, - ClearEvent, - ClearStateChangeStruct, - DepositConfigStruct, - DepositEvent, - OrderConfigStruct, - WithdrawConfigStruct, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { - eighteenZeros, - max_uint256, - ONE, -} from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs } from "../../utils/events"; -import { - generateEvaluableConfig, - memoryOperand, - MemoryType, - op, -} from "../../utils/interpreter/interpreter"; -import { AllStandardOps } from "../../utils/interpreter/ops/allStandardOps"; -import { fixedPointDiv, fixedPointMul, minBN } from "../../utils/math"; -import { - compareSolStructs, - compareStructs, -} from "../../utils/test/compareStructs"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { encodeMeta } from "../../utils/orderBook/order"; - -const Opcode = AllStandardOps; - -describe("OrderBook bounty", async function () { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("order clearer should receive correct bounty amounts in their vaults, and can withdraw their vault balance for each token", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - const aliceOrder = encodeMeta("Order_A"); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const constants_A = [max_uint256, ratio_A]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_A = concat([ - aOpMax, - aRatio, - ]); - - const EvaluableConfig_A = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const orderConfig_A: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: aliceInputVault }, - ], - validOutputs: [ - { token: tokenB.address, decimals: 18, vaultId: aliceOutputVault }, - ], - evaluableConfig: EvaluableConfig_A, - meta: aliceOrder, - }; - - const txOrder_A = await orderBook.connect(alice).addOrder(orderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txOrder_A, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, orderConfig_A); - - // Order_B - - // bob undervalues his units, offering better value than alice expects - // order clearer is ultimately rewarded with this difference as a bounty - // i.e. an excess of tokenA which bob didn't need to give to alice to - // fulfill her bid order - const ratio_B = fixedPointDiv(ONE, ratio_A.add(10 + eighteenZeros)); - const constants_B = [max_uint256, ratio_B]; - const bOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const bRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const bidSource = concat([ - bOpMax, - bRatio, - ]); - - const bobOrder = encodeMeta("Order_B"); - - const EvaluableConfig_B = await generateEvaluableConfig( - [bidSource, []], - constants_B - ); - - const orderConfig_B: OrderConfigStruct = { - validInputs: [ - { token: tokenB.address, decimals: 18, vaultId: bobInputVault }, - ], - validOutputs: [ - { token: tokenA.address, decimals: 18, vaultId: bobOutputVault }, - ], - evaluableConfig: EvaluableConfig_B, - meta: bobOrder, - }; - - const txOrder_B = await orderBook.connect(bob).addOrder(orderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txOrder_B, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, orderConfig_B); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - - const { sender: _depositAliceSender, config: _depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: _depositBobSender, config: _depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(_depositAliceSender === alice.address); - compareStructs(_depositAliceConfig, depositConfigStructAlice); - assert(_depositBobSender === bob.address); - compareStructs(_depositBobConfig, depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - const txClearOrder = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const { - sender: _clearSender, - alice: clearA_, - bob: clearB_, - clearConfig: _clearBountyConfig, - } = (await getEventArgs( - txClearOrder, - "Clear", - orderBook - )) as ClearEvent["args"]; - const { sender: _afterClearSender, clearStateChange: _clearStateChange } = - (await getEventArgs( - txClearOrder, - "AfterClear", - orderBook - )) as AfterClearEvent["args"]; - - const aOutputMaxExpected = amountA; - const bOutputMaxExpected = amountB; - - const aOutputExpected = minBN( - aOutputMaxExpected, - fixedPointMul(ratio_B, amountA) - ); - const bOutputExpected = minBN( - bOutputMaxExpected, - fixedPointMul(ratio_A, amountB) - ); - - const expectedClearStateChange: ClearStateChangeStruct = { - aliceOutput: aOutputExpected, - bobOutput: bOutputExpected, - aliceInput: fixedPointMul(ratio_A, aOutputExpected), - bobInput: fixedPointMul(ratio_B, bOutputExpected), - }; - - assert(_afterClearSender === bountyBot.address); - assert(_clearSender === bountyBot.address); - compareSolStructs(clearA_, Order_A); - compareSolStructs(clearB_, Order_B); - compareStructs(_clearBountyConfig, clearConfig); - compareStructs(_clearStateChange, expectedClearStateChange); - - const _actualBounty = { - a: _clearStateChange.aliceOutput.sub(_clearStateChange.bobInput), - b: _clearStateChange.bobOutput.sub(_clearStateChange.aliceInput), - }; - - // alice pays 90% of input amount to bob and 10% to order clearer to make - // up the difference - assert(_actualBounty.b.eq(amountB.div(10))); - - const _vaultBalance0 = await orderBook.vaultBalance( - bountyBot.address, - tokenA.address, - bountyBotVaultB - ); - assert(_actualBounty.b.eq(_vaultBalance0)); - - const withdrawConfigStruct: WithdrawConfigStruct = { - token: tokenA.address, - vaultId: bountyBotVaultB, - amount: _vaultBalance0, - }; - - const _clearerBalance0 = await tokenA.balanceOf(bountyBot.address); - - // order clearer withdraws bounty from vault - await orderBook.connect(bountyBot).withdraw(withdrawConfigStruct); - - const _clearerBalance1 = await tokenA.balanceOf(bountyBot.address); - - assert(_clearerBalance0.isZero()); - assert(_clearerBalance1.eq(_vaultBalance0)); - - const _vaultBalance1 = await orderBook.vaultBalance( - bountyBot.address, - tokenA.address, - bountyBotVaultB - ); - assert(_vaultBalance1.isZero()); - }); -}); diff --git a/test/OrderBook/clear.ts b/test/OrderBook/clear.ts deleted file mode 100644 index fcd2927d7..000000000 --- a/test/OrderBook/clear.ts +++ /dev/null @@ -1,3111 +0,0 @@ -import { strict as assert } from "assert"; - -import { ethers } from "hardhat"; -import type { ReserveToken18, ReserveTokenDecimals } from "../../typechain"; -import { - AddOrderEvent, - AfterClearEvent, - ClearConfigStruct, - ClearEvent, - ClearStateChangeStruct, - ContextEvent, - DepositConfigStruct, - DepositEvent, - OrderConfigStruct, - OrderNotFoundEvent, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { - eighteenZeros, - max_uint256, - ONE, - sixZeros, - twentyZeros, -} from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs, getEvents } from "../../utils/events"; - -import { fixedPointDiv, fixedPointMul, minBN } from "../../utils/math"; -import { encodeMeta, getOrderConfig } from "../../utils/orderBook/order"; -import { assertError } from "../../utils/test/assertError"; -import { - compareSolStructs, - compareStructs, -} from "../../utils/test/compareStructs"; - -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; - -describe("OrderBook clear order", async function () { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - describe("should scale outputMax with decimals", () => { - it("should scale outputMax based on input/output token decimals (input token has SAME decimals as output: 6 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 6; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("Order_A") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB06.address, - tokenBDecimals, - bobInputVault, - tokenA06.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txBobAddOrder = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txBobAddOrder, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA06.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB06.transfer(alice.address, depositAmountB); - await tokenA06.transfer(bob.address, depositAmountA); - await tokenB06 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA06 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ); - const aliceOutputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenB06.address, - aliceOutputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobInputVault - ); - const bobOutputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenA06.address, - bobOutputVault - ); - - // transferred during clear - assert(aliceInputVaultBalance.eq(depositAmountA.div(2))); - assert(bobInputVaultBalance.eq(depositAmountB.div(2))); - - // never moved after being deposited - assert(aliceOutputVaultBalance.eq(depositAmountB.div(2))); - assert(bobOutputVaultBalance.eq(depositAmountA.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has MORE decimals than output: 20 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 20; - const tokenBDecimals = 6; - - const tokenA20 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA20.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA20.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB06.address, - tokenBDecimals, - bobInputVault, - tokenA20.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txBobAddOrder = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txBobAddOrder, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA20.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB06.transfer(alice.address, depositAmountB); - await tokenA20.transfer(bob.address, depositAmountA); - await tokenB06 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA20 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA20.address, - aliceInputVault - ); - const aliceOutputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenB06.address, - aliceOutputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobInputVault - ); - const bobOutputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenA20.address, - bobOutputVault - ); - - // transferred during clear - assert(aliceInputVaultBalance.eq(depositAmountA.div(2))); - assert(bobInputVaultBalance.eq(depositAmountB.div(2))); - - // never moved after being deposited - assert(aliceOutputVaultBalance.eq(depositAmountB.div(2))); - assert(bobOutputVaultBalance.eq(depositAmountA.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has MORE decimals than output: 18 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA18.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB06.address, - tokenBDecimals, - bobInputVault, - tokenA18.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA18.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB06.transfer(alice.address, depositAmountB); - await tokenA18.transfer(bob.address, depositAmountA); - await tokenB06 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA18 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA18.address, - aliceInputVault - ); - const aliceOutputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenB06.address, - aliceOutputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobInputVault - ); - const bobOutputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenA18.address, - bobOutputVault - ); - - // transferred during clear - assert(aliceInputVaultBalance.eq(depositAmountA.div(2))); - assert(bobInputVaultBalance.eq(depositAmountB.div(2))); - - // never moved after being deposited - assert(aliceOutputVaultBalance.eq(depositAmountB.div(2))); - assert(bobOutputVaultBalance.eq(depositAmountA.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has LESS decimals than output: 6 vs 20)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 20; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB20 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB20.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB20.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB20.address, - tokenBDecimals, - bobInputVault, - tokenA06.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + twentyZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB20.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA06.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB20.transfer(alice.address, depositAmountB); - await tokenA06.transfer(bob.address, depositAmountA); - await tokenB20 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA06 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ); - const aliceOutputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenB20.address, - aliceOutputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB20.address, - bobInputVault - ); - const bobOutputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenA06.address, - bobOutputVault - ); - - // transferred during clear - assert(aliceInputVaultBalance.eq(depositAmountA.div(2))); - assert(bobInputVaultBalance.eq(depositAmountB.div(2))); - - // never moved after being deposited - assert(aliceOutputVaultBalance.eq(depositAmountB.div(2))); - assert(bobOutputVaultBalance.eq(depositAmountA.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has LESS decimals than output: 6 vs 18)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 18; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB18.address, - tokenBDecimals, - bobInputVault, - tokenA06.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA06.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB18.transfer(alice.address, depositAmountB); - await tokenA06.transfer(bob.address, depositAmountA); - await tokenB18 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA06 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ); - const aliceOutputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenB18.address, - aliceOutputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB18.address, - bobInputVault - ); - const bobOutputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenA06.address, - bobOutputVault - ); - - // transferred during clear - assert(aliceInputVaultBalance.eq(depositAmountA.div(2))); - assert(bobInputVaultBalance.eq(depositAmountB.div(2))); - - // never moved after being deposited - assert(aliceOutputVaultBalance.eq(depositAmountB.div(2))); - assert(bobOutputVaultBalance.eq(depositAmountA.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has LESS decimals than output: 0 vs 18)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 0; - const tokenBDecimals = 18; - - const tokenA00 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA00.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA00.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB18.address, - tokenBDecimals, - bobInputVault, - tokenA00.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA00.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB18.transfer(alice.address, depositAmountB); - await tokenA00.transfer(bob.address, depositAmountA); - await tokenB18 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA00 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA00.address, - aliceInputVault - ); - const aliceOutputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenB18.address, - aliceOutputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB18.address, - bobInputVault - ); - const bobOutputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenA00.address, - bobOutputVault - ); - - // transferred during clear - assert(aliceInputVaultBalance.eq(depositAmountA.div(2))); - assert(bobInputVaultBalance.eq(depositAmountB.div(2))); - - // never moved after being deposited - assert(aliceOutputVaultBalance.eq(depositAmountB.div(2))); - assert(bobOutputVaultBalance.eq(depositAmountA.div(2))); - }); - }); - - describe("should scale ratio with decimals", () => { - it("should scale ratio based on input/output token decimals (input token has SAME decimals as output: 6 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 6; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB06.address, - tokenBDecimals, - bobInputVault, - tokenA06.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA06.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB06.transfer(alice.address, depositAmountB); - await tokenA06.transfer(bob.address, depositAmountA); - await tokenB06 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA06 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobInputVault - ); - - assert(aliceInputVaultBalance.eq(depositAmountA)); - assert(bobInputVaultBalance.eq(depositAmountB)); - }); - - it("should scale ratio based on input/output token decimals (input token has MORE decimals than output: 20 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 20; - const tokenBDecimals = 6; - - const tokenA20 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA20.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - max_uint256, - tokenA20.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB06.address, - tokenBDecimals, - bobInputVault, - tokenA20.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA20.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB06.transfer(alice.address, depositAmountB); - await tokenA20.transfer(bob.address, depositAmountA); - await tokenB06 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA20 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA20.address, - aliceInputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobInputVault - ); - - assert(aliceInputVaultBalance.eq(depositAmountA)); - assert(bobInputVaultBalance.eq(depositAmountB)); - }); - - it("should scale ratio based on input/output token decimals (input token has MORE decimals than output: 18 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - max_uint256, - tokenA18.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB06.address, - tokenBDecimals, - bobInputVault, - tokenA18.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA18.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB06.transfer(alice.address, depositAmountB); - await tokenA18.transfer(bob.address, depositAmountA); - await tokenB06 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA18 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA18.address, - aliceInputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobInputVault - ); - - assert(aliceInputVaultBalance.eq(depositAmountA)); - assert(bobInputVaultBalance.eq(depositAmountB)); - }); - - it("should scale ratio based on input/output token decimals (input token has LESS decimals than output: 6 vs 20)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 20; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB20 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB20.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB20.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB20.address, - tokenBDecimals, - bobInputVault, - tokenA06.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + twentyZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB20.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA06.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB20.transfer(alice.address, depositAmountB); - await tokenA06.transfer(bob.address, depositAmountA); - await tokenB20 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA06 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB20.address, - bobInputVault - ); - - assert(aliceInputVaultBalance.eq(depositAmountA)); - assert(bobInputVaultBalance.eq(depositAmountB)); - }); - - it("should scale ratio based on input/output token decimals (input token has LESS decimals than output: 6 vs 18)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 18; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB18.address, - tokenBDecimals, - bobInputVault, - tokenA06.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA06.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB18.transfer(alice.address, depositAmountB); - await tokenA06.transfer(bob.address, depositAmountA); - await tokenB18 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA06 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB18.address, - bobInputVault - ); - - assert(aliceInputVaultBalance.eq(depositAmountA)); - assert(bobInputVaultBalance.eq(depositAmountB)); - }); - - it("should scale ratio based on input/output token decimals (input token has LESS decimals than output: 0 vs 18)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 0; - const tokenBDecimals = 18; - - const tokenA00 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA00.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A = await getOrderConfig( - ratio_A, - max_uint256, - tokenA00.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B = await getOrderConfig( - ratio_B, - max_uint256, - tokenB18.address, - tokenBDecimals, - bobInputVault, - tokenA00.address, - tokenADecimals, - bobOutputVault, - encodeMeta("") - ); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - const { order: Order_B } = await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - ); - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA00.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB18.transfer(alice.address, depositAmountB); - await tokenA00.transfer(bob.address, depositAmountA); - await tokenB18 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA00 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA00.address, - aliceInputVault - ); - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB18.address, - bobInputVault - ); - - assert(aliceInputVaultBalance.eq(depositAmountA)); - assert(bobInputVaultBalance.eq(depositAmountB)); - }); - }); - - it("orders must be live to clear (order B)", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const aliceOrder = encodeMeta("Order_A"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const bobOrder = encodeMeta("Order_B"); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_B, - max_uint256, - tokenB.address, - 18, - bobInputVault, - tokenA.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { - sender: sender_B, - order: Order_B, - orderHash: hashOrder_B, - } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === bob.address); - compareStructs(depositBobConfig, depositConfigStructBob); - - // order B is removed - await orderBook.connect(bob).removeOrder(Order_B); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - const txClearOrder = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const { - sender: clearSender, - owner: cancelOrderOwner, - orderHash: cancelOrderHash, - } = (await getEventArgs( - txClearOrder, - "OrderNotFound", - orderBook - )) as OrderNotFoundEvent["args"]; - - assert(clearSender === bountyBot.address); - assert(cancelOrderOwner === bob.address); - assert(cancelOrderHash.eq(hashOrder_B)); - }); - - it("orders must be live to clear (order A)", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const aliceOrder = encodeMeta("Order_A"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { - sender: sender_A, - order: Order_A, - orderHash: hashOrder_A, - } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const bobOrder = encodeMeta("Order_B"); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_B, - max_uint256, - tokenB.address, - 18, - bobInputVault, - tokenA.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === bob.address); - compareStructs(depositBobConfig, depositConfigStructBob); - - // order A is removed - await orderBook.connect(alice).removeOrder(Order_A); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - const txClearOrder = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const { - sender: clearSender, - owner: cancelOrderOwner, - orderHash: cancelOrderHash, - } = (await getEventArgs( - txClearOrder, - "OrderNotFound", - orderBook - )) as OrderNotFoundEvent["args"]; - - assert(clearSender === bountyBot.address); - assert(cancelOrderOwner === alice.address); - assert(cancelOrderHash.eq(hashOrder_A)); - }); - - it("should validate input/output tokens", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const aliceOrder = encodeMeta("Order_A"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - const bobOrder = encodeMeta("Order_B"); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_B, - max_uint256, - tokenB.address, - 18, - bobInputVault, - tokenA.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === bob.address); - compareStructs(depositBobConfig, depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - // Override Order_B config - const bigConfigInvalid0 = { - ...Order_B, - validOutputs: [ - { - ...Order_B.validOutputs[0], - token: tokenB.address, // will result in mismatch - }, - ], - }; - const bigConfigInvalid1 = { - ...Order_B, - validInputs: [ - { - ...Order_B.validInputs[0], - token: tokenA.address, // will result in mismatch - }, - ], - }; - - await assertError( - async () => - await orderBook - .connect(bountyBot) - .clear(Order_A, bigConfigInvalid1, clearConfig, [], []), - `TokenMismatch("${tokenB.address}", "${tokenA.address}")`, - "did not validate input token" - ); - await assertError( - async () => - await orderBook - .connect(bountyBot) - .clear(Order_A, bigConfigInvalid0, clearConfig, [], []), - `TokenMismatch("${tokenA.address}", "${tokenB.address}")`, - "did not validate output token" - ); - }); - - it("should enforce different owner for Order_A and Order_B", async function () { - const signers = await ethers.getSigners(); - - const alice1 = signers[1]; - const alice2 = alice1; // 'Bob' is actually Alice in this case - const bountyBot = signers[3]; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const aliceOrder = encodeMeta("Order_A"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice1) - .addOrder(OrderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice1.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const bobOrder = encodeMeta("Order_B"); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_B, - max_uint256, - tokenB.address, - 18, - bobInputVault, - tokenA.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderBob = await orderBook - .connect(alice2) - .addOrder(OrderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === alice2.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice1.address, amountB); - await tokenA.transfer(alice2.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice1) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(alice2) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice (alice1) deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice1) - .deposit(depositConfigStructAlice); - // Bob (alice2) deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(alice2) - .deposit(depositConfigStructBob); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice1.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === alice2.address); - compareStructs(depositBobConfig, depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - const txClearOrder = orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - await assertError( - async () => await txClearOrder, - `SameOwner("${alice2.address}")`, - "did not revert with same owner for Order_A and Order_B " - ); - }); - - it("should add Order_A and Order_B and clear the order", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const aliceOrder = encodeMeta("Order_A"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const bobOrder = encodeMeta("Order_B"); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_B, - max_uint256, - tokenB.address, - 18, - bobInputVault, - tokenA.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === bob.address); - compareStructs(depositBobConfig, depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - const txClearOrder = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const { - sender: clearSender, - alice: clearA_, - bob: clearB_, - clearConfig: clearBountyConfig, - } = (await getEventArgs( - txClearOrder, - "Clear", - orderBook - )) as ClearEvent["args"]; - const { sender: afterClearSender, clearStateChange: clearStateChange } = - (await getEventArgs( - txClearOrder, - "AfterClear", - orderBook - )) as AfterClearEvent["args"]; - - const aOutputMaxExpected = amountA; - const bOutputMaxExpected = amountB; - - const aOutputExpected = minBN( - aOutputMaxExpected, - fixedPointMul(ratio_B, amountA) - ); - const bOutputExpected = minBN( - bOutputMaxExpected, - fixedPointMul(ratio_A, amountB) - ); - - const expectedClearStateChange: ClearStateChangeStruct = { - aliceOutput: aOutputExpected, - bobOutput: bOutputExpected, - aliceInput: fixedPointMul(ratio_A, aOutputExpected), - bobInput: fixedPointMul(ratio_B, bOutputExpected), - }; - - assert(afterClearSender === bountyBot.address); - assert(clearSender === bountyBot.address); - compareSolStructs(clearA_, Order_A); - compareSolStructs(clearB_, Order_B); - compareStructs(clearBountyConfig, clearConfig); - compareStructs(clearStateChange, expectedClearStateChange); - }); - - it("should ensure that misconfigured decimals on tokens only harm the misconfigurer (order B)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 18; - - const incorrectTokenADecimals = 10; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_B, - max_uint256, - tokenB18.address, - tokenBDecimals, - bobInputVault, - tokenA06.address, - incorrectTokenADecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - - // Deposit amount calculated with incorrect decimals - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow( - 18 + incorrectTokenADecimals - tokenBDecimals - ) - ); - - const expectedAliceInputVaultAmount = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA06.address, - vaultId: bobOutputVault, - amount: depositAmountA, - }; - await tokenB18.transfer(alice.address, depositAmountB); - await tokenA06.transfer(bob.address, depositAmountA); - await tokenB18 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA06 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA00 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const aliceInputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ); - - const aliceOutputVaultBalance = await orderBook.vaultBalance( - alice.address, - tokenB18.address, - aliceOutputVault - ); - - const bobInputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenB18.address, - bobInputVault - ); - - const bobOutputVaultBalance = await orderBook.vaultBalance( - bob.address, - tokenA06.address, - bobOutputVault - ); - - const bountyBotVaultA_ = await orderBook.vaultBalance( - bountyBot.address, - tokenB18.address, - bountyBotVaultA - ); - - assert( - aliceInputVaultBalance.add(bobOutputVaultBalance).eq(depositAmountA) - ); - assert(bobInputVaultBalance.add(bountyBotVaultA_).eq(depositAmountB)); - - assert(aliceInputVaultBalance.eq(expectedAliceInputVaultAmount)); - assert(aliceOutputVaultBalance.isZero()); - }); - - it("should validate context emitted in context event when handleIO dispatch is zero", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const aliceOrder = encodeMeta("Order_A"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { - sender: sender_A, - order: Order_A, - orderHash: hashOrder_A, - } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const bobOrder = encodeMeta("Order_B"); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_B, - max_uint256, - tokenB.address, - 18, - bobInputVault, - tokenA.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { - sender: sender_B, - order: Order_B, - orderHash: hashOrder_B, - } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === bob.address); - compareStructs(depositBobConfig, depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - const txClearOrder = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const { - sender: clearSender, - alice: clearA_, - bob: clearB_, - clearConfig: clearBountyConfig, - } = (await getEventArgs( - txClearOrder, - "Clear", - orderBook - )) as ClearEvent["args"]; - const { clearStateChange: clearStateChange } = (await getEventArgs( - txClearOrder, - "AfterClear", - orderBook - )) as AfterClearEvent["args"]; - - const aOutputMaxExpected = amountA; - const bOutputMaxExpected = amountB; - - const aOutputExpected = minBN( - aOutputMaxExpected, - fixedPointMul(ratio_B, amountA) - ); - const bOutputExpected = minBN( - bOutputMaxExpected, - fixedPointMul(ratio_A, amountB) - ); - - const expectedClearStateChange: ClearStateChangeStruct = { - aliceOutput: aOutputExpected, - bobOutput: bOutputExpected, - aliceInput: fixedPointMul(ratio_A, aOutputExpected), - bobInput: fixedPointMul(ratio_B, bOutputExpected), - }; - - assert(clearSender === bountyBot.address); - compareSolStructs(clearA_, Order_A); - compareSolStructs(clearB_, Order_B); - compareStructs(clearBountyConfig, clearConfig); - compareStructs(clearStateChange, expectedClearStateChange); - - // Asserting Context Events - const contextEvents = (await getEvents( - txClearOrder, - "Context", - orderBook - )) as ContextEvent["args"][]; - - const { sender: sender0, context: context0_ } = contextEvents[0]; - const { sender: sender1, context: context1_ } = contextEvents[1]; - - assert(sender0 === bountyBot.address); - assert(sender1 === bountyBot.address); - - const aop = minBN(amountB, fixedPointMul(amountB, ratio_B)); - const bop = minBN(amountA, fixedPointMul(amountA, ratio_A)); - const aip = fixedPointMul(aop, ratio_A); - const bip = fixedPointMul(bop, ratio_B); - - const expectedEvent0 = [ - [ - ethers.BigNumber.from(bountyBot.address), - ethers.BigNumber.from(orderBook.address), - ], - [ - hashOrder_A, - ethers.BigNumber.from(alice.address), - ethers.BigNumber.from(bob.address), - ], - [amountB, ratio_A], - [ - ethers.BigNumber.from(tokenA.address), - ethers.BigNumber.from(18), - aliceInputVault, - 0, - aip, - ], - [ - ethers.BigNumber.from(tokenB.address), - ethers.BigNumber.from(18), - aliceOutputVault, - amountB, - aop, - ], - ]; - - for (let i = 0; i < expectedEvent0.length; i++) { - const rowArray = expectedEvent0[i]; - for (let j = 0; j < rowArray.length; j++) { - const colElement = rowArray[j]; - if (!context0_[i][j].eq(colElement)) { - assert.fail(`mismatch at position (${i},${j}), - expected ${colElement} - got ${context0_[i][j]}`); - } - } - } - - const expectedEvent1 = [ - [ - ethers.BigNumber.from(bountyBot.address), - ethers.BigNumber.from(orderBook.address), - ], - [ - hashOrder_B, - ethers.BigNumber.from(bob.address), - ethers.BigNumber.from(alice.address), - ], - [amountA, ratio_B], - [ - ethers.BigNumber.from(tokenB.address), - ethers.BigNumber.from(18), - bobInputVault, - 0, - bip, - ], - [ - ethers.BigNumber.from(tokenA.address), - ethers.BigNumber.from(18), - bobOutputVault, - amountA, - bop, - ], - ]; - - for (let i = 0; i < expectedEvent1.length; i++) { - const rowArray = expectedEvent1[i]; - for (let j = 0; j < rowArray.length; j++) { - const colElement = rowArray[j]; - if (!context1_[i][j].eq(colElement)) { - assert.fail(`mismatch at position (${i},${j}), - expected ${colElement} - got ${context1_[i][j]}`); - } - } - } - }); -}); diff --git a/test/OrderBook/construction.ts b/test/OrderBook/construction.ts deleted file mode 100644 index da7fdc9cb..000000000 --- a/test/OrderBook/construction.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { strict as assert } from "assert"; - -import { ethers } from "hardhat"; - -import { OrderBook } from "../../typechain/contracts/orderbook/OrderBook"; - -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { getTouchDeployer } from "../../utils/deploy/interpreter/shared/rainterpreterExpressionDeployer/deploy"; -import { - assertError, - getRainMetaDocumentFromContract, - validateContractMetaAgainstABI, - zeroAddress, -} from "../../utils"; -import { DeployerDiscoverableMetaV1ConstructionConfigStruct } from "../../typechain/contracts/factory/CloneFactory"; - -describe("OrderBook Constructor", async function () { - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should fail if deploy is initiated with bad callerMeta", async function () { - const orderBookFactory = await ethers.getContractFactory("OrderBook", {}); - const touchDeployer = await getTouchDeployer(); - - const config0: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), - deployer: touchDeployer.address, - }; - const orderBook = (await orderBookFactory.deploy(config0)) as OrderBook; - - assert(!(orderBook.address === zeroAddress), "OrderBook did not deploy"); - - const config1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("lobby"), - deployer: touchDeployer.address, - }; - - await assertError( - async () => await orderBookFactory.deploy(config1), - "UnexpectedMetaHash", - "Stake Deployed for bad hash" - ); - }); - - it("should validate contract meta with abi ", async function () { - assert( - validateContractMetaAgainstABI("orderbook"), - "Contract Meta Inconsistent with Contract ABI" - ); - }); -}); diff --git a/test/OrderBook/counterparty.ts b/test/OrderBook/counterparty.ts deleted file mode 100644 index dd8597488..000000000 --- a/test/OrderBook/counterparty.ts +++ /dev/null @@ -1,406 +0,0 @@ -import { strict as assert } from "assert"; - -import { concat } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { ReserveToken18 } from "../../typechain"; -import { - AddOrderEvent, - AfterClearEvent, - ClearConfigStruct, - ClearEvent, - ClearStateChangeStruct, - DepositConfigStruct, - DepositEvent, - OrderConfigStruct, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { - eighteenZeros, - max_uint256, - ONE, -} from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs } from "../../utils/events"; -import { - generateEvaluableConfig, - memoryOperand, - MemoryType, - op, -} from "../../utils/interpreter/interpreter"; -import { AllStandardOps } from "../../utils/interpreter/ops/allStandardOps"; -import { fixedPointDiv, fixedPointMul, minBN } from "../../utils/math"; -import { - compareSolStructs, - compareStructs, -} from "../../utils/test/compareStructs"; - -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { encodeMeta } from "../../utils/orderBook/order"; - -const Opcode = AllStandardOps; - -describe("OrderBook counterparty in context", async function () { - const cCounterparty = op(Opcode.context, 0x0102); - - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should expose counterparty context to RainInterpreter calculations (e.g. Order_A will noop if Order_B counterparty does not match Carol's address)", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, carol, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const carolInputVault = ethers.BigNumber.from(randomUint256()); - const carolOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const outputMax_A = max_uint256; - const outputMaxIfNotMatchingCounterparty_A = 0; - - const constants_A = [ - outputMax_A, - outputMaxIfNotMatchingCounterparty_A, - ratio_A, - carol.address, - ]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aOpMaxIfNotMatch = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 2) - ); - const expectedCounterparty = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 3) - ); - - // prettier-ignore - const source_A = concat([ - cCounterparty, - expectedCounterparty, - op(Opcode.equal_to), - aOpMax, - aOpMaxIfNotMatch, - op(Opcode.eager_if), - aRatio, - ]); - const aliceOrder = encodeMeta("Order_A"); - - const EvaluableConfig_A = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const OrderConfig_A: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: aliceInputVault }, - ], - validOutputs: [ - { token: tokenB.address, decimals: 18, vaultId: aliceOutputVault }, - ], - evaluableConfig: EvaluableConfig_A, - meta: aliceOrder, - }; - - const txOrder_A = await orderBook.connect(alice).addOrder(OrderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txOrder_A, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - BAD MATCH - - const ratio_B = fixedPointDiv(ONE, ratio_A); - const constants_B = [max_uint256, ratio_B]; - const bOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const bRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_B = concat([ - bOpMax, - bRatio, - ]); - const bobOrder = encodeMeta("Order_B"); - const EvaluableConfig_B = await generateEvaluableConfig( - [source_B, []], - constants_B - ); - const OrderConfig_B: OrderConfigStruct = { - validInputs: [ - { token: tokenB.address, decimals: 18, vaultId: bobInputVault }, - ], - validOutputs: [ - { token: tokenA.address, decimals: 18, vaultId: bobOutputVault }, - ], - evaluableConfig: EvaluableConfig_B, - meta: bobOrder, - }; - - const txOrder_B = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txOrder_B, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // Order_B - GOOD MATCH - - const ratio_C = fixedPointDiv(ONE, ratio_A); - const constants_C = [max_uint256, ratio_C]; - const cOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const cRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_C = concat([ - cOpMax, - cRatio, - ]); - const carolOrder = encodeMeta("Order_C"); - const EvaluableConfig_C = await generateEvaluableConfig( - [source_C, []], - constants_C - ); - const OrderConfig_C: OrderConfigStruct = { - validInputs: [ - { token: tokenB.address, decimals: 18, vaultId: carolInputVault }, - ], - validOutputs: [ - { token: tokenA.address, decimals: 18, vaultId: carolOutputVault }, - ], - evaluableConfig: EvaluableConfig_C, - meta: carolOrder, - }; - - const txOrder_C = await orderBook.connect(carol).addOrder(OrderConfig_C); - - const { sender: sender_C, order: Order_C } = (await getEventArgs( - txOrder_C, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_C === carol.address, "wrong sender"); - compareStructs(Order_C, OrderConfig_C); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - await tokenA.transfer(carol.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - const depositConfigStructCarol: DepositConfigStruct = { - token: tokenA.address, - vaultId: carolOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - await tokenA - .connect(carol) - .approve(orderBook.address, depositConfigStructCarol.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - // Carol deposits tokenA into her output vault - const txDepositOrderCarol = await orderBook - .connect(carol) - .deposit(depositConfigStructCarol); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositCarolSender, config: depositCarolConfig } = - (await getEventArgs( - txDepositOrderCarol, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === bob.address); - compareStructs(depositBobConfig, depositConfigStructBob); - assert(depositCarolSender === carol.address); - compareStructs(depositCarolConfig, depositConfigStructCarol); - - // BOUNTY BOT CLEARS THE ORDER - BAD MATCH - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - const badClearOrder = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - - const { - sender: badClearSender, - alice: badClearA_, - bob: badClearB_, - clearConfig: badClearBountyConfig, - } = (await getEventArgs( - badClearOrder, - "Clear", - orderBook - )) as ClearEvent["args"]; - - const { - sender: badAfterClearSender, - clearStateChange: badClearStateChange, - } = (await getEventArgs( - badClearOrder, - "AfterClear", - orderBook - )) as AfterClearEvent["args"]; - - const expectedBadClearStateChange: ClearStateChangeStruct = { - aliceOutput: 0, - bobOutput: 0, - aliceInput: 0, - bobInput: 0, - }; - - assert(badAfterClearSender === bountyBot.address); - assert(badClearSender === bountyBot.address); - compareSolStructs(badClearA_, Order_A); - compareSolStructs(badClearB_, Order_B); - compareStructs(badClearBountyConfig, clearConfig); - compareStructs(badClearStateChange, expectedBadClearStateChange); - - // BOUNTY BOT CLEARS THE ORDER - GOOD MATCH - - const txClearOrder = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_C, clearConfig, [], []); - - const { - sender: clearSender, - alice: clearA_, - bob: clearB_, - clearConfig: clearBountyConfig, - } = (await getEventArgs( - txClearOrder, - "Clear", - orderBook - )) as ClearEvent["args"]; - const { sender: afterClearSender, clearStateChange: clearStateChange } = - (await getEventArgs( - txClearOrder, - "AfterClear", - orderBook - )) as AfterClearEvent["args"]; - - const aOutputMaxExpected = amountA; - const bOutputMaxExpected = amountB; - - const aOutputExpected = minBN( - aOutputMaxExpected, - fixedPointMul(ratio_B, amountA) - ); - const bOutputExpected = minBN( - bOutputMaxExpected, - fixedPointMul(ratio_A, amountB) - ); - - const expectedClearStateChange: ClearStateChangeStruct = { - aliceOutput: aOutputExpected, - bobOutput: bOutputExpected, - aliceInput: fixedPointMul(ratio_A, aOutputExpected), - bobInput: fixedPointMul(ratio_B, bOutputExpected), - }; - - assert(afterClearSender === bountyBot.address); - assert(clearSender === bountyBot.address); - compareSolStructs(clearA_, Order_A); - compareSolStructs(clearB_, Order_C); - compareStructs(clearBountyConfig, clearConfig); - compareStructs(clearStateChange, expectedClearStateChange); - }); -}); diff --git a/test/OrderBook/decimals.ts b/test/OrderBook/decimals.ts deleted file mode 100644 index fbdcf79f3..000000000 --- a/test/OrderBook/decimals.ts +++ /dev/null @@ -1,336 +0,0 @@ -import { concat } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { ReserveTokenDecimals } from "../../typechain"; -import { - AddOrderEvent, - ClearConfigStruct, - DepositConfigStruct, - OrderConfigStruct, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { - eighteenZeros, - max_uint256, - max_uint8, - ONE, -} from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs } from "../../utils/events"; -import { - generateEvaluableConfig, - memoryOperand, - MemoryType, - op, -} from "../../utils/interpreter/interpreter"; -import { AllStandardOps } from "../../utils/interpreter/ops/allStandardOps"; -import { fixedPointDiv } from "../../utils/math"; -import { assertError } from "../../utils/test/assertError"; - -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import { encodeMeta } from "../../utils/orderBook/order"; - -const Opcode = AllStandardOps; - -describe("OrderBook decimals", async function () { - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should not be able to provide OOB decimals beyond uint8", async function () { - const signers = await ethers.getSigners(); - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - 6, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - 18, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB18.initialize(); - - const decimalsInBounds = max_uint8; - const decimalsOutOfBounds = max_uint8.add(1); - // const tokenADecimals = await tokenA06.decimals(); - const tokenBDecimals = await tokenB18.decimals(); - - const [, alice] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - const outputMax_A = ethers.BigNumber.from(3 + eighteenZeros); - const constants_A = [outputMax_A, ratio_A]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_A = concat([ - aOpMax, - aRatio, - ]); - const EvaluableConfig_A0 = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - // IN BOUNDS - const OrderConfig_A0: OrderConfigStruct = { - validInputs: [ - { - token: tokenA06.address, - decimals: decimalsInBounds, - vaultId: aliceInputVault, - }, - ], - validOutputs: [ - { - token: tokenB18.address, - decimals: tokenBDecimals, - vaultId: aliceOutputVault, - }, - ], - evaluableConfig: EvaluableConfig_A0, - meta: encodeMeta(""), - }; - await orderBook.connect(alice).addOrder(OrderConfig_A0); - const EvaluableConfig_A1 = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - // OUT OF BOUNDS - const OrderConfig_A1: OrderConfigStruct = { - validInputs: [ - { - token: tokenA06.address, - decimals: decimalsOutOfBounds, - vaultId: aliceInputVault, - }, - ], - validOutputs: [ - { - token: tokenB18.address, - decimals: tokenBDecimals, - vaultId: aliceOutputVault, - }, - ], - evaluableConfig: EvaluableConfig_A1, - meta: encodeMeta(""), - }; - await assertError( - async () => await orderBook.connect(alice).addOrder(OrderConfig_A1), - "value out-of-bounds", - "did not revert with OOB error" - ); - }); - - it("ensure decimals can be read from context for _calculateOrderIO() and _recordVaultIO()", async function () { - const signers = await ethers.getSigners(); - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - 6, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - 18, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB18.initialize(); - - const tokenADecimals = await tokenA06.decimals(); - const tokenBDecimals = await tokenB18.decimals(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - // note 18 decimals for ratio - // 1e18 means that 1 unit of tokenA is equivalent to 1 unit of tokenB - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - - // note 18 decimals for outputMax - // 3e18 means that only 3 units of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(3 + eighteenZeros); - - const constants_A = [outputMax_A, ratio_A, tokenADecimals, tokenBDecimals]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - const vTokenADecimals = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 2) - ); - const vTokenBDecimals = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 3) - ); - // prettier-ignore - const source_A = concat([ - op(Opcode.context, 0x0301), // input decimals - vTokenADecimals, - op(Opcode.equal_to), - op(Opcode.ensure, 1), - - op(Opcode.context, 0x0401), // output decimals - vTokenBDecimals, - op(Opcode.equal_to), - op(Opcode.ensure, 1), - - aOpMax, - aRatio, - ]); - - const EvaluableConfig_A = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const OrderConfig_A: OrderConfigStruct = { - validInputs: [ - { - token: tokenA06.address, - decimals: tokenADecimals, - vaultId: aliceInputVault, - }, - ], - validOutputs: [ - { - token: tokenB18.address, - decimals: tokenBDecimals, - vaultId: aliceOutputVault, - }, - ], - evaluableConfig: EvaluableConfig_A, - meta: encodeMeta(""), - }; - - const txOrder_A = await orderBook.connect(alice).addOrder(OrderConfig_A); - - const { order: Order_A } = (await getEventArgs( - txOrder_A, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); // no need to account for decimals difference - - const constants_B = [max_uint256, ratio_B]; - const bOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const bRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_B = concat([ - bOpMax, - bRatio, - ]); - const EvaluableConfig_B = await generateEvaluableConfig( - [source_B, []], - constants_B - ); - const OrderConfig_B: OrderConfigStruct = { - validInputs: [ - { - token: tokenB18.address, - decimals: tokenBDecimals, - vaultId: bobInputVault, - }, - ], - validOutputs: [ - { - token: tokenA06.address, - decimals: tokenADecimals, - vaultId: bobOutputVault, - }, - ], - evaluableConfig: EvaluableConfig_B, - meta: encodeMeta(""), - }; - - const txOrder_B = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { order: Order_B } = (await getEventArgs( - txOrder_B, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + "0".repeat(tokenBDecimals)); - const amountA = ethers.BigNumber.from("1000" + "0".repeat(tokenADecimals)); - - await tokenB18.transfer(alice.address, amountB); - await tokenA06.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA06.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB18 - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA06 - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB18 into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenA06 into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, [], []); - }); -}); diff --git a/test/OrderBook/deposit.ts b/test/OrderBook/deposit.ts deleted file mode 100644 index 0b3c29166..000000000 --- a/test/OrderBook/deposit.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { strict as assert } from "assert"; -import { ethers } from "hardhat"; - -import type { ReserveToken18 } from "../../typechain"; -import { - DepositConfigStruct, - DepositEvent, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { eighteenZeros } from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs } from "../../utils/events"; -import { compareStructs } from "../../utils/test/compareStructs"; - -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../utils/deploy/registry1820/deploy"; - -describe("OrderBook vault deposit", async function () { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should allow deposits", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === bob.address); - compareStructs(depositBobConfig, depositConfigStructBob); - }); -}); diff --git a/test/OrderBook/expressions.ts b/test/OrderBook/expressions.ts deleted file mode 100644 index 9bdf5e5ab..000000000 --- a/test/OrderBook/expressions.ts +++ /dev/null @@ -1,2267 +0,0 @@ -import { strict as assert } from "assert"; -import { arrayify, solidityKeccak256 } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { ReserveToken18, ReserveTokenDecimals } from "../../typechain"; -import { - AddOrderEvent, - AfterClearEvent, - ClearConfigStruct, - ClearEvent, - ClearStateChangeStruct, - DepositConfigStruct, - DepositEvent, - OrderConfigStruct, - TakeOrderConfigStruct, - TakeOrderEvent, - TakeOrdersConfigStruct, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { - assertError, - fixedPointDiv, - fixedPointMul, - getCallerMetaForContract, - minBN, - randomUint256, -} from "../../utils"; -import { - eighteenZeros, - max_uint256, - ONE, - sixZeros, -} from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs } from "../../utils/events"; -import { - generateEvaluableConfig, - opMetaHash, - standardEvaluableConfig, -} from "../../utils/interpreter/interpreter"; -import { - compareSolStructs, - compareStructs, -} from "../../utils/test/compareStructs"; - -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import { encodeMeta } from "../../utils/orderBook/order"; -import { rainlang } from "../../utils/extensions/rainlang"; -import { SignedContextV1Struct } from "../../typechain/contracts/lobby/Lobby"; - -describe("OrderBook expression checks", async () => { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - const callerMetaHash = getCallerMetaForContract("orderbook"); - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should add Order_A and Order_B and clear the order with signed context", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - //Random Context Values - - const contextValA = randomUint256(); - const contextValB = randomUint256(); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const aliceOrder = encodeMeta("Order_A"); - - const { sources: orderConfigSourceA, constants: ordeConfigConstantsA } = - await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* source */ - : ensure(equal-to(${bob.address} context<5 0>())), - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - : ensure(equal-to(${bob.address} context<5 0>())); - : ensure(equal-to(${contextValB} context<6 0>())); - ` - ); - - const evaluableConfigA = await generateEvaluableConfig( - orderConfigSourceA, - ordeConfigConstantsA - ); - const OrderConfig_A: OrderConfigStruct = { - validInputs: [ - { - token: tokenA.address, - decimals: 18, - vaultId: aliceInputVault, - }, - ], - validOutputs: [ - { - token: tokenB.address, - decimals: 18, - vaultId: aliceOutputVault, - }, - ], - evaluableConfig: evaluableConfigA, - meta: aliceOrder, - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - - const bobOrder = encodeMeta("Order_B"); - - const { sources: orderConfigSourceB, constants: orderConfigConstantsB } = - await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* source */ - : ensure(equal-to(${alice.address} context<5 0>())), - _: ${max_uint256}, - _: ${ratio_B}; - - /* HANDLE IO */ - : ensure(equal-to(${alice.address} context<5 0>())); - : ensure(equal-to(${contextValA} context<6 0>())); - ` - ); - - const evaluableConfigB = await generateEvaluableConfig( - orderConfigSourceB, - orderConfigConstantsB - ); - - const OrderConfig_B: OrderConfigStruct = { - validInputs: [ - { - token: tokenB.address, - decimals: 18, - vaultId: bobInputVault, - }, - ], - validOutputs: [ - { - token: tokenA.address, - decimals: 18, - vaultId: bobOutputVault, - }, - ], - evaluableConfig: evaluableConfigB, - meta: bobOrder, - }; - - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // DEPOSITS - - const amountB = ethers.BigNumber.from("1000" + eighteenZeros); - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenB.transfer(alice.address, amountB); - await tokenA.transfer(bob.address, amountA); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobOutputVault, - amount: amountA, - }; - - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - // Bob deposits tokenA into his output vault - const txDepositOrderBob = await orderBook - .connect(bob) - .deposit(depositConfigStructBob); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - const { sender: depositBobSender, config: depositBobConfig } = - (await getEventArgs( - txDepositOrderBob, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - assert(depositBobSender === bob.address); - compareStructs(depositBobConfig, depositConfigStructBob); - - // BOUNTY BOT CLEARS THE ORDER - - const clearConfig: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - - //Building Signed Context A - const contextA = [contextValA]; - const hashA = solidityKeccak256(["uint256[]"], [contextA]); - const goodSignatureA = await alice.signMessage(arrayify(hashA)); - - const signedContextsA: SignedContextV1Struct[] = [ - { - signer: alice.address, - signature: goodSignatureA, - context: contextA, - }, - ]; - - //Building Signed Context B - const contextB = [contextValB]; - const hashB = solidityKeccak256(["uint256[]"], [contextB]); - const goodSignatureB = await bob.signMessage(arrayify(hashB)); - - const signedContextsB: SignedContextV1Struct[] = [ - { - signer: bob.address, - signature: goodSignatureB, - context: contextB, - }, - ]; - - const txClearOrder = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig, signedContextsA, signedContextsB); - - const { - sender: clearSender, - alice: clearA_, - bob: clearB_, - clearConfig: clearBountyConfig, - } = (await getEventArgs( - txClearOrder, - "Clear", - orderBook - )) as ClearEvent["args"]; - const { sender: afterClearSender, clearStateChange: clearStateChange } = - (await getEventArgs( - txClearOrder, - "AfterClear", - orderBook - )) as AfterClearEvent["args"]; - - const aOutputMaxExpected = amountA; - const bOutputMaxExpected = amountB; - - const aOutputExpected = minBN( - aOutputMaxExpected, - fixedPointMul(ratio_B, amountA) - ); - const bOutputExpected = minBN( - bOutputMaxExpected, - fixedPointMul(ratio_A, amountB) - ); - - const expectedClearStateChange: ClearStateChangeStruct = { - aliceOutput: aOutputExpected, - bobOutput: bOutputExpected, - aliceInput: fixedPointMul(ratio_A, aOutputExpected), - bobInput: fixedPointMul(ratio_B, bOutputExpected), - }; - - assert(afterClearSender === bountyBot.address); - assert(clearSender === bountyBot.address); - compareSolStructs(clearA_, Order_A); - compareSolStructs(clearB_, Order_B); - compareStructs(clearBountyConfig, clearConfig); - compareStructs(clearStateChange, expectedClearStateChange); - }); - - it("should ensure signed context is visible in calculateIO and handleIO for takeOrder", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - // Random Context Value - const contextVal1 = randomUint256(); - const contextVal2 = randomUint256(); - - const depositAmountA = ethers.BigNumber.from(1 + eighteenZeros); - - const depositConfigStructAliceTokenA18: DepositConfigStruct = { - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }; - - await tokenA18.transfer(alice.address, depositAmountA); - - await tokenA18.connect(alice).approve(orderBook.address, depositAmountA); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenA18); - - const depositAmountB = ethers.BigNumber.from(1 + sixZeros); - - const depositConfigStructAliceTokenB06: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenB06); - - // ORDERS - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - order-taker : ${bob.address} , - context-val-1 : ${contextVal1} , - context-val-2 : ${contextVal2} , - - - : ensure(equal-to(order-taker context<5 0>())), - : ensure(equal-to(context-val-1 context<6 0>())), - : ensure(equal-to(context-val-2 context<6 1>())), - - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - order-taker : ${bob.address} , - context-val-1 : ${contextVal1} , - context-val-2 : ${contextVal2} , - - : ensure(equal-to(order-taker context<5 0>())), - : ensure(equal-to(context-val-1 context<6 0>())), - : ensure(equal-to(context-val-2 context<6 1>())); - ` - ); - - // prettier-ignore - - const EvaluableConfigAlice = await generateEvaluableConfig( - sources, - constants - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // TAKE ORDER BOB - - const context1 = [contextVal1, contextVal2]; - const hash1 = solidityKeccak256(["uint256[]"], [context1]); - const goodSignature1 = await bob.signMessage(arrayify(hash1)); - - const signedContexts1: SignedContextV1Struct[] = [ - { - signer: bob.address, - signature: goodSignature1, - context: context1, - }, - ]; - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: signedContexts1, - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA.mul(2), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.mul(2))); - }); - - it("should ensure order sender and contract address are visible in calculateIO and handleIO", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - const depositAmountA = ethers.BigNumber.from(1 + eighteenZeros); - - const depositConfigStructAliceTokenA18: DepositConfigStruct = { - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }; - - await tokenA18.transfer(alice.address, depositAmountA); - - await tokenA18.connect(alice).approve(orderBook.address, depositAmountA); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenA18); - - const depositAmountB = ethers.BigNumber.from(1 + sixZeros); - - const depositConfigStructAliceTokenB06: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenB06); - - // ORDERS - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - expected-sender : ${bob.address} , - orderbook : ${orderBook.address} , - - : ensure(equal-to(expected-sender context<0 0>())), - : ensure(equal-to(orderbook context<0 1>())), - : ensure(equal-to(expected-sender orderbook-caller-address())), - : ensure(equal-to(orderbook orderbook-contract-address())), - - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - expected-sender : ${bob.address} , - orderbook : ${orderBook.address} , - - : ensure(equal-to(expected-sender context<0 0>())), - : ensure(equal-to(orderbook context<0 1>())), - : ensure(equal-to(expected-sender orderbook-caller-address())), - : ensure(equal-to(orderbook orderbook-contract-address())); - ` - ); - - // prettier-ignore - - const EvaluableConfigAlice = await generateEvaluableConfig( - sources, - constants - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA.mul(2), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.mul(2))); - }); - - it("should ensure OWNER and COUNTERPARTY are visible in calculateIO and handleIO", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - const aliceVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - expected-owner : ${alice.address} , - expected-counterparty : ${bob.address} , - - : ensure(equal-to(expected-owner context<1 1>())), - : ensure(equal-to(expected-counterparty context<1 2>())), - : ensure(equal-to(expected-owner order-owner-address())), - : ensure(equal-to(expected-counterparty counterparty-address())), - - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - expected-owner : ${alice.address} , - expected-counterparty : ${bob.address} , - - : ensure(equal-to(expected-owner context<1 1>())), - : ensure(equal-to(expected-counterparty context<1 2>())), - : ensure(equal-to(expected-owner order-owner-address())), - : ensure(equal-to(expected-counterparty counterparty-address())); - ` - ); - - const EvaluableConfigAlice = await generateEvaluableConfig( - sources, - constants - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(1 + sixZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - const depositAmountA = ethers.BigNumber.from(1 + eighteenZeros); - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should ensure balance before is visible in calculateIO and handleIO", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - const depositAmountA = ethers.BigNumber.from(1 + eighteenZeros); - - const depositConfigStructAliceTokenA18: DepositConfigStruct = { - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }; - - await tokenA18.transfer(alice.address, depositAmountA); - - await tokenA18.connect(alice).approve(orderBook.address, depositAmountA); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenA18); - - const depositAmountB = ethers.BigNumber.from(1 + sixZeros); - - const depositConfigStructAliceTokenB06: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenB06); - - // ORDERS - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - expected-input-token-balance : ${depositAmountA} , - expected-output-token-balance : ${depositAmountB} , - - : ensure(equal-to(expected-input-token-balance context<3 3>())), - : ensure(equal-to(expected-output-token-balance context<4 3>())), - : ensure(equal-to(expected-input-token-balance vault-input-balance-before())), - : ensure(equal-to(expected-output-token-balance vault-output-balance-before())), - - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - expected-input-token-balance : ${depositAmountA} , - expected-output-token-balance : ${depositAmountB} , - - : ensure(equal-to(expected-input-token-balance context<3 3>())), - : ensure(equal-to(expected-output-token-balance context<4 3>())), - : ensure(equal-to(expected-input-token-balance vault-input-balance-before())), - : ensure(equal-to(expected-output-token-balance vault-output-balance-before())); - ` - ); - - const EvaluableConfigAlice = await generateEvaluableConfig( - sources, - constants - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA.mul(2), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.mul(2))); - }); - - it("should ensure vault id is visible in calculateIO and handleIO", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - const depositAmountA = ethers.BigNumber.from(1 + eighteenZeros); - - const depositAmountB = ethers.BigNumber.from(1 + sixZeros); - - const depositConfigStructAliceTokenB06: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenB06); - - // ORDERS - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - expected-vault-id : ${aliceVault} , - - : ensure(equal-to(expected-vault-id context<3 2>())), - : ensure(equal-to(expected-vault-id context<4 2>())), - : ensure(equal-to(expected-vault-id vault-input-id())), - : ensure(equal-to(expected-vault-id vault-output-id())), - - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - expected-vault-id : ${aliceVault} , - - : ensure(equal-to(expected-vault-id context<3 2>())), - : ensure(equal-to(expected-vault-id context<4 2>())), - : ensure(equal-to(expected-vault-id vault-input-id())), - : ensure(equal-to(expected-vault-id vault-output-id())); - ` - ); - - const EvaluableConfigAlice = await generateEvaluableConfig( - sources, - constants - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should ensure tokens are visible in calculateIO and handleIO", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - const depositAmountA = ethers.BigNumber.from(1 + eighteenZeros); - - const depositAmountB = ethers.BigNumber.from(1 + sixZeros); - - const depositConfigStructAliceTokenB06: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenB06); - - // ORDERS - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - expected-input-token : ${tokenA18.address} , - expected-output-token : ${tokenB06.address} , - - : ensure(equal-to(expected-input-token context<3 0>())), - : ensure(equal-to(expected-output-token context<4 0>())), - : ensure(equal-to(expected-input-token vault-input-token-address())), - : ensure(equal-to(expected-output-token vault-output-token-address())), - - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - expected-input-token : ${tokenA18.address} , - expected-output-token : ${tokenB06.address} , - - : ensure(equal-to(expected-input-token context<3 0>())), - : ensure(equal-to(expected-output-token context<4 0>())), - : ensure(equal-to(expected-input-token vault-input-token-address())), - : ensure(equal-to(expected-output-token vault-output-token-address())); - ` - ); - - const EvaluableConfigAlice = await generateEvaluableConfig( - sources, - constants - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should ensure decimals are visible in calculateIO and handleIO", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - const depositAmountA = ethers.BigNumber.from(1 + eighteenZeros); - - const depositAmountB = ethers.BigNumber.from(1 + sixZeros); - - const depositConfigStructAliceTokenB06: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenB06); - - // ORDERS - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - expected-input-token-deciamls : ${tokenADecimals} , - expected-output-token-deciamls : ${tokenBDecimals} , - - : ensure(equal-to(expected-input-token-deciamls context<3 1>())), - : ensure(equal-to(expected-output-token-deciamls context<4 1>())), - : ensure(equal-to(expected-input-token-deciamls vault-input-token-decimals())), - : ensure(equal-to(expected-output-token-deciamls vault-output-token-decimals())), - - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - expected-input-token-deciamls : ${tokenADecimals} , - expected-output-token-deciamls : ${tokenBDecimals} , - - : ensure(equal-to(expected-input-token-deciamls context<3 1>())), - : ensure(equal-to(expected-output-token-deciamls context<4 1>())), - : ensure(equal-to(expected-input-token-deciamls vault-input-token-decimals())), - : ensure(equal-to(expected-output-token-deciamls vault-output-token-decimals())); - ` - ); - - const EvaluableConfigAlice = await generateEvaluableConfig( - sources, - constants - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should ensure SET in calculateIO is visible in GET in handleIO", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - const depositAmountA = ethers.BigNumber.from(1 + eighteenZeros); - - const depositAmountB = ethers.BigNumber.from(1 + sixZeros); - - const depositConfigStructAliceTokenB06: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - await orderBook.connect(alice).deposit(depositConfigStructAliceTokenB06); - - // ORDERS - - const ratio_A = ethers.BigNumber.from(1 + eighteenZeros); - const key1 = ethers.BigNumber.from(randomUint256()); - const key2 = ethers.BigNumber.from(randomUint256()); - const key3 = ethers.BigNumber.from(randomUint256()); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - compare-key : ${key1} , - input-token-key : ${key2} , - output-token-key : ${key3} , - - :set(compare-key greater-than(vault-input-token-decimals() vault-output-token-decimals())) , - :set(input-token-key vault-input-token-decimals()) , - :set(output-token-key vault-output-token-decimals()) , - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - compare-key : ${key1} , - input-token-key : ${key2} , - output-token-key : ${key3} , - - expected-input-token-decimals : ${tokenADecimals} , - expected-output-token-decimals : ${tokenBDecimals} , - - : ensure(equal-to(get(compare-key) 1)), - : ensure(equal-to(get(input-token-key) expected-input-token-decimals)), - : ensure(equal-to(get(output-token-key) expected-output-token-decimals)); - - ` - ); - - const EvaluableConfigAlice = await generateEvaluableConfig( - sources, - constants - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should ensure balance diff is zero in calculateIO and actual value in handleIO", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const aip = minBN(amountB, minBN(max_uint256, amountB)); // minimum of remainingInput and outputMax - const aop = fixedPointMul(aip, ratio_A); - - const aliceOrder = encodeMeta("aliceOrder"); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - - : ensure(equal-to(0 context<3 4>())), - : ensure(equal-to(0 context<4 4>())), - : ensure(equal-to(0 vault-input-balance-increase())), - : ensure(equal-to(0 vault-output-balance-decrease())), - - - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - expected-input-diff : ${aip} , - expected-output-diff : ${aop} , - - : ensure(equal-to(expected-output-diff context<3 4>())), - : ensure(equal-to(expected-input-diff context<4 4>())), - : ensure(equal-to(expected-output-diff vault-input-balance-increase())), - : ensure(equal-to(expected-input-diff vault-output-balance-decrease())); - ` - ); - - const EvaluableConfig = await generateEvaluableConfig(sources, constants); - - const OrderConfig: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: aliceInputVault }, - ], - validOutputs: [ - { token: tokenB.address, decimals: 18, vaultId: aliceOutputVault }, - ], - evaluableConfig: EvaluableConfig, - meta: aliceOrder, - }; - - const txAddOrder = await orderBook.connect(alice).addOrder(OrderConfig); - - const { order: Order_A } = (await getEventArgs( - txAddOrder, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - - // TAKE ORDER - - // Bob takes order with direct wallet transfer - const takeOrderConfigStruct: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB, - maximumInput: amountB, - maximumIORatio: ratio_A, - orders: [takeOrderConfigStruct], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(bob.address, amountA); - await tokenA.connect(bob).approve(orderBook.address, amountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(amountB), "wrong input"); - assert(output.eq(amountA), "wrong output"); - - compareStructs(config, takeOrderConfigStruct); - - const tokenAAliceBalance = await tokenA.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB.balanceOf(alice.address); - const tokenABobBalance = await tokenA.balanceOf(bob.address); - const tokenBBobBalance = await tokenB.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(amountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA.address, - vaultId: aliceInputVault, - amount: amountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(amountA)); - }); - - it("should ensure accessing calcualtions context during calculation reverts", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const aliceOrder = encodeMeta("aliceOrder"); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - output-max : order-output-max(), - ratio : order-io-ratio(), - - _: ${max_uint256}, - _: ${ratio_A}; - ` - ); - - const EvaluableConfig = await generateEvaluableConfig( - [sources[0], []], - constants - ); - - const OrderConfig: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: aliceInputVault }, - ], - validOutputs: [ - { token: tokenB.address, decimals: 18, vaultId: aliceOutputVault }, - ], - evaluableConfig: EvaluableConfig, - meta: aliceOrder, - }; - - const txAddOrder = await orderBook.connect(alice).addOrder(OrderConfig); - - const { order: Order_A } = (await getEventArgs( - txAddOrder, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - - // TAKE ORDER - - // Bob takes order with direct wallet transfer - const takeOrderConfigStruct: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB, - maximumInput: amountB, - maximumIORatio: ratio_A, - orders: [takeOrderConfigStruct], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(bob.address, amountA); - await tokenA.connect(bob).approve(orderBook.address, amountA); - - await assertError( - async () => - await orderBook.connect(bob).takeOrders(takeOrdersConfigStruct), - "VM Exception while processing transaction: reverted with panic code 0x32 (Array accessed at an out-of-bounds or negative index)", - "Accessed Calculations Context in Calculations" - ); - }); - - it("should sacle ratio according to decimal difference and scale outputMax to token deciamls and cap it to the vault balance of the owner", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - - const { sources, constants } = await standardEvaluableConfig( - rainlang` - /* meta hash */ - @${opMetaHash} - @${callerMetaHash} - - /* CalculateIO source */ - _: ${max_uint256}, - _: ${ratio_A}; - - /* HANDLE IO */ - expected-max-output : ${depositAmountB} , - expected-ratio : ${maximumIORatio} , - - : ensure(equal-to(expected-max-output context<2 0>())), - : ensure(equal-to(expected-ratio context<2 1>())), - : ensure(equal-to(expected-max-output order-output-max())), - : ensure(equal-to(expected-ratio order-io-ratio())); - ` - ); - - const EvaluableConfig = await generateEvaluableConfig(sources, constants); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceInputVault, - }, - ], - validOutputs: [ - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceOutputVault, - }, - ], - evaluableConfig: EvaluableConfig, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - - // TAKE ORDER - - // Bob takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice], - }; - - // We want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA18.transfer(bob.address, depositAmountA); // 2 orders - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); // 2 orders - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructAlice); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - }); -}); diff --git a/test/OrderBook/manyToMany.ts b/test/OrderBook/manyToMany.ts deleted file mode 100644 index 961e9f129..000000000 --- a/test/OrderBook/manyToMany.ts +++ /dev/null @@ -1,509 +0,0 @@ -import { strict as assert } from "assert"; - -import { concat } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { ReserveToken18 } from "../../typechain"; -import { - AddOrderEvent, - AfterClearEvent, - ClearConfigStruct, - ClearEvent, - ClearStateChangeStruct, - DepositConfigStruct, - OrderConfigStruct, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { - eighteenZeros, - max_uint256, - ONE, - sixteenZeros, -} from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs } from "../../utils/events"; -import { - generateEvaluableConfig, - memoryOperand, - MemoryType, - op, -} from "../../utils/interpreter/interpreter"; -import { AllStandardOps } from "../../utils/interpreter/ops/allStandardOps"; -import { fixedPointDiv, fixedPointMul, minBN } from "../../utils/math"; -import { - compareSolStructs, - compareStructs, -} from "../../utils/test/compareStructs"; -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import { encodeMeta } from "../../utils/orderBook/order"; - -const Opcode = AllStandardOps; - -describe("OrderBook many-to-many", async function () { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - let tokenC: ReserveToken18; - let tokenD: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenC = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenD = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - await tokenC.initialize(); - await tokenD.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should support a 'slosh' many-to-many orders setup", async function () { - const signers = await ethers.getSigners(); - - const [, alice] = signers; - - const orderBook = await deployOrderBook(); - - const vaultAlice = ethers.BigNumber.from(randomUint256()); - - const threshold = ethers.BigNumber.from(102 + sixteenZeros); // 2% - - const constants = [max_uint256, threshold]; - - const vMaxAmount = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const vThreshold = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - - // prettier-ignore - const source = concat([ - vMaxAmount, - vThreshold, - ]); - const aliceOrder = encodeMeta("aliceOrder"); - - const evaluableConfig = await generateEvaluableConfig( - [source, []], - constants - ); - - const orderConfig: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: vaultAlice }, - { token: tokenB.address, decimals: 18, vaultId: vaultAlice }, - { token: tokenA.address, decimals: 18, vaultId: vaultAlice }, - { token: tokenD.address, decimals: 18, vaultId: vaultAlice }, - ], - validOutputs: [ - { token: tokenA.address, decimals: 18, vaultId: vaultAlice }, - { token: tokenB.address, decimals: 18, vaultId: vaultAlice }, - { token: tokenC.address, decimals: 18, vaultId: vaultAlice }, - { token: tokenD.address, decimals: 18, vaultId: vaultAlice }, - ], - evaluableConfig: evaluableConfig, - meta: aliceOrder, - }; - - const _txAddOrder = await orderBook.connect(alice).addOrder(orderConfig); - }); - - it("should add many orders and clear the orders", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, bountyBot] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultA = ethers.BigNumber.from(randomUint256()); - const bountyBotVaultB = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const constants_A = [max_uint256, ratio_A]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_A = concat([ - aOpMax, - aRatio, - ]); - const aliceOrder = encodeMeta("Order_A"); - - const EvaluableConfig_A = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const OrderConfig_A: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: aliceInputVault }, - { token: tokenC.address, decimals: 18, vaultId: aliceInputVault }, - ], - validOutputs: [ - { token: tokenB.address, decimals: 18, vaultId: aliceOutputVault }, - { token: tokenD.address, decimals: 18, vaultId: aliceOutputVault }, - ], - evaluableConfig: EvaluableConfig_A, - meta: aliceOrder, - }; - - const txOrder_A = await orderBook.connect(alice).addOrder(OrderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txOrder_A, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - const constants_B = [max_uint256, ratio_B]; - const bOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const bRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_B = concat([ - bOpMax, - bRatio, - ]); - const bobOrder = encodeMeta("Order_B"); - - const EvaluableConfig_B = await generateEvaluableConfig( - [source_B, []], - constants_B - ); - - const OrderConfig_B: OrderConfigStruct = { - validInputs: [ - { token: tokenB.address, decimals: 18, vaultId: bobOutputVault }, - { token: tokenD.address, decimals: 18, vaultId: bobOutputVault }, - ], - validOutputs: [ - { token: tokenA.address, decimals: 18, vaultId: bobInputVault }, - { token: tokenC.address, decimals: 18, vaultId: bobInputVault }, - ], - evaluableConfig: EvaluableConfig_B, - meta: bobOrder, - }; - - const txOrder_B = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txOrder_B, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - - // DEPOSITS - - const amount = ethers.BigNumber.from("1000" + eighteenZeros); - - await tokenA.transfer(bob.address, amount); - await tokenB.transfer(alice.address, amount); - await tokenC.transfer(bob.address, amount); - await tokenD.transfer(alice.address, amount); - - const depositConfigStructBobA: DepositConfigStruct = { - token: tokenA.address, - vaultId: bobInputVault, - amount, - }; - const depositConfigStructAliceB: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount, - }; - const depositConfigStructBobC: DepositConfigStruct = { - token: tokenC.address, - vaultId: bobInputVault, - amount, - }; - const depositConfigStructAliceD: DepositConfigStruct = { - token: tokenD.address, - vaultId: aliceOutputVault, - amount, - }; - - await tokenA - .connect(bob) - .approve(orderBook.address, depositConfigStructBobA.amount); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAliceB.amount); - await tokenC - .connect(bob) - .approve(orderBook.address, depositConfigStructBobC.amount); - await tokenD - .connect(alice) - .approve(orderBook.address, depositConfigStructAliceD.amount); - - // Alice deposits tokens into her output vault - const _txDepositOrderAliceB = await orderBook - .connect(alice) - .deposit(depositConfigStructAliceB); - const _txDepositOrderAliceD = await orderBook - .connect(alice) - .deposit(depositConfigStructAliceD); - - // Bob deposits tokens into his output vault - const _txDepositOrderBobA = await orderBook - .connect(bob) - .deposit(depositConfigStructBobA); - const _txDepositOrderBobC = await orderBook - .connect(bob) - .deposit(depositConfigStructBobC); - - // BOUNTY BOT CLEARS THE ORDERS - - const clearConfig0: ClearConfigStruct = { - aliceInputIOIndex: 0, - aliceOutputIOIndex: 0, - bobInputIOIndex: 0, - bobOutputIOIndex: 0, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - const txClearOrder0 = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig0, [], []); - - const { - sender: clearSender0, - alice: clearA_, - bob: clearB_, - clearConfig: clearBountyConfig0, - } = (await getEventArgs( - txClearOrder0, - "Clear", - orderBook - )) as ClearEvent["args"]; - const { sender: afterClearSender0, clearStateChange: clearStateChange0 } = - (await getEventArgs( - txClearOrder0, - "AfterClear", - orderBook - )) as AfterClearEvent["args"]; - - const aOutputMaxExpected0 = amount; - const bOutputMaxExpected0 = amount; - - const aOutputExpected0 = minBN( - aOutputMaxExpected0, - fixedPointMul(ratio_B, amount) - ); - const bOutputExpected0 = minBN( - bOutputMaxExpected0, - fixedPointMul(ratio_A, amount) - ); - - const expectedClearStateChange0: ClearStateChangeStruct = { - aliceOutput: aOutputExpected0, - bobOutput: bOutputExpected0, - aliceInput: fixedPointMul(ratio_A, aOutputExpected0), - bobInput: fixedPointMul(ratio_B, bOutputExpected0), - }; - - assert(afterClearSender0 === bountyBot.address); - assert(clearSender0 === bountyBot.address); - compareSolStructs(clearA_, Order_A); - compareSolStructs(clearB_, Order_B); - compareStructs(clearBountyConfig0, clearConfig0); - compareStructs(clearStateChange0, expectedClearStateChange0); - - const clearConfig1: ClearConfigStruct = { - aliceInputIOIndex: 1, - aliceOutputIOIndex: 1, - bobInputIOIndex: 1, - bobOutputIOIndex: 1, - aliceBountyVaultId: bountyBotVaultA, - bobBountyVaultId: bountyBotVaultB, - }; - const txClearOrder1 = await orderBook - .connect(bountyBot) - .clear(Order_A, Order_B, clearConfig1, [], []); - - const { - sender: clearSender1, - alice: clearC_, - bob: clearD_, - clearConfig: clearBountyConfig1, - } = (await getEventArgs( - txClearOrder1, - "Clear", - orderBook - )) as ClearEvent["args"]; - const { sender: afterClearSender1, clearStateChange: clearStateChange1 } = - (await getEventArgs( - txClearOrder1, - "AfterClear", - orderBook - )) as AfterClearEvent["args"]; - - const cOutputMaxExpected1 = amount; - const dOutputMaxExpected1 = amount; - - const cOutputExpected1 = minBN( - cOutputMaxExpected1, - fixedPointMul(ratio_B, amount) - ); - const dOutputExpected1 = minBN( - dOutputMaxExpected1, - fixedPointMul(ratio_A, amount) - ); - - const expectedClearStateChange1: ClearStateChangeStruct = { - aliceOutput: cOutputExpected1, - bobOutput: dOutputExpected1, - aliceInput: fixedPointMul(ratio_A, cOutputExpected1), - bobInput: fixedPointMul(ratio_B, dOutputExpected1), - }; - - assert(afterClearSender1 === bountyBot.address); - assert(clearSender1 === bountyBot.address); - compareSolStructs(clearC_, Order_A); - compareSolStructs(clearD_, Order_B); - compareStructs(clearBountyConfig1, clearConfig1); - compareStructs(clearStateChange1, expectedClearStateChange1); - }); - - it("should add many-to-many orders", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVaultA = ethers.BigNumber.from(randomUint256()); - const aliceVaultB = ethers.BigNumber.from(randomUint256()); - const bobVaultB = ethers.BigNumber.from(randomUint256()); - const bobVaultA = ethers.BigNumber.from(randomUint256()); - - // Order_A - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const constants_A = [max_uint256, ratio_A]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_A = concat([ - aOpMax, - aRatio, - ]); - const aliceOrder = encodeMeta("Order_A"); - - const EvaluableConfig_A = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const OrderConfig_A: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: aliceVaultA }, - { token: tokenB.address, decimals: 18, vaultId: aliceVaultB }, - ], - validOutputs: [ - { token: tokenB.address, decimals: 18, vaultId: aliceVaultB }, - { token: tokenA.address, decimals: 18, vaultId: aliceVaultA }, - ], - evaluableConfig: EvaluableConfig_A, - meta: aliceOrder, - }; - - const txOrder_A = await orderBook.connect(alice).addOrder(OrderConfig_A); - - const { sender: sender_A, order: Order_A } = (await getEventArgs( - txOrder_A, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_A === alice.address, "wrong sender"); - compareStructs(Order_A, OrderConfig_A); - - // Order_B - - const ratio_B = fixedPointDiv(ONE, ratio_A); - const constants_B = [max_uint256, ratio_B]; - const bOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const bRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_B = concat([ - bOpMax, - bRatio, - ]); - const bobOrder = encodeMeta("Order_B"); - - const EvaluableConfig_B = await generateEvaluableConfig( - [source_B, []], - constants_B - ); - - const OrderConfig_B: OrderConfigStruct = { - validInputs: [ - { token: tokenB.address, decimals: 18, vaultId: bobVaultB }, - { token: tokenA.address, decimals: 18, vaultId: bobVaultA }, - ], - validOutputs: [ - { token: tokenA.address, decimals: 18, vaultId: bobVaultA }, - { token: tokenB.address, decimals: 18, vaultId: bobVaultB }, - ], - evaluableConfig: EvaluableConfig_B, - meta: bobOrder, - }; - - const txOrder_B = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { sender: sender_B, order: Order_B } = (await getEventArgs( - txOrder_B, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(sender_B === bob.address, "wrong sender"); - compareStructs(Order_B, OrderConfig_B); - }); -}); diff --git a/test/OrderBook/removeOrder.ts b/test/OrderBook/removeOrder.ts deleted file mode 100644 index 707026f39..000000000 --- a/test/OrderBook/removeOrder.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { strict as assert } from "assert"; - -import { concat } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { ReserveToken18 } from "../../typechain"; -import { - AddOrderEvent, - OrderConfigStruct, - RemoveOrderEvent, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { eighteenZeros, max_uint256 } from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; - -import { getEventArgs } from "../../utils/events"; -import { - generateEvaluableConfig, - memoryOperand, - MemoryType, - op, -} from "../../utils/interpreter/interpreter"; -import { AllStandardOps } from "../../utils/interpreter/ops/allStandardOps"; -import { assertError } from "../../utils/test/assertError"; -import { compareStructs } from "../../utils/test/compareStructs"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { encodeMeta } from "../../utils/orderBook/order"; - -const Opcode = AllStandardOps; - -describe("OrderBook remove order", async function () { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should support removing orders", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const constants_A = [max_uint256, ratio_A]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_A = concat([ - aOpMax, - aRatio, - ]); - const aliceOrder = encodeMeta("Order_A"); - - const EvaluableConfig_A = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const OrderConfig_A: OrderConfigStruct = { - validInputs: [ - { token: tokenA.address, decimals: 18, vaultId: aliceInputVault }, - ], - validOutputs: [ - { token: tokenB.address, decimals: 18, vaultId: aliceOutputVault }, - ], - evaluableConfig: EvaluableConfig_A, - meta: aliceOrder, - }; - - const txOrder_A = await orderBook.connect(alice).addOrder(OrderConfig_A); - - const { sender: liveSender_A, order: LiveOrder_A } = (await getEventArgs( - txOrder_A, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - assert(liveSender_A === alice.address, "wrong sender"); - compareStructs(LiveOrder_A, OrderConfig_A); - - // REMOVE Order_A - - await assertError( - async () => await orderBook.connect(bob).removeOrder(LiveOrder_A), - `NotOrderOwner("${bob.address}", "${alice.address}")`, - "bob wrongly removed alice's order" - ); - - const txRemoveOrder = await orderBook - .connect(alice) - .removeOrder(LiveOrder_A); - - const { sender: deadSender_A, order: DeadOrder_A } = (await getEventArgs( - txRemoveOrder, - "RemoveOrder", - orderBook - )) as RemoveOrderEvent["args"]; - - assert(deadSender_A === alice.address, "wrong sender"); - compareStructs(DeadOrder_A, OrderConfig_A); - }); -}); diff --git a/test/OrderBook/takeOrders.ts b/test/OrderBook/takeOrders.ts deleted file mode 100644 index d1a06a4fe..000000000 --- a/test/OrderBook/takeOrders.ts +++ /dev/null @@ -1,4887 +0,0 @@ -import { strict as assert } from "assert"; -import { concat } from "ethers/lib/utils"; -import { ethers } from "hardhat"; -import type { ReserveToken18, ReserveTokenDecimals } from "../../typechain"; -import { - AddOrderEvent, - ContextEvent, - DepositConfigStruct, - DepositEvent, - OrderConfigStruct, - OrderExceedsMaxRatioEvent, - OrderNotFoundEvent, - OrderZeroAmountEvent, - TakeOrderConfigStruct, - TakeOrderEvent, - TakeOrdersConfigStruct, -} from "../../typechain/contracts/orderbook/OrderBook"; -import { - RainterpreterOps, - assertError, - fixedPointMul, - randomUint256, - minBN, -} from "../../utils"; -import { - eighteenZeros, - max_uint256, - ONE, - sixteenZeros, - sixZeros, - tenZeros, - twentyZeros, -} from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { getEventArgs, getEvents } from "../../utils/events"; -import { - generateEvaluableConfig, - memoryOperand, - MemoryType, - op, -} from "../../utils/interpreter/interpreter"; -import { encodeMeta, getOrderConfig } from "../../utils/orderBook/order"; -import { compareStructs } from "../../utils/test/compareStructs"; - -import deploy1820 from "../../utils/deploy/registry1820/deploy"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; - -const Opcode = RainterpreterOps; - -describe("OrderBook take orders", async function () { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should respect output maximum of a given order", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const outputMax_A = amountB.sub(1); // will only sell 999 tokenBs to each buyer - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const aliceOrder = encodeMeta("Order_A"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const bobOrder = encodeMeta("Order_B"); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA.address, - 18, - bobInputVault, - tokenB.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB.address, - vaultId: bobOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB.transfer(bob.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenB - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB.mul(2), - maximumInput: amountB.mul(2), - maximumIORatio: ratio_A, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(carol.address, amountA.mul(2)); - await tokenA.connect(carol).approve(orderBook.address, amountA.mul(2)); - - await assertError( - async () => - await orderBook.connect(carol).takeOrders(takeOrdersConfigStruct), - `MinimumInput(${amountB.mul(2)}, ${amountB.mul(2).sub(2)})`, - "did not respect order output max" - ); - }); - - it("should validate minimum input", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - const aliceOrder = encodeMeta("Order_A"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const bobOrder = encodeMeta("Order_B"); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - bobInputVault, - tokenB.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB.address, - vaultId: bobOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB.transfer(bob.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenB - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct0: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB.mul(2).add(1), // min > max should ALWAYS fail - maximumInput: amountB.mul(2), - maximumIORatio: ratio_A, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - const takeOrdersConfigStruct1: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB.mul(2).add(1), // gt total vault deposits - maximumInput: amountB.mul(2).add(1), - maximumIORatio: ratio_A, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - const takeOrdersConfigStruct2: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB.mul(2), - maximumInput: amountB.mul(2), - maximumIORatio: ratio_A.sub(1), // lt actual ratio - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(carol.address, amountA.mul(2)); - await tokenA.connect(carol).approve(orderBook.address, amountA.mul(2)); - - await assertError( - async () => - await orderBook.connect(carol).takeOrders(takeOrdersConfigStruct0), - `MinimumInput(${amountB.mul(2).add(1)}, ${amountB.mul(2)})`, - "did not validate minimum input gt maximum input" - ); - await assertError( - async () => - await orderBook.connect(carol).takeOrders(takeOrdersConfigStruct1), - `MinimumInput(${amountB.mul(2).add(1)}, ${amountB.mul(2)})`, - "did not validate minimum input gt total deposits" - ); - await assertError( - async () => - await orderBook.connect(carol).takeOrders(takeOrdersConfigStruct2), - `MinimumInput(${amountB.mul(2)}, 0)`, - "did not validate maximumIORatio" - ); - }); - - it("should autoscale expression ratio based on input/output token decimals", async function () { - const signers = await ethers.getSigners(); - - // e.g. tether - const XDec = 6; - // e.g. dai - const YDec = 18; - - const tokenX = (await basicDeploy("ReserveTokenDecimals", {}, [ - XDec, - ])) as ReserveTokenDecimals; - const tokenY = (await basicDeploy("ReserveTokenDecimals", {}, [ - YDec, - ])) as ReserveTokenDecimals; - await tokenX.initialize(); - await tokenY.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const vaultId = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio = ethers.BigNumber.from("10").pow(18); - const constants = [max_uint256, ratio]; - const vInfinity = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const vRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source = concat([ - vInfinity, - vRatio, - ]); - - const evaluableConfig = await generateEvaluableConfig( - [source, []], - constants - ); - - const orderConfig: OrderConfigStruct = { - validInputs: [ - { token: tokenX.address, decimals: XDec, vaultId }, - { token: tokenY.address, decimals: YDec, vaultId }, - ], - validOutputs: [ - { token: tokenX.address, decimals: XDec, vaultId }, - { token: tokenY.address, decimals: YDec, vaultId }, - ], - evaluableConfig: evaluableConfig, - meta: encodeMeta(""), - }; - - const txAddOrder = await orderBook.connect(alice).addOrder(orderConfig); - - const { order } = (await getEventArgs( - txAddOrder, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const depositX: DepositConfigStruct = { - token: tokenX.address, - vaultId, - amount: 1, - }; - - await tokenX.transfer(alice.address, 1); - await tokenX.connect(alice).approve(orderBook.address, 1); - await orderBook.connect(alice).deposit(depositX); - - // TAKE ORDER - - // For the math below to work without loss of precision we need Y decimals - // to be larger than X decimals because we've only deposited 1 of X. - assert(YDec >= XDec); - const takeOrdersConfig: TakeOrdersConfigStruct = { - output: tokenY.address, - input: tokenX.address, - minimumInput: 1, - maximumInput: 1, - // maximum IO ratio is from order's perspective. - // 1e18 = 1:1 - maximumIORatio: ethers.BigNumber.from("10").pow(18 + YDec - XDec), - // inputs and outputs are inverse from order's perspective - orders: [{ order, inputIOIndex: 1, outputIOIndex: 0, signedContext: [] }], - }; - - const amountY = ethers.BigNumber.from("10").pow(YDec - XDec); - await tokenY.transfer(bob.address, amountY); - - const bobBeforeX = await tokenX.balanceOf(bob.address); - const bobBeforeY = await tokenY.balanceOf(bob.address); - - assert( - bobBeforeX.eq(0), - `wrong X balance before expected 0 got ${bobBeforeX}` - ); - assert( - bobBeforeY.eq(amountY), - `wrong Y balance after expected ${amountY} got ${bobBeforeY}` - ); - - await tokenY.connect(bob).approve(orderBook.address, amountY); - await orderBook.connect(bob).takeOrders(takeOrdersConfig); - - const bobAfterX = await tokenX.balanceOf(bob.address); - const bobAfterY = await tokenY.balanceOf(bob.address); - - assert( - bobAfterX.eq(1), - `wrong X balance after expected 1 got ${bobAfterX}` - ); - assert( - bobAfterY.eq(0), - `wrong Y balance after expected 0 got ${bobAfterY}` - ); - - // INVERSE - - const inverseTakeOrdersConfig: TakeOrdersConfigStruct = { - output: tokenX.address, - input: tokenY.address, - minimumInput: amountY, - maximumInput: amountY, - maximumIORatio: ethers.BigNumber.from("10").pow(18 + XDec - YDec), - orders: [{ order, inputIOIndex: 0, outputIOIndex: 1, signedContext: [] }], - }; - - await tokenX.connect(bob).approve(orderBook.address, 1); - await orderBook.connect(bob).takeOrders(inverseTakeOrdersConfig); - - const bobInverseX = await tokenX.balanceOf(bob.address); - const bobInverseY = await tokenY.balanceOf(bob.address); - - assert( - bobInverseX.eq(bobBeforeX), - `wrong inverse X balance expected ${bobBeforeX} got ${bobInverseX}` - ); - assert( - bobInverseY.eq(bobBeforeY), - `wrong inverse Y balance expected ${bobBeforeY} got ${bobInverseY}` - ); - }); - - describe("should scale outputMax with decimals", () => { - it("should scale outputMax based on input/output token decimals (input token has SAME decimals as output: 6 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - 6, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - 6, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB06.initialize(); - - const tokenADecimals = await tokenA06.decimals(); - const tokenBDecimals = await tokenB06.decimals(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - bobInputVault, - tokenB06.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB06.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - await tokenB06.transfer(bob.address, depositAmountB); - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB06.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA06.address, - input: tokenB06.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA06.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA06 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert( - takeOrderAlice.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderAlice.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert( - takeOrderBob.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderBob.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA06.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA06.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - const tokenACarolBalance = await tokenA06.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB06.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenACarolBalance.eq(depositAmountA)); // this is what is leftover since not all of Carol's approved tokenA could be used when taking orders with max output of 1, without output limit this would be depositAmountA.mul(2) - assert(tokenBCarolBalance.eq(depositAmountB)); // similarly, this is only what Carol could get from Alice and Bob's orders, without output limit this would be depositAmountB.mul(2) - - await orderBook.connect(alice).withdraw({ - token: tokenA06.address, - vaultId: aliceInputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ), - }); - await orderBook.connect(alice).withdraw({ - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenB06.address, - aliceOutputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenA06.address, - vaultId: bobInputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenA06.address, - bobInputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenB06.address, - vaultId: bobOutputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobOutputVault - ), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA06.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA06.balanceOf(bob.address); - const tokenBAliceBalanceWithdrawn = await tokenB06.balanceOf( - alice.address - ); - const tokenBBobBalanceWithdrawn = await tokenB06.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenBAliceBalanceWithdrawn.eq(depositAmountB.div(2))); - assert(tokenBBobBalanceWithdrawn.eq(depositAmountB.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has LESS decimals than output: 20 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 20; - const tokenBDecimals = 6; - - const tokenA20 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA20.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA20.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA20.address, - tokenADecimals, - bobInputVault, - tokenB06.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB06.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - await tokenB06.transfer(bob.address, depositAmountB); - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB06.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA20.address, - input: tokenB06.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA20.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA20 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert( - takeOrderAlice.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderAlice.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert( - takeOrderBob.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderBob.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA20.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA20.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - const tokenACarolBalance = await tokenA20.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB06.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenACarolBalance.eq(depositAmountA)); // this is what is leftover since not all of Carol's approved tokenA could be used when taking orders with max output of 1, without output limit this would be depositAmountA.mul(2) - assert(tokenBCarolBalance.eq(depositAmountB)); // similarly, this is only what Carol could get from Alice and Bob's orders, without output limit this would be depositAmountB.mul(2) - - await orderBook.connect(alice).withdraw({ - token: tokenA20.address, - vaultId: aliceInputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenA20.address, - aliceInputVault - ), - }); - await orderBook.connect(alice).withdraw({ - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenB06.address, - aliceOutputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenA20.address, - vaultId: bobInputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenA20.address, - bobInputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenB06.address, - vaultId: bobOutputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobOutputVault - ), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA20.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA20.balanceOf(bob.address); - const tokenBAliceBalanceWithdrawn = await tokenB06.balanceOf( - alice.address - ); - const tokenBBobBalanceWithdrawn = await tokenB06.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenBAliceBalanceWithdrawn.eq(depositAmountB.div(2))); - assert(tokenBBobBalanceWithdrawn.eq(depositAmountB.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has LESS decimals than output: 18 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA18.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA18.address, - tokenADecimals, - bobInputVault, - tokenB06.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB06.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - await tokenB06.transfer(bob.address, depositAmountB); - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB06.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA18.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA18 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert( - takeOrderAlice.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderAlice.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert( - takeOrderBob.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderBob.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - const tokenACarolBalance = await tokenA18.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB06.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenACarolBalance.eq(depositAmountA)); // this is what is leftover since not all of Carol's approved tokenA could be used when taking orders with max output of 1, without output limit this would be depositAmountA.mul(2) - assert(tokenBCarolBalance.eq(depositAmountB)); // similarly, this is only what Carol could get from Alice and Bob's orders, without output limit this would be depositAmountB.mul(2) - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceInputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenA18.address, - aliceInputVault - ), - }); - await orderBook.connect(alice).withdraw({ - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenB06.address, - aliceOutputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenA18.address, - vaultId: bobInputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenA18.address, - bobInputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenB06.address, - vaultId: bobOutputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenB06.address, - bobOutputVault - ), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA18.balanceOf(bob.address); - const tokenBAliceBalanceWithdrawn = await tokenB06.balanceOf( - alice.address - ); - const tokenBBobBalanceWithdrawn = await tokenB06.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenBAliceBalanceWithdrawn.eq(depositAmountB.div(2))); - assert(tokenBBobBalanceWithdrawn.eq(depositAmountB.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has LESS decimals than output: 6 vs 20)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 20; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB20 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB20.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB20.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - bobInputVault, - tokenB20.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + twentyZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB20.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB20.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB20.transfer(alice.address, depositAmountB); - await tokenB20.transfer(bob.address, depositAmountB); - await tokenB20.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB20.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA06.address, - input: tokenB20.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA06.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA06 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert( - takeOrderAlice.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderAlice.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert( - takeOrderBob.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderBob.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA06.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB20.balanceOf(alice.address); - const tokenABobBalance = await tokenA06.balanceOf(bob.address); - const tokenBBobBalance = await tokenB20.balanceOf(bob.address); - const tokenACarolBalance = await tokenA06.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB20.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenACarolBalance.eq(depositAmountA)); // this is what is leftover since not all of Carol's approved tokenA could be used when taking orders with max output of 1, without output limit this would be depositAmountA.mul(2) - assert(tokenBCarolBalance.eq(depositAmountB)); // similarly, this is only what Carol could get from Alice and Bob's orders, without output limit this would be depositAmountB.mul(2) - - await orderBook.connect(alice).withdraw({ - token: tokenA06.address, - vaultId: aliceInputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ), - }); - await orderBook.connect(alice).withdraw({ - token: tokenB20.address, - vaultId: aliceOutputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenB20.address, - aliceOutputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenA06.address, - vaultId: bobInputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenA06.address, - bobInputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenB20.address, - vaultId: bobOutputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenB20.address, - bobOutputVault - ), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA06.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA06.balanceOf(bob.address); - const tokenBAliceBalanceWithdrawn = await tokenB20.balanceOf( - alice.address - ); - const tokenBBobBalanceWithdrawn = await tokenB20.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenBAliceBalanceWithdrawn.eq(depositAmountB.div(2))); - assert(tokenBBobBalanceWithdrawn.eq(depositAmountB.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has LESS decimals than output: 6 vs 18)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 18; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA06.address, - tokenADecimals, - bobInputVault, - tokenB18.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB18.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB18.transfer(alice.address, depositAmountB); - await tokenB18.transfer(bob.address, depositAmountB); - await tokenB18.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB18.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA06.address, - input: tokenB18.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA06.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA06 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert( - takeOrderAlice.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderAlice.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert( - takeOrderBob.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderBob.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA06.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB18.balanceOf(alice.address); - const tokenABobBalance = await tokenA06.balanceOf(bob.address); - const tokenBBobBalance = await tokenB18.balanceOf(bob.address); - const tokenACarolBalance = await tokenA06.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB18.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenACarolBalance.eq(depositAmountA)); // this is what is leftover since not all of Carol's approved tokenA could be used when taking orders with max output of 1, without output limit this would be depositAmountA.mul(2) - assert(tokenBCarolBalance.eq(depositAmountB)); // similarly, this is only what Carol could get from Alice and Bob's orders, without output limit this would be depositAmountB.mul(2) - - await orderBook.connect(alice).withdraw({ - token: tokenA06.address, - vaultId: aliceInputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenA06.address, - aliceInputVault - ), - }); - await orderBook.connect(alice).withdraw({ - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenB18.address, - aliceOutputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenA06.address, - vaultId: bobInputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenA06.address, - bobInputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenB18.address, - vaultId: bobOutputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenB18.address, - bobOutputVault - ), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA06.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA06.balanceOf(bob.address); - const tokenBAliceBalanceWithdrawn = await tokenB18.balanceOf( - alice.address - ); - const tokenBBobBalanceWithdrawn = await tokenB18.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenBAliceBalanceWithdrawn.eq(depositAmountB.div(2))); - assert(tokenBBobBalanceWithdrawn.eq(depositAmountB.div(2))); - }); - - it("should scale outputMax based on input/output token decimals (input token has LESS decimals than output: 0 vs 18)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 0; - const tokenBDecimals = 18; - - const tokenA00 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA00.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - // note 18 decimals for outputMax - // 1e18 means that only 1 unit of tokenB can be outputted per order - const outputMax_A = ethers.BigNumber.from(1 + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA00.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - outputMax_A, - tokenA00.address, - tokenADecimals, - bobInputVault, - tokenB18.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB18.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB18.transfer(alice.address, depositAmountB); - await tokenB18.transfer(bob.address, depositAmountB); - await tokenB18.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB18.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA00.address, - input: tokenB18.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA00.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA00 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert( - takeOrderAlice.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderAlice.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert( - takeOrderBob.input.eq(depositAmountB.div(2)), - "wrong input, output max wasn't respected" - ); - assert( - takeOrderBob.output.eq(depositAmountA.div(2)), - "wrong output, output max wasn't respected" - ); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA00.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB18.balanceOf(alice.address); - const tokenABobBalance = await tokenA00.balanceOf(bob.address); - const tokenBBobBalance = await tokenB18.balanceOf(bob.address); - const tokenACarolBalance = await tokenA00.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB18.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenACarolBalance.eq(depositAmountA)); // this is what is leftover since not all of Carol's approved tokenA could be used when taking orders with max output of 1, without output limit this would be depositAmountA.mul(2) - assert(tokenBCarolBalance.eq(depositAmountB)); // similarly, this is only what Carol could get from Alice and Bob's orders, without output limit this would be depositAmountB.mul(2) - - await orderBook.connect(alice).withdraw({ - token: tokenA00.address, - vaultId: aliceInputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenA00.address, - aliceInputVault - ), - }); - await orderBook.connect(alice).withdraw({ - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: await orderBook.vaultBalance( - alice.address, - tokenB18.address, - aliceOutputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenA00.address, - vaultId: bobInputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenA00.address, - bobInputVault - ), - }); - await orderBook.connect(bob).withdraw({ - token: tokenB18.address, - vaultId: bobOutputVault, - amount: await orderBook.vaultBalance( - bob.address, - tokenB18.address, - bobOutputVault - ), - }); - - const tokenAAliceBalanceWithdrawn = await tokenA00.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA00.balanceOf(bob.address); - const tokenBAliceBalanceWithdrawn = await tokenB18.balanceOf( - alice.address - ); - const tokenBBobBalanceWithdrawn = await tokenB18.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA.div(2))); - assert(tokenBAliceBalanceWithdrawn.eq(depositAmountB.div(2))); - assert(tokenBBobBalanceWithdrawn.eq(depositAmountB.div(2))); - }); - }); - - describe("should scale ratio with decimals", () => { - it("should scale ratio based on input/output token decimals (input token has SAME decimals as output: 6 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 6; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - bobInputVault, - tokenB06.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB06.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - await tokenB06.transfer(bob.address, depositAmountB); - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB06.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA06.address, - input: tokenB06.address, - minimumInput: depositAmountB.mul(2), - maximumInput: depositAmountB.mul(2), - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA06.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA06 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert(takeOrderAlice.input.eq(depositAmountB), "wrong input"); - assert(takeOrderAlice.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert(takeOrderBob.input.eq(depositAmountB), "wrong input"); - assert(takeOrderBob.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA06.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA06.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - const tokenACarolBalance = await tokenA06.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB06.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); - assert(tokenACarolBalance.isZero()); - assert(tokenBCarolBalance.eq(depositAmountB.mul(2))); - - await orderBook.connect(alice).withdraw({ - token: tokenA06.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - await orderBook.connect(bob).withdraw({ - token: tokenA06.address, - vaultId: bobInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA06.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA06.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should scale ratio based on input/output token decimals (input token has MORE decimals than output: 20 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 20; - const tokenBDecimals = 6; - - const tokenA20 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA20.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA20.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA20.address, - tokenADecimals, - bobInputVault, - tokenB06.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB06.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - await tokenB06.transfer(bob.address, depositAmountB); - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB06.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA20.address, - input: tokenB06.address, - minimumInput: depositAmountB.mul(2), - maximumInput: depositAmountB.mul(2), - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA20.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA20 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert(takeOrderAlice.input.eq(depositAmountB), "wrong input"); - assert(takeOrderAlice.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert(takeOrderBob.input.eq(depositAmountB), "wrong input"); - assert(takeOrderBob.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA20.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA20.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - const tokenACarolBalance = await tokenA20.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB06.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); - assert(tokenACarolBalance.isZero()); - assert(tokenBCarolBalance.eq(depositAmountB.mul(2))); - - await orderBook.connect(alice).withdraw({ - token: tokenA20.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - await orderBook.connect(bob).withdraw({ - token: tokenA20.address, - vaultId: bobInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA20.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA20.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should scale ratio based on input/output token decimals (input token has MORE decimals than output: 18 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA18.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA18.address, - tokenADecimals, - bobInputVault, - tokenB06.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB06.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - await tokenB06.transfer(bob.address, depositAmountB); - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB06.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB.mul(2), - maximumInput: depositAmountB.mul(2), - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA18.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA18 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert(takeOrderAlice.input.eq(depositAmountB), "wrong input"); - assert(takeOrderAlice.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert(takeOrderBob.input.eq(depositAmountB), "wrong input"); - assert(takeOrderBob.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - const tokenACarolBalance = await tokenA18.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB06.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); - assert(tokenACarolBalance.isZero()); - assert(tokenBCarolBalance.eq(depositAmountB.mul(2))); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - await orderBook.connect(bob).withdraw({ - token: tokenA18.address, - vaultId: bobInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA18.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should scale ratio based on input/output token decimals (input token has LESS decimals than output: 6 vs 20)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 20; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB20 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB20.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB20.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - bobInputVault, - tokenB20.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + twentyZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB20.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB20.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB20.transfer(alice.address, depositAmountB); - await tokenB20.transfer(bob.address, depositAmountB); - await tokenB20.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB20.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA06.address, - input: tokenB20.address, - minimumInput: depositAmountB.mul(2), - maximumInput: depositAmountB.mul(2), - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA06.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA06 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert(takeOrderAlice.input.eq(depositAmountB), "wrong input"); - assert(takeOrderAlice.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert(takeOrderBob.input.eq(depositAmountB), "wrong input"); - assert(takeOrderBob.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA06.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB20.balanceOf(alice.address); - const tokenABobBalance = await tokenA06.balanceOf(bob.address); - const tokenBBobBalance = await tokenB20.balanceOf(bob.address); - const tokenACarolBalance = await tokenA06.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB20.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); - assert(tokenACarolBalance.isZero()); - assert(tokenBCarolBalance.eq(depositAmountB.mul(2))); - - await orderBook.connect(alice).withdraw({ - token: tokenA06.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - await orderBook.connect(bob).withdraw({ - token: tokenA06.address, - vaultId: bobInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA06.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA06.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should scale ratio based on input/output token decimals (input token has LESS decimals than output: 6 vs 18)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 18; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - bobInputVault, - tokenB18.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB18.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB18.transfer(alice.address, depositAmountB); - await tokenB18.transfer(bob.address, depositAmountB); - await tokenB18.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB18.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA06.address, - input: tokenB18.address, - minimumInput: depositAmountB.mul(2), - maximumInput: depositAmountB.mul(2), - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA06.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA06 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert(takeOrderAlice.input.eq(depositAmountB), "wrong input"); - assert(takeOrderAlice.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert(takeOrderBob.input.eq(depositAmountB), "wrong input"); - assert(takeOrderBob.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA06.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB18.balanceOf(alice.address); - const tokenABobBalance = await tokenA06.balanceOf(bob.address); - const tokenBBobBalance = await tokenB18.balanceOf(bob.address); - const tokenACarolBalance = await tokenA06.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB18.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); - assert(tokenACarolBalance.isZero()); - assert(tokenBCarolBalance.eq(depositAmountB.mul(2))); - - await orderBook.connect(alice).withdraw({ - token: tokenA06.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - await orderBook.connect(bob).withdraw({ - token: tokenA06.address, - vaultId: bobInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA06.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA06.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA)); - }); - - it("should scale ratio based on input/output token decimals (input token has LESS decimals than output: 0 vs 18)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 0; - const tokenBDecimals = 18; - - const tokenA00 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA00.initialize(); - await tokenB18.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from(10).pow(18); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA00.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA00.address, - tokenADecimals, - bobInputVault, - tokenB18.address, - tokenBDecimals, - bobOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook - .connect(bob) - .addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB18.address, - vaultId: bobOutputVault, - amount: depositAmountB, - }; - - await tokenB18.transfer(alice.address, depositAmountB); - await tokenB18.transfer(bob.address, depositAmountB); - await tokenB18.connect(alice).approve(orderBook.address, depositAmountB); - await tokenB18.connect(bob).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA00.address, - input: tokenB18.address, - minimumInput: depositAmountB.mul(2), - maximumInput: depositAmountB.mul(2), - maximumIORatio, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - // Similarly, we want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul( - depositAmountB, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - await tokenA00.transfer(carol.address, depositAmountA.mul(2)); // 2 orders - await tokenA00 - .connect(carol) - .approve(orderBook.address, depositAmountA.mul(2)); // 2 orders - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert(takeOrderAlice.input.eq(depositAmountB), "wrong input"); - assert(takeOrderAlice.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert(takeOrderBob.input.eq(depositAmountB), "wrong input"); - assert(takeOrderBob.output.eq(depositAmountA), "wrong output"); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA00.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB18.balanceOf(alice.address); - const tokenABobBalance = await tokenA00.balanceOf(bob.address); - const tokenBBobBalance = await tokenB18.balanceOf(bob.address); - const tokenACarolBalance = await tokenA00.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB18.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); - assert(tokenACarolBalance.isZero()); - assert(tokenBCarolBalance.eq(depositAmountB.mul(2))); - - await orderBook.connect(alice).withdraw({ - token: tokenA00.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - await orderBook.connect(bob).withdraw({ - token: tokenA00.address, - vaultId: bobInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA00.balanceOf( - alice.address - ); - const tokenABobBalanceWithdrawn = await tokenA00.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - assert(tokenABobBalanceWithdrawn.eq(depositAmountA)); - }); - }); - - it("should validate input/output tokens", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - const aliceOrder = encodeMeta("Order_A"); - const bobOrder = encodeMeta("Order_B"); - - // ORDERS - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - bobInputVault, - tokenB.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB.address, - vaultId: bobOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB.transfer(bob.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenB - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct0: TakeOrdersConfigStruct = { - output: tokenB.address, // will result in mismatch - input: tokenB.address, - minimumInput: amountB.mul(2), - maximumInput: amountB.mul(2), - maximumIORatio: ratio_A, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - const takeOrdersConfigStruct1: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenA.address, // will result in mismatch - minimumInput: amountB.mul(2), - maximumInput: amountB.mul(2), - maximumIORatio: ratio_A, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(carol.address, amountA.mul(2)); - await tokenA.connect(carol).approve(orderBook.address, amountA.mul(2)); - - await assertError( - async () => - await orderBook.connect(carol).takeOrders(takeOrdersConfigStruct0), - `TokenMismatch("${tokenA.address}", "${tokenB.address}")`, - "did not validate output token" - ); - await assertError( - async () => - await orderBook.connect(carol).takeOrders(takeOrdersConfigStruct1), - `TokenMismatch("${tokenB.address}", "${tokenA.address}")`, - "did not validate input token" - ); - }); - - it("should emit event when an order has zero output max amount", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - // ASK ORDER 0 - - const aliceOrder = encodeMeta("Order_A"); - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const OrderConfig_A0: OrderConfigStruct = await getOrderConfig( - ratio_A, - ethers.BigNumber.from(0), - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrderAlice0 = await orderBook - .connect(alice) - .addOrder(OrderConfig_A0); - - const { order: Order_A0 } = (await getEventArgs( - txAddOrderAlice0, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // ASK ORDER 1 - - const OrderConfig_A1: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrderAlice1 = await orderBook - .connect(alice) - .addOrder(OrderConfig_A1); - - const { order: Order_A1 } = (await getEventArgs( - txAddOrderAlice1, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - - const takeOrderConfigStruct0: TakeOrderConfigStruct = { - order: Order_A0, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStruct1: TakeOrderConfigStruct = { - order: Order_A1, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB, - maximumInput: amountB, - maximumIORatio: ratio_A, - orders: [takeOrderConfigStruct0, takeOrderConfigStruct1], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(bob.address, amountA); - await tokenA.connect(bob).approve(orderBook.address, amountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const ordersExpired = (await getEvents( - txTakeOrders, - "OrderZeroAmount", - orderBook - )) as OrderZeroAmountEvent["args"][]; - - assert(ordersExpired.length === 1); - assert(ordersExpired[0].sender === bob.address); - assert(ordersExpired[0].owner === alice.address); - assert(ordersExpired[0].orderHash); // not sure how to verify order hash from config, solidityKeccak256 has rejected all types I've thrown at it - }); - - it("should emit event when an order exceeds max IO ratio", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - const aliceOrder0 = encodeMeta("Order0"); - const aliceOrder1 = encodeMeta("Order1"); - - // ASK ORDER 0 - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const OrderConfig_A0: OrderConfigStruct = await getOrderConfig( - ratio_A.add(1), - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder0 - ); - const OrderConfig_A1: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder1 - ); - - const txAddOrder0 = await orderBook.connect(alice).addOrder(OrderConfig_A0); - const txAddOrder1 = await orderBook.connect(alice).addOrder(OrderConfig_A1); - - const { order: Order_A0 } = (await getEventArgs( - txAddOrder0, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_A1 } = (await getEventArgs( - txAddOrder1, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - - const takeOrderConfigStruct0: TakeOrderConfigStruct = { - order: Order_A0, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStruct1: TakeOrderConfigStruct = { - order: Order_A1, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB, - maximumInput: amountB, - maximumIORatio: ratio_A, - orders: [takeOrderConfigStruct0, takeOrderConfigStruct1], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(bob.address, amountA); - await tokenA.connect(bob).approve(orderBook.address, amountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const ordersExceedsMaxRatio = (await getEvents( - txTakeOrders, - "OrderExceedsMaxRatio", - orderBook - )) as OrderExceedsMaxRatioEvent["args"][]; - - assert(ordersExceedsMaxRatio.length === 1); - assert(ordersExceedsMaxRatio[0].sender === bob.address); - assert(ordersExceedsMaxRatio[0].owner === alice.address); - assert(ordersExceedsMaxRatio[0].orderHash); // not sure how to verify order hash from config, solidityKeccak256 has rejected all types I've thrown at it - }); - - it("should emit event when an order wasn't found", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - const aliceOrder = encodeMeta("aliceOrder"); - - // ASK ORDER - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrder = await orderBook.connect(alice).addOrder(OrderConfig_A); - - const { order: Order_A } = (await getEventArgs( - txAddOrder, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - - // TAKE BAD ORDER - - const takeOrderConfigStructGood: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBad: TakeOrderConfigStruct = { - order: { ...Order_A, owner: bob.address }, // order hash won't match any added orders - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB, - maximumInput: amountB, - maximumIORatio: ratio_A, - orders: [takeOrderConfigStructBad, takeOrderConfigStructGood], // test bad order before good order (when remaining input is non-zero) - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(bob.address, amountA); - await tokenA.connect(bob).approve(orderBook.address, amountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const ordersNotFound = (await getEvents( - txTakeOrders, - "OrderNotFound", - orderBook - )) as OrderNotFoundEvent["args"][]; - - assert(ordersNotFound.length === 1); - assert(ordersNotFound[0].sender === bob.address); - assert(ordersNotFound[0].owner === bob.address); - assert(ordersNotFound[0].orderHash); - }); - - it("should take multiple orders on the good path (clear multiple orders directly from buyer wallet)", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - const bobInputVault = ethers.BigNumber.from(randomUint256()); - const bobOutputVault = ethers.BigNumber.from(randomUint256()); - - const aliceOrder = encodeMeta("Order_A"); - const bobOrder = encodeMeta("Order_B"); - - // ORDERS - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const OrderConfig_B: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - bobInputVault, - tokenB.address, - 18, - bobOutputVault, - bobOrder - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - const txAddOrderBob = await orderBook.connect(bob).addOrder(OrderConfig_B); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - const { order: Order_B } = (await getEventArgs( - txAddOrderBob, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - const depositConfigStructBob: DepositConfigStruct = { - token: tokenB.address, - vaultId: bobOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB.transfer(bob.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - await tokenB - .connect(bob) - .approve(orderBook.address, depositConfigStructBob.amount); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - // Bob deposits tokenB into his output vault - await orderBook.connect(bob).deposit(depositConfigStructBob); - - // TAKE ORDER - - // Carol takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_B, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB.mul(2), - maximumInput: amountB.mul(2), - maximumIORatio: ratio_A, - orders: [takeOrderConfigStructAlice, takeOrderConfigStructBob], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(carol.address, amountA.mul(2)); - await tokenA.connect(carol).approve(orderBook.address, amountA.mul(2)); - - const txTakeOrders = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - assert( - events.length === 2, - `wrong number of TakeOrder events - expected 2 - got ${events.length}` - ); - - const [takeOrderAlice, takeOrderBob] = events; - - assert(takeOrderAlice.sender === carol.address, "wrong sender"); - assert(takeOrderAlice.input.eq(amountB), "wrong input"); - assert(takeOrderAlice.output.eq(amountA), "wrong output"); - compareStructs(takeOrderAlice.config, takeOrderConfigStructAlice); - - assert(takeOrderBob.sender === carol.address, "wrong sender"); - assert(takeOrderBob.input.eq(amountB), "wrong input"); - assert(takeOrderBob.output.eq(amountA), "wrong output"); - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB.balanceOf(alice.address); - const tokenABobBalance = await tokenA.balanceOf(bob.address); - const tokenBBobBalance = await tokenB.balanceOf(bob.address); - const tokenACarolBalance = await tokenA.balanceOf(carol.address); - const tokenBCarolBalance = await tokenB.balanceOf(carol.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); // Bob has not yet withdrawn - assert(tokenBBobBalance.isZero()); - assert(tokenACarolBalance.isZero()); - assert(tokenBCarolBalance.eq(amountB.mul(2))); - - await orderBook.connect(alice).withdraw({ - token: tokenA.address, - vaultId: aliceInputVault, - amount: amountA, - }); - await orderBook.connect(bob).withdraw({ - token: tokenA.address, - vaultId: bobInputVault, - amount: amountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA.balanceOf(alice.address); - const tokenABobBalanceWithdrawn = await tokenA.balanceOf(bob.address); - assert(tokenAAliceBalanceWithdrawn.eq(amountA)); - assert(tokenABobBalanceWithdrawn.eq(amountA)); - }); - - it("should take order on the good path (clear an order directly from buyer wallet)", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - const aliceOrder = encodeMeta("Order_A"); - - // ASK ORDER - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrder = await orderBook.connect(alice).addOrder(OrderConfig_A); - - const { order: Order_A } = (await getEventArgs( - txAddOrder, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - - // TAKE ORDER - - // Bob takes order with direct wallet transfer - const takeOrderConfigStruct: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB, - maximumInput: amountB, - maximumIORatio: ratio_A, - orders: [takeOrderConfigStruct], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(bob.address, amountA); - await tokenA.connect(bob).approve(orderBook.address, amountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(amountB), "wrong input"); - assert(output.eq(amountA), "wrong output"); - - compareStructs(config, takeOrderConfigStruct); - - const tokenAAliceBalance = await tokenA.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB.balanceOf(alice.address); - const tokenABobBalance = await tokenA.balanceOf(bob.address); - const tokenBBobBalance = await tokenB.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(amountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA.address, - vaultId: aliceInputVault, - amount: amountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(amountA)); - }); - - it("should take slosh order on a good path for tokens with different decimals(clear multiple orders directly from buyer wallet)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 18; - const tokenCDecimals = 12; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - const tokenC12 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenCDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - await tokenC12.initialize(); - - const [, alice, bob, carol] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1.02 from the perspective of the expression. - const ratio_A = ethers.BigNumber.from(102 + sixteenZeros); - - const constants_A = [max_uint256, ratio_A]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_A = concat([ - aOpMax, - aRatio, - ]); - - const EvaluableConfigAlice = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - { - token: tokenC12.address, - decimals: tokenCDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: tokenADecimals, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - { - token: tokenC12.address, - decimals: tokenCDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(1 + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB.mul(2), - }; - - await tokenB06.transfer(alice.address, depositAmountB.mul(2)); - - await tokenB06 - .connect(alice) - .approve(orderBook.address, depositAmountB.mul(2)); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - const depositAmountA = ethers.BigNumber.from(102 + "0000"); - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const events = (await getEvents( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - const [takeOrderBob] = events; - - assert( - takeOrderBob.sender === bob.address, - `wrong sender expected ${takeOrderBob.sender} got ${bob.address}` - ); - assert(takeOrderBob.input.eq(depositAmountB), "wrong input"); - assert(takeOrderBob.output.eq(depositAmountA), "wrong output"); - - compareStructs(takeOrderBob.config, takeOrderConfigStructBob); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - - // TAKE ORDER Carol - - const takeOrderConfigStructCarol: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 2, - outputIOIndex: 1, - signedContext: [], - }; - - const maximumIORatioCarol = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenCDecimals - tokenBDecimals) - ); - - const takeOrdersConfigStructCarol: TakeOrdersConfigStruct = { - output: tokenC12.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio: maximumIORatioCarol, - orders: [takeOrderConfigStructCarol], - }; - - const depositAmountC = ethers.BigNumber.from(102 + tenZeros); - - await tokenC12.transfer(carol.address, depositAmountC); - await tokenC12.connect(carol).approve(orderBook.address, depositAmountC); - - const txTakeOrdersCarol = await orderBook - .connect(carol) - .takeOrders(takeOrdersConfigStructCarol); - - const events1 = (await getEvents( - txTakeOrdersCarol, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"][]; - - const [takeOrderCarol] = events1; - - assert(takeOrderCarol.sender === carol.address, `wrong sender`); - assert(takeOrderCarol.input.eq(depositAmountB), "wrong input"); - assert(takeOrderCarol.output.eq(depositAmountC), "wrong output"); - - compareStructs(takeOrderCarol.config, takeOrdersConfigStructCarol); - - const tokenBAliceBalance1 = await tokenB06.balanceOf(alice.address); - const tokenCAliceBalance = await tokenC12.balanceOf(alice.address); - const tokenBCarolBalance = await tokenB06.balanceOf(carol.address); - const tokenCCarolBalance = await tokenC12.balanceOf(carol.address); - - assert(tokenCAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance1.isZero()); - assert(tokenCCarolBalance.isZero()); - assert(tokenBCarolBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenC12.address, - vaultId: aliceVault, - amount: depositAmountC, - }); - - const tokenCAliceBalanceWithdrawn = await tokenC12.balanceOf(alice.address); - assert(tokenCAliceBalanceWithdrawn.eq(depositAmountC)); - }); - - it("should ensure that misconfigured decimals on tokens only harm the misconfigurer", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 18; - const tokenCDecimals = 12; - - const incorrectDeciamls = 10; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - const tokenC12 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenCDecimals, - ])) as ReserveTokenDecimals; - - await tokenA18.initialize(); - await tokenB06.initialize(); - await tokenC12.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1.02 from the perspective of the expression. - const ratio_A = ethers.BigNumber.from(102 + sixteenZeros); - - const constants_A = [max_uint256, ratio_A]; - const aOpMax = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 0) - ); - const aRatio = op( - Opcode.read_memory, - memoryOperand(MemoryType.Constant, 1) - ); - // prettier-ignore - const source_A = concat([ - aOpMax, - aRatio, - ]); - - const EvaluableConfigAlice = await generateEvaluableConfig( - [source_A, []], - constants_A - ); - - const OrderConfigAlice: OrderConfigStruct = { - validInputs: [ - { - token: tokenA18.address, - decimals: incorrectDeciamls, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - { - token: tokenC12.address, - decimals: tokenCDecimals, - vaultId: aliceVault, - }, - ], - validOutputs: [ - { - token: tokenA18.address, - decimals: incorrectDeciamls, - vaultId: aliceVault, - }, - { - token: tokenB06.address, - decimals: tokenBDecimals, - vaultId: aliceVault, - }, - { - token: tokenC12.address, - decimals: tokenCDecimals, - vaultId: aliceVault, - }, - ], - evaluableConfig: EvaluableConfigAlice, - meta: encodeMeta(""), - }; - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfigAlice); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - // Alice and Bob will each deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(1 + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceVault, - amount: depositAmountB.mul(2), - }; - - await tokenB06.transfer(alice.address, depositAmountB.mul(2)); - - await tokenB06 - .connect(alice) - .approve(orderBook.address, depositAmountB.mul(2)); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - - // TAKE ORDER BOB - - const takeOrderConfigStructBob: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 1, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructBob], - }; - - const depositAmountA = ethers.BigNumber.from(102 + "0000"); - - await tokenA18.transfer(bob.address, depositAmountA); - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); - - await assertError( - async () => - await orderBook.connect(bob).takeOrders(takeOrdersConfigStruct), - `MinimumInput(${depositAmountB}, 0)`, - "Take Orders without hitting minimum input executed" - ); - }); - - it("should validate context emitted in context event when handleIO dispatch is zero", async function () { - const signers = await ethers.getSigners(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - const aliceOrder = encodeMeta("aliceOrder"); - - // ASK ORDER - - const ratio_A = ethers.BigNumber.from("90" + eighteenZeros); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA.address, - 18, - aliceInputVault, - tokenB.address, - 18, - aliceOutputVault, - aliceOrder - ); - - const txAddOrder = await orderBook.connect(alice).addOrder(OrderConfig_A); - - const { order: Order_A, orderHash: hashOrder_A } = (await getEventArgs( - txAddOrder, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - - const amountB = ethers.BigNumber.from("2" + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - - await tokenB.transfer(alice.address, amountB); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAlice.amount); - - // Alice deposits tokenB into her output vault - const txDepositOrderAlice = await orderBook - .connect(alice) - .deposit(depositConfigStructAlice); - - const { sender: depositAliceSender, config: depositAliceConfig } = - (await getEventArgs( - txDepositOrderAlice, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositAliceSender === alice.address); - compareStructs(depositAliceConfig, depositConfigStructAlice); - - // TAKE ORDER - - // Bob takes order with direct wallet transfer - const takeOrderConfigStruct: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA.address, - input: tokenB.address, - minimumInput: amountB, - maximumInput: amountB, - maximumIORatio: ratio_A, - orders: [takeOrderConfigStruct], - }; - - const amountA = amountB.mul(ratio_A).div(ONE); - await tokenA.transfer(bob.address, amountA); - await tokenA.connect(bob).approve(orderBook.address, amountA); - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(amountB), "wrong input"); - assert(output.eq(amountA), "wrong output"); - - compareStructs(config, takeOrderConfigStruct); - - const tokenAAliceBalance = await tokenA.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB.balanceOf(alice.address); - const tokenABobBalance = await tokenA.balanceOf(bob.address); - const tokenBBobBalance = await tokenB.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(amountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA.address, - vaultId: aliceInputVault, - amount: amountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(amountA)); - - // Asserting Context Events - const contextEvents = (await getEvents( - txTakeOrders, - "Context", - orderBook - )) as ContextEvent["args"][]; - - const { sender: sender0, context: context0_ } = contextEvents[0]; - - assert(sender0 === bob.address); - - const aip = minBN(amountB, minBN(max_uint256, amountB)); // minimum of remainingInput and outputMax - const aop = fixedPointMul(aip, ratio_A); - const opMax = minBN(max_uint256, amountB); - - const expectedEvent0 = [ - [ - ethers.BigNumber.from(bob.address), - ethers.BigNumber.from(orderBook.address), - ], - [ - hashOrder_A, - ethers.BigNumber.from(alice.address), - ethers.BigNumber.from(bob.address), - ], - [opMax, ratio_A], - [ - ethers.BigNumber.from(tokenA.address), - ethers.BigNumber.from(18), - aliceInputVault, - 0, - aop, - ], - [ - ethers.BigNumber.from(tokenB.address), - ethers.BigNumber.from(18), - aliceOutputVault, - amountB, - aip, - ], - ]; - - for (let i = 0; i < expectedEvent0.length; i++) { - const rowArray = expectedEvent0[i]; - for (let j = 0; j < rowArray.length; j++) { - const colElement = rowArray[j]; - if (!context0_[i][j].eq(colElement)) { - assert.fail(`mismatch at position (${i},${j}), - expected ${colElement} - got ${context0_[i][j]}`); - } - } - } - }); - - it("precision check for takeOrders (6 vs 18) ", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 18; - const tokenBDecimals = 6; - - const tokenA18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA18.initialize(); - await tokenB06.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from("1000000000000034567"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA18.address, - tokenADecimals, - aliceInputVault, - tokenB06.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - // Alice will deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + sixZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB06.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - - await tokenB06.transfer(alice.address, depositAmountB); - await tokenB06.connect(alice).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - - // TAKE ORDER - - // Bob takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ); - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA18.address, - input: tokenB06.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice], - }; - - // We want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul(depositAmountB, maximumIORatio); - - await tokenA18.transfer(bob.address, depositAmountA); // 2 orders - await tokenA18.connect(bob).approve(orderBook.address, depositAmountA); // 2 orders - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructAlice); - - const tokenAAliceBalance = await tokenA18.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB06.balanceOf(alice.address); - const tokenABobBalance = await tokenA18.balanceOf(bob.address); - const tokenBBobBalance = await tokenB06.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA18.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA18.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - }); - - it("precision check for takeOrders(18 vs 6)", async function () { - const signers = await ethers.getSigners(); - - const tokenADecimals = 6; - const tokenBDecimals = 18; - - const tokenA06 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenADecimals, - ])) as ReserveTokenDecimals; - const tokenB18 = (await basicDeploy("ReserveTokenDecimals", {}, [ - tokenBDecimals, - ])) as ReserveTokenDecimals; - await tokenA06.initialize(); - await tokenB18.initialize(); - - const [, alice, bob] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - // ORDERS - - // The ratio is 1:1 from the perspective of the expression. - // This is a statement of economic equivalence in 18 decimal fixed point. - const ratio_A = ethers.BigNumber.from("1999765000000034567"); - - const OrderConfig_A: OrderConfigStruct = await getOrderConfig( - ratio_A, - max_uint256, - tokenA06.address, - tokenADecimals, - aliceInputVault, - tokenB18.address, - tokenBDecimals, - aliceOutputVault, - encodeMeta("") - ); - - const txAddOrderAlice = await orderBook - .connect(alice) - .addOrder(OrderConfig_A); - - const { order: Order_A } = (await getEventArgs( - txAddOrderAlice, - "AddOrder", - orderBook - )) as AddOrderEvent["args"]; - - // DEPOSIT - // Alice will deposit 2 units of tokenB - const depositAmountB = ethers.BigNumber.from(2 + eighteenZeros); - - const depositConfigStructAlice: DepositConfigStruct = { - token: tokenB18.address, - vaultId: aliceOutputVault, - amount: depositAmountB, - }; - - await tokenB18.transfer(alice.address, depositAmountB); - await tokenB18.connect(alice).approve(orderBook.address, depositAmountB); - - // Alice deposits tokenB into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAlice); - - // TAKE ORDER - - // Bob takes orders with direct wallet transfer - const takeOrderConfigStructAlice: TakeOrderConfigStruct = { - order: Order_A, - inputIOIndex: 0, - outputIOIndex: 0, - signedContext: [], - }; - - // We want the takeOrders max ratio to be exact, for the purposes of testing. We scale the original ratio 'up' by the difference between A decimals and B decimals. - const maximumIORatio = fixedPointMul( - ratio_A, - ethers.BigNumber.from(10).pow(18 + tokenADecimals - tokenBDecimals) - ).add(ethers.BigNumber.from(1)); // Rounded Up - - const takeOrdersConfigStruct: TakeOrdersConfigStruct = { - output: tokenA06.address, - input: tokenB18.address, - minimumInput: depositAmountB, // 2 orders, without outputMax limit this would be depositAmountB.mul(2) - maximumInput: depositAmountB, - maximumIORatio, - orders: [takeOrderConfigStructAlice], - }; - - // We want Carol to only approve exactly what is necessary to take the orders. We scale the tokenB deposit amount 'up' by the difference between A decimals and B decimals. - const depositAmountA = fixedPointMul(depositAmountB, maximumIORatio); - - await tokenA06.transfer(bob.address, depositAmountA); // 2 orders - await tokenA06.connect(bob).approve(orderBook.address, depositAmountA); // 2 orders - - const txTakeOrders = await orderBook - .connect(bob) - .takeOrders(takeOrdersConfigStruct); - - const { sender, config, input, output } = (await getEventArgs( - txTakeOrders, - "TakeOrder", - orderBook - )) as TakeOrderEvent["args"]; - - assert(sender === bob.address, "wrong sender"); - assert(input.eq(depositAmountB), "wrong input"); - assert(output.eq(depositAmountA), "wrong output"); - - compareStructs(config, takeOrderConfigStructAlice); - - const tokenAAliceBalance = await tokenA06.balanceOf(alice.address); - const tokenBAliceBalance = await tokenB18.balanceOf(alice.address); - const tokenABobBalance = await tokenA06.balanceOf(bob.address); - const tokenBBobBalance = await tokenB18.balanceOf(bob.address); - - assert(tokenAAliceBalance.isZero()); // Alice has not yet withdrawn - assert(tokenBAliceBalance.isZero()); - assert(tokenABobBalance.isZero()); - assert(tokenBBobBalance.eq(depositAmountB)); - - await orderBook.connect(alice).withdraw({ - token: tokenA06.address, - vaultId: aliceInputVault, - amount: depositAmountA, - }); - - const tokenAAliceBalanceWithdrawn = await tokenA06.balanceOf(alice.address); - assert(tokenAAliceBalanceWithdrawn.eq(depositAmountA)); - }); -}); diff --git a/test/OrderBook/vaultBalance.ts b/test/OrderBook/vaultBalance.ts deleted file mode 100644 index 0f55ae755..000000000 --- a/test/OrderBook/vaultBalance.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { strict as assert } from "assert"; - -import { ethers } from "hardhat"; -import type { ReserveToken18 } from "../../typechain"; -import { DepositConfigStruct } from "../../typechain/contracts/orderbook/OrderBook"; -import { randomUint256 } from "../../utils/bytes"; -import { eighteenZeros } from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../utils/deploy/registry1820/deploy"; - -describe("OrderBook vaultBalance", async function () { - let tokenA: ReserveToken18; - let tokenB: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - tokenB = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - await tokenB.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should record vault balance and allow reading balance via getter", async function () { - const signers = await ethers.getSigners(); - - const [, alice] = signers; - - const orderBook = await deployOrderBook(); - - const aliceInputVault = ethers.BigNumber.from(randomUint256()); - const aliceOutputVault = ethers.BigNumber.from(randomUint256()); - - const amountA = ethers.BigNumber.from("1000" + eighteenZeros); - const amountB = ethers.BigNumber.from("500" + eighteenZeros); - - await tokenA.transfer(alice.address, amountA); - await tokenB.transfer(alice.address, amountB); - - const depositConfigStructAliceA: DepositConfigStruct = { - token: tokenA.address, - vaultId: aliceOutputVault, - amount: amountA, - }; - const depositConfigStructAliceB: DepositConfigStruct = { - token: tokenB.address, - vaultId: aliceOutputVault, - amount: amountB, - }; - - await tokenA - .connect(alice) - .approve(orderBook.address, depositConfigStructAliceA.amount); - await tokenB - .connect(alice) - .approve(orderBook.address, depositConfigStructAliceB.amount); - - // Alice deposits tokenA into her output vault - await orderBook.connect(alice).deposit(depositConfigStructAliceA); - await orderBook.connect(alice).deposit(depositConfigStructAliceB); - - const _vaultBalanceInputA = await orderBook.vaultBalance( - alice.address, - tokenA.address, - aliceInputVault - ); - const _vaultBalanceInputB = await orderBook.vaultBalance( - alice.address, - tokenB.address, - aliceInputVault - ); - - const _vaultBalanceOutputA = await orderBook.vaultBalance( - alice.address, - tokenA.address, - aliceOutputVault - ); - const _vaultBalanceOutputB = await orderBook.vaultBalance( - alice.address, - tokenB.address, - aliceOutputVault - ); - - assert(_vaultBalanceInputA.isZero()); - assert(_vaultBalanceInputB.isZero()); - assert(_vaultBalanceOutputA.eq(amountA)); - assert(_vaultBalanceOutputB.eq(amountB)); - }); -}); diff --git a/test/OrderBook/withdraw.ts b/test/OrderBook/withdraw.ts deleted file mode 100644 index b3785296f..000000000 --- a/test/OrderBook/withdraw.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { strict as assert } from "assert"; - -import { ethers } from "hardhat"; -import type { ReserveToken18 } from "../../typechain"; -import { - DepositConfigStruct, - DepositEvent, - WithdrawConfigStruct, - WithdrawEvent, -} from "../../typechain/contracts/orderbook/OrderBook"; - -import { eighteenZeros } from "../../utils/constants/bigNumber"; -import { basicDeploy } from "../../utils/deploy/basicDeploy"; -import { deployOrderBook } from "../../utils/deploy/orderBook/deploy"; -import deploy1820 from "../../utils/deploy/registry1820/deploy"; - -import { getEventArgs } from "../../utils/events"; -import { compareStructs } from "../../utils/test/compareStructs"; - -describe("OrderBook withdraw from vault", async function () { - let tokenA: ReserveToken18; - - beforeEach(async () => { - tokenA = (await basicDeploy("ReserveToken18", {})) as ReserveToken18; - await tokenA.initialize(); - }); - - before(async () => { - // Deploy ERC1820Registry - const signers = await ethers.getSigners(); - await deploy1820(signers[0]); - }); - - it("should allow withdrawals from vaults", async function () { - const signers = await ethers.getSigners(); - const [, alice] = signers; - - const orderBook = await deployOrderBook(); - const vaultId = ethers.BigNumber.from(1); - - // DEPOSITS - - const amount = ethers.BigNumber.from("1000" + eighteenZeros); - await tokenA.transfer(alice.address, amount); - - const depositConfigStruct: DepositConfigStruct = { - token: tokenA.address, - vaultId, - amount, - }; - - await tokenA - .connect(alice) - .approve(orderBook.address, depositConfigStruct.amount); - - // Alice deposits tokenA into her non-append-only vault - const txDeposit = await orderBook - .connect(alice) - .deposit(depositConfigStruct); - - const { sender: depositSender, config: depositConfig } = - (await getEventArgs( - txDeposit, - "Deposit", - orderBook - )) as DepositEvent["args"]; - - assert(depositSender === alice.address); - compareStructs(depositConfig, depositConfigStruct); - - const aliceTokenABalance0 = await tokenA.balanceOf(alice.address); - - const withdrawConfigStruct: WithdrawConfigStruct = { - token: tokenA.address, - vaultId: vaultId, - amount, - }; - - const txWithdraw = await orderBook - .connect(alice) - .withdraw(withdrawConfigStruct); - - const { sender: withdrawSender, config: withdrawConfig } = - (await getEventArgs( - txWithdraw, - "Withdraw", - orderBook - )) as WithdrawEvent["args"]; - - assert(withdrawSender === alice.address); - compareStructs(withdrawConfig, withdrawConfigStruct); - - const aliceTokenABalance1 = await tokenA.balanceOf(alice.address); - - assert(aliceTokenABalance0.isZero()); - assert(aliceTokenABalance1.eq(amount)); - }); -}); diff --git a/test/Sale/construction.ts b/test/Sale/construction.ts index 171681107..ce8dfe7dd 100644 --- a/test/Sale/construction.ts +++ b/test/Sale/construction.ts @@ -218,7 +218,7 @@ describe("Sale construction", async function () { const deployerDiscoverableMetaConfig1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), + meta: getRainMetaDocumentFromContract("stake"), deployer: touchDeployer.address, }; diff --git a/test/Stake/construction.ts b/test/Stake/construction.ts index c2f3ba78e..054bc5851 100644 --- a/test/Stake/construction.ts +++ b/test/Stake/construction.ts @@ -153,7 +153,7 @@ describe("Stake construction", async function () { const deployerDiscoverableMetaConfig1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), + meta: getRainMetaDocumentFromContract("sale"), deployer: touchDeployer.address, }; diff --git a/test/Tier/CombineTier/erc165TierV2.ts b/test/Tier/CombineTier/erc165TierV2.ts index a013fa601..98d6667eb 100644 --- a/test/Tier/CombineTier/erc165TierV2.ts +++ b/test/Tier/CombineTier/erc165TierV2.ts @@ -211,7 +211,7 @@ describe("CombineTier ERC165 tests", async function () { ); const config1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), + meta: getRainMetaDocumentFromContract("sale"), deployer: touchDeployer.address, }; diff --git a/test/Verify/Auto/AutoApprove/construction.ts b/test/Verify/Auto/AutoApprove/construction.ts index 896a2253b..7ded4bf78 100644 --- a/test/Verify/Auto/AutoApprove/construction.ts +++ b/test/Verify/Auto/AutoApprove/construction.ts @@ -157,7 +157,7 @@ describe("AutoApprove construction", async function () { assert(!(autoApprove.address === zeroAddress), "autoApprove not deployed"); const config_1: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), + meta: getRainMetaDocumentFromContract("sale"), deployer: touchDeployer.address, }; diff --git a/utils/deploy/orderBook/deploy.ts b/utils/deploy/orderBook/deploy.ts deleted file mode 100644 index 4bada844c..000000000 --- a/utils/deploy/orderBook/deploy.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ethers } from "hardhat"; -import { OrderBook } from "../../../typechain"; -import { DeployerDiscoverableMetaV1ConstructionConfigStruct } from "../../../typechain/contracts/factory/CloneFactory"; - -import { getRainMetaDocumentFromContract } from "../../meta"; -import { getTouchDeployer } from "../interpreter/shared/rainterpreterExpressionDeployer/deploy"; - -export const deployOrderBook = async (): Promise => { - const touchDeployer = await getTouchDeployer(); - const config_: DeployerDiscoverableMetaV1ConstructionConfigStruct = { - meta: getRainMetaDocumentFromContract("orderbook"), - deployer: touchDeployer.address, - }; - const orderBookFactory = await ethers.getContractFactory("OrderBook", {}); - const orderBook = (await orderBookFactory.deploy(config_)) as OrderBook; - - return orderBook; -}; diff --git a/utils/meta/contract/getRainContractMeta.ts b/utils/meta/contract/getRainContractMeta.ts index 2fd1500ae..2cfe36f48 100644 --- a/utils/meta/contract/getRainContractMeta.ts +++ b/utils/meta/contract/getRainContractMeta.ts @@ -3,7 +3,6 @@ import FlowERC20 from "../../../contracts/flow/erc20/FlowERC20.meta.json"; import FlowERC721 from "../../../contracts/flow/erc721/FlowERC721.meta.json"; import FlowERC1155 from "../../../contracts/flow/erc1155/FlowERC1155.meta.json"; import Lobby from "../../../contracts/lobby/Lobby.meta.json"; -import Orderbook from "../../../contracts/orderbook/OrderBook.meta.json"; import Sale from "../../../contracts/sale/Sale.meta.json"; import Stake from "../../../contracts/stake/Stake.meta.json"; import CombineTier from "../../../contracts/tier/CombineTier.meta.json"; @@ -27,7 +26,6 @@ export const getRainContractMetaBytes = (contract: ContractMeta): string => { let meta; if (contract === "sale") meta = Sale; if (contract === "stake") meta = Stake; - if (contract === "orderbook") meta = Orderbook; if (contract === "flow") meta = Flow; if (contract === "flow20") meta = FlowERC20; if (contract === "flow721") meta = FlowERC721; @@ -66,9 +64,6 @@ export const validateContractMetaAgainstABI = ( if (contractName_ === "stake") { (meta = Stake), (name = "Stake"); } - if (contractName_ === "orderbook") { - (meta = Orderbook), (name = "OrderBook"); - } if (contractName_ === "flow") { (meta = Flow), (name = "Flow"); } diff --git a/utils/meta/rainMetaDocument/index.ts b/utils/meta/rainMetaDocument/index.ts index 8f48abe32..baa3e9023 100644 --- a/utils/meta/rainMetaDocument/index.ts +++ b/utils/meta/rainMetaDocument/index.ts @@ -68,7 +68,6 @@ export const getAbi = (contractName_: ContractMeta): string => { if (contractName_ === "sale") name = "Sale"; if (contractName_ === "stake") name = "Stake"; - if (contractName_ === "orderbook") name = "OrderBook"; if (contractName_ === "flow") name = "Flow"; if (contractName_ === "flow20") name = "FlowERC20"; if (contractName_ === "flow721") name = "FlowERC721"; diff --git a/utils/types/contractMeta.ts b/utils/types/contractMeta.ts index 0f09d1584..6c95585b6 100644 --- a/utils/types/contractMeta.ts +++ b/utils/types/contractMeta.ts @@ -1,7 +1,6 @@ export type ContractMeta = | "sale" | "stake" - | "orderbook" | "flow" | "flow20" | "flow721"