Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions Clarinet.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
[project]
name = "TrustScore"
description = ""
name = 'TrustScore'
description = ''
authors = []
telemetry = true
cache_dir = "./.cache"

# [contracts.counter]
# path = "contracts/counter.clar"

cache_dir = './.cache'
requirements = []
[contracts.trustscore]
path = 'contracts/trustscore.clar'
clarity_version = 2
epoch = 2.5
[repl.analysis]
passes = ["check_checker"]
check_checker = { trusted_sender = false, trusted_caller = false, callee_filter = false }
passes = ['check_checker']

# Check-checker settings:
# trusted_sender: if true, inputs are trusted after tx_sender has been checked.
# trusted_caller: if true, inputs are trusted after contract-caller has been checked.
# callee_filter: if true, untrusted data may be passed into a private function without a
# warning, if it gets checked inside. This check will also propagate up to the
# caller.
# More informations: https://www.hiro.so/blog/new-safety-checks-in-clarinet
[repl.analysis.check_checker]
strict = false
trusted_sender = false
trusted_caller = false
callee_filter = false
234 changes: 234 additions & 0 deletions contracts/trustscore.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
;; TrustScore Protocol: Decentralized Reputation System
;; A reputation management system where participants can:
;; 1. Register evaluation requests for entities
;; 2. Submit scored assessments during evaluation periods
;; 3. Finalize reputation scores based on weighted assessments
;; 4. Access historical trust metrics

(define-constant protocol-admin tx-sender)

;; Error definitions
(define-constant err-access-denied (err u200))
(define-constant err-evaluation-exists (err u201))
(define-constant err-evaluation-missing (err u202))
(define-constant err-period-expired (err u203))
(define-constant err-period-ongoing (err u204))
(define-constant err-score-below-threshold (err u205))
(define-constant err-unauthorized-evaluator (err u206))
(define-constant err-invalid-entity (err u207))
(define-constant err-score-finalized (err u208))
(define-constant err-invalid-timeframe (err u209))
(define-constant err-invalid-threshold (err u210))
(define-constant err-admin-only (err u211))
(define-constant err-period-closed (err u212))
(define-constant err-empty-entity-name (err u213))
(define-constant err-empty-description (err u214))
(define-constant err-empty-metadata (err u215))

;; Storage maps
(define-map evaluations
{ evaluation-id: uint }
{
entity-owner: principal,
entity-name: (string-ascii 64),
entity-description: (string-ascii 256),
metadata-link: (string-ascii 256),
start-height: uint,
end-height: uint,
min-threshold: uint,
aggregate-score: uint,
top-evaluator: (optional principal),
active-status: bool,
score-locked: bool
}
)

(define-map evaluator-submissions
{ evaluation-id: uint, evaluator: principal }
{ score-value: uint, timestamp-height: uint }
)

;; ID tracking
(define-data-var evaluation-counter uint u1)

;; Protocol fee in basis points (3% = 300)
(define-data-var protocol-fee-basis uint u300)

;; Getters

(define-read-only (fetch-evaluation (evaluation-id uint))
(map-get? evaluations { evaluation-id: evaluation-id })
)

(define-read-only (fetch-evaluator-score (evaluation-id uint) (evaluator principal))
(map-get? evaluator-submissions { evaluation-id: evaluation-id, evaluator: evaluator })
)

(define-read-only (evaluation-registered (evaluation-id uint))
(is-some (fetch-evaluation evaluation-id))
)

(define-read-only (is-evaluation-active (evaluation-id uint))
(match (fetch-evaluation evaluation-id)
eval-data (and
(get active-status eval-data)
(< block-height (get end-height eval-data))
)
false
)
)

(define-read-only (evaluation-complete (evaluation-id uint))
(match (fetch-evaluation evaluation-id)
eval-data (>= block-height (get end-height eval-data))
false
)
)

(define-read-only (current-evaluation-id)
(var-get evaluation-counter)
)

(define-read-only (fetch-protocol-fee)
(var-get protocol-fee-basis)
)

(define-read-only (compute-protocol-fee (amount uint))
(/ (* amount (var-get protocol-fee-basis)) u10000)
)

;; Internal helpers

(define-private (compute-net-settlement (amount uint))
(- amount (compute-protocol-fee amount))
)

