Skip to content

Commit 0817952

Browse files
authored
fix: properly forward fulfills to recipient address (#40)
1 parent 666db48 commit 0817952

File tree

2 files changed

+294
-1
lines changed

2 files changed

+294
-1
lines changed

src/__tests__/gifting.spec.ts

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
2+
import { expect } from "chai";
3+
import { BigNumber } from "ethers";
4+
import { parseEther } from "ethers/lib/utils";
5+
import { ethers } from "hardhat";
6+
import { ItemType, MAX_INT } from "../constants";
7+
import { CreateOrderInput, CurrencyItem } from "../types";
8+
import { describeWithFixture } from "./utils/setup";
9+
10+
describeWithFixture(
11+
"As a user I want to buy now and gift it to another address",
12+
(fixture) => {
13+
let offerer: SignerWithAddress;
14+
let zone: SignerWithAddress;
15+
let fulfiller: SignerWithAddress;
16+
let recipient: SignerWithAddress;
17+
let standardCreateOrderInput: CreateOrderInput;
18+
const nftId = "1";
19+
const erc1155Amount = "3";
20+
21+
beforeEach(async () => {
22+
[offerer, zone, fulfiller, recipient] = await ethers.getSigners();
23+
});
24+
25+
describe("A single ERC721 is to be transferred", async () => {
26+
describe("[Buy now] I want to buy a single ERC721 for someone else", async () => {
27+
beforeEach(async () => {
28+
const { testErc721 } = fixture;
29+
30+
await testErc721.mint(offerer.address, nftId);
31+
32+
standardCreateOrderInput = {
33+
startTime: "0",
34+
offer: [
35+
{
36+
itemType: ItemType.ERC721,
37+
token: testErc721.address,
38+
identifier: nftId,
39+
},
40+
],
41+
consideration: [
42+
{
43+
amount: parseEther("10").toString(),
44+
recipient: offerer.address,
45+
},
46+
],
47+
// 2.5% fee
48+
fees: [{ recipient: zone.address, basisPoints: 250 }],
49+
};
50+
});
51+
52+
describe("with ETH", () => {
53+
it("ERC721 <=> ETH", async () => {
54+
const { seaport, testErc721 } = fixture;
55+
56+
const { executeAllActions } = await seaport.createOrder(
57+
standardCreateOrderInput
58+
);
59+
60+
const order = await executeAllActions();
61+
62+
const { actions } = await seaport.fulfillOrder({
63+
order,
64+
accountAddress: fulfiller.address,
65+
recipientAddress: recipient.address,
66+
});
67+
68+
expect(actions.length).to.eq(1);
69+
70+
const action = actions[0];
71+
72+
expect(action.type).eq("exchange");
73+
74+
await action.transactionMethods.transact();
75+
76+
const owner = await testErc721.ownerOf(nftId);
77+
78+
expect(owner).to.equal(recipient.address);
79+
});
80+
});
81+
82+
describe("with ERC20", () => {
83+
beforeEach(async () => {
84+
const { testErc20 } = fixture;
85+
86+
// Use ERC20 instead of eth
87+
standardCreateOrderInput = {
88+
...standardCreateOrderInput,
89+
consideration: standardCreateOrderInput.consideration.map(
90+
(item) => ({ ...item, token: testErc20.address })
91+
),
92+
};
93+
testErc20.mint(
94+
fulfiller.address,
95+
BigNumber.from(
96+
(standardCreateOrderInput.consideration[0] as CurrencyItem)
97+
.amount
98+
)
99+
);
100+
});
101+
102+
it("ERC721 <=> ERC20", async () => {
103+
const { seaport, testErc20, testErc721 } = fixture;
104+
105+
const { executeAllActions } = await seaport.createOrder(
106+
standardCreateOrderInput
107+
);
108+
109+
const order = await executeAllActions();
110+
111+
const { actions } = await seaport.fulfillOrder({
112+
order,
113+
accountAddress: fulfiller.address,
114+
recipientAddress: recipient.address,
115+
});
116+
117+
const approvalAction = actions[0];
118+
119+
expect(approvalAction).to.deep.equal({
120+
type: "approval",
121+
token: testErc20.address,
122+
identifierOrCriteria: "0",
123+
itemType: ItemType.ERC20,
124+
transactionMethods: approvalAction.transactionMethods,
125+
operator: seaport.contract.address,
126+
});
127+
128+
await approvalAction.transactionMethods.transact();
129+
130+
expect(
131+
await testErc20.allowance(
132+
fulfiller.address,
133+
seaport.contract.address
134+
)
135+
).to.equal(MAX_INT);
136+
137+
const fulfillAction = actions[1];
138+
139+
expect(fulfillAction).to.be.deep.equal({
140+
type: "exchange",
141+
transactionMethods: fulfillAction.transactionMethods,
142+
});
143+
144+
await fulfillAction.transactionMethods.transact();
145+
146+
const owner = await testErc721.ownerOf(nftId);
147+
148+
expect(owner).to.equal(recipient.address);
149+
});
150+
});
151+
});
152+
});
153+
154+
describe("A single ERC1155 is to be transferred", async () => {
155+
describe("[Buy now] I want to buy a single ERC1155 for someone else", async () => {
156+
beforeEach(async () => {
157+
const { testErc1155 } = fixture;
158+
159+
await testErc1155.mint(offerer.address, nftId, erc1155Amount);
160+
161+
standardCreateOrderInput = {
162+
offer: [
163+
{
164+
itemType: ItemType.ERC1155,
165+
token: testErc1155.address,
166+
identifier: nftId,
167+
amount: erc1155Amount,
168+
},
169+
],
170+
consideration: [
171+
{
172+
amount: parseEther("10").toString(),
173+
recipient: offerer.address,
174+
},
175+
],
176+
// 2.5% fee
177+
fees: [{ recipient: zone.address, basisPoints: 250 }],
178+
};
179+
});
180+
181+
describe("with ETH", () => {
182+
it("ERC1155 <=> ETH", async () => {
183+
const { seaport, testErc1155 } = fixture;
184+
185+
const { executeAllActions } = await seaport.createOrder(
186+
standardCreateOrderInput,
187+
offerer.address
188+
);
189+
190+
const order = await executeAllActions();
191+
192+
const { actions } = await seaport.fulfillOrder({
193+
order,
194+
accountAddress: fulfiller.address,
195+
recipientAddress: recipient.address,
196+
});
197+
198+
const fulfillAction = actions[0];
199+
200+
expect(fulfillAction).to.be.deep.equal({
201+
type: "exchange",
202+
transactionMethods: fulfillAction.transactionMethods,
203+
});
204+
205+
await fulfillAction.transactionMethods.transact();
206+
207+
const balance = await testErc1155.balanceOf(
208+
recipient.address,
209+
nftId
210+
);
211+
212+
expect(balance).to.equal(erc1155Amount);
213+
});
214+
});
215+
216+
describe("with ERC20", () => {
217+
beforeEach(async () => {
218+
const { testErc20 } = fixture;
219+
220+
// Use ERC20 instead of eth
221+
standardCreateOrderInput = {
222+
...standardCreateOrderInput,
223+
consideration: standardCreateOrderInput.consideration.map(
224+
(item) => ({ ...item, token: testErc20.address })
225+
),
226+
};
227+
testErc20.mint(
228+
fulfiller.address,
229+
BigNumber.from(
230+
(standardCreateOrderInput.consideration[0] as CurrencyItem)
231+
.amount
232+
)
233+
);
234+
});
235+
236+
it("ERC1155 <=> ERC20", async () => {
237+
const { seaport, testErc20, testErc1155 } = fixture;
238+
239+
const { executeAllActions } = await seaport.createOrder(
240+
standardCreateOrderInput
241+
);
242+
243+
const order = await executeAllActions();
244+
245+
const { actions } = await seaport.fulfillOrder({
246+
order,
247+
accountAddress: fulfiller.address,
248+
recipientAddress: recipient.address,
249+
});
250+
251+
const approvalAction = actions[0];
252+
253+
expect(approvalAction).to.deep.equal({
254+
type: "approval",
255+
token: testErc20.address,
256+
identifierOrCriteria: "0",
257+
itemType: ItemType.ERC20,
258+
transactionMethods: approvalAction.transactionMethods,
259+
operator: seaport.contract.address,
260+
});
261+
262+
await approvalAction.transactionMethods.transact();
263+
264+
expect(
265+
await testErc20.allowance(
266+
fulfiller.address,
267+
seaport.contract.address
268+
)
269+
).to.equal(MAX_INT);
270+
271+
const fulfillAction = actions[1];
272+
273+
expect(fulfillAction).to.be.deep.equal({
274+
type: "exchange",
275+
transactionMethods: fulfillAction.transactionMethods,
276+
});
277+
278+
await fulfillAction.transactionMethods.transact();
279+
280+
const balance = await testErc1155.balanceOf(
281+
recipient.address,
282+
nftId
283+
);
284+
285+
expect(balance).to.equal(erc1155Amount);
286+
});
287+
});
288+
});
289+
});
290+
}
291+
);

src/utils/fulfill.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,9 @@ export async function fulfillStandardOrder({
419419
signer
420420
);
421421

422-
const useAdvanced = Boolean(unitsToFill) || hasCriteriaItems;
422+
const isGift = recipientAddress !== ethers.constants.AddressZero;
423+
424+
const useAdvanced = Boolean(unitsToFill) || hasCriteriaItems || isGift;
423425

424426
const orderAccountingForTips: OrderStruct = {
425427
...order,

0 commit comments

Comments
 (0)