@@ -942,6 +942,263 @@ contract SignalBuyContractTest is BalanceUtil {
942
942
userMakerAsset.assertChange (- int256 (DEFAULT_ORDER.userTokenAmount.div (2 )));
943
943
}
944
944
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
+
945
1202
/*********************************
946
1203
* cancelSignalBuy *
947
1204
*********************************/
0 commit comments