@@ -942,6 +942,263 @@ contract SignalBuyContractTest is BalanceUtil {
942942 userMakerAsset.assertChange (- int256 (DEFAULT_ORDER.userTokenAmount.div (2 )));
943943 }
944944
945+ /*********************************
946+ * ETH/WETH settlement *
947+ *********************************/
948+
949+ struct ETHandWETHAssetSnapshot {
950+ BalanceSnapshot.Snapshot dealerETHAsset;
951+ BalanceSnapshot.Snapshot dealerWETHAsset;
952+ BalanceSnapshot.Snapshot userETHAsset;
953+ BalanceSnapshot.Snapshot userWETHAsset;
954+ }
955+ ETHandWETHAssetSnapshot assetSnapshots;
956+
957+ function testSettlementETHToETHWithNoFee () public {
958+ SignalBuyContractLibEIP712.Order memory order = DEFAULT_ORDER;
959+ order.dealerToken = IERC20 (LibConstant.ETH_ADDRESS);
960+ order.minDealerTokenAmount = 1e18 ;
961+ bytes memory orderMakerSig = _signOrder (userPrivateKey, order, SignatureValidator.SignatureType.EIP712);
962+
963+ SignalBuyContractLibEIP712.Fill memory fill = DEFAULT_FILL;
964+ fill.orderHash = getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order));
965+ fill.dealerTokenAmount = order.minDealerTokenAmount;
966+
967+ ISignalBuyContract.TraderParams memory traderParams = DEFAULT_TRADER_PARAMS;
968+ traderParams.dealerTokenAmount = fill.dealerTokenAmount;
969+ traderParams.dealerSig = _signFill (dealerPrivateKey, fill, SignatureValidator.SignatureType.EIP712);
970+
971+ SignalBuyContractLibEIP712.AllowFill memory allowFill = DEFAULT_ALLOW_FILL;
972+ allowFill.orderHash = getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order));
973+ allowFill.fillAmount = traderParams.dealerTokenAmount;
974+
975+ ISignalBuyContract.CoordinatorParams memory crdParams = DEFAULT_CRD_PARAMS;
976+ crdParams.sig = _signAllowFill (coordinatorPrivateKey, allowFill, SignatureValidator.SignatureType.EIP712);
977+
978+ assetSnapshots.dealerETHAsset = BalanceSnapshot.take (dealer, LibConstant.ETH_ADDRESS);
979+ assetSnapshots.dealerWETHAsset = BalanceSnapshot.take (dealer, address (weth));
980+ assetSnapshots.userETHAsset = BalanceSnapshot.take (user, LibConstant.ETH_ADDRESS);
981+ assetSnapshots.userWETHAsset = BalanceSnapshot.take (user, address (weth));
982+
983+ // Case 1: Tx failed due to mismatch msg.value
984+ vm.expectRevert ("SignalBuyContract: mismatch dealer token (ETH) amount " );
985+ vm.prank (dealer, dealer);
986+ signalBuyContract.fillSignalBuy { value: fill.dealerTokenAmount - 1 }(order, orderMakerSig, traderParams, crdParams);
987+
988+ // Case 2: Tx succeeded
989+ vm.expectEmit (true , true , true , true );
990+ emit SignalBuyFilledByTrader (
991+ getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order)),
992+ order.user,
993+ dealer,
994+ getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getAllowFillStructHash (allowFill)),
995+ DEFAULT_TRADER_PARAMS.recipient,
996+ ISignalBuyContract.FillReceipt (
997+ address (order.userToken),
998+ address (order.dealerToken),
999+ order.userTokenAmount,
1000+ order.minDealerTokenAmount,
1001+ 0 , // remainingUserTokenAmount should be zero after order fully filled
1002+ 0 , // tokenlonFee = 0
1003+ 0 // dealerStrategyFee = 0
1004+ )
1005+ );
1006+ vm.prank (dealer, dealer);
1007+ signalBuyContract.fillSignalBuy { value: fill.dealerTokenAmount }(order, orderMakerSig, traderParams, crdParams);
1008+
1009+ assetSnapshots.dealerETHAsset.assertChange (- int256 (order.minDealerTokenAmount));
1010+ assetSnapshots.dealerWETHAsset.assertChange (0 );
1011+ assetSnapshots.userETHAsset.assertChange (int256 (order.minDealerTokenAmount));
1012+ assetSnapshots.userWETHAsset.assertChange (0 );
1013+ }
1014+
1015+ function testSettlementWETHToWETHWithNoFee () public {
1016+ SignalBuyContractLibEIP712.Order memory order = DEFAULT_ORDER;
1017+ order.dealerToken = weth;
1018+ order.minDealerTokenAmount = 1e18 ;
1019+ bytes memory orderMakerSig = _signOrder (userPrivateKey, order, SignatureValidator.SignatureType.EIP712);
1020+
1021+ SignalBuyContractLibEIP712.Fill memory fill = DEFAULT_FILL;
1022+ fill.orderHash = getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order));
1023+ fill.dealerTokenAmount = order.minDealerTokenAmount;
1024+
1025+ ISignalBuyContract.TraderParams memory traderParams = DEFAULT_TRADER_PARAMS;
1026+ traderParams.dealerTokenAmount = fill.dealerTokenAmount;
1027+ traderParams.dealerSig = _signFill (dealerPrivateKey, fill, SignatureValidator.SignatureType.EIP712);
1028+
1029+ SignalBuyContractLibEIP712.AllowFill memory allowFill = DEFAULT_ALLOW_FILL;
1030+ allowFill.orderHash = getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order));
1031+ allowFill.fillAmount = traderParams.dealerTokenAmount;
1032+
1033+ ISignalBuyContract.CoordinatorParams memory crdParams = DEFAULT_CRD_PARAMS;
1034+ crdParams.sig = _signAllowFill (coordinatorPrivateKey, allowFill, SignatureValidator.SignatureType.EIP712);
1035+
1036+ assetSnapshots.dealerETHAsset = BalanceSnapshot.take (dealer, LibConstant.ETH_ADDRESS);
1037+ assetSnapshots.dealerWETHAsset = BalanceSnapshot.take (dealer, address (weth));
1038+ assetSnapshots.userETHAsset = BalanceSnapshot.take (user, LibConstant.ETH_ADDRESS);
1039+ assetSnapshots.userWETHAsset = BalanceSnapshot.take (user, address (weth));
1040+
1041+ // Case 1: Tx failed due to invalid msg.value
1042+ vm.expectRevert ("SignalBuyContract: mismatch dealer token (ETH) amount " );
1043+ vm.prank (dealer, dealer);
1044+ signalBuyContract.fillSignalBuy { value: 1 }(order, orderMakerSig, traderParams, crdParams);
1045+
1046+ // Case 2: Tx succeeded
1047+ vm.expectEmit (true , true , true , true );
1048+ emit SignalBuyFilledByTrader (
1049+ getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order)),
1050+ order.user,
1051+ dealer,
1052+ getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getAllowFillStructHash (allowFill)),
1053+ DEFAULT_TRADER_PARAMS.recipient,
1054+ ISignalBuyContract.FillReceipt (
1055+ address (order.userToken),
1056+ address (order.dealerToken),
1057+ order.userTokenAmount,
1058+ order.minDealerTokenAmount,
1059+ 0 , // remainingUserTokenAmount should be zero after order fully filled
1060+ 0 , // tokenlonFee = 0
1061+ 0 // dealerStrategyFee = 0
1062+ )
1063+ );
1064+ vm.prank (dealer, dealer);
1065+ signalBuyContract.fillSignalBuy (order, orderMakerSig, traderParams, crdParams);
1066+
1067+ assetSnapshots.dealerETHAsset.assertChange (0 );
1068+ assetSnapshots.dealerWETHAsset.assertChange (- int256 (order.minDealerTokenAmount));
1069+ assetSnapshots.userETHAsset.assertChange (0 );
1070+ assetSnapshots.userWETHAsset.assertChange (int256 (order.minDealerTokenAmount));
1071+ }
1072+
1073+ function testSettlementETHToWETHWithAddedTokenlonFee () public {
1074+ // tokenlonFeeFactor : 10%
1075+ vm.startPrank (owner, owner);
1076+ signalBuyContract.setFactors (1000 );
1077+ vm.warp (block .timestamp + signalBuyContract.factorActivateDelay ());
1078+ signalBuyContract.activateFactors ();
1079+ vm.stopPrank ();
1080+
1081+ SignalBuyContractLibEIP712.Order memory order = DEFAULT_ORDER;
1082+ order.dealerToken = weth;
1083+ order.minDealerTokenAmount = 1e18 ;
1084+ bytes memory orderMakerSig = _signOrder (userPrivateKey, order, SignatureValidator.SignatureType.EIP712);
1085+
1086+ SignalBuyContractLibEIP712.Fill memory fill = DEFAULT_FILL;
1087+ fill.orderHash = getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order));
1088+ // Increase dealer token amount so the dealerToken/userToken ratio is better than order's dealerToken/userToken ratio
1089+ // to account for tokenlon fee
1090+ fill.dealerTokenAmount = order.minDealerTokenAmount.mul (115 ).div (100 ); // 15% more
1091+
1092+ ISignalBuyContract.TraderParams memory traderParams = DEFAULT_TRADER_PARAMS;
1093+ traderParams.dealerTokenAmount = fill.dealerTokenAmount;
1094+ traderParams.dealerSig = _signFill (dealerPrivateKey, fill, SignatureValidator.SignatureType.EIP712);
1095+
1096+ SignalBuyContractLibEIP712.AllowFill memory allowFill = DEFAULT_ALLOW_FILL;
1097+ allowFill.orderHash = getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order));
1098+ allowFill.fillAmount = traderParams.dealerTokenAmount;
1099+
1100+ ISignalBuyContract.CoordinatorParams memory crdParams = DEFAULT_CRD_PARAMS;
1101+ crdParams.sig = _signAllowFill (coordinatorPrivateKey, allowFill, SignatureValidator.SignatureType.EIP712);
1102+
1103+ assetSnapshots.dealerETHAsset = BalanceSnapshot.take (dealer, LibConstant.ETH_ADDRESS);
1104+ assetSnapshots.dealerWETHAsset = BalanceSnapshot.take (dealer, address (weth));
1105+ assetSnapshots.userETHAsset = BalanceSnapshot.take (user, LibConstant.ETH_ADDRESS);
1106+ assetSnapshots.userWETHAsset = BalanceSnapshot.take (user, address (weth));
1107+
1108+ // Case 1: Tx failed due to invalid msg.value
1109+ vm.expectRevert ("SignalBuyContract: mismatch dealer token (ETH) amount " );
1110+ vm.prank (dealer, dealer);
1111+ signalBuyContract.fillSignalBuy { value: 1 }(order, orderMakerSig, traderParams, crdParams);
1112+
1113+ // Case 2: Tx succeeded
1114+ vm.expectEmit (true , true , true , true );
1115+ emit SignalBuyFilledByTrader (
1116+ getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order)),
1117+ order.user,
1118+ dealer,
1119+ getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getAllowFillStructHash (allowFill)),
1120+ DEFAULT_TRADER_PARAMS.recipient,
1121+ ISignalBuyContract.FillReceipt (
1122+ address (order.userToken),
1123+ address (order.dealerToken),
1124+ order.userTokenAmount,
1125+ fill.dealerTokenAmount,
1126+ 0 , // remainingUserTokenAmount should be zero after order fully filled
1127+ fill.dealerTokenAmount.div (10 ), // tokenlonFee = 10% dealerTokenAmount
1128+ 0 // dealerStrategyFee = 0
1129+ )
1130+ );
1131+ vm.prank (dealer, dealer);
1132+ signalBuyContract.fillSignalBuy { value: fill.dealerTokenAmount }(order, orderMakerSig, traderParams, crdParams);
1133+
1134+ assetSnapshots.dealerETHAsset.assertChange (- int256 (fill.dealerTokenAmount));
1135+ assetSnapshots.dealerWETHAsset.assertChange (0 );
1136+ assetSnapshots.userETHAsset.assertChange (0 );
1137+ assetSnapshots.userWETHAsset.assertChange (int256 (fill.dealerTokenAmount.mul (9 ).div (10 ))); // 10% fee for Tokenlon
1138+ }
1139+
1140+ function testSettlementWETHToETHWithAddedGasFeeAndStrategyFee () public {
1141+ SignalBuyContractLibEIP712.Order memory order = DEFAULT_ORDER;
1142+ order.dealerToken = IERC20 (LibConstant.ETH_ADDRESS);
1143+ order.minDealerTokenAmount = 1e18 ;
1144+ bytes memory orderMakerSig = _signOrder (userPrivateKey, order, SignatureValidator.SignatureType.EIP712);
1145+
1146+ SignalBuyContractLibEIP712.Fill memory fill = DEFAULT_FILL;
1147+ fill.orderHash = getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order));
1148+ // Increase dealer token amount so the dealerToken/userToken ratio is better than order's dealerToken/userToken ratio
1149+ // to account for gas fee and dealer strategy fee
1150+ fill.dealerTokenAmount = order.minDealerTokenAmount.mul (11 ).div (10 ); // 10% more
1151+
1152+ ISignalBuyContract.TraderParams memory traderParams = DEFAULT_TRADER_PARAMS;
1153+ traderParams.gasFeeFactor = 50 ; // gasFeeFactor: 0.5%
1154+ traderParams.dealerStrategyFeeFactor = 250 ; // dealerStrategyFeeFactor: 2.5%
1155+ traderParams.dealerTokenAmount = fill.dealerTokenAmount;
1156+ traderParams.dealerSig = _signFill (dealerPrivateKey, fill, SignatureValidator.SignatureType.EIP712);
1157+
1158+ SignalBuyContractLibEIP712.AllowFill memory allowFill = DEFAULT_ALLOW_FILL;
1159+ allowFill.orderHash = getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order));
1160+ allowFill.fillAmount = traderParams.dealerTokenAmount;
1161+
1162+ ISignalBuyContract.CoordinatorParams memory crdParams = DEFAULT_CRD_PARAMS;
1163+ crdParams.sig = _signAllowFill (coordinatorPrivateKey, allowFill, SignatureValidator.SignatureType.EIP712);
1164+
1165+ assetSnapshots.dealerETHAsset = BalanceSnapshot.take (dealer, LibConstant.ETH_ADDRESS);
1166+ assetSnapshots.dealerWETHAsset = BalanceSnapshot.take (dealer, address (weth));
1167+ assetSnapshots.userETHAsset = BalanceSnapshot.take (user, LibConstant.ETH_ADDRESS);
1168+ assetSnapshots.userWETHAsset = BalanceSnapshot.take (user, address (weth));
1169+
1170+ // Case 1: Tx failed due to invalid msg.value
1171+ vm.expectRevert ("SignalBuyContract: mismatch dealer token (ETH) amount " );
1172+ vm.prank (dealer, dealer);
1173+ signalBuyContract.fillSignalBuy { value: 1 }(order, orderMakerSig, traderParams, crdParams);
1174+
1175+ // Case 2: Tx succeeded
1176+ vm.expectEmit (true , true , true , true );
1177+ emit SignalBuyFilledByTrader (
1178+ getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getOrderStructHash (order)),
1179+ order.user,
1180+ dealer,
1181+ getEIP712Hash (signalBuyContract.EIP712_DOMAIN_SEPARATOR (), SignalBuyContractLibEIP712._getAllowFillStructHash (allowFill)),
1182+ DEFAULT_TRADER_PARAMS.recipient,
1183+ ISignalBuyContract.FillReceipt (
1184+ address (order.userToken),
1185+ address (order.dealerToken),
1186+ order.userTokenAmount,
1187+ fill.dealerTokenAmount,
1188+ 0 , // remainingUserTokenAmount should be zero after order fully filled
1189+ 0 , // tokenlonFee = 0
1190+ fill.dealerTokenAmount.mul (3 ).div (100 ) // dealerStrategyFee = 0.5% + 2.5% = 3% dealerTokenAmount
1191+ )
1192+ );
1193+ vm.prank (dealer, dealer);
1194+ signalBuyContract.fillSignalBuy (order, orderMakerSig, traderParams, crdParams);
1195+
1196+ assetSnapshots.dealerETHAsset.assertChange (0 );
1197+ assetSnapshots.dealerWETHAsset.assertChange (- int256 (fill.dealerTokenAmount.mul (97 ).div (100 ))); // 3% fee for SignalBuy is deducted from dealerTokenAmount directly
1198+ assetSnapshots.userETHAsset.assertChange (int256 (fill.dealerTokenAmount.mul (97 ).div (100 ))); // 3% fee for SignalBuy
1199+ assetSnapshots.userWETHAsset.assertChange (0 );
1200+ }
1201+
9451202 /*********************************
9461203 * cancelSignalBuy *
9471204 *********************************/
0 commit comments