Skip to content

Commit 7c1f362

Browse files
Axel ValavaaraAxel Valavaara
authored andcommitted
merchantAnswersAndFinalizes
1 parent 2a66ab6 commit 7c1f362

2 files changed

Lines changed: 59 additions & 29 deletions

File tree

src/OrderContract.sol

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ contract OrderContract is ReentrancyGuard{
3838
error OrderContract__EnoughTimeHasNotPassed();
3939
error OrderContract__CannotProposeOrderAnswerInCurrentState();
4040
error OrderContract__NotOrderByMerchanProvided();
41+
error OrderContract__MerchantHasNoAccessToOffer();
42+
error OrderContract__MerchantAlreadySetForOrder();
43+
error OrderContract__CannotSetMerchantForOrderInCurrentState();
4144

4245
/* type declarations */
4346
enum OrderStatus {
@@ -94,6 +97,15 @@ contract OrderContract is ReentrancyGuard{
9497

9598
}
9699

100+
modifier onlySellerWithOffer(uint64 offerId) {
101+
// Ensure the caller is the seller for the offer
102+
if (offers[offerId].seller != msg.sender) {
103+
revert OrderContract__MerchantHasNoAccessToOffer();
104+
}
105+
_;
106+
107+
}
108+
97109
/* functions */
98110

99111
constructor(address agentControllerAddress, address pyUSDAddress, address a3aTokenAddress) {
@@ -170,30 +182,29 @@ contract OrderContract is ReentrancyGuard{
170182

171183

172184
/**
173-
* @notice Agent controller proposes the answer for the order. Marks order as Proposed.
185+
* @notice merchant proposes the answer for the order. Marks order as Proposed.
174186
* @param answerHash The hash/CID of the answer generated for the prompt. The order that user should later confirm.
175187
* @param offerId The ID of the offer to propose answer for
176188
* @param priceForOffer amount the user should pay for the offer
177-
* @param seller the merchant/seller address fulfilling the order.
178189
*/
179-
function proposeOrderAnswer(bytes32 answerHash, uint64 offerId, uint256 priceForOffer, address seller) external onlyAgentController {
190+
function proposeOrderAnswer(bytes32 answerHash, uint64 offerId, uint256 priceForOffer) external onlySellerWithOffer(offerId) {
180191
if (offers[offerId].status != OrderStatus.InProgress) {
181192
revert OrderContract__CannotProposeOrderAnswerInCurrentState();
182193
}
183194

184195
offers[offerId].answerHash = answerHash;
185196
offers[offerId].status = OrderStatus.Proposed;
186197
offers[offerId].price = priceForOffer;
187-
offers[offerId].seller = seller;
188-
merchantOrderIds[seller].push(offerId);
198+
199+
merchantOrderIds[msg.sender].push(offerId);
189200
}
190201
/**
191202
* @notice Finalize the order by transferring the paid amount to the seller.
192-
* Marks order as Completed. Only our order agent should be able to confirm. To prevent fraud.
203+
* Marks order as Completed. Only the merhcant can confirm.
193204
* @param offerId The ID of the offer to finalize
194205
* @return bool indicating successful finalization
195206
*/
196-
function finalizeOrder(uint64 offerId) external onlyAgentController nonReentrant returns(bool){
207+
function finalizeOrder(uint64 offerId) external onlySellerWithOffer(offerId) nonReentrant returns(bool){
197208
if (offers[offerId].status != OrderStatus.Confirmed) {
198209
revert OrderContract__OrderCannotBeFinalizedInCurrentState();
199210
}
@@ -241,6 +252,16 @@ contract OrderContract is ReentrancyGuard{
241252
A3AToken(i_a3aToken).mint(msg.sender, (PyUsdAmount*ADDITIONAL_PRECISION)*100);
242253
}
243254

255+
function setSellerForOrder(uint64 offerId, address newSeller) external onlyAgentController {
256+
if (offers[offerId].seller != address(0)) {
257+
revert OrderContract__MerchantAlreadySetForOrder();
258+
}
259+
if (offers[offerId].status != OrderStatus.InProgress) {
260+
revert OrderContract__CannotSetMerchantForOrderInCurrentState();
261+
}
262+
offers[offerId].seller = newSeller;
263+
}
264+
244265

245266

246267
/* getter functions */

test/unit/OrderContractTest.t.sol

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,18 @@ contract OrderContractTest is Test {
126126

127127
modifier orderProposedAnsweredByAgent(uint64 offerId, bytes32 answerHash) {
128128
uint256 priceForOffer = 5 ether;
129+
vm.prank(SELLER);
130+
orderContract.proposeOrderAnswer(answerHash, offerId, priceForOffer);
131+
_;
132+
}
133+
134+
modifier merchantSetForOrder(uint64 offerId, address seller) {
129135
vm.prank(addressController);
130-
orderContract.proposeOrderAnswer(answerHash, offerId, priceForOffer, SELLER);
136+
orderContract.setSellerForOrder(offerId, seller);
131137
_;
132138
}
133139

134-
function testOnlyUserWithOfferCanConfirmOrder() public proposeOrderForUser orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
140+
function testOnlyUserWithOfferCanConfirmOrder() public proposeOrderForUser merchantSetForOrder(1, SELLER) orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
135141
// Arrange
136142
uint64 offerId = 1;
137143
// Act / Assert
@@ -141,12 +147,13 @@ contract OrderContractTest is Test {
141147

142148
}
143149

144-
function testConfirmOrderUpdatesAmountPaidAndTimeStamp() public proposeOrderForUser orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
150+
function testConfirmOrderUpdatesAmountPaidAndTimeStamp() public proposeOrderForUser merchantSetForOrder(1, SELLER) orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
145151
// Arrange
146152
uint64 offerId = 1;
147153
uint256 priceForOffer = 5 ether;
148154
uint256 expectedAmountPaid = priceForOffer + orderContract.getAgentFee(); // AGENT_FEE i 1 ether
149155
uint256 expectedTimeStamp = block.timestamp;
156+
150157
// Act
151158
vm.startPrank(USER);
152159
ERC20Mock(pyUSD).approve(address(orderContract), expectedAmountPaid);
@@ -158,7 +165,7 @@ contract OrderContractTest is Test {
158165
assertEq(orderContract.getOfferIdToTimestamp(offerId), expectedTimeStamp);
159166
}
160167

161-
function testConfirmOrderEmitsEvent() public proposeOrderForUser orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))){
168+
function testConfirmOrderEmitsEvent() public proposeOrderForUser merchantSetForOrder(1, SELLER) orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))){
162169
// Arrange
163170
uint64 offerId = 1;
164171
uint256 priceForOffer = 5 ether;
@@ -188,41 +195,41 @@ contract OrderContractTest is Test {
188195
// proposeOrderAnswer tests //
189196
//////////////////////////////////////
190197

191-
function testOnlyControllerCanProposeAnswer() public proposeOrderForUser {
192-
uint256 priceForOffer = 5 ether;
198+
function testOnlySellerCanProposeAnswer() public proposeOrderForUser {
193199
// Arrange
200+
uint256 priceForOffer = 5 ether;
194201
uint64 offerId = 1;
195202
bytes32 answerHash = keccak256(abi.encodePacked("Test Answer"));
196203
// Act / Assert
197204
vm.prank(address(0x123));
198-
vm.expectRevert(OrderContract.OrderContract__notAgentController.selector);
199-
orderContract.proposeOrderAnswer(answerHash, offerId, priceForOffer, SELLER);
205+
vm.expectRevert(OrderContract.OrderContract__MerchantHasNoAccessToOffer.selector);
206+
orderContract.proposeOrderAnswer(answerHash, offerId, priceForOffer);
200207
}
201208

202-
function testProposeOrderAnswerUpdatesState() public proposeOrderForUser {
209+
function testProposeOrderAnswerUpdatesState() public proposeOrderForUser merchantSetForOrder(1, SELLER) {
203210
// Arrange
204211
uint256 priceForOffer = 5 ether;
205212
uint64 offerId = 1;
206213
bytes32 answerHash = keccak256(abi.encodePacked("Test Answer"));
207214
// Act
208-
vm.prank(addressController);
209-
orderContract.proposeOrderAnswer(answerHash, offerId, priceForOffer, SELLER);
215+
vm.prank(SELLER);
216+
orderContract.proposeOrderAnswer(answerHash, offerId, priceForOffer);
210217
// Assert
211218
bytes32 storedAnswerHash = orderContract.getAnswerHash(offerId);
212219
assertEq(storedAnswerHash, answerHash);
213220
OrderContract.OrderStatus status = orderContract.getOfferStatus(offerId);
214221
assertEq(uint8(status), uint8(OrderContract.OrderStatus.Proposed));
215222
}
216223

217-
function testProposeOrderAnswerRevertsIfNotInProgress() public {
224+
function testProposeOrderAnswerRevertsIfNotInProgress() public proposeOrderForUser merchantSetForOrder(1, SELLER) orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
218225
// Arrange
219226
uint256 priceForOffer = 5 ether;
220227
uint64 offerId = 1;
221228
bytes32 answerHash = keccak256(abi.encodePacked("Test Answer"));
222229
// Act / Assert
223-
vm.prank(addressController);
230+
vm.prank(SELLER);
224231
vm.expectRevert(OrderContract.OrderContract__CannotProposeOrderAnswerInCurrentState.selector);
225-
orderContract.proposeOrderAnswer(answerHash, offerId, priceForOffer, SELLER);
232+
orderContract.proposeOrderAnswer(answerHash, offerId, priceForOffer);
226233
}
227234

228235

@@ -236,7 +243,9 @@ contract OrderContractTest is Test {
236243
orderContract.proposeOrder(promptHash, USER);
237244
vm.stopPrank();
238245
vm.prank(addressController);
239-
orderContract.proposeOrderAnswer(keccak256(abi.encodePacked("Test Answer")), 1, priceForOffer, SELLER);
246+
orderContract.setSellerForOrder(1, SELLER);
247+
vm.prank(SELLER);
248+
orderContract.proposeOrderAnswer(keccak256(abi.encodePacked("Test Answer")), 1, priceForOffer);
240249
uint256 expectedAmountPaid = 5 ether + orderContract.getAgentFee();
241250
vm.startPrank(USER);
242251
ERC20Mock(pyUSD).approve(address(orderContract), expectedAmountPaid);
@@ -245,11 +254,11 @@ contract OrderContractTest is Test {
245254
_;
246255
}
247256

248-
function testFinalizeOrderCanOnlyBeCalledWhenStateIsConfirmed() public {
257+
function testFinalizeOrderCanOnlyBeCalledWhenStateIsConfirmed() public proposeOrderForUser merchantSetForOrder(1, SELLER) orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
249258
// Arrange
250259
uint64 offerId = 1;
251260
// Act / Assert
252-
vm.prank(addressController);
261+
vm.prank(SELLER);
253262
vm.expectRevert(OrderContract.OrderContract__OrderCannotBeFinalizedInCurrentState.selector);
254263
orderContract.finalizeOrder(offerId);
255264
}
@@ -258,7 +267,7 @@ contract OrderContractTest is Test {
258267
// Arrange
259268
uint64 offerId = 1;
260269
// Act
261-
vm.prank(addressController);
270+
vm.prank(SELLER);
262271
orderContract.finalizeOrder(offerId);
263272
// Assert
264273
OrderContract.OrderStatus status = orderContract.getOfferStatus(offerId);
@@ -271,7 +280,7 @@ contract OrderContractTest is Test {
271280
uint256 sellerBalanceBefore = ERC20Mock(pyUSD).balanceOf(SELLER);
272281
uint256 expectedAmountPaid = 5 ether + orderContract.getAgentFee();
273282
// Act
274-
vm.prank(addressController);
283+
vm.prank(SELLER);
275284
orderContract.finalizeOrder(offerId);
276285
uint256 sellerBalanceAfter = ERC20Mock(pyUSD).balanceOf(SELLER);
277286
// Assert
@@ -303,7 +312,7 @@ contract OrderContractTest is Test {
303312
assertEq(controller, expectedController);
304313
}
305314

306-
function testGetOrderIDsByMerchant() public proposeOrderForUser orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
315+
function testGetOrderIDsByMerchant() public proposeOrderForUser merchantSetForOrder(1, SELLER) orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
307316
// Arrange
308317
uint64 offerId = 1;
309318
// Act
@@ -313,7 +322,7 @@ contract OrderContractTest is Test {
313322
assertEq(orderIds[0], offerId);
314323
}
315324

316-
function testGetMerchantOrderDetailsRevertsIfNotMerchant() public proposeOrderForUser orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
325+
function testGetMerchantOrderDetailsRevertsIfNotMerchant() public proposeOrderForUser merchantSetForOrder(1, SELLER) orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
317326
// Arrange
318327
uint64 offerId = 1;
319328
// Act / Assert
@@ -322,7 +331,7 @@ contract OrderContractTest is Test {
322331
orderContract.getMerchantOrderDetails(USER, offerId);
323332
}
324333

325-
function testGetMerchantOrderDetails() public proposeOrderForUser orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
334+
function testGetMerchantOrderDetails() public proposeOrderForUser merchantSetForOrder(1, SELLER) orderProposedAnsweredByAgent(1, keccak256(abi.encodePacked("Test Answer"))) {
326335
// Arrange
327336
uint64 offerId = 1;
328337
// Act

0 commit comments

Comments
 (0)