(define-private (verify-entity-name (name (string-ascii 64)))
(> (len name) u0)
)

(define-private (verify-description (desc (string-ascii 256)))
(> (len desc) u0)
)

(define-private (verify-metadata (meta (string-ascii 256)))
(> (len meta) u0)
)

;; Core functions

(define-public (register-evaluation
(entity-name (string-ascii 64))
(entity-description (string-ascii 256))
(metadata-link (string-ascii 256))
(timeframe uint)
(min-threshold uint))
(let ((eval-id (var-get evaluation-counter))
(start-height block-height)
(end-height (+ block-height timeframe)))
(begin
(asserts! (verify-entity-name entity-name) err-empty-entity-name)
(asserts! (verify-description entity-description) err-empty-description)
(asserts! (verify-metadata metadata-link) err-empty-metadata)
(asserts! (> timeframe u0) err-invalid-timeframe)
(asserts! (> min-threshold u0) err-invalid-threshold)

(map-set evaluations
{ evaluation-id: eval-id }
{
entity-owner: tx-sender,
entity-name: entity-name,
entity-description: entity-description,
metadata-link: metadata-link,
start-height: start-height,
end-height: end-height,
min-threshold: min-threshold,
aggregate-score: u0,
top-evaluator: none,
active-status: true,
score-locked: false
}
)

(var-set evaluation-counter (+ eval-id u1))

(ok eval-id)
)
)
)

(define-public (submit-score (evaluation-id uint) (score-value uint))
(let ((eval-data (unwrap! (fetch-evaluation evaluation-id) err-evaluation-missing)))
(begin
(asserts! (get active-status eval-data) err-period-closed)
(asserts! (< block-height (get end-height eval-data)) err-period-expired)

(asserts! (if (is-some (get top-evaluator eval-data))
(> score-value (get aggregate-score eval-data))
(>= score-value (get min-threshold eval-data)))
err-score-below-threshold)

(map-set evaluator-submissions
{ evaluation-id: evaluation-id, evaluator: tx-sender }
{ score-value: score-value, timestamp-height: block-height }
)

(map-set evaluations
{ evaluation-id: evaluation-id }
(merge eval-data {
aggregate-score: score-value,
top-evaluator: (some tx-sender)
})
)

(ok true)
)
)
)

(define-public (terminate-evaluation (evaluation-id uint))
(let ((eval-data (unwrap! (fetch-evaluation evaluation-id) err-evaluation-missing)))
(begin
(asserts! (is-eq tx-sender (get entity-owner eval-data)) err-invalid-entity)
(asserts! (get active-status eval-data) err-period-closed)
(asserts! (< block-height (get end-height eval-data)) err-period-expired)

(map-set evaluations
{ evaluation-id: evaluation-id }
(merge eval-data {
active-status: false,
end-height: block-height
})
)

(ok true)
)
)
)

(define-public (cancel-evaluation (evaluation-id uint))
(let ((eval-data (unwrap! (fetch-evaluation evaluation-id) err-evaluation-missing)))
(begin
(asserts! (is-eq tx-sender (get entity-owner eval-data)) err-invalid-entity)
(asserts! (get active-status eval-data) err-period-closed)
(asserts! (is-eq (get aggregate-score eval-data) u0) err-score-below-threshold)

(map-set evaluations
{ evaluation-id: evaluation-id }
(merge eval-data { active-status: false })
)

(ok true)
)
)
)

;; Admin controls

(define-public (adjust-protocol-fee (new-fee-basis uint))
(begin
(asserts! (is-eq tx-sender protocol-admin) err-admin-only)
(asserts! (<= new-fee-basis u1000) err-access-denied)
(ok (var-set protocol-fee-basis new-fee-basis))
)
)
21 changes: 21 additions & 0 deletions tests/trustscore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

import { describe, expect, it } from "vitest";

const accounts = simnet.getAccounts();
const address1 = accounts.get("wallet_1")!;

/*
The test below is an example. To learn more, read the testing documentation here:
https://docs.hiro.so/stacks/clarinet-js-sdk
*/

describe("example tests", () => {
it("ensures simnet is well initalised", () => {
expect(simnet.blockHeight).toBeDefined();
});

// it("shows an example", () => {
// const { result } = simnet.callReadOnlyFn("counter", "get-counter", [], address1);
// expect(result).toBeUint(0);
// });
});