Skip to content

Commit

Permalink
(fix) Fix tip amount scaling in fulfillOrders (#559)
Browse files Browse the repository at this point in the history
* init

Signed-off-by: Frank Li <[email protected]>

* scale tips outside of fulfillavailable

Signed-off-by: Frank Li <[email protected]>

* fix lint

Signed-off-by: Frank Li <[email protected]>

* bump ver

Signed-off-by: Frank Li <[email protected]>

---------

Signed-off-by: Frank Li <[email protected]>
  • Loading branch information
frankisawesome authored May 30, 2024
1 parent d7d643c commit 0a45354
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 20 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opensea/seaport-js",
"version": "4.0.0",
"version": "4.0.1",
"description": "[Seaport](https://github.com/ProjectOpenSea/seaport) is a new marketplace protocol for safely and efficiently buying and selling NFTs. This is a TypeScript library intended to make interfacing with the contract reasonable and easy.",
"license": "MIT",
"author": "OpenSea Developers",
Expand Down
53 changes: 34 additions & 19 deletions src/seaport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ import {
shouldUseBasicFulfill,
validateAndSanitizeFromOrderStatus,
} from "./utils/fulfill";
import { isCurrencyItem } from "./utils/item";
import { getMaximumSizeForOrder, isCurrencyItem } from "./utils/item";
import {
adjustTipsForPartialFills,
areAllCurrenciesSame,
deductFees,
feeToConsiderationItem,
Expand Down Expand Up @@ -1029,24 +1030,38 @@ export class Seaport {
]);

const ordersMetadata: FulfillOrdersMetadata = fulfillOrderDetails.map(
(orderDetails, index) => ({
order: orderDetails.order,
unitsToFill: orderDetails.unitsToFill,
orderStatus: scaleOrderStatusToMaxUnits(
orderDetails.order,
orderStatuses[index],
),
offerCriteria: orderDetails.offerCriteria ?? [],
considerationCriteria: orderDetails.considerationCriteria ?? [],
tips:
orderDetails.tips?.map((tip) => ({
...mapInputItemToOfferItem(tip),
recipient: tip.recipient,
})) ?? [],
extraData: orderDetails.extraData ?? "0x",
offererBalancesAndApprovals: offerersBalancesAndApprovals[index],
offererOperator: allOffererOperators[index],
}),
(orderDetails, index) => {
const order = {
order: orderDetails.order,
unitsToFill: orderDetails.unitsToFill,
orderStatus: scaleOrderStatusToMaxUnits(
orderDetails.order,
orderStatuses[index],
),
offerCriteria: orderDetails.offerCriteria ?? [],
considerationCriteria: orderDetails.considerationCriteria ?? [],
tips:
orderDetails.tips?.map((tip) => ({
...mapInputItemToOfferItem(tip),
recipient: tip.recipient,
})) ?? [],
extraData: orderDetails.extraData ?? "0x",
offererBalancesAndApprovals: offerersBalancesAndApprovals[index],
offererOperator: allOffererOperators[index],
};
if (order.tips.length > 0) {
order.tips = adjustTipsForPartialFills(
order.tips,
order.unitsToFill || 1,
// Max total amount to fulfill for scaling
getMaximumSizeForOrder({
...order.order,
}),
);
}

return order;
},
);

return fulfillAvailableOrders({
Expand Down
1 change: 1 addition & 0 deletions src/utils/fulfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ export function fulfillAvailableOrders({
...order.parameters.consideration,
...tips,
];

return {
...order,
parameters: {
Expand Down
85 changes: 85 additions & 0 deletions test/partial-fulfill.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,91 @@ describeWithFixture(
expect(fulfillStandardOrderSpy.calledOnce);
});

it("ERC1155 <=> ETH adjust tips correctly using fulfillOrders", async () => {
// same test as above, but instead of fulfillOrder we use fulfillOrders
const tips = [
{
amount: parseEther("1").toString(),
recipient: await fulfiller.getAddress(),
},
];
const { seaport, testErc1155 } = fixture;

const { executeAllActions } = await seaport.createOrder(
standardCreateOrderInput,
);

const order = await executeAllActions();

expect(order.parameters.orderType).eq(OrderType.PARTIAL_OPEN);

const orderStatus = await seaport.getOrderStatus(
seaport.getOrderHash(order.parameters),
);

const ownerToTokenToIdentifierBalances =
await getBalancesForFulfillOrder(
order,
await fulfiller.getAddress(),
);

const { actions } = await seaport.fulfillOrders({
fulfillOrderDetails: [
{
order,
unitsToFill: 2,
tips,
},
],
accountAddress: await fulfiller.getAddress(),
domain: OPENSEA_DOMAIN,
});

expect(actions.length).to.eq(1);

const action = actions[0];

expect(action).to.deep.equal({
type: "exchange",
transactionMethods: action.transactionMethods,
});

const { value } = await action.transactionMethods.buildTransaction();

// This test verifies that the tips are adjusted correctly in the transaction.
// The expected total is 2 ETH for tokens and 0.2 ETH for tips, totaling 2.2 ETH.
expect(value?.toString()).to.eq(parseEther("2.2").toString());

const transaction = await action.transactionMethods.transact();
expect(transaction.data.slice(-8)).to.eq(OPENSEA_DOMAIN_TAG);

const receipt = await transaction.wait();

const offererErc1155Balance = await testErc1155.balanceOf(
await offerer.getAddress(),
nftId,
);

const fulfillerErc1155Balance = await testErc1155.balanceOf(
await fulfiller.getAddress(),
nftId,
);

expect(offererErc1155Balance).eq(BigInt(8));
expect(fulfillerErc1155Balance).eq(BigInt(2));

await verifyBalancesAfterFulfill({
ownerToTokenToIdentifierBalances,
order,
unitsToFill: 2,
orderStatus,
fulfillerAddress: await fulfiller.getAddress(),
fulfillReceipt: receipt!,
});

expect(fulfillStandardOrderSpy.calledOnce);
});

it("ERC1155 <=> ETH adjust tips correctly with low denomination", async () => {
// Note: For simplicity in this test, tips are returned to the fulfiller.
const totalTips = "2"; // 2 wei
Expand Down

0 comments on commit 0a45354

Please sign in to comment.