Skip to content

Commit c7fe002

Browse files
committed
feat: buyToken
1 parent 320781c commit c7fe002

File tree

2 files changed

+187
-32
lines changed

2 files changed

+187
-32
lines changed

hardhat/contracts/LarsKristoHellheads.sol

+86-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.20;
33

4-
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
4+
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Royalty.sol";
55

6-
contract LarsKristoHellheads is ERC721 {
6+
contract LarsKristoHellheads is ERC721Royalty {
77
error ERC721InvalidPrice(uint256 price);
8+
error ERC721InvalidPurchaseAmount(uint256 tokenId, uint256 price, uint256 balance);
89

9-
address public owner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; // larskristo.eth
10+
event Purchase(
11+
address indexed from,
12+
address indexed to,
13+
uint256 indexed tokenId,
14+
uint256 price,
15+
uint256 royaltyAmount,
16+
uint256 transactionFee,
17+
uint256 amount
18+
);
19+
20+
address public author = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; // larskristo.eth
21+
address public operator = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC; // svpervnder.eth
1022

1123
mapping(uint256 tokenId => uint256) private _tokenPrices;
24+
uint256 private _transactionFraction = 3;
1225

1326
string[] tokenURIs = [
1427
"QmbbdDACM5nkGqRG3cSmk8hYL46XWFkT8zvkbnrbcbSqa1",
@@ -232,24 +245,69 @@ contract LarsKristoHellheads is ERC721 {
232245

233246
constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {
234247
for (uint i = 0; i < tokenURIs.length; i++) {
235-
_safeMint(owner, i);
248+
_safeMint(author, i);
236249
_tokenPrices[i] = 0.5 ether; // initial token price
237250
}
251+
252+
_setDefaultRoyalty(author, 10); // 10% royalty
238253
}
239254

240-
// function buyToken(uint256 tokenId) public {
241-
// _requireTokenPriceSet(tokenId);
242-
// }
255+
function buyToken(uint256 tokenId) public payable returns (uint256, uint256, uint256) {
256+
uint256 price = _requireTokenPriceSet(tokenId);
257+
uint256 balance = msg.value;
258+
259+
if (balance != price) {
260+
revert ERC721InvalidPurchaseAmount(tokenId, price, balance);
261+
}
262+
263+
// pay royalties to author
264+
(address royaltyReceiver, uint256 royaltyAmount) = royaltyInfo(tokenId, price);
265+
payable(royaltyReceiver).transfer(royaltyAmount);
266+
267+
// charge the transaction fee
268+
uint256 transactionFee = getTransactionFee(tokenId);
269+
payable(operator).transfer(transactionFee);
270+
271+
// transfer the token to the new owner
272+
address previousOwner = ownerOf(tokenId);
273+
_safeTransfer(previousOwner, _msgSender(), tokenId);
274+
275+
// transfer the payment to the previous owner
276+
uint256 priceMinusRoyalty = balance - royaltyAmount - transactionFee;
277+
payable(previousOwner).transfer(priceMinusRoyalty);
278+
279+
emit Purchase(previousOwner, _msgSender(), tokenId, price, royaltyAmount, transactionFee, priceMinusRoyalty);
280+
281+
// reset the token price
282+
_tokenPrices[tokenId] = 0;
283+
284+
return (tokenId, price, balance);
285+
}
243286

244287
/**
245288
* @dev Retrieves the price of a token.
246289
* @param tokenId The ID of the token.
247290
* @return The price of the token.
248291
*/
249292
function getTokenPrice(uint256 tokenId) public view returns (uint256) {
293+
_requireTokenPriceSet(tokenId);
294+
250295
return _tokenPrices[tokenId];
251296
}
252297

298+
/**
299+
* @dev Retrieves the transaction fee for a given token ID.
300+
* @param tokenId The ID of the token.
301+
* @return The transaction fee amount.
302+
*/
303+
function getTransactionFee(uint256 tokenId) public view returns (uint256) {
304+
uint256 _price = _requireTokenPriceSet(tokenId);
305+
306+
uint256 _transactionFee = (_price * _transactionFraction) / _feeDenominator();
307+
308+
return _transactionFee;
309+
}
310+
253311
/**
254312
* @dev Sets the token for sale with the specified price.
255313
*
@@ -259,12 +317,12 @@ contract LarsKristoHellheads is ERC721 {
259317
* @param tokenId The ID of the token to set for sale.
260318
* @param price The price at which to sell the token.
261319
*/
262-
function setTokenForSale(address _owner, uint256 tokenId, uint256 price) public {
263-
if (price <= 0) {
264-
revert ERC721InvalidPrice(price);
265-
}
320+
function setTokenForSale(uint256 tokenId, uint256 price) public {
321+
_requireTokenPriceSet(tokenId);
266322

267-
_checkAuthorized(_owner, _msgSender(), tokenId);
323+
address owner = ownerOf(tokenId);
324+
325+
_checkAuthorized(owner, _msgSender(), tokenId);
268326

269327
_tokenPrices[tokenId] = price;
270328
}
@@ -310,4 +368,20 @@ contract LarsKristoHellheads is ERC721 {
310368

311369
return _owner;
312370
}
371+
372+
/**
373+
* @dev Checks if the token price is set for a given token ID.
374+
* @param tokenId The ID of the token to check the price for.
375+
* @return The price of the token.
376+
* @dev Throws an error if the token price is not set.
377+
*/
378+
function _requireTokenPriceSet(uint256 tokenId) internal view returns (uint256) {
379+
uint256 _price = _tokenPrices[tokenId];
380+
381+
if (_price <= 0) {
382+
revert ERC721InvalidPrice(_price);
383+
}
384+
385+
return _price;
386+
}
313387
}

hardhat/test/LarsKristoHellheads.ts

+101-20
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async function createERC721Contract() {
3636

3737
describe("Lease", function () {
3838
it("Initialize: call constructor", async function () {
39-
const [owner, , signer] = await ethers.getSigners();
39+
const [author, , operator] = await ethers.getSigners();
4040

4141
// console.log({ owner: owner.address, signer: signer.address });
4242

@@ -55,15 +55,15 @@ describe("Lease", function () {
5555

5656
// console.log({ ownerOf0, ownerOf1, ownerOf2, ownerOf3 });
5757

58-
expect(ownerOf0).to.equal(owner.address);
59-
expect(ownerOf1).to.equal(owner.address);
60-
expect(ownerOf2).to.equal(owner.address);
61-
expect(ownerOf3).to.equal(owner.address);
58+
expect(ownerOf0).to.equal(author.address);
59+
expect(ownerOf1).to.equal(author.address);
60+
expect(ownerOf2).to.equal(author.address);
61+
expect(ownerOf3).to.equal(author.address);
6262

63-
const token0 = await ERC721.tokenURI(0);
64-
const token1 = await ERC721.tokenURI(1);
65-
const token2 = await ERC721.tokenURI(2);
66-
const token3 = await ERC721.tokenURI(3);
63+
// const token0 = await ERC721.tokenURI(0);
64+
// const token1 = await ERC721.tokenURI(1);
65+
// const token2 = await ERC721.tokenURI(2);
66+
// const token3 = await ERC721.tokenURI(3);
6767

6868
// console.log({ token0, token1, token2, token3 });
6969

@@ -72,11 +72,11 @@ describe("Lease", function () {
7272
expect(ethers.formatEther(price)).to.equal("0.5");
7373

7474
// check approval for all
75-
await expect(ERC721.connect(owner).setApprovalForAll(signer.address, true))
75+
await expect(ERC721.connect(author).setApprovalForAll(operator.address, true))
7676
.to.emit(ERC721, "ApprovalForAll")
77-
.withArgs(owner.address, signer.address, true);
77+
.withArgs(author.address, operator.address, true);
7878

79-
const isApprovedForAll = await ERC721.isApprovedForAll(owner.address, signer.address);
79+
const isApprovedForAll = await ERC721.isApprovedForAll(author.address, operator.address);
8080
expect(isApprovedForAll).to.be.true;
8181
});
8282

@@ -90,30 +90,111 @@ describe("Lease", function () {
9090
expect(ethers.formatEther(price)).to.equal("0.5");
9191
});
9292

93+
it("royaltyInfo", async function () {
94+
const [author] = await ethers.getSigners();
95+
96+
const ERC721 = await createERC721Contract();
97+
98+
const price = await ERC721.getTokenPrice(0);
99+
100+
const royaltyInfo = await ERC721.royaltyInfo(0, price);
101+
102+
// console.log({ price: ethers.formatEther(price), royaltyInfo });
103+
104+
const [receiver, royaltyAmount] = royaltyInfo;
105+
106+
expect(ethers.formatEther(royaltyAmount)).to.equal("0.0005");
107+
expect(receiver).to.equal(author.address);
108+
});
109+
93110
it("setTokenForSale", async function () {
94-
const [owner, , signer, someoneElse] = await ethers.getSigners();
111+
const [author, , operator, someoneElse] = await ethers.getSigners();
95112

96113
const ERC721 = await createERC721Contract();
97114

98-
await ERC721.connect(owner).setApprovalForAll(signer.address, true);
115+
await ERC721.connect(author).setApprovalForAll(operator.address, true);
99116

100-
await ERC721.connect(signer).setTokenForSale(owner.address, 0, BigInt(ethers.parseEther("0.6")));
117+
await ERC721.connect(operator).setTokenForSale(0, BigInt(ethers.parseEther("0.6")));
101118

102119
const price1 = await ERC721.getTokenPrice(0);
103120

104-
console.log({ price1: ethers.formatEther(price1) });
121+
// console.log({ price1: ethers.formatEther(price1) });
105122

106123
expect(ethers.formatEther(price1)).to.equal("0.6");
107124

108-
await ERC721.connect(owner).setTokenForSale(owner.address, 1, BigInt(ethers.parseEther("0.6")));
125+
await ERC721.connect(author).setTokenForSale(1, BigInt(ethers.parseEther("0.6")));
109126

110127
const price2 = await ERC721.getTokenPrice(0);
111128

112-
console.log({ price2: ethers.formatEther(price2) });
129+
// console.log({ price2: ethers.formatEther(price2) });
113130

114131
expect(ethers.formatEther(price2)).to.equal("0.6");
115132

116-
await expect(ERC721.connect(someoneElse).setTokenForSale(owner.address, 2, BigInt(ethers.parseEther("0.6")))).to.be
117-
.reverted;
133+
await expect(ERC721.connect(someoneElse).setTokenForSale(2, BigInt(ethers.parseEther("0.6")))).to.be.reverted;
134+
});
135+
136+
it("buyToken", async function () {
137+
const [author, , operator, buyer] = await ethers.getSigners();
138+
139+
let authorBalance = await ethers.provider.getBalance(author.address);
140+
let operatorBalance = await ethers.provider.getBalance(operator.address);
141+
let buyerBalance = await ethers.provider.getBalance(buyer.address);
142+
143+
console.log({
144+
authorBalance: ethers.formatEther(authorBalance),
145+
operatorBalance: ethers.formatEther(operatorBalance),
146+
buyerBalance: ethers.formatEther(buyerBalance),
147+
});
148+
149+
const ERC721 = await createERC721Contract();
150+
151+
await ERC721.connect(author).setApprovalForAll(operator.address, true);
152+
153+
await ERC721.connect(operator).setTokenForSale(0, BigInt(ethers.parseEther("1")));
154+
155+
authorBalance = await ethers.provider.getBalance(author.address);
156+
operatorBalance = await ethers.provider.getBalance(operator.address);
157+
buyerBalance = await ethers.provider.getBalance(buyer.address);
158+
159+
console.log({
160+
authorBalance: ethers.formatEther(authorBalance),
161+
operatorBalance: ethers.formatEther(operatorBalance),
162+
buyerBalance: ethers.formatEther(buyerBalance),
163+
});
164+
165+
ERC721.on("Transfer", (from, to, tokenId) => {
166+
// console.log({ from, to, tokenId });
167+
expect(from).to.equal(author.address);
168+
expect(to).to.equal(buyer.address);
169+
expect(tokenId).to.equal(0);
170+
});
171+
172+
await expect(ERC721.connect(buyer).buyToken(0, { value: ethers.parseEther("1") }))
173+
.to.emit(ERC721, "Purchase")
174+
.withArgs(
175+
author.address,
176+
buyer.address,
177+
BigInt(0),
178+
ethers.parseEther("1"),
179+
ethers.parseEther("0.001"),
180+
ethers.parseEther("0.0003"),
181+
ethers.parseEther("0.9987"),
182+
);
183+
184+
const ownerOf0 = await ERC721.ownerOf(0);
185+
186+
// console.log({ ownerOf0 });
187+
188+
expect(ownerOf0).to.equal(buyer.address);
189+
190+
authorBalance = await ethers.provider.getBalance(author.address);
191+
operatorBalance = await ethers.provider.getBalance(operator.address);
192+
buyerBalance = await ethers.provider.getBalance(buyer.address);
193+
194+
console.log({
195+
authorBalance: ethers.formatEther(authorBalance),
196+
operatorBalance: ethers.formatEther(operatorBalance),
197+
buyerBalance: ethers.formatEther(buyerBalance),
198+
});
118199
});
119200
});

0 commit comments

Comments
 (0)