1- // SPDX-Liense -Identifier: AGPL-3.0-only
1+ // SPDX-License -Identifier: AGPL-3.0-only
22pragma solidity ^ 0.8.0 ;
33
44import { RecipeMarketHub, ERC20 , RecipeMarketHubTestBase } from "../utils/RecipeMarketHub/RecipeMarketHubTestBase.sol " ;
@@ -18,15 +18,24 @@ contract RecipeVerifier is RecipeMarketHubTestBase {
1818 uint256 fork;
1919
2020 function setUp () public {
21- // Replace this with whatever network the Royco Recipe IAM was created on
21+ // Replace this with the correct network if needed
2222 fork = vm.createFork (MAINNET_RPC_URL);
2323
24+ // Replace with the correct RecipeMarketHub instance
2425 RECIPE_MARKET_HUB = RecipeMarketHub (0x783251f103555068c1E9D755f69458f39eD937c0 );
2526
26- // Replace this with the market hash of the market you are trying to verify
27+ // Replace with the market hash of the market you want to verify
2728 MARKET_HASH = 0x83c459782b2ff36629401b1a592354fc085f29ae00cf97b803f73cac464d389b ;
2829 }
2930
31+ function getRoleOrAddress (address candidate , address ap , address wallet , address hub ) internal pure returns (string memory ) {
32+ if (candidate == ap) return "AP " ;
33+ if (candidate == wallet) return "WeirollWallet " ;
34+ if (candidate == hub) return "RecipeMarketHub " ;
35+ // Return the hex string of the address if not one of the known roles
36+ return _addressToString (candidate);
37+ }
38+
3039 function test_RecipeMarketVerification () external {
3140 vm.selectFork (fork);
3241
@@ -35,15 +44,17 @@ contract RecipeVerifier is RecipeMarketHubTestBase {
3544 // Get the token to deposit for this market
3645 (, ERC20 marketInputToken , uint256 lockupTime ,,,,) = RECIPE_MARKET_HUB.marketHashToWeirollMarket (MARKET_HASH);
3746
38- uint256 offerAmount = 5_000_000 * (10 ** (marketInputToken.decimals ()));
39- uint256 numDepositors = 100 ;
47+ // Tune this amount for total offer amount
48+ uint256 offerAmount = 10_000 * (10 ** (marketInputToken.decimals ()));
49+ // Tune this to simulate this amount of APs filling the offer
50+ uint256 numDepositors = 1 ;
4051
4152 // Create an IP offer in the market
4253 address [] memory incentives = new address [](0 );
4354 uint256 [] memory incentiveAmounts = new uint256 [](0 );
4455 bytes32 ipOfferHash = RECIPE_MARKET_HUB.createIPOffer (MARKET_HASH, offerAmount, 0 , incentives, incentiveAmounts);
4556
46- // Fill the IP Offer
57+ // Fill the IP Offer (Deposit phase)
4758 address [] memory aps = new address [](numDepositors);
4859 uint256 [] memory fillAmounts = new uint256 [](numDepositors);
4960 address [] memory weirollWallets = new address [](numDepositors);
@@ -52,7 +63,13 @@ contract RecipeVerifier is RecipeMarketHubTestBase {
5263 ipOfferHashes[0 ] = ipOfferHash;
5364 uint256 [] memory depositorFillAmounts = new uint256 [](1 );
5465
55- // Distribute fill amounts based on random amounts
66+ console2.log ("----------------------------------------------------- " );
67+ console2.log ("Deposit Recipe Flow: " );
68+ console2.log ("----------------------------------------------------- " );
69+
70+ // -------------------------
71+ // Deposit Phase (Filling the Offer)
72+ // -------------------------
5673 for (uint256 i = 0 ; i < numDepositors; i++ ) {
5774 (address ap ,) = makeAddrAndKey (string (abi.encode (i)));
5875 aps[i] = ap;
@@ -64,24 +81,64 @@ contract RecipeVerifier is RecipeMarketHubTestBase {
6481 fillAmounts[i] = fillAmount;
6582
6683 // Fund the AP and handle approval
67- deal (address (marketInputToken), ap, fillAmounts[i] );
84+ deal (address (marketInputToken), ap, fillAmount );
6885 vm.startPrank (ap);
69- marketInputToken.approve (address (RECIPE_MARKET_HUB), fillAmounts[i] );
86+ marketInputToken.approve (address (RECIPE_MARKET_HUB), fillAmount );
7087
71- depositorFillAmounts[0 ] = fillAmounts[i] ;
72- // Record the logs to capture Transfer events
88+ depositorFillAmounts[0 ] = fillAmount ;
89+ // Record the logs to capture Transfer events during deposit
7390 vm.recordLogs ();
7491 // AP Fills the offer (no funding vault)
7592 RECIPE_MARKET_HUB.fillIPOffers (ipOfferHashes, depositorFillAmounts, address (0 ), address (0xbeef ));
7693 vm.stopPrank ();
7794
95+ Vm.Log[] memory depositLogs = vm.getRecordedLogs ();
96+
7897 // Extract the Weiroll wallet address
79- weirollWallets[i] = address (uint160 (uint256 (vm.getRecordedLogs ()[0 ].topics[2 ])));
98+ weirollWallets[i] = address (uint160 (uint256 (depositLogs[0 ].topics[2 ])));
99+
100+ // Process each Transfer event log for deposit
101+ for (uint256 j = 0 ; j < depositLogs.length ; j++ ) {
102+ Vm.Log memory log = depositLogs[j];
103+
104+ if (log.topics[0 ] == TRANSFER_EVENT_SIG) {
105+ address from = address (uint160 (uint256 (log.topics[1 ])));
106+ address to = address (uint160 (uint256 (log.topics[2 ])));
107+ uint256 amount = abi.decode (log.data, (uint256 ));
108+
109+ if (i == 0 ) {
110+ string memory tokenName;
111+ try ERC20 (log.emitter).name () returns (string memory name ) {
112+ tokenName = name;
113+ } catch {
114+ tokenName = "<Unknown Token> " ;
115+ }
116+
117+ // Identify roles or addresses
118+ string memory fromEntity = getRoleOrAddress (from, aps[i], weirollWallets[i], address (RECIPE_MARKET_HUB));
119+ string memory toEntity = getRoleOrAddress (to, aps[i], weirollWallets[i], address (RECIPE_MARKET_HUB));
120+
121+ // Single sentence log for deposit
122+ console2.log (
123+ string (abi.encodePacked (fromEntity, " sent " , _uintToString (amount), " " , tokenName, " to " , toEntity, " during deposit. " ))
124+ );
125+ }
126+ }
127+ }
80128 }
81129
130+ // -------------------------
131+ // Withdrawal Phase
132+ // -------------------------
133+
82134 // Time travel to when the deposits are withdrawable
83135 vm.warp (block .timestamp + lockupTime);
84136
137+ console2.log ("" );
138+ console2.log ("----------------------------------------------------- " );
139+ console2.log ("Withdrawal Recipe Flow: " );
140+ console2.log ("----------------------------------------------------- " );
141+
85142 for (uint256 i = 0 ; i < numDepositors; ++ i) {
86143 vm.warp (block .timestamp + (i * 30 minutes));
87144
@@ -92,37 +149,92 @@ contract RecipeVerifier is RecipeMarketHubTestBase {
92149 RECIPE_MARKET_HUB.executeWithdrawalScript (weirollWallets[i]);
93150 vm.stopPrank ();
94151
95- Vm.Log[] memory logs = vm.getRecordedLogs ();
152+ Vm.Log[] memory withdrawLogs = vm.getRecordedLogs ();
96153
97154 bool apReceivedTokens = false ;
98155 bool walletReceivedTokens = false ;
99156
100- for (uint256 j = 0 ; j < logs.length ; j++ ) {
101- Vm.Log memory log = logs[j];
157+ // Process each Transfer event log for withdrawal
158+ for (uint256 j = 0 ; j < withdrawLogs.length ; j++ ) {
159+ Vm.Log memory log = withdrawLogs[j];
102160
103- // Check if the log matches the Transfer signature and was emitted by the token contract
104161 if (log.topics[0 ] == TRANSFER_EVENT_SIG) {
162+ address from = address (uint160 (uint256 (log.topics[1 ])));
105163 address to = address (uint160 (uint256 (log.topics[2 ])));
106- string memory tokenName = ERC20 (log.emitter).name ();
107164 uint256 amount = abi.decode (log.data, (uint256 ));
108165
109- if (to == aps[i]) {
110- if (i == 0 ) {
111- console2.log ("AP received " , amount, " of " , tokenName);
166+ if (i == 0 ) {
167+ string memory tokenName;
168+ try ERC20 (log.emitter).name () returns (string memory name ) {
169+ tokenName = name;
170+ } catch {
171+ tokenName = "<Unknown Token> " ;
112172 }
173+
174+ // Identify roles or addresses
175+ string memory fromEntity = getRoleOrAddress (from, aps[i], weirollWallets[i], address (RECIPE_MARKET_HUB));
176+ string memory toEntity = getRoleOrAddress (to, aps[i], weirollWallets[i], address (RECIPE_MARKET_HUB));
177+
178+ // Single sentence log for withdrawal
179+ console2.log (
180+ string (abi.encodePacked (fromEntity, " sent " , _uintToString (amount), " " , tokenName, " to " , toEntity, " during withdrawal. " ))
181+ );
182+ }
183+
184+ if (to == aps[i]) {
113185 apReceivedTokens = true ;
114186 } else if (to == weirollWallets[i]) {
115- if (i == 0 ) {
116- console2.log ("Weiroll Wallet received " , amount, " of " , tokenName);
117- }
118187 walletReceivedTokens = true ;
119188 }
120189 }
121190 }
122191
192+ // Ensure tokens ended up in either the AP or the Weiroll wallet
123193 assert (apReceivedTokens || walletReceivedTokens);
124194 }
125-
195+ console2. log ( " ----------------------------------------------------- " );
126196 console2.log ("Market Successfully Verified. " );
127197 }
198+
199+ // Utility function to convert uint to string
200+ function _uintToString (uint256 v ) internal pure returns (string memory ) {
201+ if (v == 0 ) {
202+ return "0 " ;
203+ }
204+ uint256 j = v;
205+ uint256 length;
206+ while (j != 0 ) {
207+ length++ ;
208+ j /= 10 ;
209+ }
210+ bytes memory bstr = new bytes (length);
211+ uint256 k = length;
212+ j = v;
213+ while (j != 0 ) {
214+ k = k - 1 ;
215+ uint8 temp = uint8 (48 + (j % 10 ));
216+ bstr[k] = bytes1 (temp);
217+ j /= 10 ;
218+ }
219+ return string (bstr);
220+ }
221+
222+ function _addressToString (address x ) internal pure returns (string memory ) {
223+ bytes memory s = new bytes (42 );
224+ s[0 ] = "0 " ;
225+ s[1 ] = "x " ;
226+ for (uint256 i = 0 ; i < 20 ; i++ ) {
227+ bytes1 b = bytes1 (uint8 (uint256 (uint160 (x)) / (2 ** (8 * (19 - i)))));
228+ bytes1 hi = bytes1 (uint8 (b) / 16 );
229+ bytes1 lo = bytes1 (uint8 (b) - 16 * uint8 (hi));
230+ s[2 + 2 * i] = _char (hi);
231+ s[3 + 2 * i] = _char (lo);
232+ }
233+ return string (s);
234+ }
235+
236+ function _char (bytes1 b ) internal pure returns (bytes1 c ) {
237+ if (uint8 (b) < 10 ) return bytes1 (uint8 (b) + 0x30 );
238+ else return bytes1 (uint8 (b) + 0x57 );
239+ }
128240}
0 commit comments