Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
36018a2
docs: Update repository URLs in CONTRIBUTING.md and Clarinet.toml
nicholas-source Sep 28, 2024
e721e3e
Add basic structure and constants
nicholas-source Sep 28, 2024
75a4255
Add data variables and maps
nicholas-source Sep 28, 2024
8893839
Implement create-proposal function
nicholas-source Sep 28, 2024
f425c1c
Implement vote function
nicholas-source Sep 28, 2024
a09ef13
Implement read-only functions
nicholas-source Sep 28, 2024
1c1c10c
Implement validation for proposal title, description, and start/end b…
nicholas-source Sep 28, 2024
62a4f1c
Implement error constant for proposal not found
nicholas-source Sep 28, 2024
8cde11d
Add detailed comments to enhance code clarity and maintainability in …
nicholas-source Sep 28, 2024
0bd469e
Add configuration file and API endpoint
nicholas-source Sep 28, 2024
1e619b6
Add .env file with configuration variables
nicholas-source Sep 28, 2024
b513934
Add basic structure and import statements
nicholas-source Sep 29, 2024
9579882
Implement createProposal route and service
nicholas-source Sep 29, 2024
2116023
Add .env file to .gitignore
nicholas-source Sep 29, 2024
4ca7aaa
Implement getProposal route and service
nicholas-source Sep 29, 2024
232a37d
Refactor code for readability
nicholas-source Sep 29, 2024
d9bfe72
Fix bugs in route handlers and service functions
nicholas-source Sep 29, 2024
3b19bc9
Add security checks
nicholas-source Sep 29, 2024
08f69ea
Revert changes
nicholas-source Sep 29, 2024
0acb4e7
feat(votes): add vote routes and service methods for casting, fetchin…
nicholas-source Sep 29, 2024
729bc44
feat(logging): add request logger middleware to log incoming requests
nicholas-source Sep 29, 2024
8e5e57f
feat(error-handling): add global error handler middleware
nicholas-source Sep 29, 2024
3f83dbd
feat(proposals): integrate proposal routes placeholder
nicholas-source Sep 29, 2024
5074eb9
refactor(network): extract network configuration logic into utility f…
nicholas-source Sep 29, 2024
32ef2c8
security(validation): add input validation and sanitization to vote r…
nicholas-source Sep 29, 2024
307f803
security(error-handling): prevent stack trace exposure in production …
nicholas-source Sep 29, 2024
c51b6b4
revert(proposals): remove placeholder proposal route until fully impl…
nicholas-source Sep 29, 2024
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
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PORT=3000
NETWORK=testnet
CONTRACT_ADDRESS=your_contract_address
CONTRACT_NAME=echovote
PRIVATE_KEY=your_private_key
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ coverage
*.info
costs-reports.json
node_modules
.vscode
.vscode
.env
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ We welcome contributions of all kinds to EchoVote! Whether it's improving docume

### 1. Fork the repository

