Skip to content

Commit

Permalink
fix: add ccip016 (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
friedger committed Dec 4, 2024
1 parent 628a387 commit eec24c1
Showing 1 changed file with 326 additions and 0 deletions.
326 changes: 326 additions & 0 deletions contracts/proposals/ccip016-missed-payouts.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
;; TRAITS

(impl-trait .proposal-trait.proposal-trait)
(impl-trait .ccip015-trait.ccip015-trait)

;; ERRORS

(define-constant ERR_PANIC (err u22000))
(define-constant ERR_SAVING_VOTE (err u22001))
(define-constant ERR_VOTED_ALREADY (err u22002))
(define-constant ERR_NOTHING_STACKED (err u22003))
(define-constant ERR_USER_NOT_FOUND (err u22004))
(define-constant ERR_PROPOSAL_NOT_ACTIVE (err u22005))
(define-constant ERR_PROPOSAL_STILL_ACTIVE (err u22006))
(define-constant ERR_VOTE_FAILED (err u22007))

;; CONSTANTS

(define-constant SELF (as-contract tx-sender))
(define-constant CCIP_016 {
name: "CityCoins Treasury Redemption (NYC)",
link: "https://github.com/citycoins/governance/blob/feat/add-ccip-022/ccips/ccip-022/ccip-022-citycoins-treasury-redemption-nyc.md",
hash: "",
})

(define-constant VOTE_SCALE_FACTOR (pow u10 u16)) ;; 16 decimal places

;; set city ID
(define-constant NYC_ID (default-to u2 (contract-call? .ccd004-city-registry get-city-id "nyc")))

;; DATA VARS

;; vote block heights
(define-data-var voteActive bool true)
(define-data-var voteStart uint u0)
(define-data-var voteEnd uint u0)

;; start the vote when deployed
(var-set voteStart block-height)

;; DATA MAPS

(define-map CityVotes
uint ;; city ID
{ ;; vote
totalAmountYes: uint,
totalAmountNo: uint,
totalVotesYes: uint,
totalVotesNo: uint,
}
)

(define-map UserVotes
uint ;; user ID
{ ;; vote
vote: bool,
nyc: uint,
}
)
;; PUBLIC FUNCTIONS

(define-public (execute (sender principal))
(let ((miaMissedStxAmount (stx-get-balance .ccd002-treasury-nyc-mining-v2)))
;; check vote is complete/passed
(try! (is-executable))
;; update vote variables
(var-set voteEnd block-height)
(var-set voteActive false)
;; transfer funds to this contract
(try! (contract-call? .ccd002-treasury-mia-mining-v2 withdraw-stx 1092453541 (as-contract tx-sender)))
;; initialize redemption extension
(try! (contract-call? .ccd012-redemption-nyc initialize-redemption))
(ok true))
)

(define-public (vote-on-proposal (vote bool))
(let
(
(voterId (unwrap! (contract-call? .ccd003-user-registry get-user-id contract-caller) ERR_USER_NOT_FOUND))
(voterRecord (map-get? UserVotes voterId))
)
;; check if vote is active
(asserts! (var-get voteActive) ERR_PROPOSAL_NOT_ACTIVE)
;; check if vote record exists for user
(match voterRecord record
;; if the voterRecord exists
(let
(
(oldVote (get vote record))
(nycVoteAmount (get nyc record))
)
;; check vote is not the same as before
(asserts! (not (is-eq oldVote vote)) ERR_VOTED_ALREADY)
;; record the new vote for the user
(map-set UserVotes voterId
(merge record { vote: vote })
)
;; update vote stats for each city
(update-city-votes NYC_ID nycVoteAmount vote true)
(ok true)
)
;; if the voterRecord does not exist
(let
(
(nycVoteAmount (scale-down (default-to u0 (get-nyc-vote voterId true))))
)
;; check that the user has a positive vote
(asserts! (or (> nycVoteAmount u0)) ERR_NOTHING_STACKED)
;; insert new user vote record
(asserts! (map-insert UserVotes voterId {
vote: vote,
nyc: nycVoteAmount
}) ERR_SAVING_VOTE)
;; update vote stats for each city
(update-city-votes NYC_ID nycVoteAmount vote false)
(ok true)
)
)
)
)

;; READ ONLY FUNCTIONS

(define-read-only (is-executable)
(let
(
(votingRecord (unwrap! (get-vote-totals) ERR_PANIC))
(nycRecord (get nyc votingRecord))
(voteTotals (get totals votingRecord))
)
;; check that there is at least one vote
(asserts! (or (> (get totalVotesYes voteTotals) u0) (> (get totalVotesNo voteTotals) u0)) ERR_VOTE_FAILED)
;; check that the yes total is more than no total
(asserts! (> (get totalVotesYes voteTotals) (get totalVotesNo voteTotals)) ERR_VOTE_FAILED)
;; check the "yes" votes are at least 25% of the total
(asserts! (>= (get totalAmountYes nycRecord) (/ (get totalAmountYes voteTotals) u4)) ERR_VOTE_FAILED)
;; allow execution
(ok true)
)
)

(define-read-only (is-vote-active)
(some (var-get voteActive))
)

(define-read-only (get-proposal-info)
(some CCIP_022)
)

(define-read-only (get-vote-period)
(if (and
(> (var-get voteStart) u0)
(> (var-get voteEnd) u0))
;; if both are set, return values
(some {
startBlock: (var-get voteStart),
endBlock: (var-get voteEnd),
length: (- (var-get voteEnd) (var-get voteStart))
})
;; else return none
none
)
)

(define-read-only (get-vote-total-nyc)
(map-get? CityVotes NYC_ID)
)

(define-read-only (get-vote-total-nyc-or-default)
(default-to { totalAmountYes: u0, totalAmountNo: u0, totalVotesYes: u0, totalVotesNo: u0 } (get-vote-total-nyc))
)

(define-read-only (get-vote-totals)
(let
(
(nycRecord (get-vote-total-nyc-or-default))
)
(some {
nyc: nycRecord,
totals: {
totalAmountYes: (get totalAmountYes nycRecord),
totalAmountNo: (get totalAmountNo nycRecord),
totalVotesYes: (get totalVotesYes nycRecord),
totalVotesNo: (get totalVotesNo nycRecord),
}
})
)
)

(define-read-only (get-voter-info (id uint))
(map-get? UserVotes id)
)

;; NYC vote calculation
;; returns (some uint) or (none)
;; optionally scaled by VOTE_SCALE_FACTOR (10^6)
(define-read-only (get-nyc-vote (userId uint) (scaled bool))
(let
(
;; MAINNET: NYC cycle 82 / first block BTC 838,250 STX 145,643
;; cycle 2 / u4500 used in tests
(cycle82Hash (unwrap! (get-block-hash u4500) none))
(cycle82Data (at-block cycle82Hash (contract-call? .ccd007-citycoin-stacking get-stacker NYC_ID u2 userId)))
(cycle82Amount (get stacked cycle82Data))
;; MAINNET: NYC cycle 83 / first block BTC 840,350 STX 147,282
;; cycle 3 / u6600 used in tests
(cycle83Hash (unwrap! (get-block-hash u6600) none))
(cycle83Data (at-block cycle83Hash (contract-call? .ccd007-citycoin-stacking get-stacker NYC_ID u3 userId)))
(cycle83Amount (get stacked cycle83Data))
;; NYC vote calculation
(scaledVote (/ (+ (scale-up cycle82Amount) (scale-up cycle83Amount)) u2))
)
;; check that at least one value is positive
(asserts! (or (> cycle82Amount u0) (> cycle83Amount u0)) none)
;; return scaled or unscaled value
(if scaled (some scaledVote) (some (/ scaledVote VOTE_SCALE_FACTOR)))
)
)

;; PRIVATE FUNCTIONS

;; update city vote map
(define-private (update-city-votes (cityId uint) (voteAmount uint) (vote bool) (changedVote bool))
(let
(
(cityRecord (default-to
{ totalAmountYes: u0, totalAmountNo: u0, totalVotesYes: u0, totalVotesNo: u0 }
(map-get? CityVotes cityId)))
)
;; do not record if amount is 0
(if (> voteAmount u0)
;; handle vote
(if vote
;; handle yes vote
(map-set CityVotes cityId {
totalAmountYes: (+ voteAmount (get totalAmountYes cityRecord)),
totalVotesYes: (+ u1 (get totalVotesYes cityRecord)),
totalAmountNo: (if changedVote (- (get totalAmountNo cityRecord) voteAmount) (get totalAmountNo cityRecord)),
totalVotesNo: (if changedVote (- (get totalVotesNo cityRecord) u1) (get totalVotesNo cityRecord))
})
;; handle no vote
(map-set CityVotes cityId {
totalAmountYes: (if changedVote (- (get totalAmountYes cityRecord) voteAmount) (get totalAmountYes cityRecord)),
totalVotesYes: (if changedVote (- (get totalVotesYes cityRecord) u1) (get totalVotesYes cityRecord)),
totalAmountNo: (+ voteAmount (get totalAmountNo cityRecord)),
totalVotesNo: (+ u1 (get totalVotesNo cityRecord)),
})
)
;; ignore calls with vote amount equal to 0
false)
)
)

;; get block hash by height
(define-private (get-block-hash (blockHeight uint))
(get-block-info? id-header-hash blockHeight)
)

;; CREDIT: ALEX math-fixed-point-16.clar

(define-private (scale-up (a uint))
(* a VOTE_SCALE_FACTOR)
)

(define-private (scale-down (a uint))
(/ a VOTE_SCALE_FACTOR)
)

;; pay rewards with memo "missed mia rewards"
(define-private (pay-rewards (user principal) (amount uint))
(try! (as-contract (stx-transfer? user amount (some 0x6d6973736564206d69612072657761726473))))
)

(define-private (pay-all-rewards)
(pay-rewards 'SP32VE3A2AXWPGT7HH4B76005TJZQK7CF1MM9R0MD 30487)
(pay-rewards 'SP1XHV60VPS13DYRN0HEYG8GYYA1S6QF90AXJ0NQR 332545167)
(pay-rewards 'SP30A13XJEHMK81JVEHMS0FEHFENS1W5KEEFYJDVM 66509033)
(pay-rewards 'SP1FV4FZ8D32S7GKYRPFWK6YHRJE5BZEYKABK72Q3 13301806)
(pay-rewards 'SP3B1TPV7Z1767ZQ01RW93HRYR88ZQFX9M7NNXT3V 2261)
(pay-rewards 'SP33HRM920VHATSFNQ455WMKW9KCT74A5GT8280TB 11971626)
(pay-rewards 'SPEW3AKP366Y0CY2322M6BWQY0C00JZAG59EP93C 332545)
(pay-rewards 'SP3EXTHZ7PHAJ8DDJB7AMVQXDZ6T68364EZ01WB20 3120065)
(pay-rewards 'SP28R593JKNFH8PTWNECR84A83EESKC3CC5P826R5 3827795)
(pay-rewards 'SP3PX186AERH9CD2A2R73KJYGX79EXHJP23RFGCZ4 14209396)
(pay-rewards 'SP2JDKWQ77WN7S0PRCS872HFJ21ZT78P6G1WCW2B 304010)
(pay-rewards 'SP16BC59Y29FYZPP7WF8QB376STCVW33W4J9BWP06 50387930)
(pay-rewards 'SP28R593JKNFH8PTWNECR84A83EESKC3CC5P826R5 12507392)
(pay-rewards 'SP2JCF3ME5QC779DQ2X1CM9S62VNJF44GC23MKQXK 100775860)
(pay-rewards 'SP3YJ9487PS0JDDYBBVH0RW3JPY48V0A86PQGDA6V 11464259)
(pay-rewards 'SP3NX54B0VA0G002FBJE44C1ZJTV7F34VTPS7NB4J 17149984)
(pay-rewards 'SP3EXTHZ7PHAJ8DDJB7AMVQXDZ6T68364EZ01WB20 1705078)
(pay-rewards 'SP19PMPW8J540BTF9S2D4J7W3RBB5CZM28P1BK573 312186)
(pay-rewards 'SP2Q8TC8QK1QGQEJFT24S4GBD6TQJ0HDC17RWNDQ8 71986915)
(pay-rewards 'SP1ADC8EX6BRGEZVGGHJR44FYVSSD9VRA2JSHZ70B 11170529)
(pay-rewards 'SP2DP70FRC4FFCZR5B2F6S112NK79WAFWHCWPYKQZ 13137459)
(pay-rewards 'SP2DP70FRC4FFCZR5B2F6S112NK79WAFWHCWPYKQZ 539736)
(pay-rewards 'SP3WBYAEWN0JER1VPBW8TRT1329BGP9RGC5S2519W 273278)
(pay-rewards 'SP3EXTHZ7PHAJ8DDJB7AMVQXDZ6T68364EZ01WB20 1470550)
(pay-rewards 'SP3CHC5CKZGPZ3W4Q4JASMM5ZSMD3P7TQWNSE6BQ8 1047119)
(pay-rewards 'SP2Q8TC8QK1QGQEJFT24S4GBD6TQJ0HDC17RWNDQ8 1961478)
(pay-rewards 'SP1SYW6GETS33ZDY40N502NK8014KM4BTQ4RE4FS1 2144033)
(pay-rewards 'SP3B1TPV7Z1767ZQ01RW93HRYR88ZQFX9M7NNXT3V 3848617)
(pay-rewards 'SP19PMPW8J540BTF9S2D4J7W3RBB5CZM28P1BK573 249741)
(pay-rewards 'SP2GGT4HSMSGS5XPEYHCAJTB7HJBPTMHJJ0MBSGHP 17617546)
(pay-rewards 'SP30V7ZYEGGY0WQ6EJYZ040V3VHF4234FSTHP128D 328364)
(pay-rewards 'SP1ADC8EX6BRGEZVGGHJR44FYVSSD9VRA2JSHZ70B 9293346)
(pay-rewards 'SP2W4B3KR2PYA980C4DFACT6K4MG6Z0DPT6X4CWH7 17747744)
(pay-rewards 'SP2DP70FRC4FFCZR5B2F6S112NK79WAFWHCWPYKQZ 9104521)
(pay-rewards 'SP3EXTHZ7PHAJ8DDJB7AMVQXDZ6T68364EZ01WB20 1234791)
(pay-rewards 'SP22YMFBE5CN1KGCHQDGZ06FF7B3HYFVN92P6Y5X9 643815)
(pay-rewards 'SP3F0GZC9WG53MH7SHMFVSM54XKNNHQXJ8Q301GQ7 57803059)
(pay-rewards 'SPR51QGAQ1QKCZ8YBJFHKVMTS6Z858BV20QY0819 15764470)
(pay-rewards 'SP19PMPW8J540BTF9S2D4J7W3RBB5CZM28P1BK573 240529)
(pay-rewards 'SP245RKH32CE9JPM26XKM4S0EVX3J17ANA595GA2Y 47293)
(pay-rewards 'SP32D4KF64M0FQQK267W8PA08SDXG00DNBB3WCXKT 185159124)
(pay-rewards 'SP3BNAH4NPD79KWTABW4GH6QMQ10V34T8MMM39ZYP 1728757)
(pay-rewards 'SP2YM5DT3RG8BBD10C59V3AGVTN66GKQ1A91T85Q4 14721)
(pay-rewards 'SP1BN2V664W50A1HAWDHT2M83M3NMN0AG3B16R2SA 2920272)
(pay-rewards 'SP3EXTHZ7PHAJ8DDJB7AMVQXDZ6T68364EZ01WB20 736087)
(pay-rewards 'SPVWPTGBEWVQ58J1RDNZQ34TMCRVF49RRFRKXC0Q 2327221)
(pay-rewards 'SP2VKCNC1M54EENQT3TWC4D974XRM4519YHKR24PK 133699)
(pay-rewards 'SP2W4B3KR2PYA980C4DFACT6K4MG6Z0DPT6X4CWH7 852515)
(pay-rewards 'SPR51QGAQ1QKCZ8YBJFHKVMTS6Z858BV20QY0819 15597806)
(pay-rewards 'SPPYQ5TW7PWMNKHVNG194MACDAEGA1759D5DC7YA 424122)
(pay-rewards 'SP2GGT4HSMSGS5XPEYHCAJTB7HJBPTMHJJ0MBSGHP 4447404)
)

0 comments on commit eec24c1

Please sign in to comment.