Skip to content

Commit a9f38a2

Browse files
committed
feat/nft-ecosystem
1 parent ec65f4e commit a9f38a2

File tree

7 files changed

+1380
-0
lines changed

7 files changed

+1380
-0
lines changed
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
# Gnoland NFT Marketplace
2+
3+
A fully-featured NFT marketplace for Gnoland blockchain with automatic royalty distribution (GRC-2981 compliant).
4+
5+
## Features
6+
7+
- GRC-721 compatible - works with any NFT collection implementing the standard
8+
- Automatic royalties - supports GRC-2981 with automatic distribution
9+
- Fixed price listings for immediate purchase
10+
- Secure atomic transfers
11+
- Configurable marketplace fees (default 2.5%)
12+
- Complete on-chain sales history
13+
- Volume and royalty statistics
14+
15+
## Architecture
16+
17+
```
18+
Marketplace Realm
19+
├── Listings (Active)
20+
├── Sales (History)
21+
└── Payment Distribution
22+
├── Seller
23+
├── Royalty (if GRC-2981)
24+
└── Marketplace Fee
25+
26+
NFT Collection (GRC-721)
27+
├── OwnerOf()
28+
├── TransferFrom()
29+
├── Approve() / SetApprovalForAll()
30+
└── RoyaltyInfo() (optional)
31+
```
32+
33+
## Usage
34+
35+
### For Sellers
36+
37+
#### Step 1: Approve the Marketplace
38+
39+
```bash
40+
# Approve for all NFTs (recommended)
41+
gnokey maketx call \
42+
-pkgpath "gno.land/r/[username]/MYNFT" \
43+
-func "SetApprovalForAll" \
44+
-args "g1marketplace_address" \
45+
-args "true" \
46+
-broadcast \
47+
yourkey
48+
49+
# Or approve for a specific NFT
50+
gnokey maketx call \
51+
-pkgpath "gno.land/r/[username]/MYNFT" \
52+
-func "Approve" \
53+
-args "g1marketplace_address" \
54+
-args "1" \
55+
-broadcast \
56+
yourkey
57+
```
58+
59+
#### Step 2: Create a Listing
60+
61+
62+
```bash
63+
gnokey maketx call \
64+
-pkgpath "gno.land/r/pierre115/gnopensea" \
65+
-func "CreateListing" \
66+
-args "mynft.Getter()" \
67+
-args "1" \
68+
-args "5000000" \
69+
-broadcast \
70+
yourkey
71+
```
72+
73+
Parameters:
74+
- `nftGetter`: Function returning the NFT collection instance
75+
- `tokenId`: Token ID to sell
76+
- `price`: Price in ugnot (5000000 = 5 GNOT)
77+
78+
#### Step 3: Manage Listing
79+
80+
```bash
81+
# Update price
82+
gnokey maketx call \
83+
-pkgpath "gno.land/r/pierre115/gnopensea" \
84+
-func "UpdatePrice" \
85+
-args "1" \
86+
-args "10000000" \
87+
-broadcast \
88+
yourkey
89+
90+
# Cancel listing
91+
gnokey maketx call \
92+
-pkgpath "gno.land/r/pierre115/gnopensea" \
93+
-func "CancelListing" \
94+
-args "1" \
95+
-broadcast \
96+
yourkey
97+
```
98+
99+
### For Buyers
100+
101+
```bash
102+
gnokey maketx call \
103+
-pkgpath "gno.land/r/pierre115/gnopensea" \
104+
-func "BuyNFT" \
105+
-args "1" \
106+
-send "5000000ugnot" \
107+
-broadcast \
108+
yourkey
109+
```
110+
111+
Purchase process:
112+
1. Payment verified
113+
2. Royalties calculated (if GRC-2981 supported)
114+
3. Payments distributed
115+
4. NFT transferred
116+
5. Excess refunded
117+
118+
### For Admins
119+
120+
```bash
121+
# Set marketplace fee (in basis points: 100 = 1%, max 1000 = 10%)
122+
gnokey maketx call \
123+
-pkgpath "gno.land/r/pierre115/gnopensea" \
124+
-func "SetMarketplaceFee" \
125+
-args "500" \
126+
-broadcast \
127+
adminkey
128+
129+
# Withdraw accumulated fees
130+
gnokey maketx call \
131+
-pkgpath "gno.land/r/pierre115/gnopensea" \
132+
-func "WithdrawFees" \
133+
-broadcast \
134+
adminkey
135+
```
136+
137+
## Functions Reference
138+
139+
### Public Functions
140+
141+
- `CreateListing(nftGetter, tokenId, price)` - List an NFT for sale
142+
- `BuyNFT(listingId)` - Purchase a listed NFT
143+
- `CancelListing(listingId)` - Cancel your listing
144+
- `UpdatePrice(listingId, newPrice)` - Update listing price
145+
146+
### Read Functions
147+
148+
- `GetListing(listingId)` - Get listing details
149+
- `GetSale(saleId)` - Get sale details
150+
- `GetActiveListingsCount()` - Number of active listings
151+
- `GetTotalSales()` - Total sales count
152+
- `GetTotalVolume()` - Total volume traded
153+
- `GetTotalRoyaltiesPaid()` - Total royalties distributed
154+
- `GetRoyaltyBreakdown(listingId)` - Calculate payment distribution
155+
- `GetBalance()` - Marketplace balance
156+
- `GetMarketplaceFee()` - Current fee (basis points)
157+
- `GetMarketplaceAddress()` - Marketplace realm address
158+
159+
### Admin Functions
160+
161+
- `SetMarketplaceFee(newFee)` - Update marketplace fee
162+
- `WithdrawFees()` - Withdraw accumulated fees
163+
164+
## Payment Distribution
165+
166+
Example with 10% royalty:
167+
168+
```
169+
Sale Price: 100 GNOT
170+
├── Marketplace Fee (2.5%): 2.5 GNOT
171+
├── Creator Royalty (10%): 10 GNOT
172+
└── Seller Receives: 87.5 GNOT
173+
```
174+
175+
Example without royalty:
176+
177+
```
178+
Sale Price: 100 GNOT
179+
├── Marketplace Fee (2.5%): 2.5 GNOT
180+
└── Seller Receives: 97.5 GNOT
181+
```
182+
183+
## Complete Workflow Example
184+
185+
```bash
186+
# 1. Deploy NFT collection
187+
gnokey maketx addpkg \
188+
--pkgpath "gno.land/r/alice/mycollection" \
189+
--pkgdir "./mycollection" \
190+
--broadcast \
191+
alice
192+
193+
# 2. Mint an NFT
194+
gnokey maketx call \
195+
-pkgpath "gno.land/r/alice/mycollection" \
196+
-func "Mint" \
197+
-send "1000000ugnot" \
198+
-broadcast \
199+
alice
200+
201+
# 3. Approve marketplace
202+
gnokey maketx call \
203+
-pkgpath "gno.land/r/alice/mycollection" \
204+
-func "SetApprovalForAll" \
205+
-args "g1marketplace_address" \
206+
-args "true" \
207+
-broadcast \
208+
alice
209+
210+
# 4. List for 5 GNOT
211+
gnokey maketx call \
212+
-pkgpath "gno.land/r/pierre115/gnopensea" \
213+
-func "CreateListing" \
214+
-args "mycollection.Getter()" \
215+
-args "1" \
216+
-args "5000000" \
217+
-broadcast \
218+
alice
219+
220+
# 5. Purchase NFT
221+
gnokey maketx call \
222+
-pkgpath "gno.land/r/pierre115/gnopensea" \
223+
-func "BuyNFT" \
224+
-args "1" \
225+
-send "5000000ugnot" \
226+
-broadcast \
227+
bob
228+
```
229+
230+
## Querying Data
231+
232+
```bash
233+
# View marketplace home
234+
curl https://test4.gno.land/r/demo/marketplace:
235+
236+
# View statistics
237+
curl https://test4.gno.land/r/demo/marketplace:stats
238+
239+
# View specific listing
240+
curl https://test4.gno.land/r/demo/marketplace:listing/1
241+
242+
# View sale details
243+
curl https://test4.gno.land/r/demo/marketplace:sale/1
244+
```
245+
246+
## Security Features
247+
248+
### Built-in Protections
249+
250+
- Ownership verification before listing and sale
251+
- Approval checks at listing and sale time
252+
- Payment validation with automatic refunds
253+
- Atomic transactions (all-or-nothing)
254+
- Admin fee limits (0-10%)
255+
256+
### Best Practices
257+
258+
Do:
259+
- Verify listing details before purchasing
260+
- Use `SetApprovalForAll()` for easier management
261+
- Check marketplace fee before listing
262+
- Verify royalty percentages
263+
264+
Don't:
265+
- Send more than listing price (wastes gas)
266+
- List NFTs you don't own
267+
- Forget to approve marketplace
268+
269+
## NFT Collection Integration
270+
271+
### Minimal Requirements
272+
273+
```go
274+
import "gno.land/p/demo/grc/grc721"
275+
276+
var nft *grc721.basicNFT
277+
278+
func init() {
279+
nft = grc721.NewBasicNFT("MyCollection", "MC")
280+
}
281+
282+
func Getter() grc721.NFTGetter {
283+
return nft.Getter()
284+
}
285+
```
286+
287+
### With Royalties (Optional)
288+
289+
```go
290+
var nft *grc721.royaltyNFT
291+
292+
func init() {
293+
nft = grc721.NewNFTWithRoyalty("MyCollection", "MC")
294+
}
295+
296+
func Mint() {
297+
// ... mint logic
298+
royaltyInfo := grc721.RoyaltyInfo{
299+
PaymentAddress: creator,
300+
Percentage: 10, // 10%
301+
}
302+
nft.SetTokenRoyalty(tokenId, royaltyInfo)
303+
}
304+
305+
func Getter() grc721.NFTGetter {
306+
return nft.Getter()
307+
}
308+
```
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package gnopensea
2+
3+
import (
4+
"chain/banker"
5+
"chain/runtime"
6+
)
7+
8+
// ============= ADMIN =============
9+
10+
func SetMarketplaceFee(newFee int64) {
11+
caller := runtime.PreviousRealm().Address()
12+
if caller != owner {
13+
panic("Only owner can modify fees")
14+
}
15+
16+
if newFee < 0 || newFee > 1000 { // Max 10%
17+
panic("Fees must be between 0% and 10%")
18+
}
19+
20+
marketplaceFee = newFee
21+
}
22+
23+
func WithdrawFees() {
24+
caller := runtime.PreviousRealm().Address()
25+
if caller != owner {
26+
panic("Only owner can withdraw fees")
27+
}
28+
29+
// Use NewBanker instead of GetBanker
30+
bnkr := banker.NewBanker(banker.BankerTypeRealmSend)
31+
realmAddr := runtime.CurrentRealm().Address()
32+
33+
// Get balance of the realm
34+
balance := bnkr.GetCoins(realmAddr)
35+
36+
if balance.AmountOf("ugnot") > 0 {
37+
bnkr.SendCoins(realmAddr, owner, balance)
38+
}
39+
}
40+
41+
// AddAdmin - Add a new admin address
42+
func AddAdmin(newAdmin address) {
43+
caller := runtime.PreviousRealm().Address()
44+
if caller != owner {
45+
panic("Only owner can add admins")
46+
}
47+
admins.Set(newAdmin.String(), true)
48+
}
49+
50+
// RemoveAdmin - Remove an admin address
51+
func RemoveAdmin(admin address) {
52+
caller := runtime.PreviousRealm().Address()
53+
if caller != owner {
54+
panic("Only owner can remove admins")
55+
}
56+
if admin == owner {
57+
panic("Cannot remove owner as admin")
58+
}
59+
admins.Remove(admin.String())
60+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module = "gno.land/r/pierre115/gnopensea"
2+
gno = "0.9"

0 commit comments

Comments
 (0)