- Navigate to the [EchoVote repository](https://github.com/nicholas-source/echovote).
- Navigate to the [EchoVote repository](https://github.com/nicholas-source/echo_vote).
- Click the "Fork" button in the top-right corner to create your copy of the repository.

### 2. Clone your fork

- Clone your forked repository to your local machine:
```bash
git clone https://github.com/nicholas-source/echovote.git
git clone https://github.com/nicholas-source/echo_vote.git
cd echovote
```

Expand Down
30 changes: 14 additions & 16 deletions Clarinet.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
[project]
name = "echo_vote"
description = ""
name = 'echo_vote'
description = ''
authors = []
telemetry = true
cache_dir = "./.cache"

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

cache_dir = './.cache'
requirements = []
[contracts.echovote]
path = 'contracts/echovote.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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

### `LICENSE`

```md
Expand All @@ -23,3 +22,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
179 changes: 179 additions & 0 deletions contracts/echovote.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
;; EchoVote: A decentralized voting system
;; This contract allows for creating, voting on, and managing proposals

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-unauthorized (err u100))
(define-constant err-already-voted (err u101))
(define-constant err-proposal-not-active (err u102))
(define-constant err-invalid-vote (err u103))
(define-constant err-invalid-title (err u104))
(define-constant err-invalid-description (err u105))
(define-constant err-invalid-start-block (err u106))
(define-constant err-invalid-end-block (err u107))
(define-constant err-proposal-ended (err u108))
(define-constant err-proposal-not-found (err u404))

;; Data variables
(define-data-var proposal-count uint u0)

;; Data maps
(define-map proposals
uint
{
title: (string-utf8 256),
description: (string-utf8 1024),
creator: principal,
start-block: uint,
end-block: uint,
is-active: bool,
total-votes: uint
}
)

(define-map votes
{ proposal-id: uint, voter: principal }
uint
)

(define-map vote-counts
{ proposal-id: uint, option: uint }
uint
)

;; Private functions

;; Validate text length
(define-private (validate-text (text (string-utf8 256)))
(let ((length (len text)))
(and (> length u0) (<= length u256))
)
)

;; Validate long text length
(define-private (validate-long-text (text (string-utf8 1024)))
(let ((length (len text)))
(and (> length u0) (<= length u1024))
)
)

;; Validate block range
(define-private (validate-blocks (start-block uint) (end-block uint))
(let ((current-block block-height))
(and
(> start-block current-block)
(> end-block start-block)
)
)
)

;; Increment vote count for a specific option
(define-private (increment-vote-count (proposal-id uint) (vote-option uint))
(let
(
(current-count (default-to u0 (map-get? vote-counts { proposal-id: proposal-id, option: vote-option })))
(new-count (+ current-count u1))
)
(map-set vote-counts { proposal-id: proposal-id, option: vote-option } new-count)
(increment-total-votes proposal-id)
)
)

;; Increment total votes for a proposal
(define-private (increment-total-votes (proposal-id uint))
(match (map-get? proposals proposal-id)
proposal (begin
(map-set proposals proposal-id
(merge proposal { total-votes: (+ (get total-votes proposal) u1) })
)
true
)
false
)
)

;; Public functions

;; Create a new proposal
(define-public (create-proposal (title (string-utf8 256)) (description (string-utf8 1024)) (start-block uint) (end-block uint))
(let
(
(new-proposal-id (+ (var-get proposal-count) u1))
)
(asserts! (is-eq tx-sender contract-owner) err-unauthorized)
(asserts! (validate-text title) err-invalid-title)
(asserts! (validate-long-text description) err-invalid-description)
(asserts! (validate-blocks start-block end-block) err-invalid-start-block)
(map-set proposals new-proposal-id
{
title: title,
description: description,
creator: tx-sender,
start-block: start-block,
end-block: end-block,
is-active: true,
total-votes: u0
}
)
(var-set proposal-count new-proposal-id)
(ok new-proposal-id)
)
)

;; Cast a vote on a proposal
(define-public (vote (proposal-id uint) (vote-option uint))
(let
(
(proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-active))
(current-block block-height)
)
(asserts! (>= current-block (get start-block proposal)) err-proposal-not-active)
(asserts! (<= current-block (get end-block proposal)) err-proposal-ended)
(asserts! (get is-active proposal) err-proposal-not-active)
(asserts! (is-none (map-get? votes { proposal-id: proposal-id, voter: tx-sender })) err-already-voted)
(asserts! (and (>= vote-option u1) (<= vote-option u5)) err-invalid-vote)

(map-set votes { proposal-id: proposal-id, voter: tx-sender } vote-option)
(increment-vote-count proposal-id vote-option)
(ok true)
)
)

;; End an active proposal
(define-public (end-proposal (proposal-id uint))
(let
(
(proposal (unwrap! (map-get? proposals proposal-id) err-proposal-not-active))
(current-block block-height)
)
(asserts! (is-eq tx-sender contract-owner) err-unauthorized)
(asserts! (>= current-block (get end-block proposal)) err-proposal-not-active)
(map-set proposals proposal-id (merge proposal { is-active: false }))
(ok true)
)
)

;; Read-only functions

;; Get proposal details
(define-read-only (get-proposal (proposal-id uint))
(map-get? proposals proposal-id)
)

;; Get a user's vote for a proposal
(define-read-only (get-vote (proposal-id uint) (voter principal))
(map-get? votes { proposal-id: proposal-id, voter: voter })
)

;; Get vote count for a specific option
(define-read-only (get-vote-count (proposal-id uint) (vote-option uint))
(default-to u0 (map-get? vote-counts { proposal-id: proposal-id, option: vote-option }))
)

;; Get total votes for a proposal
(define-read-only (get-total-votes (proposal-id uint))
(match (map-get? proposals proposal-id)
proposal (ok (get total-votes proposal))
(err err-proposal-not-found)
)
)
Loading