feat: adds royalty support for IERC2981 lookup addresses

* chore: copy LSSVMRouter.sol

* feat: creating router with royalties + integrating tests

* feat: adds robust swaps implementation with royalties

* feat: improves tests with royalty issuance checks

* feat: adds gas optimizations and event emission on royalty issuance

* feat: adds event emission on internal issuances

* feat: gas optimization on issueTokenRoyalties

* feat: rms double substraction

* feat: improved comments

* feat: avoids double transfers when no royalty

Co-authored-by: 0xng <87835144+0xng@users.noreply.github.com>
Co-authored-by: 0xGorilla <0xGorilla@defi.sucks>
This commit is contained in:
wei3erHase
2022-09-06 18:53:28 +02:00
committed by wei3erHase
co-authored by 0xng 0xGorilla
parent d52312975c
commit 346800b102
51 changed files with 4094 additions and 747 deletions
+968 -704
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -14,3 +14,9 @@
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/rari-capital/solmate
[submodule "lib/royalty-registry-solidity.git"]
path = lib/royalty-registry-solidity.git
url = https://github.com/manifoldxyz/royalty-registry-solidity.git
[submodule "lib/libraries-solidity"]
path = lib/libraries-solidity
url = https://github.com/manifoldxyz/libraries-solidity
+1
View File
@@ -15,6 +15,7 @@
"prettier-plugin-solidity": "^1.0.0-beta.13",
"rimraf": "^3.0.2"
},
"dependencies": {},
"scripts": {
"prepublishOnly": "copyfiles -u 1 \"./src/**/*.sol\" --exclude \"./src/test/**/*.sol\" ./",
"postpublish": "rimraf ./*.sol",
+3 -1
View File
@@ -1,4 +1,6 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
ds-test/=lib/ds-test/src/
solmate/=lib/solmate/src/
solmate/=lib/solmate/src/
manifoldxyz/=lib/royalty-registry-solidity.git/contracts
@manifoldxyz/=lib/
+39 -17
View File
@@ -368,6 +368,7 @@ contract LSSVMRouter {
)
external
payable
virtual
checkDeadline(deadline)
returns (uint256 remainingValue)
{
@@ -425,7 +426,13 @@ contract LSSVMRouter {
address payable ethRecipient,
address nftRecipient,
uint256 deadline
) public payable checkDeadline(deadline) returns (uint256 remainingValue) {
)
public
payable
virtual
checkDeadline(deadline)
returns (uint256 remainingValue)
{
remainingValue = msg.value;
uint256 pairCost;
CurveErrorCodes.Error error;
@@ -475,14 +482,19 @@ contract LSSVMRouter {
@param nftRecipient The address that will receive the NFT output
@param deadline The Unix timestamp (in seconds) at/after which the swap will revert
@return remainingValue The unspent token amount
*/
function robustSwapERC20ForAnyNFTs(
RobustPairSwapAny[] calldata swapList,
uint256 inputAmount,
address nftRecipient,
uint256 deadline
) external checkDeadline(deadline) returns (uint256 remainingValue) {
)
external
virtual
checkDeadline(deadline)
returns (uint256 remainingValue)
{
remainingValue = inputAmount;
uint256 pairCost;
CurveErrorCodes.Error error;
@@ -529,7 +541,7 @@ contract LSSVMRouter {
uint256 inputAmount,
address nftRecipient,
uint256 deadline
) public checkDeadline(deadline) returns (uint256 remainingValue) {
) public virtual checkDeadline(deadline) returns (uint256 remainingValue) {
remainingValue = inputAmount;
uint256 pairCost;
CurveErrorCodes.Error error;
@@ -576,7 +588,7 @@ contract LSSVMRouter {
RobustPairSwapSpecificForToken[] calldata swapList,
address payable tokenRecipient,
uint256 deadline
) public checkDeadline(deadline) returns (uint256 outputAmount) {
) public virtual checkDeadline(deadline) returns (uint256 outputAmount) {
// Try doing each swap
uint256 numSwaps = swapList.length;
for (uint256 i; i < numSwaps; ) {
@@ -627,7 +639,12 @@ contract LSSVMRouter {
*/
function robustSwapETHForSpecificNFTsAndNFTsToToken(
RobustPairNFTsFoTokenAndTokenforNFTsTrade calldata params
) external payable returns (uint256 remainingValue, uint256 outputAmount) {
)
external
payable
virtual
returns (uint256 remainingValue, uint256 outputAmount)
{
{
remainingValue = msg.value;
uint256 pairCost;
@@ -734,7 +751,12 @@ contract LSSVMRouter {
*/
function robustSwapERC20ForSpecificNFTsAndNFTsToToken(
RobustPairNFTsFoTokenAndTokenforNFTsTrade calldata params
) external payable returns (uint256 remainingValue, uint256 outputAmount) {
)
external
payable
virtual
returns (uint256 remainingValue, uint256 outputAmount)
{
{
remainingValue = params.inputAmount;
uint256 pairCost;
@@ -906,7 +928,7 @@ contract LSSVMRouter {
uint256 inputAmount,
address payable ethRecipient,
address nftRecipient
) internal returns (uint256 remainingValue) {
) internal virtual returns (uint256 remainingValue) {
remainingValue = inputAmount;
uint256 pairCost;
@@ -959,7 +981,7 @@ contract LSSVMRouter {
uint256 inputAmount,
address payable ethRecipient,
address nftRecipient
) internal returns (uint256 remainingValue) {
) internal virtual returns (uint256 remainingValue) {
remainingValue = inputAmount;
uint256 pairCost;
@@ -1002,7 +1024,7 @@ contract LSSVMRouter {
/**
@notice Internal function used to swap an ERC20 token for any NFTs
@dev Note that we don't need to query the pair's bonding curve first for pricing data because
we just calculate and take the required amount from the caller during swap time.
we just calculate and take the required amount from the caller during swap time.
However, we can't "pull" ETH, which is why for the ETH->NFT swaps, we need to calculate the pricing info
to figure out how much the router should send to the pool.
@param swapList The list of pairs and swap calldata
@@ -1014,7 +1036,7 @@ contract LSSVMRouter {
PairSwapAny[] calldata swapList,
uint256 inputAmount,
address nftRecipient
) internal returns (uint256 remainingValue) {
) internal virtual returns (uint256 remainingValue) {
remainingValue = inputAmount;
// Do swaps
@@ -1040,7 +1062,7 @@ contract LSSVMRouter {
/**
@notice Internal function used to swap an ERC20 token for specific NFTs
@dev Note that we don't need to query the pair's bonding curve first for pricing data because
we just calculate and take the required amount from the caller during swap time.
we just calculate and take the required amount from the caller during swap time.
However, we can't "pull" ETH, which is why for the ETH->NFT swaps, we need to calculate the pricing info
to figure out how much the router should send to the pool.
@param swapList The list of pairs and swap calldata
@@ -1052,7 +1074,7 @@ contract LSSVMRouter {
PairSwapSpecific[] calldata swapList,
uint256 inputAmount,
address nftRecipient
) internal returns (uint256 remainingValue) {
) internal virtual returns (uint256 remainingValue) {
remainingValue = inputAmount;
// Do swaps
@@ -1077,10 +1099,10 @@ contract LSSVMRouter {
/**
@notice Swaps NFTs for tokens, designed to be used for 1 token at a time
@dev Calling with multiple tokens is permitted, BUT minOutput will be
@dev Calling with multiple tokens is permitted, BUT minOutput will be
far from enough of a safety check because different tokens almost certainly have different unit prices.
@param swapList The list of pairs and swap calldata
@param minOutput The minimum number of tokens to be receieved frm the swaps
@param swapList The list of pairs and swap calldata
@param minOutput The minimum number of tokens to be receieved from the swaps
@param tokenRecipient The address that receives the tokens
@return outputAmount The number of tokens to be received
*/
@@ -1088,7 +1110,7 @@ contract LSSVMRouter {
PairSwapSpecific[] calldata swapList,
uint256 minOutput,
address payable tokenRecipient
) internal returns (uint256 outputAmount) {
) internal virtual returns (uint256 outputAmount) {
// Do swaps
uint256 numSwaps = swapList.length;
for (uint256 i; i < numSwaps; ) {
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.0;
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
contract Test2981 is ERC2981 {
constructor(address receiver, uint96 bps) {
_setDefaultRoyalty(receiver, bps);
}
}
+11
View File
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
import {RoyaltyRegistry} from "manifoldxyz/RoyaltyRegistry.sol";
// Gives more realistic scenarios where swaps have to go through multiple pools, for more accurate gas profiling
contract TestRoyaltyRegistry is RoyaltyRegistry {
}
@@ -0,0 +1,217 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;
import {DSTest} from "ds-test/test.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
import {RoyaltyRegistry} from "manifoldxyz/RoyaltyRegistry.sol";
import {ICurve} from "../../bonding-curves/ICurve.sol";
import {LSSVMPairFactory} from "../../LSSVMPairFactory.sol";
import {LSSVMPair} from "../../LSSVMPair.sol";
import {LSSVMPairETH} from "../../LSSVMPairETH.sol";
import {LSSVMPairERC20} from "../../LSSVMPairERC20.sol";
import {LSSVMPairEnumerableETH} from "../../LSSVMPairEnumerableETH.sol";
import {LSSVMPairMissingEnumerableETH} from "../../LSSVMPairMissingEnumerableETH.sol";
import {LSSVMPairEnumerableERC20} from "../../LSSVMPairEnumerableERC20.sol";
import {LSSVMPairMissingEnumerableERC20} from "../../LSSVMPairMissingEnumerableERC20.sol";
import {LSSVMRouterWithRoyalties, LSSVMRouter} from "../../LSSVMRouterWithRoyalties.sol";
import {IERC721Mintable} from "../interfaces/IERC721Mintable.sol";
import {ConfigurableWithRoyalties} from "../mixins/ConfigurableWithRoyalties.sol";
import {RouterCaller} from "../mixins/RouterCaller.sol";
// Gives more realistic scenarios where swaps have to go through multiple pools, for more accurate gas profiling
abstract contract RouterMultiPoolWithRoyalties is
DSTest,
ERC721Holder,
ConfigurableWithRoyalties,
RouterCaller
{
IERC721Mintable test721;
ERC2981 test2981;
RoyaltyRegistry royaltyRegistry;
ICurve bondingCurve;
LSSVMPairFactory factory;
LSSVMRouter router;
mapping(uint256 => LSSVMPair) pairs;
address payable constant feeRecipient = payable(address(69));
uint256 constant protocolFeeMultiplier = 3e15;
uint256 numInitialNFTs = 10;
function setUp() public {
bondingCurve = setupCurve();
test721 = setup721();
test2981 = setup2981();
royaltyRegistry = setupRoyaltyRegistry();
royaltyRegistry.setRoyaltyLookupAddress(
address(test721),
address(test2981)
);
LSSVMPairEnumerableETH enumerableETHTemplate = new LSSVMPairEnumerableETH();
LSSVMPairMissingEnumerableETH missingEnumerableETHTemplate = new LSSVMPairMissingEnumerableETH();
LSSVMPairEnumerableERC20 enumerableERC20Template = new LSSVMPairEnumerableERC20();
LSSVMPairMissingEnumerableERC20 missingEnumerableERC20Template = new LSSVMPairMissingEnumerableERC20();
factory = new LSSVMPairFactory(
enumerableETHTemplate,
missingEnumerableETHTemplate,
enumerableERC20Template,
missingEnumerableERC20Template,
feeRecipient,
protocolFeeMultiplier
);
router = new LSSVMRouterWithRoyalties(factory);
factory.setBondingCurveAllowed(bondingCurve, true);
factory.setRouterAllowed(router, true);
// set NFT approvals
test721.setApprovalForAll(address(factory), true);
test721.setApprovalForAll(address(router), true);
// mint NFT #1-10 to caller
for (uint256 i = 1; i <= numInitialNFTs; i++) {
test721.mint(address(this), i);
}
// Pair 1 has NFT#1 at 1 ETH price, willing to also buy at the same price
// Pair 2 has NFT#2 at 2 ETH price, willing to also buy at the same price
// Pair 3 has NFT#3 at 3 ETH price, willing to also buy at the same price
// Pair 4 has NFT#4 at 4 ETH price, willing to also buy at the same price
// Pair 5 has NFT#5 at 5 ETH price, willing to also buy at the same price
// For all, assume no price changes
for (uint256 i = 1; i <= 5; i++) {
uint256[] memory idList = new uint256[](1);
idList[0] = i;
pairs[i] = this.setupPair{value: modifyInputAmount(i * 1 ether)}(
factory,
test721,
bondingCurve,
payable(address(0)),
LSSVMPair.PoolType.TRADE,
modifyDelta(0),
0,
uint128(i * 1 ether),
idList,
(i * 1 ether),
address(router)
);
}
}
function test_swapTokenForAny5NFTs() public {
// Swap across all 5 pools
LSSVMRouter.PairSwapAny[]
memory swapList = new LSSVMRouter.PairSwapAny[](5);
uint256 totalInputAmount = 0;
uint256 totalRoyaltyAmount = 0;
for (uint256 i = 0; i < 5; i++) {
uint256 inputAmount;
(, , , inputAmount, ) = pairs[i + 1].getBuyNFTQuote(1);
// calculate royalty and add it to the input amount
uint256 royaltyAmount = calcRoyalty(inputAmount);
inputAmount += royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
totalInputAmount += inputAmount;
swapList[i] = LSSVMRouter.PairSwapAny({
pair: pairs[i + 1],
numItems: 1
});
}
uint256 startBalance = test721.balanceOf(address(this));
this.swapTokenForAnyNFTs{value: modifyInputAmount(totalInputAmount)}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
totalInputAmount
);
uint256 endBalance = test721.balanceOf(address(this));
require((endBalance - startBalance) == 5, "Too few NFTs acquired");
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
function test_swapTokenForSpecific5NFTs() public {
// Swap across all 5 pools
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](5);
uint256 totalInputAmount = 0;
uint256 totalRoyaltyAmount = 0;
for (uint256 i = 0; i < 5; i++) {
uint256 inputAmount;
(, , , inputAmount, ) = pairs[i + 1].getBuyNFTQuote(1);
// calculate royalty and add it to the input amount
uint256 royaltyAmount = calcRoyalty(inputAmount);
inputAmount += royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
totalInputAmount += inputAmount;
uint256[] memory nftIds = new uint256[](1);
nftIds[0] = i + 1;
swapList[i] = LSSVMRouter.PairSwapSpecific({
pair: pairs[i + 1],
nftIds: nftIds
});
}
uint256 startBalance = test721.balanceOf(address(this));
this.swapTokenForSpecificNFTs{
value: modifyInputAmount(totalInputAmount)
}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
totalInputAmount
);
uint256 endBalance = test721.balanceOf(address(this));
require((endBalance - startBalance) == 5, "Too few NFTs acquired");
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
function test_swap5NFTsForToken() public {
// Swap across all 5 pools
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](5);
uint256 totalOutputAmount = 0;
uint256 totalRoyaltyAmount = 0;
for (uint256 i = 0; i < 5; i++) {
uint256 outputAmount;
(, , , outputAmount, ) = pairs[i + 1].getSellNFTQuote(1);
// calculate royalty and rm it from the output amount
uint256 royaltyAmount = calcRoyalty(outputAmount);
outputAmount -= royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
totalOutputAmount += outputAmount;
uint256[] memory nftIds = new uint256[](1);
// Set it to be an ID we own
nftIds[0] = i + 6;
swapList[i] = LSSVMRouter.PairSwapSpecific({
pair: pairs[i + 1],
nftIds: nftIds
});
}
uint256 startBalance = test721.balanceOf(address(this));
router.swapNFTsForToken(
swapList,
totalOutputAmount,
payable(address(this)),
block.timestamp
);
uint256 endBalance = test721.balanceOf(address(this));
require((startBalance - endBalance) == 5, "Too few NFTs sold");
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
}
@@ -0,0 +1,508 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;
import {DSTest} from "ds-test/test.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
import {RoyaltyRegistry} from "manifoldxyz/RoyaltyRegistry.sol";
import {LinearCurve} from "../../bonding-curves/LinearCurve.sol";
import {ICurve} from "../../bonding-curves/ICurve.sol";
import {LSSVMPairFactory} from "../../LSSVMPairFactory.sol";
import {LSSVMPair} from "../../LSSVMPair.sol";
import {LSSVMPairETH} from "../../LSSVMPairETH.sol";
import {LSSVMPairERC20} from "../../LSSVMPairERC20.sol";
import {LSSVMPairEnumerableETH} from "../../LSSVMPairEnumerableETH.sol";
import {LSSVMPairMissingEnumerableETH} from "../../LSSVMPairMissingEnumerableETH.sol";
import {LSSVMPairEnumerableERC20} from "../../LSSVMPairEnumerableERC20.sol";
import {LSSVMPairMissingEnumerableERC20} from "../../LSSVMPairMissingEnumerableERC20.sol";
import {LSSVMRouterWithRoyalties, LSSVMRouter} from "../../LSSVMRouterWithRoyalties.sol";
import {IERC721Mintable} from "../interfaces/IERC721Mintable.sol";
import {Hevm} from "../utils/Hevm.sol";
import {ConfigurableWithRoyalties} from "../mixins/ConfigurableWithRoyalties.sol";
import {RouterCaller} from "../mixins/RouterCaller.sol";
abstract contract RouterRobustSwapWithRoyalties is
DSTest,
ERC721Holder,
ConfigurableWithRoyalties,
RouterCaller
{
IERC721Mintable test721;
ERC2981 test2981;
RoyaltyRegistry royaltyRegistry;
ICurve bondingCurve;
LSSVMPairFactory factory;
LSSVMRouter router;
// Create 3 pairs
LSSVMPair pair1;
LSSVMPair pair2;
LSSVMPair pair3;
address payable constant feeRecipient = payable(address(69));
// Set protocol fee to be 10%
uint256 constant protocolFeeMultiplier = 1e17;
function setUp() public {
// Create contracts
bondingCurve = setupCurve();
test721 = setup721();
test2981 = setup2981();
royaltyRegistry = setupRoyaltyRegistry();
royaltyRegistry.setRoyaltyLookupAddress(
address(test721),
address(test2981)
);
LSSVMPairEnumerableETH enumerableETHTemplate = new LSSVMPairEnumerableETH();
LSSVMPairMissingEnumerableETH missingEnumerableETHTemplate = new LSSVMPairMissingEnumerableETH();
LSSVMPairEnumerableERC20 enumerableERC20Template = new LSSVMPairEnumerableERC20();
LSSVMPairMissingEnumerableERC20 missingEnumerableERC20Template = new LSSVMPairMissingEnumerableERC20();
factory = new LSSVMPairFactory(
enumerableETHTemplate,
missingEnumerableETHTemplate,
enumerableERC20Template,
missingEnumerableERC20Template,
feeRecipient,
protocolFeeMultiplier
);
router = new LSSVMRouterWithRoyalties(factory);
// Set approvals
test721.setApprovalForAll(address(factory), true);
test721.setApprovalForAll(address(router), true);
factory.setBondingCurveAllowed(bondingCurve, true);
factory.setRouterAllowed(router, true);
uint256[] memory empty;
uint256 nftIndex = 0;
// Create 3 pairs with 0 delta and 0 trade fee
// pair 1 has spot price of 0.1 TOKEN, then pair 2 has 0.2 TOKEN, and pair 3 has 0.3 TOKEN
// Send 10 NFTs to each pair
// (0-9), (10-19), (20-29)
pair1 = this.setupPair{value: modifyInputAmount(10 ether)}(
factory,
test721,
bondingCurve,
payable(address(0)),
LSSVMPair.PoolType.TRADE,
modifyDelta(0),
0,
0.1 ether,
empty,
10 ether,
address(router)
);
for (uint256 j = 0; j < 10; j++) {
test721.mint(address(this), nftIndex);
test721.safeTransferFrom(address(this), address(pair1), nftIndex);
nftIndex++;
}
pair2 = this.setupPair{value: modifyInputAmount(10 ether)}(
factory,
test721,
bondingCurve,
payable(address(0)),
LSSVMPair.PoolType.TRADE,
modifyDelta(0),
0,
0.2 ether,
empty,
10 ether,
address(router)
);
for (uint256 j = 0; j < 10; j++) {
test721.mint(address(this), nftIndex);
test721.safeTransferFrom(address(this), address(pair2), nftIndex);
nftIndex++;
}
pair3 = this.setupPair{value: modifyInputAmount(10 ether)}(
factory,
test721,
bondingCurve,
payable(address(0)),
LSSVMPair.PoolType.TRADE,
modifyDelta(0),
0,
0.3 ether,
empty,
10 ether,
address(router)
);
for (uint256 j = 0; j < 10; j++) {
test721.mint(address(this), nftIndex);
test721.safeTransferFrom(address(this), address(pair3), nftIndex);
nftIndex++;
}
// Mint NFTs 30-39 to this contract
for (uint256 i = 0; i < 10; i++) {
test721.mint(address(this), nftIndex);
nftIndex++;
}
}
// Test where pair 1 and pair 2 swap tokens for NFT succeed but pair 3 fails
function test_robustSwapTokenForAny2NFTs() public {
LSSVMRouter.RobustPairSwapAny[]
memory swapList = new LSSVMRouter.RobustPairSwapAny[](3);
(, , , uint256 pair1InputAmount, ) = pair1.getBuyNFTQuote(2);
(, , , uint256 pair2InputAmount, ) = pair2.getBuyNFTQuote(2);
uint256 totalRoyaltyAmount = 0;
// calculate royalty and add it to the input amount
uint256 royaltyAmount = calcRoyalty(pair1InputAmount);
pair1InputAmount += royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
royaltyAmount = calcRoyalty(pair2InputAmount);
pair2InputAmount += royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
swapList[0] = LSSVMRouter.RobustPairSwapAny({
swapInfo: LSSVMRouter.PairSwapAny({pair: pair1, numItems: 2}),
maxCost: pair2InputAmount
});
swapList[1] = LSSVMRouter.RobustPairSwapAny({
swapInfo: LSSVMRouter.PairSwapAny({pair: pair2, numItems: 2}),
maxCost: pair2InputAmount
});
swapList[2] = LSSVMRouter.RobustPairSwapAny({
swapInfo: LSSVMRouter.PairSwapAny({pair: pair3, numItems: 2}),
maxCost: pair2InputAmount
});
uint256 beforeNFTBalance = test721.balanceOf(address(this));
// Expect to have the first two swapPairs succeed, and the last one silently fail
// with 10% protocol fee:
uint256 remainingValue = this.robustSwapTokenForAnyNFTs{
value: modifyInputAmount(pair2InputAmount * 3)
}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
pair2InputAmount * 3
);
uint256 afterNFTBalance = test721.balanceOf(address(this));
// If the first two swap pairs succeed, we gain 4 NFTs
assertEq((afterNFTBalance - beforeNFTBalance), 4, "Incorrect NFT swap");
assertEq(
remainingValue,
pair2InputAmount * 3 - (pair1InputAmount + pair2InputAmount),
"Incorrect refund"
);
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
// Test where pair 1 and pair 2 swap tokens for NFT succeed but pair 3 fails
function test_robustSwapTokenFor2SpecificNFTs() public {
uint256 totalRoyaltyAmount = 0;
uint256[] memory nftIds1 = new uint256[](2);
nftIds1[0] = 0;
nftIds1[1] = 1;
uint256[] memory nftIds2 = new uint256[](2);
nftIds2[0] = 10;
nftIds2[1] = 11;
uint256[] memory nftIds3 = new uint256[](2);
nftIds3[0] = 20;
nftIds3[1] = 21;
(, , , uint256 pair1InputAmount, ) = pair1.getBuyNFTQuote(2);
(, , , uint256 pair2InputAmount, ) = pair2.getBuyNFTQuote(2);
// calculate royalty and add it to the input amount
uint256 royaltyAmount = calcRoyalty(pair1InputAmount);
pair1InputAmount += royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
royaltyAmount = calcRoyalty(pair2InputAmount);
pair2InputAmount += royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
LSSVMRouter.RobustPairSwapSpecific[]
memory swapList = new LSSVMRouter.RobustPairSwapSpecific[](3);
swapList[0] = LSSVMRouter.RobustPairSwapSpecific({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair1,
nftIds: nftIds1
}),
maxCost: pair2InputAmount
});
swapList[1] = LSSVMRouter.RobustPairSwapSpecific({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair2,
nftIds: nftIds2
}),
maxCost: pair2InputAmount
});
swapList[2] = LSSVMRouter.RobustPairSwapSpecific({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair3,
nftIds: nftIds3
}),
maxCost: pair2InputAmount
});
uint256 beforeNFTBalance = test721.balanceOf(address(this));
// Expect to have the first two swapPairs succeed, and the last one silently fail
// with 10% protocol fee:
uint256 remainingValue = this.robustSwapTokenForSpecificNFTs{
value: modifyInputAmount(pair2InputAmount * 3)
}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
pair2InputAmount * 3
);
uint256 afterNFTBalance = test721.balanceOf(address(this));
// If the first two swap pairs succeed we gain 4 NFTs
assertEq((afterNFTBalance - beforeNFTBalance), 4, "Incorrect NFT swap");
assertEq(
remainingValue,
pair2InputAmount * 3 - (pair1InputAmount + pair2InputAmount),
"Incorrect ETH refund"
);
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
// Test where selling to pair 2 and pair 3 succeeds, but selling to pair 1 fails
function test_robustSwap2NFTsForToken() public {
uint256 totalRoyaltyAmount = 0;
uint256[] memory nftIds1 = new uint256[](2);
nftIds1[0] = 30;
nftIds1[1] = 31;
uint256[] memory nftIds2 = new uint256[](2);
nftIds2[0] = 32;
nftIds2[1] = 33;
uint256[] memory nftIds3 = new uint256[](2);
nftIds3[0] = 34;
nftIds3[1] = 35;
(, , , uint256 pair2OutputAmount, ) = pair2.getSellNFTQuote(2);
(, , , uint256 pair3OutputAmount, ) = pair3.getSellNFTQuote(2);
// calculate royalty and rm it from the input amount
uint256 royaltyAmount = calcRoyalty(pair2OutputAmount);
pair2OutputAmount -= royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
royaltyAmount = calcRoyalty(pair3OutputAmount);
pair3OutputAmount -= royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
LSSVMRouter.RobustPairSwapSpecificForToken[]
memory swapList = new LSSVMRouter.RobustPairSwapSpecificForToken[](
3
);
swapList[0] = LSSVMRouter.RobustPairSwapSpecificForToken({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair1,
nftIds: nftIds1
}),
minOutput: pair2OutputAmount
});
swapList[1] = LSSVMRouter.RobustPairSwapSpecificForToken({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair2,
nftIds: nftIds2
}),
minOutput: pair2OutputAmount
});
swapList[2] = LSSVMRouter.RobustPairSwapSpecificForToken({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair3,
nftIds: nftIds3
}),
minOutput: pair2OutputAmount
});
uint256 beforeNFTBalance = test721.balanceOf(address(this));
// Expect to have the last two swapPairs succeed, and the first one silently fail
// with 10% protocol fee:
uint256 remainingValue = router.robustSwapNFTsForToken(
swapList,
payable(address(this)),
block.timestamp
);
uint256 afterNFTBalance = test721.balanceOf(address(this));
assertEq((beforeNFTBalance - afterNFTBalance), 4, "Incorrect NFT swap");
assertEq(
remainingValue,
pair3OutputAmount + pair2OutputAmount,
"Incorrect ETH received"
);
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
// Test where selling to pair 2 succeeds,
// but selling to pair 1 fails due to slippage
// and selling to pair 3 fails due to a bonding curve error
function test_robustSwapNFTsForTokenWithBondingCurveError() public {
uint256[] memory nftIds1 = new uint256[](2);
nftIds1[0] = 30;
nftIds1[1] = 31;
uint256[] memory nftIds2 = new uint256[](2);
nftIds2[0] = 32;
nftIds2[1] = 33;
uint256[] memory nftIds3 = new uint256[](0);
(, , , uint256 pair2OutputAmount, ) = pair2.getSellNFTQuote(2);
// calculate royalty and rm it from the output amount
uint256 royaltyAmount = calcRoyalty(pair2OutputAmount);
pair2OutputAmount -= royaltyAmount;
LSSVMRouter.RobustPairSwapSpecificForToken[]
memory swapList = new LSSVMRouter.RobustPairSwapSpecificForToken[](
3
);
swapList[0] = LSSVMRouter.RobustPairSwapSpecificForToken({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair1,
nftIds: nftIds1
}),
minOutput: pair2OutputAmount
});
swapList[1] = LSSVMRouter.RobustPairSwapSpecificForToken({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair2,
nftIds: nftIds2
}),
minOutput: pair2OutputAmount
});
swapList[2] = LSSVMRouter.RobustPairSwapSpecificForToken({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair3,
nftIds: nftIds3
}),
minOutput: pair2OutputAmount
});
uint256 beforeNFTBalance = test721.balanceOf(address(this));
// Expect to have the last two swapPairs succeed, and the first one silently fail
// with 10% protocol fee:
uint256 remainingValue = router.robustSwapNFTsForToken(
swapList,
payable(address(this)),
block.timestamp
);
uint256 afterNFTBalance = test721.balanceOf(address(this));
assertEq((beforeNFTBalance - afterNFTBalance), 2, "Incorrect NFT swap");
assertEq(remainingValue, pair2OutputAmount, "Incorrect ETH received");
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), royaltyAmount);
}
// Test where we buy and sell in the same tx
function test_robustSwapNFTsForTokenAndTokenForNFTs() public {
uint256 totalRoyaltyAmount = 0;
// Check that we own #0 and #1, and that we don't own #32 and #33
assertEq(test721.ownerOf(0), address(pair1));
assertEq(test721.ownerOf(1), address(pair1));
assertEq(test721.ownerOf(32), address(this));
assertEq(test721.ownerOf(33), address(this));
(, , , uint256 pair1InputAmount, ) = pair1.getBuyNFTQuote(2);
(, , , uint256 pair2OutputAmount, ) = pair2.getSellNFTQuote(2);
// calculate royalty and modify input and output amounts
uint256 royaltyAmount = calcRoyalty(pair1InputAmount);
pair1InputAmount += royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
royaltyAmount = calcRoyalty(pair2OutputAmount);
pair2OutputAmount -= royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
uint256[] memory nftIds1 = new uint256[](2);
nftIds1[0] = 0;
nftIds1[1] = 1;
LSSVMRouter.RobustPairSwapSpecific[]
memory tokenToNFTSwapList = new LSSVMRouter.RobustPairSwapSpecific[](
1
);
tokenToNFTSwapList[0] = LSSVMRouter.RobustPairSwapSpecific({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair1,
nftIds: nftIds1
}),
maxCost: pair1InputAmount
});
// We queue up a NFT->Token swap that should work
uint256[] memory nftIds2 = new uint256[](2);
nftIds2[0] = 32;
nftIds2[1] = 33;
LSSVMRouter.RobustPairSwapSpecificForToken[]
memory nftToTokenSwapList = new LSSVMRouter.RobustPairSwapSpecificForToken[](
1
);
nftToTokenSwapList[0] = LSSVMRouter.RobustPairSwapSpecificForToken({
swapInfo: LSSVMRouter.PairSwapSpecific({
pair: pair2,
nftIds: nftIds2
}),
minOutput: pair2OutputAmount
});
// Do the swap
uint256 inputAmount = pair1InputAmount;
this.robustSwapTokenForSpecificNFTsAndNFTsForTokens{
value: modifyInputAmount(inputAmount)
}(
router,
LSSVMRouter.RobustPairNFTsFoTokenAndTokenforNFTsTrade({
nftToTokenTrades: nftToTokenSwapList,
tokenToNFTTrades: tokenToNFTSwapList,
inputAmount: inputAmount,
tokenRecipient: payable(address(this)),
nftRecipient: address(this)
})
);
// Check that we own #0 and #1, and that we don't own #32 and #33
assertEq(test721.ownerOf(0), address(this));
assertEq(test721.ownerOf(1), address(this));
assertEq(test721.ownerOf(32), address(pair2));
assertEq(test721.ownerOf(33), address(pair2));
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
}
@@ -0,0 +1,546 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;
import {DSTest} from "ds-test/test.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
import {RoyaltyRegistry} from "manifoldxyz/RoyaltyRegistry.sol";
import {ICurve} from "../../bonding-curves/ICurve.sol";
import {LSSVMPairFactory} from "../../LSSVMPairFactory.sol";
import {LSSVMPair} from "../../LSSVMPair.sol";
import {LSSVMPairETH} from "../../LSSVMPairETH.sol";
import {LSSVMPairERC20} from "../../LSSVMPairERC20.sol";
import {LSSVMPairEnumerableETH} from "../../LSSVMPairEnumerableETH.sol";
import {LSSVMPairMissingEnumerableETH} from "../../LSSVMPairMissingEnumerableETH.sol";
import {LSSVMPairEnumerableERC20} from "../../LSSVMPairEnumerableERC20.sol";
import {LSSVMPairMissingEnumerableERC20} from "../../LSSVMPairMissingEnumerableERC20.sol";
import {LSSVMRouterWithRoyalties, LSSVMRouter} from "../../LSSVMRouterWithRoyalties.sol";
import {IERC721Mintable} from "../interfaces/IERC721Mintable.sol";
import {ConfigurableWithRoyalties} from "../mixins/ConfigurableWithRoyalties.sol";
import {RouterCaller} from "../mixins/RouterCaller.sol";
abstract contract RouterSinglePoolWithRoyalties is
DSTest,
ERC721Holder,
ConfigurableWithRoyalties,
RouterCaller
{
IERC721Mintable test721;
ERC2981 test2981;
RoyaltyRegistry royaltyRegistry;
ICurve bondingCurve;
LSSVMPairFactory factory;
LSSVMRouter router;
LSSVMPair pair;
address payable constant feeRecipient = payable(address(69));
uint256 constant protocolFeeMultiplier = 3e15;
uint256 constant numInitialNFTs = 10;
function setUp() public {
bondingCurve = setupCurve();
test721 = setup721();
test2981 = setup2981();
royaltyRegistry = setupRoyaltyRegistry();
royaltyRegistry.setRoyaltyLookupAddress(
address(test721),
address(test2981)
);
LSSVMPairEnumerableETH enumerableETHTemplate = new LSSVMPairEnumerableETH();
LSSVMPairMissingEnumerableETH missingEnumerableETHTemplate = new LSSVMPairMissingEnumerableETH();
LSSVMPairEnumerableERC20 enumerableERC20Template = new LSSVMPairEnumerableERC20();
LSSVMPairMissingEnumerableERC20 missingEnumerableERC20Template = new LSSVMPairMissingEnumerableERC20();
factory = new LSSVMPairFactory(
enumerableETHTemplate,
missingEnumerableETHTemplate,
enumerableERC20Template,
missingEnumerableERC20Template,
feeRecipient,
protocolFeeMultiplier
);
router = new LSSVMRouterWithRoyalties(factory);
factory.setBondingCurveAllowed(bondingCurve, true);
factory.setRouterAllowed(router, true);
// set NFT approvals
test721.setApprovalForAll(address(factory), true);
test721.setApprovalForAll(address(router), true);
// Setup pair parameters
uint128 delta = 0 ether;
uint128 spotPrice = 1 ether;
uint256[] memory idList = new uint256[](numInitialNFTs);
for (uint256 i = 1; i <= numInitialNFTs; i++) {
test721.mint(address(this), i);
idList[i - 1] = i;
}
// Create a pair with a spot price of 1 eth, 10 NFTs, and no price increases
pair = this.setupPair{value: modifyInputAmount(10 ether)}(
factory,
test721,
bondingCurve,
payable(address(0)),
LSSVMPair.PoolType.TRADE,
modifyDelta(uint64(delta)),
0,
spotPrice,
idList,
10 ether,
address(router)
);
// mint extra NFTs to this contract (i.e. to be held by the caller)
for (uint256 i = numInitialNFTs + 1; i <= 2 * numInitialNFTs; i++) {
test721.mint(address(this), i);
}
}
function test_swapTokenForSingleAnyNFT() public {
LSSVMRouter.PairSwapAny[]
memory swapList = new LSSVMRouter.PairSwapAny[](1);
swapList[0] = LSSVMRouter.PairSwapAny({pair: pair, numItems: 1});
uint256 inputAmount;
(, , , inputAmount, ) = pair.getBuyNFTQuote(1);
// calculate royalty and add it to the input amount
uint256 royaltyAmount = calcRoyalty(inputAmount);
inputAmount += royaltyAmount;
this.swapTokenForAnyNFTs{value: modifyInputAmount(inputAmount)}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), royaltyAmount);
}
function test_swapTokenForSingleSpecificNFT() public {
uint256[] memory nftIds = new uint256[](1);
nftIds[0] = 1;
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](1);
swapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: nftIds
});
uint256 inputAmount;
(, , , inputAmount, ) = pair.getBuyNFTQuote(1);
// calculate royalty and add it to the input amount
uint256 royaltyAmount = calcRoyalty(inputAmount);
inputAmount += royaltyAmount;
this.swapTokenForSpecificNFTs{value: modifyInputAmount(inputAmount)}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), royaltyAmount);
}
function test_swapSingleNFTForToken() public {
(, , , uint256 outputAmount, ) = pair.getSellNFTQuote(1);
// calculate royalty and rm it from the output amount
uint256 royaltyAmount = calcRoyalty(outputAmount);
outputAmount -= outputAmount;
uint256[] memory nftIds = new uint256[](1);
nftIds[0] = numInitialNFTs + 1;
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](1);
swapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: nftIds
});
router.swapNFTsForToken(
swapList,
outputAmount,
payable(address(this)),
block.timestamp
);
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), royaltyAmount);
}
function testGas_swapSingleNFTForToken5Times() public {
uint256 totalRoyaltyAmount;
for (uint256 i = 1; i <= 5; i++) {
(, , , uint256 outputAmount, ) = pair.getSellNFTQuote(1);
// calculate royalty and rm it from the output amount
uint256 royaltyAmount = calcRoyalty(outputAmount);
outputAmount -= royaltyAmount;
totalRoyaltyAmount += royaltyAmount;
uint256[] memory nftIds = new uint256[](1);
nftIds[0] = numInitialNFTs + i;
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](1);
swapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: nftIds
});
router.swapNFTsForToken(
swapList,
outputAmount,
payable(address(this)),
block.timestamp
);
}
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
function test_swapSingleNFTForAnyNFT() public {
uint256 totalRoyaltyAmount;
// construct NFT to Token swap list
uint256[] memory sellNFTIds = new uint256[](1);
sellNFTIds[0] = numInitialNFTs + 1;
LSSVMRouter.PairSwapSpecific[]
memory nftToTokenSwapList = new LSSVMRouter.PairSwapSpecific[](1);
nftToTokenSwapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: sellNFTIds
});
(, , , uint256 salePrice, ) = nftToTokenSwapList[0]
.pair
.getSellNFTQuote(sellNFTIds.length);
totalRoyaltyAmount += calcRoyalty(salePrice);
// construct Token to NFT swap list
LSSVMRouter.PairSwapAny[]
memory tokenToNFTSwapList = new LSSVMRouter.PairSwapAny[](1);
tokenToNFTSwapList[0] = LSSVMRouter.PairSwapAny({
pair: pair,
numItems: 1
});
(, , , uint256 buyPrice, ) = tokenToNFTSwapList[0].pair.getBuyNFTQuote(
1
);
totalRoyaltyAmount += calcRoyalty(buyPrice);
// NOTE: We send some tokens (more than enough) to cover the protocol fee needed
uint256 inputAmount = 0.01 ether;
inputAmount += totalRoyaltyAmount;
this.swapNFTsForAnyNFTsThroughToken{
value: modifyInputAmount(inputAmount)
}(
router,
LSSVMRouter.NFTsForAnyNFTsTrade({
nftToTokenTrades: nftToTokenSwapList,
tokenToNFTTrades: tokenToNFTSwapList
}),
0,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
// check that royalty has been issued
require(
getBalance(ROYALTY_RECEIVER) <=
(totalRoyaltyAmount * 1_010) / 1_000,
"too much"
);
require(
getBalance(ROYALTY_RECEIVER) >=
(totalRoyaltyAmount * 1_000) / 1_500,
"too less"
);
/* NOTE: test is failing with XykCurve
* reason: buyQuote is quoted before the nfts are sold
* recurring to proximity tests
*/
// assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
function test_swapSingleNFTForSpecificNFT() public {
uint256 totalRoyaltyAmount;
// construct NFT to token swap list
uint256[] memory sellNFTIds = new uint256[](1);
sellNFTIds[0] = numInitialNFTs + 1;
LSSVMRouter.PairSwapSpecific[]
memory nftToTokenSwapList = new LSSVMRouter.PairSwapSpecific[](1);
nftToTokenSwapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: sellNFTIds
});
(, , , uint256 salePrice, ) = nftToTokenSwapList[0]
.pair
.getSellNFTQuote(sellNFTIds.length);
totalRoyaltyAmount += calcRoyalty(salePrice);
// construct token to NFT swap list
uint256[] memory buyNFTIds = new uint256[](1);
buyNFTIds[0] = 1;
LSSVMRouter.PairSwapSpecific[]
memory tokenToNFTSwapList = new LSSVMRouter.PairSwapSpecific[](1);
tokenToNFTSwapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: buyNFTIds
});
(, , , uint256 buyPrice, ) = tokenToNFTSwapList[0].pair.getBuyNFTQuote(
buyNFTIds.length
);
totalRoyaltyAmount += calcRoyalty(buyPrice);
// NOTE: We send some tokens (more than enough) to cover the protocol fee
uint256 inputAmount = 0.01 ether;
inputAmount += totalRoyaltyAmount;
this.swapNFTsForSpecificNFTsThroughToken{
value: modifyInputAmount(inputAmount)
}(
router,
LSSVMRouter.NFTsForSpecificNFTsTrade({
nftToTokenTrades: nftToTokenSwapList,
tokenToNFTTrades: tokenToNFTSwapList
}),
0,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
// check that royalty has been issued
require(
getBalance(ROYALTY_RECEIVER) <=
(totalRoyaltyAmount * 1_010) / 1_000,
"too much"
);
require(
getBalance(ROYALTY_RECEIVER) >=
(totalRoyaltyAmount * 1_000) / 1_500,
"too less"
);
/* NOTE: test is failing with XykCurve
* reason: buyQuote is quoted before the nfts are sold
* recurring to proximity tests
*/
// assertEq(getBalance(ROYALTY_RECEIVER), totalRoyaltyAmount);
}
function test_swapTokenforAny5NFTs() public {
LSSVMRouter.PairSwapAny[]
memory swapList = new LSSVMRouter.PairSwapAny[](1);
swapList[0] = LSSVMRouter.PairSwapAny({pair: pair, numItems: 5});
uint256 startBalance = test721.balanceOf(address(this));
uint256 inputAmount;
(, , , inputAmount, ) = pair.getBuyNFTQuote(5);
// calculate royalty and add it to the input amount
uint256 royaltyAmount = calcRoyalty(inputAmount);
inputAmount += royaltyAmount;
this.swapTokenForAnyNFTs{value: modifyInputAmount(inputAmount)}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
uint256 endBalance = test721.balanceOf(address(this));
require((endBalance - startBalance) == 5, "Too few NFTs acquired");
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), royaltyAmount);
}
function test_swapTokenforSpecific5NFTs() public {
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](1);
uint256[] memory nftIds = new uint256[](5);
nftIds[0] = 1;
nftIds[1] = 2;
nftIds[2] = 3;
nftIds[3] = 4;
nftIds[4] = 5;
swapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: nftIds
});
uint256 startBalance = test721.balanceOf(address(this));
uint256 inputAmount;
(, , , inputAmount, ) = pair.getBuyNFTQuote(5);
// calculate royalty and add it to the input amount
uint256 royaltyAmount = calcRoyalty(inputAmount);
inputAmount += royaltyAmount;
this.swapTokenForSpecificNFTs{value: modifyInputAmount(inputAmount)}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
uint256 endBalance = test721.balanceOf(address(this));
require((endBalance - startBalance) == 5, "Too few NFTs acquired");
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), royaltyAmount);
}
function test_swap5NFTsForToken() public {
(, , , uint256 outputAmount, ) = pair.getSellNFTQuote(5);
// calculate royalty and rm it from the output amount
uint256 royaltyAmount = calcRoyalty(outputAmount);
outputAmount -= royaltyAmount;
uint256[] memory nftIds = new uint256[](5);
for (uint256 i = 0; i < 5; i++) {
nftIds[i] = numInitialNFTs + i + 1;
}
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](1);
swapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: nftIds
});
router.swapNFTsForToken(
swapList,
outputAmount,
payable(address(this)),
block.timestamp
);
// check that royalty has been issued
assertEq(getBalance(ROYALTY_RECEIVER), royaltyAmount);
}
function testFail_swapTokenForSingleAnyNFTSlippage() public {
LSSVMRouter.PairSwapAny[]
memory swapList = new LSSVMRouter.PairSwapAny[](1);
swapList[0] = LSSVMRouter.PairSwapAny({pair: pair, numItems: 1});
uint256 inputAmount;
(, , , inputAmount, ) = pair.getBuyNFTQuote(1);
inputAmount = addRoyalty(inputAmount);
inputAmount = inputAmount - 1 wei;
this.swapTokenForAnyNFTs{value: modifyInputAmount(inputAmount)}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
}
function testFail_swapTokenForSingleSpecificNFTSlippage() public {
uint256[] memory nftIds = new uint256[](1);
nftIds[0] = 1;
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](1);
swapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: nftIds
});
uint256 inputAmount;
(, , , inputAmount, ) = pair.getBuyNFTQuote(1);
inputAmount = addRoyalty(inputAmount);
inputAmount = inputAmount - 1 wei;
this.swapTokenForSpecificNFTs{value: modifyInputAmount(inputAmount)}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
}
function testFail_swapSingleNFTForNonexistentToken() public {
uint256[] memory nftIds = new uint256[](1);
nftIds[0] = numInitialNFTs + 1;
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](1);
swapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: nftIds
});
uint256 sellAmount;
(, , , sellAmount, ) = pair.getSellNFTQuote(1);
sellAmount = subRoyalty(sellAmount);
sellAmount = sellAmount + 1 wei;
router.swapNFTsForToken(
swapList,
sellAmount,
payable(address(this)),
block.timestamp
);
}
function testFail_swapTokenForAnyNFTsPastBalance() public {
uint256[] memory nftIds = new uint256[](1);
nftIds[0] = numInitialNFTs + 1;
LSSVMRouter.PairSwapAny[]
memory swapList = new LSSVMRouter.PairSwapAny[](1);
swapList[0] = LSSVMRouter.PairSwapAny({
pair: pair,
numItems: test721.balanceOf(address(pair)) + 1
});
uint256 inputAmount;
(, , , inputAmount, ) = pair.getBuyNFTQuote(
test721.balanceOf(address(pair)) + 1
);
inputAmount = addRoyalty(inputAmount);
inputAmount = inputAmount + 1 wei;
this.swapTokenForAnyNFTs{value: modifyInputAmount(inputAmount)}(
router,
swapList,
payable(address(this)),
address(this),
block.timestamp,
inputAmount
);
}
function testFail_swapSingleNFTForTokenWithEmptyList() public {
uint256[] memory nftIds = new uint256[](0);
LSSVMRouter.PairSwapSpecific[]
memory swapList = new LSSVMRouter.PairSwapSpecific[](1);
swapList[0] = LSSVMRouter.PairSwapSpecific({
pair: pair,
nftIds: nftIds
});
uint256 sellAmount;
(, , , sellAmount, ) = pair.getSellNFTQuote(1);
sellAmount = subRoyalty(sellAmount);
sellAmount = sellAmount + 1 wei;
router.swapNFTsForToken(
swapList,
sellAmount,
payable(address(this)),
block.timestamp
);
}
}
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;
import {Configurable, IERC721, LSSVMPair, ICurve, IERC721Mintable, LSSVMPairFactory} from "./Configurable.sol";
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
import {Test2981} from "../../mocks/Test2981.sol";
import {RoyaltyRegistry} from "manifoldxyz/RoyaltyRegistry.sol";
import {TestRoyaltyRegistry} from "../../mocks/TestRoyaltyRegistry.sol";
import {DSTest} from "ds-test/test.sol";
interface IVM {
function etch(address where, bytes memory what) external;
}
abstract contract ConfigurableWithRoyalties is Configurable, DSTest {
address public constant ROYALTY_RECEIVER = address(420);
uint96 public constant BPS = 30;
uint96 public constant BASE = 10_000;
function setup2981() public returns (ERC2981) {
return ERC2981(new Test2981(ROYALTY_RECEIVER, BPS));
}
// royalty registry address is set as constant in the contract
address constant ROYALTY_REGISTRY =
0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D;
function setupRoyaltyRegistry()
public
returns (RoyaltyRegistry royaltyRegistry)
{
royaltyRegistry = RoyaltyRegistry(new TestRoyaltyRegistry());
IVM(HEVM_ADDRESS).etch(ROYALTY_REGISTRY, address(royaltyRegistry).code);
royaltyRegistry = RoyaltyRegistry(ROYALTY_REGISTRY);
royaltyRegistry.initialize();
}
function addRoyalty(uint256 inputAmount)
public
pure
returns (uint256 outputAmount)
{
return inputAmount + calcRoyalty(inputAmount);
}
function subRoyalty(uint256 inputAmount)
public
pure
returns (uint256 outputAmount)
{
return inputAmount - calcRoyalty(inputAmount);
}
function calcRoyalty(uint256 inputAmount)
public
pure
returns (uint256 royaltyAmount)
{
royaltyAmount = (inputAmount * BPS) / BASE;
}
}
@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import {RouterPartialFill} from "../base/RouterPartialFill.sol";
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RPFExponentialCurveEnumerableERC20Test is
RouterPartialFill,
contract RMPWRExponentialCurveEnumerableERC20Test is
RouterMultiPoolWithRoyalties,
UsingExponentialCurve,
UsingEnumerable,
UsingERC20
@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import {RouterPartialFill} from "../base/RouterPartialFill.sol";
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RPFExponentialCurveEnumerableETHTest is
RouterPartialFill,
contract RMPWRExponentialCurveEnumerableETHTest is
RouterMultiPoolWithRoyalties,
UsingExponentialCurve,
UsingEnumerable,
UsingETH
@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import {RouterPartialFill} from "../base/RouterPartialFill.sol";
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RPFExponentialCurveMissingEnumerableERC20Test is
RouterPartialFill,
contract RMPWRExponentialCurveMissingEnumerableERC20Test is
RouterMultiPoolWithRoyalties,
UsingExponentialCurve,
UsingMissingEnumerable,
UsingERC20
@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import {RouterPartialFill} from "../base/RouterPartialFill.sol";
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RPFExponentialCurveMissingEnumerableETHTest is
RouterPartialFill,
contract RMPWRExponentialCurveMissingEnumerableETHTest is
RouterMultiPoolWithRoyalties,
UsingExponentialCurve,
UsingMissingEnumerable,
UsingETH
@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import {RouterPartialFill} from "../base/RouterPartialFill.sol";
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RPFLinearCurveEnumerableERC20Test is
RouterPartialFill,
contract RMPWRLinearCurveEnumerableERC20Test is
RouterMultiPoolWithRoyalties,
UsingLinearCurve,
UsingEnumerable,
UsingERC20
@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import {RouterPartialFill} from "../base/RouterPartialFill.sol";
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RPFLinearCurveEnumerableETHTest is
RouterPartialFill,
contract RMPWRLinearCurveEnumerableETHTest is
RouterMultiPoolWithRoyalties,
UsingLinearCurve,
UsingEnumerable,
UsingETH
@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import {RouterPartialFill} from "../base/RouterPartialFill.sol";
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RPFLinearCurveMissingEnumerableERC20Test is
RouterPartialFill,
contract RMPWRLinearCurveMissingEnumerableERC20Test is
RouterMultiPoolWithRoyalties,
UsingLinearCurve,
UsingMissingEnumerable,
UsingERC20
@@ -2,13 +2,13 @@
pragma solidity ^0.8.0;
import {RouterPartialFill} from "../base/RouterPartialFill.sol";
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RPFLinearCurveMissingEnumerableETHTest is
RouterPartialFill,
contract RMPWRLinearCurveMissingEnumerableETHTest is
RouterMultiPoolWithRoyalties,
UsingLinearCurve,
UsingMissingEnumerable,
UsingETH
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RMPWRXykCurveEnumerableERC20Test is
RouterMultiPoolWithRoyalties,
UsingXykCurve,
UsingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RMPWRXykCurveEnumerableETHTest is
RouterMultiPoolWithRoyalties,
UsingXykCurve,
UsingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RMPWRXykCurveMissingEnumerableERC20Test is
RouterMultiPoolWithRoyalties,
UsingXykCurve,
UsingMissingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterMultiPoolWithRoyalties} from "../base/RouterMultiPoolWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RMPWRXykCurveMissingEnumerableETHTest is
RouterMultiPoolWithRoyalties,
UsingXykCurve,
UsingMissingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RRSWRExponentialCurveEnumerableERC20Test is
RouterRobustSwapWithRoyalties,
UsingExponentialCurve,
UsingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RRSWRExponentialCurveEnumerableETHTest is
RouterRobustSwapWithRoyalties,
UsingExponentialCurve,
UsingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RRSWRExponentialCurveMissingEnumerableERC20Test is
RouterRobustSwapWithRoyalties,
UsingExponentialCurve,
UsingMissingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RRSWRExponentialCurveMissingEnumerableETHTest is
RouterRobustSwapWithRoyalties,
UsingExponentialCurve,
UsingMissingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RRSWRLinearCurveEnumerableERC20Test is
RouterRobustSwapWithRoyalties,
UsingLinearCurve,
UsingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RRSWRLinearCurveEnumerableETHTest is
RouterRobustSwapWithRoyalties,
UsingLinearCurve,
UsingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RRSWRLinearCurveMissingEnumerableERC20Test is
RouterRobustSwapWithRoyalties,
UsingLinearCurve,
UsingMissingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RRSWRLinearCurveMissingEnumerableETHTest is
RouterRobustSwapWithRoyalties,
UsingLinearCurve,
UsingMissingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RRSWRXykCurveEnumerableERC20Test is
RouterRobustSwapWithRoyalties,
UsingXykCurve,
UsingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RRSWRXykCurveEnumerableETHTest is
RouterRobustSwapWithRoyalties,
UsingXykCurve,
UsingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RRSWRXykCurveMissingEnumerableERC20Test is
RouterRobustSwapWithRoyalties,
UsingXykCurve,
UsingMissingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterRobustSwapWithRoyalties} from "../base/RouterRobustSwapWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RRSWRXykCurveMissingEnumerableETHTest is
RouterRobustSwapWithRoyalties,
UsingXykCurve,
UsingMissingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RSPWRExponentialCurveEnumerableERC20Test is
RouterSinglePoolWithRoyalties,
UsingExponentialCurve,
UsingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RSPWRExponentialCurveEnumerableETHTest is
RouterSinglePoolWithRoyalties,
UsingExponentialCurve,
UsingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RSPWRExponentialCurveMissingEnumerableERC20Test is
RouterSinglePoolWithRoyalties,
UsingExponentialCurve,
UsingMissingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingExponentialCurve} from "../mixins/UsingExponentialCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RSPWRExponentialCurveMissingEnumerableETHTest is
RouterSinglePoolWithRoyalties,
UsingExponentialCurve,
UsingMissingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RSPWRLinearCurveEnumerableERC20Test is
RouterSinglePoolWithRoyalties,
UsingLinearCurve,
UsingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RSPWRLinearCurveEnumerableETHTest is
RouterSinglePoolWithRoyalties,
UsingLinearCurve,
UsingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RSPWRLinearCurveMissingEnumerableERC20Test is
RouterSinglePoolWithRoyalties,
UsingLinearCurve,
UsingMissingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingLinearCurve} from "../mixins/UsingLinearCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RSPWRLinearCurveMissingEnumerableETHTest is
RouterSinglePoolWithRoyalties,
UsingLinearCurve,
UsingMissingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RSPWRXykCurveEnumerableERC20Test is
RouterSinglePoolWithRoyalties,
UsingXykCurve,
UsingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingEnumerable} from "../mixins/UsingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RSPWRXykCurveEnumerableETHTest is
RouterSinglePoolWithRoyalties,
UsingXykCurve,
UsingEnumerable,
UsingETH
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingERC20} from "../mixins/UsingERC20.sol";
contract RSPWRXykCurveMissingEnumerableERC20Test is
RouterSinglePoolWithRoyalties,
UsingXykCurve,
UsingMissingEnumerable,
UsingERC20
{}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {RouterSinglePoolWithRoyalties} from "../base/RouterSinglePoolWithRoyalties.sol";
import {UsingXykCurve} from "../mixins/UsingXykCurve.sol";
import {UsingMissingEnumerable} from "../mixins/UsingMissingEnumerable.sol";
import {UsingETH} from "../mixins/UsingETH.sol";
contract RSPWRXykCurveMissingEnumerableETHTest is
RouterSinglePoolWithRoyalties,
UsingXykCurve,
UsingMissingEnumerable,
UsingETH
{}
+3 -1
View File
@@ -1,8 +1,10 @@
NoArbBondingCurve,NoArb
RouterRobustSwap,RRS
RouterSinglePool,RSP
RouterSinglePoolWithRoyalties,RSPWR
PairAndFactory,PAF
RouterMultiPool,RMP
RouterMultiPoolWithRoyalties,RMPWR
RouterSinglePoolWithAssetRecipient,RSPWAR
RouterRobustSwapWithAssetRecipient,RRSWAR
RouterPartialFill,RPF
RouterRobustSwapWithRoyalties,RRSWR
1 NoArbBondingCurve NoArb
2 RouterRobustSwap RRS
3 RouterSinglePool RSP
4 RouterSinglePoolWithRoyalties RSPWR
5 PairAndFactory PAF
6 RouterMultiPool RMP
7 RouterMultiPoolWithRoyalties RMPWR
8 RouterSinglePoolWithAssetRecipient RSPWAR
9 RouterRobustSwapWithAssetRecipient RRSWAR
10 RouterPartialFill RouterRobustSwapWithRoyalties RPF RRSWR