strade-stx-contracts 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CoreMarketPlace.clar +227 -0
- package/DisputeResolution_clar.clar +265 -0
- package/EscrowService.clar +171 -0
- package/UserProfile.clar +280 -0
- package/ft-trait.clar +24 -0
- package/package.json +11 -4
- package/token.clar +178 -0
- package/index.js +0 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
;; CoreMarketPlace Contract
|
|
2
|
+
;; This contract manages the creation, updating, and purchasing of listings in the Strade decentralized marketplace.
|
|
3
|
+
;; It handles the core logic for marketplace interactions, including listing management and purchase fulfillment.
|
|
4
|
+
|
|
5
|
+
;; --- Constants ---
|
|
6
|
+
;; Defines immutable values used throughout the contract for error handling and configuration.
|
|
7
|
+
|
|
8
|
+
(define-constant CONTRACT_OWNER tx-sender) ;; Sets the contract deployer as the owner.
|
|
9
|
+
(define-constant ERR_NOT_AUTHORIZED (err u100)) ;; Error for unauthorized actions.
|
|
10
|
+
(define-constant ERR_LISTING_NOT_FOUND (err u101)) ;; Error when a listing cannot be found.
|
|
11
|
+
(define-constant ERR_INVALID_PRICE (err u102)) ;; Error for invalid listing prices (e.g., zero or negative).
|
|
12
|
+
(define-constant ERR_INVALID_SELLER (err u103)) ;; Error for invalid seller principals.
|
|
13
|
+
(define-constant ERR_INSUFFICIENT_BALANCE (err u104)) ;; Error when a buyer has insufficient funds.
|
|
14
|
+
(define-constant ERR_LISTING_EXPIRED (err u105)) ;; Error for expired listings.
|
|
15
|
+
(define-constant ERR_INVALID_STATUS (err u106)) ;; Error for invalid listing statuses.
|
|
16
|
+
(define-constant ERR_NOT_SELLER (err u107)) ;; Error when a user is not the seller of a listing.
|
|
17
|
+
(define-constant ERR_ALREADY_PURCHASED (err u108)) ;; Error for listings that have already been sold.
|
|
18
|
+
(define-constant ERR_INVALID_INPUT (err u109)) ;; Error for invalid input parameters.
|
|
19
|
+
(define-constant ERR_INVALID_DURATION (err u110)) ;; Error for invalid listing durations.
|
|
20
|
+
(define-constant ERR_INVALID_LISTING_ID (err u111)) ;; Error for invalid listing IDs.
|
|
21
|
+
(define-constant MAX_LISTING_DURATION u52560) ;; Maximum duration of a listing (approximately 1 year).
|
|
22
|
+
|
|
23
|
+
;; --- Data Maps ---
|
|
24
|
+
;; Defines the data structures used to store marketplace information.
|
|
25
|
+
|
|
26
|
+
(define-map Listings
|
|
27
|
+
{ listing-id: uint }
|
|
28
|
+
{
|
|
29
|
+
seller: principal, ;; The principal of the seller.
|
|
30
|
+
name: (string-utf8 64), ;; The name of the listing.
|
|
31
|
+
description: (string-utf8 256), ;; A description of the item.
|
|
32
|
+
price: uint, ;; The price of the listing in micro-STX.
|
|
33
|
+
status: (string-ascii 20), ;; The current status of the listing (e.g., "active", "sold", "cancelled").
|
|
34
|
+
created-at: uint, ;; The block height at which the listing was created.
|
|
35
|
+
expires-at: uint ;; The block height at which the listing expires.
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
;; --- Variables ---
|
|
40
|
+
;; Defines mutable variables for tracking the contract's state.
|
|
41
|
+
|
|
42
|
+
(define-data-var last-listing-id uint u0) ;; Tracks the ID of the last created listing.
|
|
43
|
+
|
|
44
|
+
;; --- Private Functions ---
|
|
45
|
+
;; Helper functions intended for internal use by the contract.
|
|
46
|
+
|
|
47
|
+
;; Checks if a given price is valid (greater than zero).
|
|
48
|
+
(define-private (is-valid-price (price uint))
|
|
49
|
+
(> price u0)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
;; Checks if a seller principal is valid (not the sender or the contract itself).
|
|
53
|
+
(define-private (is-valid-seller (seller principal))
|
|
54
|
+
(and
|
|
55
|
+
(not (is-eq seller tx-sender))
|
|
56
|
+
(not (is-eq seller (as-contract tx-sender)))
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
;; Checks if a string is within the specified length constraints.
|
|
61
|
+
(define-private (is-valid-string (str (string-utf8 256)) (max-len uint))
|
|
62
|
+
(and (>= (len str) u1) (<= (len str) max-len))
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
;; Checks if a listing duration is valid.
|
|
66
|
+
(define-private (is-valid-duration (duration uint))
|
|
67
|
+
(and (> duration u0) (<= duration MAX_LISTING_DURATION))
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
;; Checks if a listing ID is valid.
|
|
71
|
+
(define-private (is-valid-listing-id (id uint))
|
|
72
|
+
(<= id (var-get last-listing-id))
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
;; Increments and returns the next listing ID.
|
|
76
|
+
(define-private (increment-listing-id)
|
|
77
|
+
(let
|
|
78
|
+
(
|
|
79
|
+
(current-id (var-get last-listing-id))
|
|
80
|
+
)
|
|
81
|
+
(var-set last-listing-id (+ current-id u1))
|
|
82
|
+
(var-get last-listing-id)
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
;; --- Public Functions ---
|
|
87
|
+
;; Functions that can be called by any user.
|
|
88
|
+
|
|
89
|
+
;; Creates a new listing in the marketplace.
|
|
90
|
+
;; @param name: The name of the listing.
|
|
91
|
+
;; @param description: A description of the item.
|
|
92
|
+
;; @param price: The price of the listing in micro-STX.
|
|
93
|
+
;; @param duration: The duration of the listing in blocks.
|
|
94
|
+
;; @returns (ok uint): The ID of the newly created listing.
|
|
95
|
+
(define-public (create-listing (name (string-utf8 64)) (description (string-utf8 256)) (price uint) (duration uint))
|
|
96
|
+
(let
|
|
97
|
+
(
|
|
98
|
+
(listing-id (increment-listing-id))
|
|
99
|
+
(expires-at (+ stacks-block-height duration))
|
|
100
|
+
)
|
|
101
|
+
(asserts! (is-valid-price price) (err ERR_INVALID_PRICE))
|
|
102
|
+
(asserts! (is-valid-string name u64) (err ERR_INVALID_INPUT))
|
|
103
|
+
(asserts! (is-valid-string description u256) (err ERR_INVALID_INPUT))
|
|
104
|
+
(asserts! (is-valid-duration duration) (err ERR_INVALID_DURATION))
|
|
105
|
+
(map-set Listings
|
|
106
|
+
{ listing-id: listing-id }
|
|
107
|
+
{
|
|
108
|
+
seller: tx-sender,
|
|
109
|
+
name: name,
|
|
110
|
+
description: description,
|
|
111
|
+
price: price,
|
|
112
|
+
status: "active",
|
|
113
|
+
created-at: stacks-block-height,
|
|
114
|
+
expires-at: expires-at
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
(print { event: "listing_created", listing-id: listing-id, seller: tx-sender })
|
|
118
|
+
(ok listing-id)
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
;; Updates an existing listing.
|
|
123
|
+
;; @param listing-id: The ID of the listing to update.
|
|
124
|
+
;; @param new-price: The new price for the listing.
|
|
125
|
+
;; @param new-description: The new description for the listing.
|
|
126
|
+
;; @returns (ok bool): True if the update is successful.
|
|
127
|
+
(define-public (update-listing (listing-id uint) (new-price uint) (new-description (string-utf8 256)))
|
|
128
|
+
(begin
|
|
129
|
+
(asserts! (is-valid-listing-id listing-id) (err ERR_INVALID_LISTING_ID))
|
|
130
|
+
(let
|
|
131
|
+
(
|
|
132
|
+
(listing (unwrap! (map-get? Listings { listing-id: listing-id }) (err ERR_LISTING_NOT_FOUND)))
|
|
133
|
+
)
|
|
134
|
+
(asserts! (is-eq (get seller listing) tx-sender) (err ERR_NOT_SELLER))
|
|
135
|
+
(asserts! (is-eq (get status listing) "active") (err ERR_INVALID_STATUS))
|
|
136
|
+
(asserts! (is-valid-price new-price) (err ERR_INVALID_PRICE))
|
|
137
|
+
(asserts! (is-valid-string new-description u256) (err ERR_INVALID_INPUT))
|
|
138
|
+
(map-set Listings
|
|
139
|
+
{ listing-id: listing-id }
|
|
140
|
+
(merge listing
|
|
141
|
+
{
|
|
142
|
+
price: new-price,
|
|
143
|
+
description: new-description
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
(print { event: "listing_updated", listing-id: listing-id, seller: tx-sender })
|
|
148
|
+
(ok true)
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
;; Cancels a listing.
|
|
154
|
+
;; @param listing-id: The ID of the listing to cancel.
|
|
155
|
+
;; @returns (ok bool): True if the cancellation is successful.
|
|
156
|
+
(define-public (cancel-listing (listing-id uint))
|
|
157
|
+
(begin
|
|
158
|
+
(asserts! (is-valid-listing-id listing-id) (err ERR_INVALID_LISTING_ID))
|
|
159
|
+
(let
|
|
160
|
+
(
|
|
161
|
+
(listing (unwrap! (map-get? Listings { listing-id: listing-id }) (err ERR_LISTING_NOT_FOUND)))
|
|
162
|
+
)
|
|
163
|
+
(asserts! (is-eq (get seller listing) tx-sender) (err ERR_NOT_SELLER))
|
|
164
|
+
(asserts! (is-eq (get status listing) "active") (err ERR_INVALID_STATUS))
|
|
165
|
+
(map-set Listings
|
|
166
|
+
{ listing-id: listing-id }
|
|
167
|
+
(merge listing { status: "cancelled" })
|
|
168
|
+
)
|
|
169
|
+
(print { event: "listing_cancelled", listing-id: listing-id, seller: tx-sender })
|
|
170
|
+
(ok true)
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
;; Purchases a listing.
|
|
176
|
+
;; @param listing-id: The ID of the listing to purchase.
|
|
177
|
+
;; @returns (ok bool): True if the purchase is successful.
|
|
178
|
+
(define-public (purchase-listing (listing-id uint))
|
|
179
|
+
(begin
|
|
180
|
+
(asserts! (is-valid-listing-id listing-id) (err ERR_INVALID_LISTING_ID))
|
|
181
|
+
(let
|
|
182
|
+
(
|
|
183
|
+
(listing (unwrap! (map-get? Listings { listing-id: listing-id }) (err ERR_LISTING_NOT_FOUND)))
|
|
184
|
+
(price (get price listing))
|
|
185
|
+
(seller (get seller listing))
|
|
186
|
+
)
|
|
187
|
+
(asserts! (is-eq (get status listing) "active") (err ERR_INVALID_STATUS))
|
|
188
|
+
(asserts! (<= stacks-block-height (get expires-at listing)) (err ERR_LISTING_EXPIRED))
|
|
189
|
+
(asserts! (is-valid-seller seller) (err ERR_INVALID_SELLER))
|
|
190
|
+
(match (stx-transfer? price tx-sender seller)
|
|
191
|
+
success (begin
|
|
192
|
+
(map-set Listings
|
|
193
|
+
{ listing-id: listing-id }
|
|
194
|
+
(merge listing { status: "sold" })
|
|
195
|
+
)
|
|
196
|
+
(print { event: "listing_purchased", listing-id: listing-id, buyer: tx-sender, seller: seller, price: price })
|
|
197
|
+
(ok true))
|
|
198
|
+
error (err ERR_INSUFFICIENT_BALANCE))
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
;; --- Read-Only Functions ---
|
|
204
|
+
;; Functions for retrieving data from the contract without making state changes.
|
|
205
|
+
|
|
206
|
+
;; Retrieves a listing by its ID.
|
|
207
|
+
;; @param listing-id: The ID of the listing to retrieve.
|
|
208
|
+
;; @returns (optional {<listing-data>}): The listing data or none if not found.
|
|
209
|
+
(define-read-only (get-listing (listing-id uint))
|
|
210
|
+
(if (is-valid-listing-id listing-id)
|
|
211
|
+
(map-get? Listings { listing-id: listing-id })
|
|
212
|
+
none
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
;; Retrieves the ID of the last created listing.
|
|
217
|
+
;; @returns (ok uint): The last listing ID.
|
|
218
|
+
(define-read-only (get-last-listing-id)
|
|
219
|
+
(ok (var-get last-listing-id))
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
;; --- Contract Initialization ---
|
|
223
|
+
;; Initializes the contract upon deployment.
|
|
224
|
+
(begin
|
|
225
|
+
(print "CoreMarketPlace contract initialized")
|
|
226
|
+
(ok true)
|
|
227
|
+
)
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
;; DisputeResolution Contract
|
|
2
|
+
;; This contract handles the dispute resolution process for the Strade marketplace.
|
|
3
|
+
;; It allows users to raise disputes, arbitrators to vote on them, and resolves disputes based on the outcome.
|
|
4
|
+
|
|
5
|
+
;; --- Constants ---
|
|
6
|
+
;; Defines immutable values used throughout the contract for error handling and configuration.
|
|
7
|
+
|
|
8
|
+
(define-constant CONTRACT_OWNER tx-sender) ;; Sets the contract deployer as the owner.
|
|
9
|
+
(define-constant ERR_NOT_AUTHORIZED (err u100)) ;; Error for unauthorized actions.
|
|
10
|
+
(define-constant ERR_DISPUTE_NOT_FOUND (err u101)) ;; Error when a dispute cannot be found.
|
|
11
|
+
(define-constant ERR_INVALID_STATE (err u102)) ;; Error for invalid dispute states.
|
|
12
|
+
(define-constant ERR_NOT_ARBITRATOR (err u103)) ;; Error when a user is not an authorized arbitrator.
|
|
13
|
+
(define-constant ERR_ALREADY_VOTED (err u104)) ;; Error if an arbitrator has already voted.
|
|
14
|
+
(define-constant ERR_VOTING_CLOSED (err u105)) ;; Error when voting on a dispute is closed.
|
|
15
|
+
(define-constant ERR_INSUFFICIENT_VOTES (err u106)) ;; Error if there are not enough votes to resolve a dispute.
|
|
16
|
+
(define-constant ERR_INVALID_VOTE (err u107)) ;; Error for invalid vote values.
|
|
17
|
+
(define-constant ERR_NOT_INVOLVED_PARTY (err u108)) ;; Error when a user is not a party to the dispute.
|
|
18
|
+
(define-constant ERR_INVALID_ESCROW_ID (err u109)) ;; Error for invalid escrow IDs.
|
|
19
|
+
(define-constant ERR_INVALID_REASON (err u110)) ;; Error for invalid dispute reasons.
|
|
20
|
+
(define-constant ERR_INVALID_DISPUTE_ID (err u111)) ;; Error for invalid dispute IDs.
|
|
21
|
+
(define-constant ERR_INVALID_REWARD (err u112)) ;; Error for invalid arbitrator rewards.
|
|
22
|
+
(define-constant ERR_INVALID_PRINCIPAL (err u113)) ;; Error for invalid principal addresses.
|
|
23
|
+
(define-constant VOTING_PERIOD u144) ;; The duration of the voting period in blocks (approximately 24 hours).
|
|
24
|
+
(define-constant MIN_VOTES_REQUIRED u3) ;; The minimum number of votes required to resolve a dispute.
|
|
25
|
+
|
|
26
|
+
;; --- Data Maps ---
|
|
27
|
+
;; Defines the data structures used to store dispute and arbitrator information.
|
|
28
|
+
|
|
29
|
+
(define-map Disputes
|
|
30
|
+
{ dispute-id: uint }
|
|
31
|
+
{
|
|
32
|
+
escrow-id: uint, ;; The ID of the associated escrow.
|
|
33
|
+
initiator: principal, ;; The principal who initiated the dispute.
|
|
34
|
+
counterparty: principal, ;; The other party in the dispute.
|
|
35
|
+
reason: (string-utf8 256), ;; The reason for the dispute.
|
|
36
|
+
status: (string-ascii 20), ;; The current status of the dispute (e.g., "open", "resolved").
|
|
37
|
+
created-at: uint, ;; The block height at which the dispute was created.
|
|
38
|
+
votes-for: uint, ;; The number of votes in favor of the initiator.
|
|
39
|
+
votes-against: uint, ;; The number of votes against the initiator.
|
|
40
|
+
resolution: (optional (string-ascii 20)) ;; The resolution of the dispute.
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
(define-map Arbitrators principal bool) ;; Stores the set of authorized arbitrators.
|
|
45
|
+
(define-map ArbitratorVotes { dispute-id: uint, arbitrator: principal } bool) ;; Tracks votes cast by arbitrators.
|
|
46
|
+
|
|
47
|
+
;; --- Variables ---
|
|
48
|
+
;; Defines mutable variables for tracking the contract's state.
|
|
49
|
+
|
|
50
|
+
(define-data-var last-dispute-id uint u0) ;; Tracks the ID of the last created dispute.
|
|
51
|
+
(define-data-var arbitrator-reward uint u100) ;; The reward amount for arbitrators who vote on a dispute.
|
|
52
|
+
|
|
53
|
+
;; --- Private Functions ---
|
|
54
|
+
;; Helper functions intended for internal use by the contract.
|
|
55
|
+
|
|
56
|
+
;; Checks if a given user is an authorized arbitrator.
|
|
57
|
+
(define-private (is-arbitrator (user principal))
|
|
58
|
+
(default-to false (map-get? Arbitrators user))
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
;; Checks if an arbitrator has already voted on a specific dispute.
|
|
62
|
+
(define-private (has-voted (dispute-id uint) (arbitrator principal))
|
|
63
|
+
(is-some (map-get? ArbitratorVotes { dispute-id: dispute-id, arbitrator: arbitrator }))
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
;; Updates the vote count for a dispute.
|
|
67
|
+
(define-private (update-vote-count (dispute-id uint) (vote bool))
|
|
68
|
+
(match (map-get? Disputes { dispute-id: dispute-id })
|
|
69
|
+
dispute (let
|
|
70
|
+
(
|
|
71
|
+
(new-votes-for (if vote (+ (get votes-for dispute) u1) (get votes-for dispute)))
|
|
72
|
+
(new-votes-against (if vote (get votes-against dispute) (+ (get votes-against dispute) u1)))
|
|
73
|
+
)
|
|
74
|
+
(map-set Disputes { dispute-id: dispute-id }
|
|
75
|
+
(merge dispute {
|
|
76
|
+
votes-for: new-votes-for,
|
|
77
|
+
votes-against: new-votes-against
|
|
78
|
+
}))
|
|
79
|
+
(ok true))
|
|
80
|
+
(err ERR_DISPUTE_NOT_FOUND)
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
;; Checks if an escrow ID is valid.
|
|
85
|
+
(define-private (is-valid-escrow-id (escrow-id uint))
|
|
86
|
+
(> escrow-id u0)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
;; Checks if a dispute reason is valid.
|
|
90
|
+
(define-private (is-valid-reason (reason (string-utf8 256)))
|
|
91
|
+
(and (> (len reason) u0) (<= (len reason) u256))
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
;; Checks if a dispute ID is valid.
|
|
95
|
+
(define-private (is-valid-dispute-id (dispute-id uint))
|
|
96
|
+
(<= dispute-id (var-get last-dispute-id))
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
;; --- Public Functions ---
|
|
100
|
+
;; Functions that can be called by any user.
|
|
101
|
+
|
|
102
|
+
;; Raises a new dispute.
|
|
103
|
+
;; @param escrow-id: The ID of the escrow to dispute.
|
|
104
|
+
;; @param counterparty: The other party in the dispute.
|
|
105
|
+
;; @param reason: The reason for the dispute.
|
|
106
|
+
;; @returns (ok uint): The ID of the newly created dispute.
|
|
107
|
+
(define-public (raise-dispute (escrow-id uint) (counterparty principal) (reason (string-utf8 256)))
|
|
108
|
+
(let
|
|
109
|
+
(
|
|
110
|
+
(dispute-id (+ (var-get last-dispute-id) u1))
|
|
111
|
+
)
|
|
112
|
+
(asserts! (is-valid-escrow-id escrow-id) (err ERR_INVALID_ESCROW_ID))
|
|
113
|
+
(asserts! (is-valid-reason reason) (err ERR_INVALID_REASON))
|
|
114
|
+
(asserts! (not (is-eq tx-sender counterparty)) (err ERR_NOT_INVOLVED_PARTY))
|
|
115
|
+
(map-set Disputes
|
|
116
|
+
{ dispute-id: dispute-id }
|
|
117
|
+
{
|
|
118
|
+
escrow-id: escrow-id,
|
|
119
|
+
initiator: tx-sender,
|
|
120
|
+
counterparty: counterparty,
|
|
121
|
+
reason: reason,
|
|
122
|
+
status: "open",
|
|
123
|
+
created-at: stacks-block-height,
|
|
124
|
+
votes-for: u0,
|
|
125
|
+
votes-against: u0,
|
|
126
|
+
resolution: none
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
(var-set last-dispute-id dispute-id)
|
|
130
|
+
(print { event: "dispute_raised", dispute-id: dispute-id, escrow-id: escrow-id, initiator: tx-sender })
|
|
131
|
+
(ok dispute-id)
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
;; Allows an arbitrator to vote on a dispute.
|
|
136
|
+
;; @param dispute-id: The ID of the dispute to vote on.
|
|
137
|
+
;; @param vote: The arbitrator's vote (true for initiator, false for counterparty).
|
|
138
|
+
;; @returns (ok bool): True if the vote is successful.
|
|
139
|
+
(define-public (vote-on-dispute (dispute-id uint) (vote bool))
|
|
140
|
+
(let
|
|
141
|
+
(
|
|
142
|
+
(dispute (unwrap! (map-get? Disputes { dispute-id: dispute-id }) (err ERR_DISPUTE_NOT_FOUND)))
|
|
143
|
+
)
|
|
144
|
+
(asserts! (is-valid-dispute-id dispute-id) (err ERR_INVALID_DISPUTE_ID))
|
|
145
|
+
(asserts! (is-arbitrator tx-sender) (err ERR_NOT_ARBITRATOR))
|
|
146
|
+
(asserts! (is-eq (get status dispute) "open") (err ERR_VOTING_CLOSED))
|
|
147
|
+
(asserts! (<= (- stacks-block-height (get created-at dispute)) VOTING_PERIOD) (err ERR_VOTING_CLOSED))
|
|
148
|
+
(asserts! (not (has-voted dispute-id tx-sender)) (err ERR_ALREADY_VOTED))
|
|
149
|
+
(try! (update-vote-count dispute-id vote))
|
|
150
|
+
(map-set ArbitratorVotes { dispute-id: dispute-id, arbitrator: tx-sender } vote)
|
|
151
|
+
(print { event: "arbitrator_voted", dispute-id: dispute-id, arbitrator: tx-sender, vote: vote })
|
|
152
|
+
(ok true)
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
;; Resolves a dispute based on the votes.
|
|
157
|
+
;; @param dispute-id: The ID of the dispute to resolve.
|
|
158
|
+
;; @returns (ok (string-ascii 20)): The resolution of the dispute.
|
|
159
|
+
(define-public (resolve-dispute (dispute-id uint))
|
|
160
|
+
(let
|
|
161
|
+
(
|
|
162
|
+
(dispute (unwrap! (map-get? Disputes { dispute-id: dispute-id }) (err ERR_DISPUTE_NOT_FOUND)))
|
|
163
|
+
)
|
|
164
|
+
(asserts! (is-valid-dispute-id dispute-id) (err ERR_INVALID_DISPUTE_ID))
|
|
165
|
+
(asserts! (is-eq (get status dispute) "open") (err ERR_INVALID_STATE))
|
|
166
|
+
(asserts! (>= (+ (get votes-for dispute) (get votes-against dispute)) MIN_VOTES_REQUIRED) (err ERR_INSUFFICIENT_VOTES))
|
|
167
|
+
(asserts! (<= (- stacks-block-height (get created-at dispute)) VOTING_PERIOD) (err ERR_VOTING_CLOSED))
|
|
168
|
+
(let
|
|
169
|
+
(
|
|
170
|
+
(resolution (if (> (get votes-for dispute) (get votes-against dispute)) "for_initiator" "for_counterparty"))
|
|
171
|
+
)
|
|
172
|
+
(map-set Disputes { dispute-id: dispute-id }
|
|
173
|
+
(merge dispute {
|
|
174
|
+
status: "resolved",
|
|
175
|
+
resolution: (some resolution)
|
|
176
|
+
})
|
|
177
|
+
)
|
|
178
|
+
(print { event: "dispute_resolved", dispute-id: dispute-id, resolution: resolution })
|
|
179
|
+
(ok resolution)
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
;; Adds a new arbitrator.
|
|
185
|
+
;; @param arbitrator: The principal of the new arbitrator.
|
|
186
|
+
;; @returns (ok bool): True if the arbitrator is added successfully.
|
|
187
|
+
(define-public (add-arbitrator (arbitrator principal))
|
|
188
|
+
(begin
|
|
189
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
|
|
190
|
+
(asserts! (is-valid-principal arbitrator) (err ERR_INVALID_PRINCIPAL))
|
|
191
|
+
(map-set Arbitrators arbitrator true)
|
|
192
|
+
(print { event: "arbitrator_added", arbitrator: arbitrator })
|
|
193
|
+
(ok true)
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
;; Removes an arbitrator.
|
|
198
|
+
;; @param arbitrator: The principal of the arbitrator to remove.
|
|
199
|
+
;; @returns (ok bool): True if the arbitrator is removed successfully.
|
|
200
|
+
(define-public (remove-arbitrator (arbitrator principal))
|
|
201
|
+
(begin
|
|
202
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
|
|
203
|
+
(asserts! (is-valid-principal arbitrator) (err ERR_INVALID_PRINCIPAL))
|
|
204
|
+
(map-delete Arbitrators arbitrator)
|
|
205
|
+
(print { event: "arbitrator_removed", arbitrator: arbitrator })
|
|
206
|
+
(ok true)
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
;; Sets the reward for arbitrators.
|
|
211
|
+
;; @param new-reward: The new reward amount.
|
|
212
|
+
;; @returns (ok bool): True if the reward is set successfully.
|
|
213
|
+
(define-public (set-arbitrator-reward (new-reward uint))
|
|
214
|
+
(begin
|
|
215
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
|
|
216
|
+
(asserts! (> new-reward u0) (err ERR_INVALID_REWARD))
|
|
217
|
+
(var-set arbitrator-reward new-reward)
|
|
218
|
+
(print { event: "arbitrator_reward_set", new-reward: new-reward })
|
|
219
|
+
(ok true)
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
;; --- Read-Only Functions ---
|
|
224
|
+
;; Functions for retrieving data from the contract without making state changes.
|
|
225
|
+
|
|
226
|
+
;; Retrieves a dispute by its ID.
|
|
227
|
+
;; @param dispute-id: The ID of the dispute to retrieve.
|
|
228
|
+
;; @returns (optional {<dispute-data>}): The dispute data or none if not found.
|
|
229
|
+
(define-read-only (get-dispute (dispute-id uint))
|
|
230
|
+
(map-get? Disputes { dispute-id: dispute-id })
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
;; Retrieves the ID of the last created dispute.
|
|
234
|
+
;; @returns (ok uint): The last dispute ID.
|
|
235
|
+
(define-read-only (get-last-dispute-id)
|
|
236
|
+
(ok (var-get last-dispute-id))
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
;; Retrieves the arbitrator reward amount.
|
|
240
|
+
;; @returns (ok uint): The arbitrator reward amount.
|
|
241
|
+
(define-read-only (get-arbitrator-reward)
|
|
242
|
+
(ok (var-get arbitrator-reward))
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
;; Checks if a user is an authorized arbitrator.
|
|
246
|
+
;; @param user: The principal to check.
|
|
247
|
+
;; @returns (ok bool): True if the user is an arbitrator.
|
|
248
|
+
(define-read-only (is-user-arbitrator (user principal))
|
|
249
|
+
(ok (is-arbitrator user))
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
;; --- Helper function for principal validation ---
|
|
253
|
+
(define-private (is-valid-principal (principal principal))
|
|
254
|
+
(and
|
|
255
|
+
(not (is-eq principal CONTRACT_OWNER))
|
|
256
|
+
(not (is-eq principal (as-contract tx-sender)))
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
;; --- Contract Initialization ---
|
|
261
|
+
;; Initializes the contract upon deployment.
|
|
262
|
+
(begin
|
|
263
|
+
(print "DisputeResolution contract initialized")
|
|
264
|
+
(ok true)
|
|
265
|
+
)
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
;; EscrowService Contract
|
|
2
|
+
;; This contract provides a secure escrow service for transactions between buyers and sellers.
|
|
3
|
+
;; It holds funds until the buyer releases them to the seller or a dispute is resolved.
|
|
4
|
+
|
|
5
|
+
;; --- Constants ---
|
|
6
|
+
;; Defines immutable values used throughout the contract for error handling and configuration.
|
|
7
|
+
|
|
8
|
+
(define-constant CONTRACT_OWNER tx-sender) ;; Sets the contract deployer as the owner.
|
|
9
|
+
(define-constant ERR_NOT_AUTHORIZED (err u100)) ;; Error for unauthorized actions.
|
|
10
|
+
(define-constant ERR_ESCROW_NOT_FOUND (err u101)) ;; Error when an escrow cannot be found.
|
|
11
|
+
(define-constant ERR_ALREADY_RELEASED (err u102)) ;; Error if funds have already been released.
|
|
12
|
+
(define-constant ERR_TRANSFER_FAILED (err u103)) ;; Error for failed STX transfers.
|
|
13
|
+
(define-constant ERR_INVALID_ESCROW_ID (err u104)) ;; Error for invalid escrow IDs.
|
|
14
|
+
(define-constant ERR_INVALID_AMOUNT (err u105)) ;; Error for invalid transaction amounts.
|
|
15
|
+
(define-constant ERR_INVALID_SELLER (err u106)) ;; Error for invalid seller principals.
|
|
16
|
+
(define-constant ERR_ESCROW_EXPIRED (err u107)) ;; Error for expired escrows.
|
|
17
|
+
(define-constant ESCROW_DURATION u1008) ;; The duration of the escrow in blocks (approximately 7 days).
|
|
18
|
+
|
|
19
|
+
;; --- Data Maps ---
|
|
20
|
+
;; Defines the data structures used to store escrow information.
|
|
21
|
+
|
|
22
|
+
(define-map Escrows
|
|
23
|
+
{ escrow-id: uint }
|
|
24
|
+
{
|
|
25
|
+
buyer: principal, ;; The principal of the buyer.
|
|
26
|
+
seller: principal, ;; The principal of the seller.
|
|
27
|
+
amount: uint, ;; The amount of STX held in escrow.
|
|
28
|
+
state: (string-ascii 10), ;; The current state of the escrow (e.g., "locked", "released", "refunded").
|
|
29
|
+
created-at: uint, ;; The block height at which the escrow was created.
|
|
30
|
+
expires-at: uint ;; The block height at which the escrow expires.
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
;; --- Variables ---
|
|
35
|
+
;; Defines mutable variables for tracking the contract's state.
|
|
36
|
+
|
|
37
|
+
(define-data-var last-escrow-id uint u0) ;; Tracks the ID of the last created escrow.
|
|
38
|
+
|
|
39
|
+
;; --- Helper functions ---
|
|
40
|
+
|
|
41
|
+
;; Checks if a seller principal is valid.
|
|
42
|
+
(define-private (is-valid-seller (seller principal))
|
|
43
|
+
(and
|
|
44
|
+
(not (is-eq seller tx-sender))
|
|
45
|
+
(not (is-eq seller (as-contract tx-sender)))
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
;; Checks if an escrow ID is valid.
|
|
50
|
+
(define-private (is-valid-escrow-id (escrow-id uint))
|
|
51
|
+
(<= escrow-id (var-get last-escrow-id))
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
;; --- Public Functions ---
|
|
55
|
+
|
|
56
|
+
;; Creates a new escrow.
|
|
57
|
+
;; @param seller: The principal of the seller.
|
|
58
|
+
;; @param amount: The amount of STX to hold in escrow.
|
|
59
|
+
;; @returns (ok uint): The ID of the newly created escrow.
|
|
60
|
+
(define-public (create-escrow (seller principal) (amount uint))
|
|
61
|
+
(let
|
|
62
|
+
(
|
|
63
|
+
(escrow-id (+ (var-get last-escrow-id) u1))
|
|
64
|
+
(expires-at (+ stacks-block-height ESCROW_DURATION))
|
|
65
|
+
)
|
|
66
|
+
(asserts! (> amount u0) (err ERR_INVALID_AMOUNT))
|
|
67
|
+
(asserts! (is-valid-seller seller) (err ERR_INVALID_SELLER))
|
|
68
|
+
(match (stx-transfer? amount tx-sender (as-contract tx-sender))
|
|
69
|
+
success
|
|
70
|
+
(begin
|
|
71
|
+
(map-set Escrows
|
|
72
|
+
{ escrow-id: escrow-id }
|
|
73
|
+
{
|
|
74
|
+
buyer: tx-sender,
|
|
75
|
+
seller: seller,
|
|
76
|
+
amount: amount,
|
|
77
|
+
state: "locked",
|
|
78
|
+
created-at: stacks-block-height,
|
|
79
|
+
expires-at: expires-at
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
(var-set last-escrow-id escrow-id)
|
|
83
|
+
(print {event: "escrow_created", escrow-id: escrow-id, buyer: tx-sender, seller: seller, amount: amount})
|
|
84
|
+
(ok escrow-id)
|
|
85
|
+
)
|
|
86
|
+
error (err ERR_TRANSFER_FAILED)
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
;; Releases funds to the seller.
|
|
92
|
+
;; @param escrow-id: The ID of the escrow to release.
|
|
93
|
+
;; @returns (ok bool): True if the funds are released successfully.
|
|
94
|
+
(define-public (release-funds (escrow-id uint))
|
|
95
|
+
(begin
|
|
96
|
+
(asserts! (is-valid-escrow-id escrow-id) (err ERR_INVALID_ESCROW_ID))
|
|
97
|
+
(let
|
|
98
|
+
(
|
|
99
|
+
(escrow (unwrap! (map-get? Escrows { escrow-id: escrow-id }) (err ERR_ESCROW_NOT_FOUND)))
|
|
100
|
+
(seller (get seller escrow))
|
|
101
|
+
(amount (get amount escrow))
|
|
102
|
+
)
|
|
103
|
+
(asserts! (or (is-eq tx-sender CONTRACT_OWNER) (is-eq tx-sender (get buyer escrow))) (err ERR_NOT_AUTHORIZED))
|
|
104
|
+
(asserts! (is-eq (get state escrow) "locked") (err ERR_ALREADY_RELEASED))
|
|
105
|
+
(asserts! (<= stacks-block-height (get expires-at escrow)) (err ERR_ESCROW_EXPIRED))
|
|
106
|
+
(match (as-contract (stx-transfer? amount tx-sender seller))
|
|
107
|
+
success
|
|
108
|
+
(begin
|
|
109
|
+
(map-set Escrows
|
|
110
|
+
{ escrow-id: escrow-id }
|
|
111
|
+
(merge escrow { state: "released" })
|
|
112
|
+
)
|
|
113
|
+
(print {event: "funds_released", escrow-id: escrow-id, seller: seller, amount: amount})
|
|
114
|
+
(ok true)
|
|
115
|
+
)
|
|
116
|
+
error (err ERR_TRANSFER_FAILED)
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
;; Refunds the buyer.
|
|
123
|
+
;; @param escrow-id: The ID of the escrow to refund.
|
|
124
|
+
;; @returns (ok bool): True if the refund is successful.
|
|
125
|
+
(define-public (refund-buyer (escrow-id uint))
|
|
126
|
+
(begin
|
|
127
|
+
(asserts! (is-valid-escrow-id escrow-id) (err ERR_INVALID_ESCROW_ID))
|
|
128
|
+
(let
|
|
129
|
+
(
|
|
130
|
+
(escrow (unwrap! (map-get? Escrows { escrow-id: escrow-id }) (err ERR_ESCROW_NOT_FOUND)))
|
|
131
|
+
(buyer (get buyer escrow))
|
|
132
|
+
(amount (get amount escrow))
|
|
133
|
+
)
|
|
134
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
|
|
135
|
+
(asserts! (is-eq (get state escrow) "locked") (err ERR_ALREADY_RELEASED))
|
|
136
|
+
(match (as-contract (stx-transfer? amount tx-sender buyer))
|
|
137
|
+
success
|
|
138
|
+
(begin
|
|
139
|
+
(map-set Escrows
|
|
140
|
+
{ escrow-id: escrow-id }
|
|
141
|
+
(merge escrow { state: "refunded" })
|
|
142
|
+
)
|
|
143
|
+
(print {event: "buyer_refunded", escrow-id: escrow-id, buyer: buyer, amount: amount})
|
|
144
|
+
(ok true)
|
|
145
|
+
)
|
|
146
|
+
error (err ERR_TRANSFER_FAILED)
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
;; --- Read-Only Functions ---
|
|
153
|
+
|
|
154
|
+
;; Retrieves escrow details by ID.
|
|
155
|
+
;; @param escrow-id: The ID of the escrow to retrieve.
|
|
156
|
+
;; @returns (ok {<escrow-data>}): The escrow data or an error if not found.
|
|
157
|
+
(define-read-only (get-escrow (escrow-id uint))
|
|
158
|
+
(begin
|
|
159
|
+
(asserts! (is-valid-escrow-id escrow-id) (err ERR_INVALID_ESCROW_ID))
|
|
160
|
+
(match (map-get? Escrows { escrow-id: escrow-id })
|
|
161
|
+
escrow (ok escrow)
|
|
162
|
+
(err ERR_ESCROW_NOT_FOUND)
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
;; Retrieves the ID of the last created escrow.
|
|
168
|
+
;; @returns (ok uint): The last escrow ID.
|
|
169
|
+
(define-read-only (get-last-escrow-id)
|
|
170
|
+
(ok (var-get last-escrow-id))
|
|
171
|
+
)
|
package/UserProfile.clar
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
;; UserProfile Contract
|
|
2
|
+
;; This contract manages user profiles, including usernames, bios, ratings, and reputation scores.
|
|
3
|
+
;; It provides functions for users to register, update their profiles, and rate each other.
|
|
4
|
+
|
|
5
|
+
;; --- Constants ---
|
|
6
|
+
;; Defines immutable values used throughout the contract for error handling and configuration.
|
|
7
|
+
|
|
8
|
+
(define-constant CONTRACT_OWNER tx-sender) ;; Sets the contract deployer as the owner.
|
|
9
|
+
(define-constant ERR_NOT_AUTHORIZED (err u100)) ;; Error for unauthorized actions.
|
|
10
|
+
(define-constant ERR_USER_NOT_FOUND (err u101)) ;; Error when a user profile cannot be found.
|
|
11
|
+
(define-constant ERR_INVALID_RATING (err u102)) ;; Error for invalid rating values (must be between 1 and 5).
|
|
12
|
+
(define-constant ERR_SELF_RATING (err u103)) ;; Error when a user attempts to rate themselves.
|
|
13
|
+
(define-constant ERR_ALREADY_REGISTERED (err u104)) ;; Error if a user is already registered.
|
|
14
|
+
(define-constant ERR_INVALID_INPUT (err u105)) ;; Error for invalid input parameters.
|
|
15
|
+
(define-constant ERR_DATA_STORE_FAILURE (err u106)) ;; Error for data storage failures.
|
|
16
|
+
(define-constant ERR_INVALID_PRINCIPAL (err u107)) ;; Error for invalid principal addresses.
|
|
17
|
+
(define-constant ERR_INVALID_USERNAME (err u108)) ;; Error for invalid usernames.
|
|
18
|
+
(define-constant ERR_INVALID_BIO (err u109)) ;; Error for invalid bio lengths.
|
|
19
|
+
(define-constant ERR_INVALID_EMAIL (err u110)) ;; Error for invalid email formats or lengths.
|
|
20
|
+
|
|
21
|
+
;; --- Data Maps ---
|
|
22
|
+
;; Defines the data structures used to store user profile information.
|
|
23
|
+
|
|
24
|
+
(define-map Users principal
|
|
25
|
+
{
|
|
26
|
+
username: (string-utf8 64), ;; The user's chosen username.
|
|
27
|
+
bio: (string-utf8 256), ;; A short biography or description.
|
|
28
|
+
email: (string-utf8 64), ;; The user's email address.
|
|
29
|
+
registration-date: uint, ;; The block height of the user's registration.
|
|
30
|
+
total-ratings: uint, ;; The total number of ratings the user has received.
|
|
31
|
+
rating-sum: uint, ;; The sum of all ratings received.
|
|
32
|
+
reputation-score: uint ;; A calculated score based on ratings and other factors.
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
(define-map UserAuthorization principal bool) ;; Stores authorization status for specific users.
|
|
37
|
+
|
|
38
|
+
;; --- Private Functions ---
|
|
39
|
+
;; Helper functions for internal contract use.
|
|
40
|
+
|
|
41
|
+
;; Checks if a string is within the specified length constraints.
|
|
42
|
+
(define-private (is-valid-string (str (string-utf8 256)) (max-len uint))
|
|
43
|
+
(and (>= (len str) u1) (<= (len str) max-len))
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
;; Checks if a principal is a valid user address.
|
|
47
|
+
(define-private (is-valid-principal (user principal))
|
|
48
|
+
(not (is-eq user (as-contract tx-sender)))
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
;; Validates the format and length of a username.
|
|
52
|
+
(define-private (validate-username (username (string-utf8 64)))
|
|
53
|
+
(if (is-valid-string username u64)
|
|
54
|
+
(ok username)
|
|
55
|
+
(err ERR_INVALID_USERNAME))
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
;; Validates the length of a user's bio.
|
|
59
|
+
(define-private (validate-bio (bio (string-utf8 256)))
|
|
60
|
+
(if (is-valid-string bio u256)
|
|
61
|
+
(ok bio)
|
|
62
|
+
(err ERR_INVALID_BIO))
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
;; Validates the format and length of an email address.
|
|
66
|
+
(define-private (validate-email (email (string-utf8 64)))
|
|
67
|
+
(if (is-valid-string email u64)
|
|
68
|
+
(ok email)
|
|
69
|
+
(err ERR_INVALID_EMAIL))
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
;; Sets the user data in the Users map.
|
|
73
|
+
(define-private (set-user-data (user principal) (data {
|
|
74
|
+
username: (string-utf8 64),
|
|
75
|
+
bio: (string-utf8 256),
|
|
76
|
+
email: (string-utf8 64),
|
|
77
|
+
registration-date: uint,
|
|
78
|
+
total-ratings: uint,
|
|
79
|
+
rating-sum: uint,
|
|
80
|
+
reputation-score: uint
|
|
81
|
+
}))
|
|
82
|
+
(if (map-set Users user data)
|
|
83
|
+
(ok true)
|
|
84
|
+
(err ERR_DATA_STORE_FAILURE))
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
;; --- Read-Only Functions ---
|
|
88
|
+
;; Functions for retrieving data from the contract without making state changes.
|
|
89
|
+
|
|
90
|
+
;; Retrieves a user's profile.
|
|
91
|
+
;; @param user: The principal of the user to retrieve.
|
|
92
|
+
;; @returns (ok {<user-data>}): The user's profile data or an error if not found.
|
|
93
|
+
(define-read-only (get-user-profile (user principal))
|
|
94
|
+
(match (map-get? Users user)
|
|
95
|
+
user-data (ok user-data)
|
|
96
|
+
(err ERR_USER_NOT_FOUND)))
|
|
97
|
+
|
|
98
|
+
;; Retrieves a user's average rating.
|
|
99
|
+
;; @param user: The principal of the user.
|
|
100
|
+
;; @returns (ok uint): The user's average rating or 0 if no ratings.
|
|
101
|
+
(define-read-only (get-user-rating (user principal))
|
|
102
|
+
(match (map-get? Users user)
|
|
103
|
+
user-data (let (
|
|
104
|
+
(total-ratings (get total-ratings user-data))
|
|
105
|
+
(rating-sum (get rating-sum user-data))
|
|
106
|
+
)
|
|
107
|
+
(if (> total-ratings u0)
|
|
108
|
+
(ok (/ rating-sum total-ratings))
|
|
109
|
+
(ok u0)))
|
|
110
|
+
(err ERR_USER_NOT_FOUND)
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
;; Checks if a user is authorized.
|
|
115
|
+
;; @param user: The principal to check.
|
|
116
|
+
;; @returns (ok bool): True if the user is authorized.
|
|
117
|
+
(define-read-only (is-user-authorized (user principal))
|
|
118
|
+
(ok (default-to false (map-get? UserAuthorization user))))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
;; --- Public Functions ---
|
|
122
|
+
;; Functions that can be called by any user.
|
|
123
|
+
|
|
124
|
+
;; Registers a new user.
|
|
125
|
+
;; @param username: The desired username.
|
|
126
|
+
;; @param bio: A short bio.
|
|
127
|
+
;; @param email: The user's email address.
|
|
128
|
+
;; @returns (ok bool): True if registration is successful.
|
|
129
|
+
(define-public (register-user (username (string-utf8 64)) (bio (string-utf8 256)) (email (string-utf8 64)))
|
|
130
|
+
(let (
|
|
131
|
+
(existing-user (map-get? Users tx-sender))
|
|
132
|
+
)
|
|
133
|
+
(asserts! (is-none existing-user) (err ERR_ALREADY_REGISTERED))
|
|
134
|
+
(asserts! (and
|
|
135
|
+
(> (len username) u0)
|
|
136
|
+
(>= (len username) u1)
|
|
137
|
+
(<= (len username) u64)
|
|
138
|
+
(> (len bio) u0)
|
|
139
|
+
(>= (len bio) u1)
|
|
140
|
+
(<= (len bio) u256)
|
|
141
|
+
(> (len email) u0)
|
|
142
|
+
(>= (len email) u1)
|
|
143
|
+
(<= (len email) u64))
|
|
144
|
+
(err ERR_INVALID_INPUT))
|
|
145
|
+
(let (
|
|
146
|
+
(validated-username (try! (validate-username username)))
|
|
147
|
+
(validated-bio (try! (validate-bio bio)))
|
|
148
|
+
(validated-email (try! (validate-email email)))
|
|
149
|
+
)
|
|
150
|
+
(match (set-user-data tx-sender
|
|
151
|
+
{
|
|
152
|
+
username: validated-username,
|
|
153
|
+
bio: validated-bio,
|
|
154
|
+
email: validated-email,
|
|
155
|
+
registration-date: stacks-block-height,
|
|
156
|
+
total-ratings: u0,
|
|
157
|
+
rating-sum: u0,
|
|
158
|
+
reputation-score: u0
|
|
159
|
+
})
|
|
160
|
+
success (ok true)
|
|
161
|
+
error (err ERR_DATA_STORE_FAILURE))
|
|
162
|
+
))
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
;; Updates a user's profile.
|
|
166
|
+
;; @param bio: The new bio.
|
|
167
|
+
;; @param email: The new email address.
|
|
168
|
+
;; @returns (ok bool): True if the update is successful.
|
|
169
|
+
(define-public (update-profile (bio (string-utf8 256)) (email (string-utf8 64)))
|
|
170
|
+
(begin
|
|
171
|
+
(asserts! (and
|
|
172
|
+
(> (len bio) u0)
|
|
173
|
+
(>= (len bio) u1)
|
|
174
|
+
(<= (len bio) u256)
|
|
175
|
+
(> (len email) u0)
|
|
176
|
+
(>= (len email) u1)
|
|
177
|
+
(<= (len email) u64))
|
|
178
|
+
(err ERR_INVALID_INPUT))
|
|
179
|
+
(let (
|
|
180
|
+
(validated-bio (try! (validate-bio bio)))
|
|
181
|
+
(validated-email (try! (validate-email email)))
|
|
182
|
+
)
|
|
183
|
+
(match (map-get? Users tx-sender)
|
|
184
|
+
user-data
|
|
185
|
+
(match (set-user-data tx-sender
|
|
186
|
+
(merge user-data
|
|
187
|
+
{
|
|
188
|
+
bio: validated-bio,
|
|
189
|
+
email: validated-email
|
|
190
|
+
}
|
|
191
|
+
))
|
|
192
|
+
success (ok true)
|
|
193
|
+
error (err ERR_DATA_STORE_FAILURE))
|
|
194
|
+
(err ERR_USER_NOT_FOUND)
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
;; Rates a user.
|
|
201
|
+
;; @param user: The principal of the user to rate.
|
|
202
|
+
;; @param rating: The rating value (1-5).
|
|
203
|
+
;; @returns (ok bool): True if the rating is successful.
|
|
204
|
+
(define-public (rate-user (user principal) (rating uint))
|
|
205
|
+
(begin
|
|
206
|
+
(asserts! (is-valid-principal user) (err ERR_INVALID_PRINCIPAL))
|
|
207
|
+
(asserts! (not (is-eq tx-sender user)) (err ERR_SELF_RATING))
|
|
208
|
+
(asserts! (and (>= rating u1) (<= rating u5)) (err ERR_INVALID_RATING))
|
|
209
|
+
(match (map-get? Users user)
|
|
210
|
+
user-data
|
|
211
|
+
(match (set-user-data user
|
|
212
|
+
(merge user-data
|
|
213
|
+
{
|
|
214
|
+
total-ratings: (+ (get total-ratings user-data) u1),
|
|
215
|
+
rating-sum: (+ (get rating-sum user-data) rating)
|
|
216
|
+
}
|
|
217
|
+
))
|
|
218
|
+
success (ok true)
|
|
219
|
+
error (err ERR_DATA_STORE_FAILURE))
|
|
220
|
+
(err ERR_USER_NOT_FOUND)
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
;; Calculates a user's reputation score.
|
|
226
|
+
;; @param user: The principal of the user.
|
|
227
|
+
;; @returns (ok bool): True if the calculation is successful.
|
|
228
|
+
(define-public (calculate-reputation (user principal))
|
|
229
|
+
(begin
|
|
230
|
+
(asserts! (is-valid-principal user) (err ERR_INVALID_PRINCIPAL))
|
|
231
|
+
(match (map-get? Users user)
|
|
232
|
+
user-data
|
|
233
|
+
(let (
|
|
234
|
+
(total-ratings (get total-ratings user-data))
|
|
235
|
+
(rating-sum (get rating-sum user-data))
|
|
236
|
+
(avg-rating (if (> total-ratings u0) (/ rating-sum total-ratings) u0))
|
|
237
|
+
(new-reputation (+ (* avg-rating u20) (* total-ratings u2)))
|
|
238
|
+
)
|
|
239
|
+
(match (set-user-data user
|
|
240
|
+
(merge user-data
|
|
241
|
+
{
|
|
242
|
+
reputation-score: new-reputation
|
|
243
|
+
}
|
|
244
|
+
))
|
|
245
|
+
success (ok true)
|
|
246
|
+
error (err ERR_DATA_STORE_FAILURE)))
|
|
247
|
+
(err ERR_USER_NOT_FOUND)
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
;; Authorizes a user for specific actions.
|
|
253
|
+
;; @param user: The principal to authorize.
|
|
254
|
+
;; @returns (ok bool): True if authorization is successful.
|
|
255
|
+
(define-public (authorize-user (user principal))
|
|
256
|
+
(begin
|
|
257
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
|
|
258
|
+
(asserts! (is-valid-principal user) (err ERR_INVALID_PRINCIPAL))
|
|
259
|
+
(ok (map-set UserAuthorization user true))
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
;; Revokes a user's authorization.
|
|
264
|
+
;; @param user: The principal to revoke authorization from.
|
|
265
|
+
;; @returns (ok bool): True if revocation is successful.
|
|
266
|
+
(define-public (revoke-authorization (user principal))
|
|
267
|
+
(begin
|
|
268
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) (err ERR_NOT_AUTHORIZED))
|
|
269
|
+
(asserts! (is-valid-principal user) (err ERR_INVALID_PRINCIPAL))
|
|
270
|
+
(ok (map-set UserAuthorization user false))
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
;; --- Contract Initialization ---
|
|
275
|
+
;; Initializes the contract upon deployment.
|
|
276
|
+
(begin
|
|
277
|
+
(map-set UserAuthorization CONTRACT_OWNER true)
|
|
278
|
+
(print "UserProfile contract initialized")
|
|
279
|
+
(ok true)
|
|
280
|
+
)
|
package/ft-trait.clar
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
(define-trait ft-trait
|
|
2
|
+
(
|
|
3
|
+
;; Transfer from the caller to a new principal
|
|
4
|
+
(transfer (uint principal principal) (response bool uint))
|
|
5
|
+
|
|
6
|
+
;; the human readable name of the token
|
|
7
|
+
(get-name () (response (string-ascii 32) uint))
|
|
8
|
+
|
|
9
|
+
;; the ticker symbol, or empty if none
|
|
10
|
+
(get-symbol () (response (string-ascii 32) uint))
|
|
11
|
+
|
|
12
|
+
;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token
|
|
13
|
+
(get-decimals () (response uint uint))
|
|
14
|
+
|
|
15
|
+
;; the balance of the passed principal
|
|
16
|
+
(get-balance (principal) (response uint uint))
|
|
17
|
+
|
|
18
|
+
;; the current total supply (which does not need to be a constant)
|
|
19
|
+
(get-total-supply () (response uint uint))
|
|
20
|
+
|
|
21
|
+
;; an optional URI that represents metadata of this token
|
|
22
|
+
(get-token-uri () (response (optional (string-utf8 256)) uint))
|
|
23
|
+
)
|
|
24
|
+
)
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strade-stx-contracts",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Clarity smart contracts for Strade marketplace
|
|
5
|
-
"
|
|
6
|
-
"
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Clarity smart contracts for Strade marketplace",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Marvy247/Strade.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/Marvy247/Strade/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/Marvy247/Strade"
|
|
7
14
|
}
|
package/token.clar
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
;; Strade Token (BST) Contract
|
|
2
|
+
;; This contract defines the Strade Token (BST), a fungible token compliant with the SIP-010 standard.
|
|
3
|
+
;; It includes functions for transferring, minting, and burning tokens, as well as managing token metadata.
|
|
4
|
+
|
|
5
|
+
;; --- Token Properties ---
|
|
6
|
+
(define-fungible-token bst u1000000000000)
|
|
7
|
+
|
|
8
|
+
;; --- Constants ---
|
|
9
|
+
;; Defines immutable values used throughout the contract for error handling and configuration.
|
|
10
|
+
|
|
11
|
+
(define-constant CONTRACT_OWNER tx-sender) ;; Sets the contract deployer as the owner.
|
|
12
|
+
(define-constant ERR_OWNER_ONLY (err u100)) ;; Error for actions restricted to the contract owner.
|
|
13
|
+
(define-constant ERR_NOT_AUTHORIZED (err u101)) ;; Error for unauthorized actions.
|
|
14
|
+
(define-constant ERR_INVALID_AMOUNT (err u102)) ;; Error for invalid token amounts.
|
|
15
|
+
(define-constant ERR_INSUFFICIENT_BALANCE (err u103)) ;; Error when a user has an insufficient token balance.
|
|
16
|
+
(define-constant ERR_INVALID_RECIPIENT (err u104)) ;; Error for invalid recipient addresses.
|
|
17
|
+
(define-constant ERR_INVALID_URI (err u105)) ;; Error for invalid token URIs.
|
|
18
|
+
(define-constant ERR_MAX_SUPPLY_REACHED (err u106)) ;; Error when the maximum token supply is reached.
|
|
19
|
+
(define-constant ERR_CONTRACT_PAUSED (err u107)) ;; Error for actions attempted while the contract is paused.
|
|
20
|
+
(define-constant MAX_SUPPLY u10000000000000) ;; The maximum total supply of the token.
|
|
21
|
+
|
|
22
|
+
;; --- Variables ---
|
|
23
|
+
;; Defines mutable variables for tracking the token's state and metadata.
|
|
24
|
+
|
|
25
|
+
(define-data-var token-name (string-utf8 32) u"Strade Token") ;; The name of the token.
|
|
26
|
+
(define-data-var token-symbol (string-utf8 10) u"BST") ;; The symbol of the token.
|
|
27
|
+
(define-data-var token-decimals uint u6) ;; The number of decimal places for the token.
|
|
28
|
+
(define-data-var token-uri (optional (string-utf8 256)) none) ;; The URI for the token's metadata.
|
|
29
|
+
(define-data-var contract-paused bool false) ;; A flag to pause or unpause the contract.
|
|
30
|
+
|
|
31
|
+
;; --- Helper Functions ---
|
|
32
|
+
|
|
33
|
+
;; Checks if a principal is a valid recipient for token transfers.
|
|
34
|
+
(define-private (is-valid-recipient (recipient principal))
|
|
35
|
+
(not (is-eq recipient (as-contract tx-sender))))
|
|
36
|
+
|
|
37
|
+
;; Checks if the contract is currently paused.
|
|
38
|
+
(define-private (is-contract-not-paused)
|
|
39
|
+
(not (var-get contract-paused)))
|
|
40
|
+
|
|
41
|
+
;; --- SIP-010 Functions ---
|
|
42
|
+
;; Standard functions for a fungible token.
|
|
43
|
+
|
|
44
|
+
;; Transfers tokens from the sender to the recipient.
|
|
45
|
+
(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
|
|
46
|
+
(begin
|
|
47
|
+
(asserts! (is-contract-not-paused) ERR_CONTRACT_PAUSED)
|
|
48
|
+
(asserts! (is-eq tx-sender sender) ERR_NOT_AUTHORIZED)
|
|
49
|
+
(asserts! (> amount u0) ERR_INVALID_AMOUNT)
|
|
50
|
+
(asserts! (<= amount (ft-get-balance bst sender)) ERR_INSUFFICIENT_BALANCE)
|
|
51
|
+
(asserts! (is-valid-recipient recipient) ERR_INVALID_RECIPIENT)
|
|
52
|
+
(try! (ft-transfer? bst amount sender recipient))
|
|
53
|
+
(print (merge
|
|
54
|
+
{event: "token_transferred", amount: amount, sender: sender, recipient: recipient}
|
|
55
|
+
(match memo
|
|
56
|
+
some-memo {memo: (some some-memo)}
|
|
57
|
+
{memo: none}
|
|
58
|
+
)
|
|
59
|
+
))
|
|
60
|
+
(ok true)
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
;; Gets the name of the token.
|
|
65
|
+
(define-read-only (get-name)
|
|
66
|
+
(ok (var-get token-name))
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
;; Gets the symbol of the token.
|
|
70
|
+
(define-read-only (get-symbol)
|
|
71
|
+
(ok (var-get token-symbol))
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
;; Gets the number of decimals for the token.
|
|
75
|
+
(define-read-only (get-decimals)
|
|
76
|
+
(ok (var-get token-decimals))
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
;; Gets the balance of a given principal.
|
|
80
|
+
(define-read-only (get-balance (who principal))
|
|
81
|
+
(ok (ft-get-balance bst who))
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
;; Gets the total supply of the token.
|
|
85
|
+
(define-read-only (get-total-supply)
|
|
86
|
+
(ok (ft-get-supply bst))
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
;; Gets the token's metadata URI.
|
|
90
|
+
(define-read-only (get-token-uri)
|
|
91
|
+
(ok (var-get token-uri))
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
;; --- Public Management Functions ---
|
|
95
|
+
|
|
96
|
+
;; Mints new tokens and assigns them to a recipient.
|
|
97
|
+
;; @param amount: The amount of tokens to mint.
|
|
98
|
+
;; @param recipient: The principal to receive the new tokens.
|
|
99
|
+
(define-public (mint (amount uint) (recipient principal))
|
|
100
|
+
(begin
|
|
101
|
+
(asserts! (is-contract-not-paused) ERR_CONTRACT_PAUSED)
|
|
102
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY)
|
|
103
|
+
(asserts! (> amount u0) ERR_INVALID_AMOUNT)
|
|
104
|
+
(asserts! (is-valid-recipient recipient) ERR_INVALID_RECIPIENT)
|
|
105
|
+
(asserts! (<= (+ amount (ft-get-supply bst)) MAX_SUPPLY) ERR_MAX_SUPPLY_REACHED)
|
|
106
|
+
(match (ft-mint? bst amount recipient)
|
|
107
|
+
success (begin
|
|
108
|
+
(print {event: "token_minted", amount: amount, recipient: recipient})
|
|
109
|
+
(ok success))
|
|
110
|
+
error (err error))
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
;; Burns a specified amount of tokens from the sender's balance.
|
|
115
|
+
;; @param amount: The amount of tokens to burn.
|
|
116
|
+
;; @param sender: The principal whose tokens will be burned.
|
|
117
|
+
(define-public (burn (amount uint) (sender principal))
|
|
118
|
+
(begin
|
|
119
|
+
(asserts! (is-contract-not-paused) ERR_CONTRACT_PAUSED)
|
|
120
|
+
(asserts! (is-eq tx-sender sender) ERR_NOT_AUTHORIZED)
|
|
121
|
+
(asserts! (> amount u0) ERR_INVALID_AMOUNT)
|
|
122
|
+
(asserts! (<= amount (ft-get-balance bst sender)) ERR_INSUFFICIENT_BALANCE)
|
|
123
|
+
(match (ft-burn? bst amount sender)
|
|
124
|
+
success (begin
|
|
125
|
+
(print {event: "token_burned", amount: amount, sender: sender})
|
|
126
|
+
(ok success))
|
|
127
|
+
error (err error))
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
;; Sets the token's metadata URI.
|
|
132
|
+
;; @param new-uri: The new URI for the token metadata.
|
|
133
|
+
(define-public (set-token-uri (new-uri (optional (string-utf8 256))))
|
|
134
|
+
(begin
|
|
135
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY)
|
|
136
|
+
(match new-uri
|
|
137
|
+
some-uri
|
|
138
|
+
(begin
|
|
139
|
+
(asserts! (<= (len some-uri) u256) ERR_INVALID_URI)
|
|
140
|
+
(var-set token-uri (some some-uri))
|
|
141
|
+
(print {event: "token_uri_updated", new_uri: some-uri})
|
|
142
|
+
(ok true)
|
|
143
|
+
)
|
|
144
|
+
(begin
|
|
145
|
+
(var-set token-uri none)
|
|
146
|
+
(print {event: "token_uri_removed"})
|
|
147
|
+
(ok true)
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
;; Pauses the contract, disabling most functions.
|
|
154
|
+
(define-public (pause-contract)
|
|
155
|
+
(begin
|
|
156
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY)
|
|
157
|
+
(var-set contract-paused true)
|
|
158
|
+
(print {event: "contract_paused"})
|
|
159
|
+
(ok true)
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
;; Unpauses the contract, re-enabling all functions.
|
|
164
|
+
(define-public (unpause-contract)
|
|
165
|
+
(begin
|
|
166
|
+
(asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_OWNER_ONLY)
|
|
167
|
+
(var-set contract-paused false)
|
|
168
|
+
(print {event: "contract_unpaused"})
|
|
169
|
+
(ok true)
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
;; --- Contract Initialization ---
|
|
174
|
+
;; Initializes the contract upon deployment, minting the initial supply to the contract owner.
|
|
175
|
+
(begin
|
|
176
|
+
(try! (ft-mint? bst u1000000000000 CONTRACT_OWNER))
|
|
177
|
+
(print {event: "contract_deployed", initial_supply: u1000000000000})
|
|
178
|
+
)
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = { name: "strade-stx-contracts", version: "1.0.0" };
|