From becae2a46955526ad370f107dab0f798ccaec1db Mon Sep 17 00:00:00 2001 From: friedger Date: Fri, 22 Mar 2024 13:08:21 +0100 Subject: [PATCH 01/17] feat: add contract for pox-4 --- Clarinet.toml | 6 + contracts/extensions/ccd002-treasury-v3.clar | 211 +++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 contracts/extensions/ccd002-treasury-v3.clar diff --git a/Clarinet.toml b/Clarinet.toml index e7b4b0f4..7f9bd005 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -92,6 +92,9 @@ path = "contracts/extensions/ccd002-treasury.clar" [contracts.ccd002-treasury-mia-mining-v2] path = "contracts/extensions/ccd002-treasury-v2.clar" +[contracts.ccd002-treasury-mia-mining-v3] +path = "contracts/extensions/ccd002-treasury-v3.clar" + [contracts.ccd002-treasury-mia-stacking] path = "contracts/extensions/ccd002-treasury.clar" @@ -101,6 +104,9 @@ path = "contracts/extensions/ccd002-treasury.clar" [contracts.ccd002-treasury-nyc-mining-v2] path = "contracts/extensions/ccd002-treasury-v2.clar" +[contracts.ccd002-treasury-nyc-mining-v3] +path = "contracts/extensions/ccd002-treasury-v3.clar" + [contracts.ccd002-treasury-nyc-stacking] path = "contracts/extensions/ccd002-treasury.clar" diff --git a/contracts/extensions/ccd002-treasury-v3.clar b/contracts/extensions/ccd002-treasury-v3.clar new file mode 100644 index 00000000..0748342c --- /dev/null +++ b/contracts/extensions/ccd002-treasury-v3.clar @@ -0,0 +1,211 @@ +;; Title: CCD002 Treasury +;; Version: 3.0.0 +;; Summary: A treasury contract that can manage STX, SIP-009 NFTs, and SIP-010 FTs. +;; Description: An extension contract that holds assets on behalf of the DAO. +;; SIP-009 and SIP-010 assets must be allowed before they are supported. +;; Deposits can be made by anyone either by transferring to the contract or +;; using a deposit function below. +;; Withdrawals are restricted to the DAO through either extensions or proposals. +;; Stacking is enabled through PoX. + +;; TRAITS + +(impl-trait .extension-trait.extension-trait) +(impl-trait .stacking-trait.stacking-trait) +(impl-trait .ccd002-trait.ccd002-treasury-trait) +;; MAINNET: 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait +(use-trait ft-trait 'ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard.sip-010-trait) +;; MAINNET: 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait +(use-trait nft-trait 'ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait.nft-trait) + +;; CONSTANTS + +(define-constant ERR_UNAUTHORIZED (err u2000)) +(define-constant ERR_UNKNOWN_ASSSET (err u2001)) +(define-constant TREASURY (as-contract tx-sender)) + +;; DATA MAPS + +(define-map AllowedAssets principal bool) + +;; PUBLIC FUNCTIONS + +(define-public (is-dao-or-extension) + (ok (asserts! (or (is-eq tx-sender .base-dao) + (contract-call? .base-dao is-extension contract-caller)) ERR_UNAUTHORIZED + )) +) + +(define-public (callback (sender principal) (memo (buff 34))) + (ok true) +) + +(define-public (set-allowed (token principal) (enabled bool)) + (begin + (try! (is-dao-or-extension)) + (print { + event: "allow-asset", + enabled: enabled, + token: token + }) + (ok (map-set AllowedAssets token enabled)) + ) +) + +(define-public (set-allowed-list (allowList (list 100 {token: principal, enabled: bool}))) + (begin + (try! (is-dao-or-extension)) + (ok (map set-allowed-iter allowList)) + ) +) + +(define-public (deposit-stx (amount uint)) + (begin + (print { + event: "deposit-stx", + amount: amount, + caller: contract-caller, + recipient: TREASURY, + sender: tx-sender + }) + (stx-transfer? amount tx-sender TREASURY) + ) +) + +(define-public (deposit-ft (ft ) (amount uint)) + (begin + (asserts! (is-allowed (contract-of ft)) ERR_UNKNOWN_ASSSET) + (print { + event: "deposit-ft", + amount: amount, + assetContract: (contract-of ft), + caller: contract-caller, + recipient: TREASURY, + sender: tx-sender + }) + (contract-call? ft transfer amount tx-sender TREASURY none) + ) +) + +(define-public (deposit-nft (nft ) (id uint)) + (begin + (asserts! (is-allowed (contract-of nft)) ERR_UNKNOWN_ASSSET) + (print { + event: "deposit-nft", + assetContract: (contract-of nft), + caller: contract-caller, + recipient: TREASURY, + sender: tx-sender, + tokenId: id, + }) + (contract-call? nft transfer id tx-sender TREASURY) + ) +) + +(define-public (withdraw-stx (amount uint) (recipient principal)) + (begin + (try! (is-dao-or-extension)) + (print { + event: "withdraw-stx", + amount: amount, + caller: contract-caller, + recipient: recipient, + sender: tx-sender + }) + (as-contract (stx-transfer? amount TREASURY recipient)) + ) +) + +(define-public (withdraw-ft (ft ) (amount uint) (recipient principal)) + (begin + (try! (is-dao-or-extension)) + (asserts! (is-allowed (contract-of ft)) ERR_UNKNOWN_ASSSET) + (print { + event: "withdraw-ft", + assetContract: (contract-of ft), + caller: contract-caller, + recipient: recipient, + sender: tx-sender + }) + (as-contract (contract-call? ft transfer amount TREASURY recipient none)) + ) +) + +(define-public (withdraw-nft (nft ) (id uint) (recipient principal)) + (begin + (try! (is-dao-or-extension)) + (asserts! (is-allowed (contract-of nft)) ERR_UNKNOWN_ASSSET) + (print { + event: "withdraw-nft", + assetContract: (contract-of nft), + caller: contract-caller, + recipient: recipient, + sender: tx-sender, + tokenId: id + }) + (as-contract (contract-call? nft transfer id TREASURY recipient)) + ) +) + +(define-public (delegate-stx (maxAmount uint) (to principal)) + (begin + (try! (is-dao-or-extension)) + (print { + event: "delegate-stx", + amount: maxAmount, + caller: contract-caller, + delegate: to, + sender: tx-sender + }) + ;; MAINNET: 'SP000000000000000000002Q6VF78.pox-4 + ;; TESTNET: 'ST000000000000000000002AMW42H.pox-4 + (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-4 delegate-stx maxAmount to none none)) + success (ok success) + err (err (to-uint err)) + ) + ) +) + +(define-public (revoke-delegate-stx) + (begin + (try! (is-dao-or-extension)) + (print { + event: "revoke-delegate-stx", + caller: contract-caller, + sender: tx-sender + }) + ;; MAINNET: 'SP000000000000000000002Q6VF78.pox-4 + ;; TESTNET: 'ST000000000000000000002AMW42H.pox-4 + (match (as-contract (contract-call? 'ST000000000000000000002AMW42H.pox-4 revoke-delegate-stx)) + success (ok success) + err (err (to-uint err)) + ) + ) +) + +;; READ ONLY FUNCTIONS + +(define-read-only (is-allowed (assetContract principal)) + (default-to false (get-allowed-asset assetContract)) +) + +(define-read-only (get-allowed-asset (assetContract principal)) + (map-get? AllowedAssets assetContract) +) + +(define-read-only (get-balance-stx) + (stx-get-balance TREASURY) +) + +;; PRIVATE FUNCTIONS + +(define-private (set-allowed-iter (item {token: principal, enabled: bool})) + (begin + (print { + event: "allow-asset", + enabled: (get enabled item), + token: (get token item) + }) + (map-set AllowedAssets (get token item) (get enabled item)) + ) +) From a11d10c0545c67013235b8daeb20ee14d54b4394 Mon Sep 17 00:00:00 2001 From: friedger Date: Thu, 4 Apr 2024 21:50:41 +0200 Subject: [PATCH 02/17] feat: add ccip-021 contract --- .../ccip021-extend-sunset-period-2.clar | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 contracts/proposals/ccip021-extend-sunset-period-2.clar diff --git a/contracts/proposals/ccip021-extend-sunset-period-2.clar b/contracts/proposals/ccip021-extend-sunset-period-2.clar new file mode 100644 index 00000000..e599271f --- /dev/null +++ b/contracts/proposals/ccip021-extend-sunset-period-2.clar @@ -0,0 +1,265 @@ +;; TRAITS + +(impl-trait .proposal-trait.proposal-trait) +(impl-trait .ccip015-trait.ccip015-trait) + +;; ERRORS + +(define-constant ERR_PANIC (err u1700)) +(define-constant ERR_VOTED_ALREADY (err u1701)) +(define-constant ERR_NOTHING_STACKED (err u1702)) +(define-constant ERR_USER_NOT_FOUND (err u1703)) +(define-constant ERR_PROPOSAL_NOT_ACTIVE (err u1704)) +(define-constant ERR_PROPOSAL_STILL_ACTIVE (err u1705)) +(define-constant ERR_NO_CITY_ID (err u1706)) +(define-constant ERR_VOTE_FAILED (err u1707)) + +;; CONSTANTS + +(define-constant SELF (as-contract tx-sender)) +(define-constant CCIP_021 { + name: "Extend Direct Execute Sunset Period 2", + link: "https://github.com/citycoins/governance/blob/feat/add-ccip-017/ccips/ccip-017/ccip-017-extend-direct-execute-sunset-period.md", + hash: "7ddbf6152790a730faa059b564a8524abc3c70d3", +}) +(define-constant SUNSET_BLOCK u147828) + +(define-constant VOTE_SCALE_FACTOR (pow u10 u16)) ;; 16 decimal places +(define-constant MIA_SCALE_BASE (pow u10 u4)) ;; 4 decimal places +(define-constant MIA_SCALE_FACTOR u8823) ;; 0.8823 or 88.23% +;; MIA votes scaled to make 1 MIA = 1 NYC +;; full calculation available in CCIP-017 + +;; DATA VARS + +;; vote block heights +(define-data-var voteActive bool true) +(define-data-var voteStart uint u0) +(define-data-var voteEnd uint u0) + +(var-set voteStart block-height) + +;; vote tracking +(define-data-var yesVotes uint u0) +(define-data-var yesTotal uint u0) +(define-data-var noVotes uint u0) +(define-data-var noTotal uint u0) + +;; DATA MAPS + +(define-map UserVotes + uint ;; user ID + { ;; vote + vote: bool, + mia: uint, + nyc: uint, + total: uint, + } +) + +;; PUBLIC FUNCTIONS + +(define-public (execute (sender principal)) + (begin + ;; check vote complete/passed + (try! (is-executable)) + ;; update vote variables + (var-set voteEnd block-height) + (var-set voteActive false) + ;; extend sunset height in ccd001-direct-execute + (try! (contract-call? .ccd001-direct-execute set-sunset-block SUNSET_BLOCK)) + (ok true) + ) +) + +(define-public (vote-on-proposal (vote bool)) + (let + ( + (miaId (unwrap! (contract-call? .ccd004-city-registry get-city-id "mia") ERR_NO_CITY_ID)) + (nycId (unwrap! (contract-call? .ccd004-city-registry get-city-id "nyc") ERR_NO_CITY_ID)) + (voterId (unwrap! (contract-call? .ccd003-user-registry get-user-id contract-caller) ERR_USER_NOT_FOUND)) + (voterRecord (map-get? UserVotes voterId)) + ) + ;; check that proposal is active + (asserts! (var-get voteActive) ERR_PROPOSAL_NOT_ACTIVE) + ;; check if vote record exists + (match voterRecord record + ;; if the voterRecord exists + (begin + ;; check vote is not the same as before + (asserts! (not (is-eq (get vote record) vote)) ERR_VOTED_ALREADY) + ;; record the new vote for the user + (map-set UserVotes voterId + (merge record { vote: vote }) + ) + ;; update the overall vote totals + (if vote + (begin + (var-set yesVotes (+ (var-get yesVotes) u1)) + (var-set yesTotal (+ (var-get yesTotal) (get total record))) + (var-set noVotes (- (var-get noVotes) u1)) + (var-set noTotal (- (var-get noTotal) (get total record))) + ) + (begin + (var-set yesVotes (- (var-get yesVotes) u1)) + (var-set yesTotal (- (var-get yesTotal) (get total record))) + (var-set noVotes (+ (var-get noVotes) u1)) + (var-set noTotal (+ (var-get noTotal) (get total record))) + ) + ) + ) + ;; if the voterRecord does not exist + (let + ( + (scaledVoteMia (default-to u0 (get-mia-vote miaId voterId true))) + (scaledVoteNyc (default-to u0 (get-nyc-vote nycId voterId true))) + (voteMia (scale-down scaledVoteMia)) + (voteNyc (scale-down scaledVoteNyc)) + (voteTotal (+ voteMia voteNyc)) + ) + ;; record the vote for the user + (map-insert UserVotes voterId { + vote: vote, + mia: voteMia, + nyc: voteNyc, + total: voteTotal, + }) + ;; update the overall vote totals + (if vote + (begin + (var-set yesVotes (+ (var-get yesVotes) u1)) + (var-set yesTotal (+ (var-get yesTotal) voteTotal)) + ) + (begin + (var-set noVotes (+ (var-get noVotes) u1)) + (var-set noTotal (+ (var-get noTotal) voteTotal)) + ) + ) + ) + ) + ;; print voter information + (print (map-get? UserVotes voterId)) + ;; print vote totals + (print (get-vote-totals)) + (ok true) + ) +) + +;; READ ONLY FUNCTIONS + +(define-read-only (is-executable) + (begin + ;; check that there is at least one vote + (asserts! (or (> (var-get yesVotes) u0) (> (var-get noVotes) u0)) ERR_VOTE_FAILED) + ;; check that yes total is more than no total + (asserts! (> (var-get yesTotal) (var-get noTotal)) ERR_VOTE_FAILED) + (ok true) + ) +) + +(define-read-only (is-vote-active) + (some (var-get voteActive)) +) + +(define-read-only (get-proposal-info) + (some CCIP_017) +) + +(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-totals) + (some { + yesVotes: (var-get yesVotes), + yesTotal: (var-get yesTotal), + noVotes: (var-get noVotes), + noTotal: (var-get noTotal) + }) +) + +(define-read-only (get-voter-info (id uint)) + (map-get? UserVotes id) +) + +;; MIA vote calculation +;; returns (some uint) or (none) +;; optionally scaled by VOTE_SCALE_FACTOR (10^6) +(define-read-only (get-mia-vote (cityId uint) (userId uint) (scaled bool)) + (let + ( + ;; MAINNET: MIA cycle 64 / first block BTC 800,450 STX 114,689 + ;; cycle 2 / u4500 used in tests + (cycle64Hash (unwrap! (get-block-hash u4500) none)) + (cycle64Data (at-block cycle64Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u2 userId))) + (cycle64Amount (get stacked cycle64Data)) + ;; MAINNET: MIA cycle 65 / first block BTC 802,550 STX 116,486 + ;; cycle 3 / u6600 used in tests + (cycle65Hash (unwrap! (get-block-hash u6600) none)) + (cycle65Data (at-block cycle65Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u3 userId))) + (cycle65Amount (get stacked cycle65Data)) + ;; MIA vote calculation + (avgStacked (/ (+ (scale-up cycle64Amount) (scale-up cycle65Amount)) u2)) + (scaledVote (/ (* avgStacked MIA_SCALE_FACTOR) MIA_SCALE_BASE)) + ) + ;; check that at least one value is positive + (asserts! (or (> cycle64Amount u0) (> cycle65Amount u0)) none) + ;; return scaled or unscaled value + (if scaled (some scaledVote) (some (/ scaledVote VOTE_SCALE_FACTOR))) + ) +) + +;; NYC vote calculation +;; returns (some uint) or (none) +;; optionally scaled by VOTE_SCALE_FACTOR (10^6) +(define-read-only (get-nyc-vote (cityId uint) (userId uint) (scaled bool)) + (let + ( + ;; NYC cycle 64 / first block BTC 800,450 STX 114,689 + ;; cycle 2 / u4500 used in tests + (cycle64Hash (unwrap! (get-block-hash u4500) none)) + (cycle64Data (at-block cycle64Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u2 userId))) + (cycle64Amount (get stacked cycle64Data)) + ;; NYC cycle 65 / first block BTC 802,550 STX 116,486 + ;; cycle 3 / u6600 used in tests + (cycle65Hash (unwrap! (get-block-hash u6600) none)) + (cycle65Data (at-block cycle65Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u3 userId))) + (cycle65Amount (get stacked cycle65Data)) + ;; NYC vote calculation + (scaledVote (/ (+ (scale-up cycle64Amount) (scale-up cycle65Amount)) u2)) + ) + ;; check that at least one value is positive + (asserts! (or (> cycle64Amount u0) (> cycle65Amount u0)) none) + ;; return scaled or unscaled value + (if scaled (some scaledVote) (some (/ scaledVote VOTE_SCALE_FACTOR))) + ) +) + +;; PRIVATE FUNCTIONS + +;; 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) +) + From 84192250e93354c212883af8b8bd7510da989593 Mon Sep 17 00:00:00 2001 From: friedger Date: Thu, 4 Apr 2024 22:14:39 +0200 Subject: [PATCH 03/17] chore: update variables --- contracts/proposals/ccip021-extend-sunset-period-2.clar | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/proposals/ccip021-extend-sunset-period-2.clar b/contracts/proposals/ccip021-extend-sunset-period-2.clar index e599271f..8cada962 100644 --- a/contracts/proposals/ccip021-extend-sunset-period-2.clar +++ b/contracts/proposals/ccip021-extend-sunset-period-2.clar @@ -19,16 +19,16 @@ (define-constant SELF (as-contract tx-sender)) (define-constant CCIP_021 { name: "Extend Direct Execute Sunset Period 2", - link: "https://github.com/citycoins/governance/blob/feat/add-ccip-017/ccips/ccip-017/ccip-017-extend-direct-execute-sunset-period.md", - hash: "7ddbf6152790a730faa059b564a8524abc3c70d3", + link: "https://github.com/citycoins/governance/blob/feat/ccip-21/ccips/ccip-021/ccip-021-extend-direct-execute-sunset-period-2.md", + hash: "", ;; TODO }) -(define-constant SUNSET_BLOCK u147828) +(define-constant SUNSET_BLOCK u173748) (define-constant VOTE_SCALE_FACTOR (pow u10 u16)) ;; 16 decimal places (define-constant MIA_SCALE_BASE (pow u10 u4)) ;; 4 decimal places (define-constant MIA_SCALE_FACTOR u8823) ;; 0.8823 or 88.23% ;; MIA votes scaled to make 1 MIA = 1 NYC -;; full calculation available in CCIP-017 +;; full calculation available in CCIP-021 ;; DATA VARS From 5c13da0775eb152382d1489f8f581484ba5e7fb9 Mon Sep 17 00:00:00 2001 From: Friedger Date: Fri, 5 Apr 2024 07:23:13 +0200 Subject: [PATCH 04/17] Update contracts/proposals/ccip021-extend-sunset-period-2.clar Co-authored-by: Jason Schrader --- .../ccip021-extend-sunset-period-2.clar | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/proposals/ccip021-extend-sunset-period-2.clar b/contracts/proposals/ccip021-extend-sunset-period-2.clar index 8cada962..ba978b7b 100644 --- a/contracts/proposals/ccip021-extend-sunset-period-2.clar +++ b/contracts/proposals/ccip021-extend-sunset-period-2.clar @@ -5,14 +5,14 @@ ;; ERRORS -(define-constant ERR_PANIC (err u1700)) -(define-constant ERR_VOTED_ALREADY (err u1701)) -(define-constant ERR_NOTHING_STACKED (err u1702)) -(define-constant ERR_USER_NOT_FOUND (err u1703)) -(define-constant ERR_PROPOSAL_NOT_ACTIVE (err u1704)) -(define-constant ERR_PROPOSAL_STILL_ACTIVE (err u1705)) -(define-constant ERR_NO_CITY_ID (err u1706)) -(define-constant ERR_VOTE_FAILED (err u1707)) +(define-constant ERR_PANIC (err u2100)) +(define-constant ERR_VOTED_ALREADY (err u2101)) +(define-constant ERR_NOTHING_STACKED (err u2102)) +(define-constant ERR_USER_NOT_FOUND (err u2103)) +(define-constant ERR_PROPOSAL_NOT_ACTIVE (err u2104)) +(define-constant ERR_PROPOSAL_STILL_ACTIVE (err u2105)) +(define-constant ERR_NO_CITY_ID (err u2106)) +(define-constant ERR_VOTE_FAILED (err u2107)) ;; CONSTANTS From a0f025f15e6da4b1dd4d829e82470189a6b0878d Mon Sep 17 00:00:00 2001 From: Friedger Date: Fri, 5 Apr 2024 07:23:25 +0200 Subject: [PATCH 05/17] Update contracts/proposals/ccip021-extend-sunset-period-2.clar Co-authored-by: Jason Schrader --- contracts/proposals/ccip021-extend-sunset-period-2.clar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/proposals/ccip021-extend-sunset-period-2.clar b/contracts/proposals/ccip021-extend-sunset-period-2.clar index ba978b7b..7297f45f 100644 --- a/contracts/proposals/ccip021-extend-sunset-period-2.clar +++ b/contracts/proposals/ccip021-extend-sunset-period-2.clar @@ -26,7 +26,7 @@ (define-constant VOTE_SCALE_FACTOR (pow u10 u16)) ;; 16 decimal places (define-constant MIA_SCALE_BASE (pow u10 u4)) ;; 4 decimal places -(define-constant MIA_SCALE_FACTOR u8823) ;; 0.8823 or 88.23% +(define-constant MIA_SCALE_FACTOR u8916) ;; 0.8916 or 89.16% ;; MIA votes scaled to make 1 MIA = 1 NYC ;; full calculation available in CCIP-021 From d1157bd93ffa44f6db03bcbf69ffcc0b4919c02b Mon Sep 17 00:00:00 2001 From: Friedger Date: Fri, 5 Apr 2024 07:23:44 +0200 Subject: [PATCH 06/17] Update contracts/proposals/ccip021-extend-sunset-period-2.clar Co-authored-by: Jason Schrader --- contracts/proposals/ccip021-extend-sunset-period-2.clar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/proposals/ccip021-extend-sunset-period-2.clar b/contracts/proposals/ccip021-extend-sunset-period-2.clar index 7297f45f..241fa3c0 100644 --- a/contracts/proposals/ccip021-extend-sunset-period-2.clar +++ b/contracts/proposals/ccip021-extend-sunset-period-2.clar @@ -162,7 +162,7 @@ ) (define-read-only (get-proposal-info) - (some CCIP_017) + (some CCIP_021) ) (define-read-only (get-vote-period) From 0be50a32a35be9b47882a59c402b427a9108c8e2 Mon Sep 17 00:00:00 2001 From: Friedger Date: Fri, 5 Apr 2024 07:24:02 +0200 Subject: [PATCH 07/17] Update contracts/proposals/ccip021-extend-sunset-period-2.clar Co-authored-by: Jason Schrader --- .../ccip021-extend-sunset-period-2.clar | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/proposals/ccip021-extend-sunset-period-2.clar b/contracts/proposals/ccip021-extend-sunset-period-2.clar index 241fa3c0..eb1c8725 100644 --- a/contracts/proposals/ccip021-extend-sunset-period-2.clar +++ b/contracts/proposals/ccip021-extend-sunset-period-2.clar @@ -199,22 +199,22 @@ (define-read-only (get-mia-vote (cityId uint) (userId uint) (scaled bool)) (let ( - ;; MAINNET: MIA cycle 64 / first block BTC 800,450 STX 114,689 + ;; MAINNET: MIA cycle 80 / first block BTC 834,050 STX 142,301 ;; cycle 2 / u4500 used in tests - (cycle64Hash (unwrap! (get-block-hash u4500) none)) - (cycle64Data (at-block cycle64Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u2 userId))) - (cycle64Amount (get stacked cycle64Data)) - ;; MAINNET: MIA cycle 65 / first block BTC 802,550 STX 116,486 + (cycle80Hash (unwrap! (get-block-hash u4500) none)) + (cycle80Data (at-block cycle80Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u2 userId))) + (cycle80Amount (get stacked cycle80Data)) + ;; MAINNET: MIA cycle 81 / first block BTC 836,150 STX 143,989 ;; cycle 3 / u6600 used in tests - (cycle65Hash (unwrap! (get-block-hash u6600) none)) - (cycle65Data (at-block cycle65Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u3 userId))) - (cycle65Amount (get stacked cycle65Data)) + (cycle81Hash (unwrap! (get-block-hash u6600) none)) + (cycle81Data (at-block cycle81Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u3 userId))) + (cycle81Amount (get stacked cycle81Data)) ;; MIA vote calculation - (avgStacked (/ (+ (scale-up cycle64Amount) (scale-up cycle65Amount)) u2)) + (avgStacked (/ (+ (scale-up cycle80Amount) (scale-up cycle81Amount)) u2)) (scaledVote (/ (* avgStacked MIA_SCALE_FACTOR) MIA_SCALE_BASE)) ) ;; check that at least one value is positive - (asserts! (or (> cycle64Amount u0) (> cycle65Amount u0)) none) + (asserts! (or (> cycle80Amount u0) (> cycle81Amount u0)) none) ;; return scaled or unscaled value (if scaled (some scaledVote) (some (/ scaledVote VOTE_SCALE_FACTOR))) ) From aaf749b257a8e7b4bb679066807f2e8ae654e095 Mon Sep 17 00:00:00 2001 From: Friedger Date: Fri, 5 Apr 2024 07:24:20 +0200 Subject: [PATCH 08/17] Update contracts/proposals/ccip021-extend-sunset-period-2.clar Co-authored-by: Jason Schrader --- .../ccip021-extend-sunset-period-2.clar | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/proposals/ccip021-extend-sunset-period-2.clar b/contracts/proposals/ccip021-extend-sunset-period-2.clar index eb1c8725..3c59e600 100644 --- a/contracts/proposals/ccip021-extend-sunset-period-2.clar +++ b/contracts/proposals/ccip021-extend-sunset-period-2.clar @@ -226,21 +226,21 @@ (define-read-only (get-nyc-vote (cityId uint) (userId uint) (scaled bool)) (let ( - ;; NYC cycle 64 / first block BTC 800,450 STX 114,689 + ;; NYC cycle 80 / first block BTC 834,050 STX 142,301 ;; cycle 2 / u4500 used in tests - (cycle64Hash (unwrap! (get-block-hash u4500) none)) - (cycle64Data (at-block cycle64Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u2 userId))) - (cycle64Amount (get stacked cycle64Data)) - ;; NYC cycle 65 / first block BTC 802,550 STX 116,486 + (cycle80Hash (unwrap! (get-block-hash u4500) none)) + (cycle80Data (at-block cycle80Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u2 userId))) + (cycle80Amount (get stacked cycle80Data)) + ;; NYC cycle 81 / first block BTC 836,150 STX 143,989 ;; cycle 3 / u6600 used in tests - (cycle65Hash (unwrap! (get-block-hash u6600) none)) - (cycle65Data (at-block cycle65Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u3 userId))) - (cycle65Amount (get stacked cycle65Data)) + (cycle81Hash (unwrap! (get-block-hash u6600) none)) + (cycle81Data (at-block cycle81Hash (contract-call? .ccd007-citycoin-stacking get-stacker cityId u3 userId))) + (cycle81Amount (get stacked cycle81Data)) ;; NYC vote calculation - (scaledVote (/ (+ (scale-up cycle64Amount) (scale-up cycle65Amount)) u2)) + (scaledVote (/ (+ (scale-up cycle80Amount) (scale-up cycle81Amount)) u2)) ) ;; check that at least one value is positive - (asserts! (or (> cycle64Amount u0) (> cycle65Amount u0)) none) + (asserts! (or (> cycle80Amount u0) (> cycle81Amount u0)) none) ;; return scaled or unscaled value (if scaled (some scaledVote) (some (/ scaledVote VOTE_SCALE_FACTOR))) ) From f2a73ab991e247b79b52bf0f593ae17c5090097c Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 02:21:22 +0200 Subject: [PATCH 09/17] chore: add test --- ...nd-direct-execute-sunset-period-2.model.ts | 73 +++ .../ccip021-extend-sunset-period-2.test.ts | 590 ++++++++++++++++++ utils/common.ts | 1 + 3 files changed, 664 insertions(+) create mode 100644 models/proposals/ccip021-extend-direct-execute-sunset-period-2.model.ts create mode 100644 tests/proposals/ccip021-extend-sunset-period-2.test.ts diff --git a/models/proposals/ccip021-extend-direct-execute-sunset-period-2.model.ts b/models/proposals/ccip021-extend-direct-execute-sunset-period-2.model.ts new file mode 100644 index 00000000..e83fb669 --- /dev/null +++ b/models/proposals/ccip021-extend-direct-execute-sunset-period-2.model.ts @@ -0,0 +1,73 @@ +import { PROPOSALS } from "../../utils/common.ts"; +import { Chain, Account, Tx, types, ReadOnlyFn } from "../../utils/deps.ts"; + +enum ErrCode { + ERR_PANIC = 2100, + ERR_VOTED_ALREADY, + ERR_NOTHING_STACKED, + ERR_USER_NOT_FOUND, + ERR_PROPOSAL_NOT_ACTIVE, + ERR_PROPOSAL_STILL_ACTIVE, + ERR_NO_CITY_ID, + ERR_VOTE_FAILED, +} + +export class CCIP021ExtendDirectExecuteSunsetPeriod { + name = PROPOSALS.CCIP_021; + static readonly ErrCode = ErrCode; + chain: Chain; + deployer: Account; + + constructor(chain: Chain, deployer: Account) { + this.chain = chain; + this.deployer = deployer; + } + + // public functions + + // execute() excluded since called by passProposal and CCD001 + + voteOnProposal(sender: Account, vote: boolean) { + return Tx.contractCall(this.name, "vote-on-proposal", [types.bool(vote)], sender.address); + } + + // read-only functions + + isExecutable() { + return this.callReadOnlyFn("is-executable"); + } + + isVoteActive() { + return this.callReadOnlyFn("is-vote-active"); + } + + getProposalInfo() { + return this.callReadOnlyFn("get-proposal-info"); + } + + getVotePeriod() { + return this.callReadOnlyFn("get-vote-period"); + } + + getVoteTotals() { + return this.callReadOnlyFn("get-vote-totals"); + } + + getVoterInfo(userId: number) { + return this.callReadOnlyFn("get-voter-info", [types.uint(userId)]); + } + + getMiaVote(cityId: number, userId: number, scaled: boolean) { + return this.callReadOnlyFn("get-mia-vote", [types.uint(cityId), types.uint(userId), types.bool(scaled)]); + } + + getNycVote(cityId: number, userId: number, scaled: boolean) { + return this.callReadOnlyFn("get-nyc-vote", [types.uint(cityId), types.uint(userId), types.bool(scaled)]); + } + + // read-only function helper + private callReadOnlyFn(method: string, args: Array = [], sender: Account = this.deployer): ReadOnlyFn { + const result = this.chain.callReadOnlyFn(this.name, method, args, sender?.address); + return result; + } +} diff --git a/tests/proposals/ccip021-extend-sunset-period-2.test.ts b/tests/proposals/ccip021-extend-sunset-period-2.test.ts new file mode 100644 index 00000000..b1835849 --- /dev/null +++ b/tests/proposals/ccip021-extend-sunset-period-2.test.ts @@ -0,0 +1,590 @@ +import { Account, Clarinet, Chain, types, assertEquals } from "../../utils/deps.ts"; +import { constructAndPassProposal, mia, nyc, passProposal, PROPOSALS } from "../../utils/common.ts"; +import { CCD006CityMining } from "../../models/extensions/ccd006-citycoin-mining.model.ts"; +import { CCD007CityStacking } from "../../models/extensions/ccd007-citycoin-stacking.model.ts"; +import { CCIP021ExtendDirectExecuteSunsetPeriod } from "../../models/proposals/ccip021-extend-direct-execute-sunset-period.model.ts"; + +const TARGET_SUNSET_BLOCK = 147828; + +Clarinet.test({ + name: "ccip-021: execute() fails with ERR_VOTE_FAILED if there are no votes", + fn(chain: Chain, accounts: Map) { + // arrange + + // register MIA and NYC + constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCD004_CITY_REGISTRY_001); + // set activation details for MIA and NYC + passProposal(chain, accounts, PROPOSALS.TEST_CCD005_CITY_DATA_001); + // set activation status for MIA and NYC + passProposal(chain, accounts, PROPOSALS.TEST_CCD005_CITY_DATA_002); + + // act + + // execute ccip-021 + const block = passProposal(chain, accounts, PROPOSALS.CCIP_021); + + // assert + block.receipts[2].result.expectErr().expectUint(CCIP021ExtendDirectExecuteSunsetPeriod.ErrCode.ERR_VOTE_FAILED); + }, +}); + +Clarinet.test({ + name: "ccip-021: execute() fails with ERR_VOTE_FAILED if there are more no than yes votes", + fn(chain: Chain, accounts: Map) { + // arrange + const sender = accounts.get("deployer")!; + const user1 = accounts.get("wallet_1")!; + const user2 = accounts.get("wallet_2")!; + const ccd007CityStacking = new CCD007CityStacking(chain, sender, "ccd007-citycoin-stacking"); + const ccip021ExtendDirectExecuteSunsetPeriod = new CCIP021ExtendDirectExecuteSunsetPeriod(chain, sender); + + const amountStacked = 500; + const lockPeriod = 10; + + // progress the chain to avoid underflow in + // stacking reward cycle calculation + chain.mineEmptyBlockUntil(CCD007CityStacking.FIRST_STACKING_BLOCK); + // register MIA and NYC + constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCD004_CITY_REGISTRY_001); + // set activation details for MIA and NYC + passProposal(chain, accounts, PROPOSALS.TEST_CCD005_CITY_DATA_001); + // set activation status for MIA and NYC + passProposal(chain, accounts, PROPOSALS.TEST_CCD005_CITY_DATA_002); + // add stacking treasury in city data + passProposal(chain, accounts, PROPOSALS.TEST_CCD007_CITY_STACKING_007); + // mints mia to user1 and user2 + passProposal(chain, accounts, PROPOSALS.TEST_CCD007_CITY_STACKING_009); + // adds the token contract to the treasury allow list + passProposal(chain, accounts, PROPOSALS.TEST_CCD007_CITY_STACKING_010); + + // stack first cycle u1, last cycle u10 + const stackingBlock = chain.mineBlock([ccd007CityStacking.stack(user1, mia.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user2, mia.cityName, amountStacked, lockPeriod)]); + stackingBlock.receipts[0].result.expectOk().expectBool(true); + stackingBlock.receipts[1].result.expectOk().expectBool(true); + + // progress the chain to cycle 5 + // votes are counted in cycles 2-3 + // past payouts tested for cycles 1-4 + chain.mineEmptyBlockUntil(CCD007CityStacking.REWARD_CYCLE_LENGTH * 6 + 10); + ccd007CityStacking.getCurrentRewardCycle().result.expectUint(5); + + // act + + // execute two no votes + const votingBlock = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, false), ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user2, false)]); + + /* double check voting data + console.log(`voting block:\n${JSON.stringify(votingBlock, null, 2)}`); + console.log("user 1:"); + console.log(ccd007CityStacking.getStacker(mia.cityId, 2, 1)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(1)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, 1, false)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, 1, true)); + console.log("user 2:"); + console.log(ccd007CityStacking.getStacker(mia.cityId, 2, 2)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(2)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, 2, false)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, 2, true)); + */ + + // execute ccip-021 + const block = passProposal(chain, accounts, PROPOSALS.CCIP_021); + + // assert + block.receipts[2].result.expectErr().expectUint(CCIP021ExtendDirectExecuteSunsetPeriod.ErrCode.ERR_VOTE_FAILED); + }, +}); + +Clarinet.test({ + name: "ccip-021: execute() succeeds if there is a single yes vote", + fn(chain: Chain, accounts: Map) { + // arrange + const sender = accounts.get("deployer")!; + const user1 = accounts.get("wallet_1")!; + const ccd006CityMining = new CCD006CityMining(chain, sender, "ccd006-citycoin-mining"); + const ccd007CityStacking = new CCD007CityStacking(chain, sender, "ccd007-citycoin-stacking"); + const ccip021ExtendDirectExecuteSunsetPeriod = new CCIP021ExtendDirectExecuteSunsetPeriod(chain, sender); + + const miningEntries = [25000000, 25000000]; + const amountStacked = 500; + const lockPeriod = 10; + + // progress the chain to avoid underflow in + // stacking reward cycle calculation + chain.mineEmptyBlockUntil(CCD007CityStacking.FIRST_STACKING_BLOCK); + + // prepare for ccip-021 + const constructBlock = constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCIP014_POX3_001); + + // mine to put funds in the mining treasury + const miningBlock = chain.mineBlock([ccd006CityMining.mine(sender, mia.cityName, miningEntries), ccd006CityMining.mine(sender, nyc.cityName, miningEntries)]); + + // stack first cycle u1, last cycle u10 + const stackingBlock = chain.mineBlock([ccd007CityStacking.stack(user1, mia.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user1, nyc.cityName, amountStacked, lockPeriod)]); + stackingBlock.receipts[0].result.expectOk().expectBool(true); + + // progress the chain to cycle 5 + // votes are counted in cycles 2-3 + // past payouts tested for cycles 1-4 + chain.mineEmptyBlockUntil(CCD007CityStacking.REWARD_CYCLE_LENGTH * 6 + 10); + ccd007CityStacking.getCurrentRewardCycle().result.expectUint(5); + + // act + + // execute single yes vote + const votingBlock = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, true)]); + + /* double check voting data + const cycleId = 2; + const userId = 2; + console.log(`\nconstruct block:\n${JSON.stringify(constructBlock, null, 2)}`); + console.log(`\nmining block:\n${JSON.stringify(miningBlock, null, 2)}`); + console.log(`\nstacking block:\n${JSON.stringify(stackingBlock, null, 2)}`); + console.log(`\nvoting block:\n${JSON.stringify(votingBlock, null, 2)}`); + console.log("\nuser 1 mia:"); + console.log(ccd007CityStacking.getStacker(mia.cityId, cycleId, userId)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(userId)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, userId, false)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, userId, true)); + console.log("\nuser 1 nyc:"); + console.log(ccd007CityStacking.getStacker(nyc.cityId, cycleId, userId)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(userId)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getNycVote(nyc.cityId, userId, false)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getNycVote(nyc.cityId, userId, true)); + */ + + // check vote is active + ccip021ExtendDirectExecuteSunsetPeriod.isVoteActive().result.expectSome().expectBool(true); + // check proposal info + const proposalInfo = { + hash: types.ascii("7ddbf6152790a730faa059b564a8524abc3c70d3"), + link: types.ascii("https://github.com/citycoins/governance/blob/feat/add-ccip-021/ccips/ccip-021/ccip-021-extend-direct-execute-sunset-period.md"), + name: types.ascii("Extend Direct Execute Sunset Period"), + }; + assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getProposalInfo().result.expectSome().expectTuple(), proposalInfo); + // check vote period is not set (end unknown) + ccip021ExtendDirectExecuteSunsetPeriod.getVotePeriod().result.expectNone(); + + // execute ccip-021 + const block = passProposal(chain, accounts, PROPOSALS.CCIP_021); + + // assert + // check vote period is set and returns + const start = constructBlock.height - CCD007CityStacking.FIRST_STACKING_BLOCK - 1; + const end = votingBlock.height; + const votingPeriod = { + startBlock: types.uint(start), + endBlock: types.uint(end), + length: types.uint(end - start), + }; + assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVotePeriod().result.expectSome().expectTuple(), votingPeriod); + // check vote is no longer active + ccip021ExtendDirectExecuteSunsetPeriod.isVoteActive().result.expectSome().expectBool(false); + //console.log(`\nexecute block:\n${JSON.stringify(block, null, 2)}`); + // check that proposal executed + block.receipts[2].result.expectOk().expectUint(3); + // check for print event in execute block + const ccip021PrintEvent = `{event: ${types.ascii("set-sunset-block")}, height: ${types.uint(TARGET_SUNSET_BLOCK)}}`; + assertEquals(block.receipts[2].events[1].contract_event.value, ccip021PrintEvent); + }, +}); + +Clarinet.test({ + name: "ccip-021: execute() succeeds if there are more yes than no votes", + fn(chain: Chain, accounts: Map) { + // arrange + const sender = accounts.get("deployer")!; + const user1 = accounts.get("wallet_1")!; + const user2 = accounts.get("wallet_2")!; + const ccd006CityMining = new CCD006CityMining(chain, sender, "ccd006-citycoin-mining"); + const ccd007CityStacking = new CCD007CityStacking(chain, sender, "ccd007-citycoin-stacking"); + const ccip021ExtendDirectExecuteSunsetPeriod = new CCIP021ExtendDirectExecuteSunsetPeriod(chain, sender); + + const miningEntries = [25000000, 25000000]; + const amountStacked = 500; + const lockPeriod = 10; + + // progress the chain to avoid underflow in + // stacking reward cycle calculation + chain.mineEmptyBlockUntil(CCD007CityStacking.FIRST_STACKING_BLOCK); + // prepare for ccip-021 + const constructBlock = constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCIP014_POX3_001); + + // mine to put funds in the mining treasury + const miningBlock = chain.mineBlock([ccd006CityMining.mine(sender, mia.cityName, miningEntries), ccd006CityMining.mine(sender, nyc.cityName, miningEntries)]); + + // stack first cycle u1, last cycle u10 + const stackingBlock = chain.mineBlock([ccd007CityStacking.stack(user1, mia.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user1, nyc.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user2, mia.cityName, amountStacked / 2, lockPeriod), ccd007CityStacking.stack(user2, nyc.cityName, amountStacked / 2, lockPeriod)]); + stackingBlock.receipts[0].result.expectOk().expectBool(true); + + // progress the chain to cycle 5 + // votes are counted in cycles 2-3 + // past payouts tested for cycles 1-4 + chain.mineEmptyBlockUntil(CCD007CityStacking.REWARD_CYCLE_LENGTH * 6 + 10); + ccd007CityStacking.getCurrentRewardCycle().result.expectUint(5); + + // act + + // execute yes and no vote + // user 1 has more voting power + const votingBlock = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, true), ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user2, false)]); + + /* double check voting data + const cycleId = 2; + const user1Id = 2; + const user2Id = 3; + console.log(`\nconstruct block:\n${JSON.stringify(constructBlock, null, 2)}`); + console.log(`\nmining block:\n${JSON.stringify(miningBlock, null, 2)}`); + console.log(`\nstacking block:\n${JSON.stringify(stackingBlock, null, 2)}`); + console.log(`\nvoting block:\n${JSON.stringify(votingBlock, null, 2)}`); + console.log("\nuser 1 mia:"); + console.log(ccd007CityStacking.getStacker(mia.cityId, cycleId, user1Id)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user1Id)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, user1Id, false)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, user1Id, true)); + console.log("\nuser 1 nyc:"); + console.log(ccd007CityStacking.getStacker(nyc.cityId, cycleId, user1Id)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user1Id)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getNycVote(nyc.cityId, user1Id, false)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getNycVote(nyc.cityId, user1Id, true)); + console.log("\nuser 2 mia:"); + console.log(ccd007CityStacking.getStacker(mia.cityId, cycleId, user2Id)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user2Id)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, user2Id, false)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getMiaVote(mia.cityId, user2Id, true)); + console.log("\nuser 2 nyc:"); + console.log(ccd007CityStacking.getStacker(nyc.cityId, cycleId, user2Id)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user2Id)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getNycVote(nyc.cityId, user2Id, false)); + console.log(ccip021ExtendDirectExecuteSunsetPeriod.getNycVote(nyc.cityId, user2Id, true)); + */ + + // execute ccip-021 + const block = passProposal(chain, accounts, PROPOSALS.CCIP_021); + + // assert + //console.log(`\nexecute block:\n${JSON.stringify(block, null, 2)}`); + block.receipts[2].result.expectOk().expectUint(3); + }, +}); + +Clarinet.test({ + name: "ccip-021: execute() succeeds if there are more yes than no votes after a reversal", + fn(chain: Chain, accounts: Map) { + // arrange + const sender = accounts.get("deployer")!; + const user1 = accounts.get("wallet_1")!; + const user2 = accounts.get("wallet_2")!; + const ccd006CityMining = new CCD006CityMining(chain, sender, "ccd006-citycoin-mining"); + const ccd007CityStacking = new CCD007CityStacking(chain, sender, "ccd007-citycoin-stacking"); + const ccip021ExtendDirectExecuteSunsetPeriod = new CCIP021ExtendDirectExecuteSunsetPeriod(chain, sender); + + const miningEntries = [25000000, 25000000]; + const amountStacked = 500; + const lockPeriod = 10; + + // progress the chain to avoid underflow in + // stacking reward cycle calculation + chain.mineEmptyBlockUntil(CCD007CityStacking.FIRST_STACKING_BLOCK); + // prepare for ccip-021 + const constructBlock = constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCIP014_POX3_001); + + // mine to put funds in the mining treasury + const miningBlock = chain.mineBlock([ccd006CityMining.mine(sender, mia.cityName, miningEntries), ccd006CityMining.mine(sender, nyc.cityName, miningEntries)]); + + // stack first cycle u1, last cycle u10 + const stackingBlock = chain.mineBlock([ccd007CityStacking.stack(user1, mia.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user1, nyc.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user2, mia.cityName, amountStacked / 2, lockPeriod), ccd007CityStacking.stack(user2, nyc.cityName, amountStacked / 2, lockPeriod)]); + stackingBlock.receipts[0].result.expectOk().expectBool(true); + + // progress the chain to cycle 5 + // votes are counted in cycles 2-3 + // past payouts tested for cycles 1-4 + chain.mineEmptyBlockUntil(CCD007CityStacking.REWARD_CYCLE_LENGTH * 6 + 10); + ccd007CityStacking.getCurrentRewardCycle().result.expectUint(5); + + // act + + // execute yes and no vote + // user 1 has more voting power + const votingBlock = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, false), ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user2, true)]); + + // switch yes and no vote + const votingBlockReverse = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, true), ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user2, false)]); + + /* double check voting data + console.log(`\nvoting block:\n${JSON.stringify(votingBlock, null, 2)}`); + console.log(`\nvoting block reverse:\n${JSON.stringify(votingBlockReverse, null, 2)}`); + */ + + // execute ccip-021 + const block = passProposal(chain, accounts, PROPOSALS.CCIP_021); + + // assert + //console.log(`\nexecute block:\n${JSON.stringify(block, null, 2)}`); + block.receipts[2].result.expectOk().expectUint(3); + }, +}); + +Clarinet.test({ + name: "ccip-021: vote-on-proposal() fails with ERR_USER_NOT_FOUND if user is not registered in ccd003-user-registry", + fn(chain: Chain, accounts: Map) { + // arrange + const sender = accounts.get("deployer")!; + const user1 = accounts.get("wallet_1")!; + const user2 = accounts.get("wallet_2")!; + const user3 = accounts.get("wallet_3")!; + const ccd006CityMining = new CCD006CityMining(chain, sender, "ccd006-citycoin-mining"); + const ccd007CityStacking = new CCD007CityStacking(chain, sender, "ccd007-citycoin-stacking"); + const ccip021ExtendDirectExecuteSunsetPeriod = new CCIP021ExtendDirectExecuteSunsetPeriod(chain, sender); + + const miningEntries = [25000000, 25000000]; + const amountStacked = 500; + const lockPeriod = 10; + + // progress the chain to avoid underflow in + // stacking reward cycle calculation + chain.mineEmptyBlockUntil(CCD007CityStacking.FIRST_STACKING_BLOCK); + // prepare for ccip-021 + const constructBlock = constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCIP014_POX3_001); + + // mine to put funds in the mining treasury + const miningBlock = chain.mineBlock([ccd006CityMining.mine(sender, mia.cityName, miningEntries), ccd006CityMining.mine(sender, nyc.cityName, miningEntries)]); + + // stack first cycle u1, last cycle u10 + const stackingBlock = chain.mineBlock([ccd007CityStacking.stack(user1, mia.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user1, nyc.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user2, mia.cityName, amountStacked / 2, lockPeriod), ccd007CityStacking.stack(user2, nyc.cityName, amountStacked / 2, lockPeriod)]); + stackingBlock.receipts[0].result.expectOk().expectBool(true); + + // progress the chain to cycle 5 + // votes are counted in cycles 2-3 + // past payouts tested for cycles 1-4 + chain.mineEmptyBlockUntil(CCD007CityStacking.REWARD_CYCLE_LENGTH * 6 + 10); + ccd007CityStacking.getCurrentRewardCycle().result.expectUint(5); + + // act + + // execute yes and no vote + const votingBlock = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user3, true)]); + + // assert + //console.log(`votingBlock: ${JSON.stringify(votingBlock, null, 2)}`); + votingBlock.receipts[0].result.expectErr().expectUint(CCIP021ExtendDirectExecuteSunsetPeriod.ErrCode.ERR_USER_NOT_FOUND); + }, +}); + +Clarinet.test({ + name: "ccip-021: vote-on-proposal() fails with ERR_PROPOSAL_NOT_ACTIVE if called after the vote ends", + fn(chain: Chain, accounts: Map) { + // arrange + const sender = accounts.get("deployer")!; + const user1 = accounts.get("wallet_1")!; + const user2 = accounts.get("wallet_2")!; + const ccd006CityMining = new CCD006CityMining(chain, sender, "ccd006-citycoin-mining"); + const ccd007CityStacking = new CCD007CityStacking(chain, sender, "ccd007-citycoin-stacking"); + const ccip021ExtendDirectExecuteSunsetPeriod = new CCIP021ExtendDirectExecuteSunsetPeriod(chain, sender); + + const miningEntries = [25000000, 25000000]; + const amountStacked = 500; + const lockPeriod = 10; + + // progress the chain to avoid underflow in + // stacking reward cycle calculation + chain.mineEmptyBlockUntil(CCD007CityStacking.FIRST_STACKING_BLOCK); + // prepare for ccip-021 + const constructBlock = constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCIP014_POX3_001); + + // mine to put funds in the mining treasury + const miningBlock = chain.mineBlock([ccd006CityMining.mine(sender, mia.cityName, miningEntries), ccd006CityMining.mine(sender, nyc.cityName, miningEntries)]); + + // stack first cycle u1, last cycle u10 + const stackingBlock = chain.mineBlock([ccd007CityStacking.stack(user1, mia.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user1, nyc.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user2, mia.cityName, amountStacked / 2, lockPeriod), ccd007CityStacking.stack(user2, nyc.cityName, amountStacked / 2, lockPeriod)]); + stackingBlock.receipts[0].result.expectOk().expectBool(true); + + // progress the chain to cycle 5 + // votes are counted in cycles 2-3 + // past payouts tested for cycles 1-4 + chain.mineEmptyBlockUntil(CCD007CityStacking.REWARD_CYCLE_LENGTH * 6 + 10); + ccd007CityStacking.getCurrentRewardCycle().result.expectUint(5); + + // execute yes and no vote + // user 1 has more voting power + const votingBlock = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, true), ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user2, false)]); + + // execute ccip-021 + passProposal(chain, accounts, PROPOSALS.CCIP_021); + + // act + const votingBlock2 = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, true)]); + + // assert + votingBlock2.receipts[0].result.expectErr().expectUint(CCIP021ExtendDirectExecuteSunsetPeriod.ErrCode.ERR_PROPOSAL_NOT_ACTIVE); + }, +}); + +Clarinet.test({ + name: "ccip-021: vote-on-proposal() fails with ERR_VOTED_ALREADY if user already voted with the same value", + fn(chain: Chain, accounts: Map) { + // arrange + const sender = accounts.get("deployer")!; + const user1 = accounts.get("wallet_1")!; + const user2 = accounts.get("wallet_2")!; + const ccd006CityMining = new CCD006CityMining(chain, sender, "ccd006-citycoin-mining"); + const ccd007CityStacking = new CCD007CityStacking(chain, sender, "ccd007-citycoin-stacking"); + const ccip021ExtendDirectExecuteSunsetPeriod = new CCIP021ExtendDirectExecuteSunsetPeriod(chain, sender); + + const miningEntries = [25000000, 25000000]; + const amountStacked = 500; + const lockPeriod = 10; + + // progress the chain to avoid underflow in + // stacking reward cycle calculation + chain.mineEmptyBlockUntil(CCD007CityStacking.FIRST_STACKING_BLOCK); + // prepare for ccip-021 + const constructBlock = constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCIP014_POX3_001); + + // mine to put funds in the mining treasury + const miningBlock = chain.mineBlock([ccd006CityMining.mine(sender, mia.cityName, miningEntries), ccd006CityMining.mine(sender, nyc.cityName, miningEntries)]); + + // stack first cycle u1, last cycle u10 + const stackingBlock = chain.mineBlock([ccd007CityStacking.stack(user1, mia.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user1, nyc.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user2, mia.cityName, amountStacked / 2, lockPeriod), ccd007CityStacking.stack(user2, nyc.cityName, amountStacked / 2, lockPeriod)]); + stackingBlock.receipts[0].result.expectOk().expectBool(true); + + // progress the chain to cycle 5 + // votes are counted in cycles 2-3 + // past payouts tested for cycles 1-4 + chain.mineEmptyBlockUntil(CCD007CityStacking.REWARD_CYCLE_LENGTH * 6 + 10); + ccd007CityStacking.getCurrentRewardCycle().result.expectUint(5); + + // execute yes and no vote + // user 1 has more voting power + const votingBlock = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, true), ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user2, false)]); + + // act + const votingBlock2 = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, true)]); + + // assert + votingBlock2.receipts[0].result.expectErr().expectUint(CCIP021ExtendDirectExecuteSunsetPeriod.ErrCode.ERR_VOTED_ALREADY); + }, +}); + +Clarinet.test({ + name: "ccip-021: read-only functions return expected values before/after reversal", + fn(chain: Chain, accounts: Map) { + // arrange + const sender = accounts.get("deployer")!; + const user1 = accounts.get("wallet_1")!; + const user2 = accounts.get("wallet_2")!; + const ccd006CityMining = new CCD006CityMining(chain, sender, "ccd006-citycoin-mining"); + const ccd007CityStacking = new CCD007CityStacking(chain, sender, "ccd007-citycoin-stacking"); + const ccip021ExtendDirectExecuteSunsetPeriod = new CCIP021ExtendDirectExecuteSunsetPeriod(chain, sender); + + const miningEntries = [25000000, 25000000]; + const amountStacked = 500; + const lockPeriod = 10; + + const cycleId = 2; + const user1Id = 2; + const user2Id = 3; + + // progress the chain to avoid underflow in + // stacking reward cycle calculation + chain.mineEmptyBlockUntil(CCD007CityStacking.FIRST_STACKING_BLOCK); + // prepare for ccip-021 + const constructBlock = constructAndPassProposal(chain, accounts, PROPOSALS.TEST_CCIP014_POX3_001); + + // mine to put funds in the mining treasury + const miningBlock = chain.mineBlock([ccd006CityMining.mine(sender, mia.cityName, miningEntries), ccd006CityMining.mine(sender, nyc.cityName, miningEntries)]); + + // stack first cycle u1, last cycle u10 + const stackingBlock = chain.mineBlock([ccd007CityStacking.stack(user1, mia.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user1, nyc.cityName, amountStacked, lockPeriod), ccd007CityStacking.stack(user2, mia.cityName, amountStacked / 2, lockPeriod), ccd007CityStacking.stack(user2, nyc.cityName, amountStacked / 2, lockPeriod)]); + stackingBlock.receipts[0].result.expectOk().expectBool(true); + + // progress the chain to cycle 5 + // votes are counted in cycles 2-3 + // past payouts tested for cycles 1-4 + chain.mineEmptyBlockUntil(CCD007CityStacking.REWARD_CYCLE_LENGTH * 6 + 10); + ccd007CityStacking.getCurrentRewardCycle().result.expectUint(5); + + // act + // execute yes and no vote + // user 1 has more voting power + const votingBlock = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, false), ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user2, true)]); + + // assert + + // overall totals + assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoteTotals().result.expectSome().expectTuple(), { + noTotal: types.uint(941), + noVotes: types.uint(1), + yesTotal: types.uint(470), + yesVotes: types.uint(1), + }); + + // user 1 + + assertEquals(ccd007CityStacking.getStacker(mia.cityId, cycleId, user1Id).result.expectTuple(), { + claimable: types.uint(0), + stacked: types.uint(500), + }); + assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user1Id).result.expectSome().expectTuple(), { + mia: types.uint(441), + nyc: types.uint(500), + total: types.uint(941), + vote: types.bool(false), + }); + + // user 2 + assertEquals(ccd007CityStacking.getStacker(mia.cityId, cycleId, user2Id).result.expectTuple(), { + claimable: types.uint(0), + stacked: types.uint(250), + }); + assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user2Id).result.expectSome().expectTuple(), { + mia: types.uint(220), + nyc: types.uint(250), + total: types.uint(470), + vote: types.bool(true), + }); + + // act + + // switch yes and no vote + const votingBlockReverse = chain.mineBlock([ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user1, true), ccip021ExtendDirectExecuteSunsetPeriod.voteOnProposal(user2, false)]); + + // assert + + // overall totals + assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoteTotals().result.expectSome().expectTuple(), { + noTotal: types.uint(470), + noVotes: types.uint(1), + yesTotal: types.uint(941), + yesVotes: types.uint(1), + }); + // user 1 + assertEquals(ccd007CityStacking.getStacker(mia.cityId, cycleId, user1Id).result.expectTuple(), { + claimable: types.uint(0), + stacked: types.uint(500), + }); + assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user1Id).result.expectSome().expectTuple(), { + mia: types.uint(441), + nyc: types.uint(500), + total: types.uint(941), + vote: types.bool(true), + }); + // user 2 + assertEquals(ccd007CityStacking.getStacker(mia.cityId, cycleId, user2Id).result.expectTuple(), { + claimable: types.uint(0), + stacked: types.uint(250), + }); + assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user2Id).result.expectSome().expectTuple(), { + mia: types.uint(220), + nyc: types.uint(250), + total: types.uint(470), + vote: types.bool(false), + }); + + // execute ccip-021 + const block = passProposal(chain, accounts, PROPOSALS.CCIP_021); + + // assert + block.receipts[2].result.expectOk().expectUint(3); + }, +}); diff --git a/utils/common.ts b/utils/common.ts index 098bbbee..4df0b80c 100644 --- a/utils/common.ts +++ b/utils/common.ts @@ -40,6 +40,7 @@ export const PROPOSALS = { CCIP_014: ADDRESS.concat(".ccip014-pox-3"), CCIP_014_V2: ADDRESS.concat(".ccip014-pox-3-v2"), CCIP_017: ADDRESS.concat(".ccip017-extend-sunset-period"), + CCIP_021: ADDRESS.concat(".ccip021-extend-sunset-period-2"), TEST_CCD001_DIRECT_EXECUTE_001: ADDRESS.concat(".test-ccd001-direct-execute-001"), TEST_CCD001_DIRECT_EXECUTE_002: ADDRESS.concat(".test-ccd001-direct-execute-002"), TEST_CCD001_DIRECT_EXECUTE_003: ADDRESS.concat(".test-ccd001-direct-execute-003"), From c75583da1c78ce942928b8282d8f7dc1008d128c Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 02:24:17 +0200 Subject: [PATCH 10/17] fix: remove unused contracts --- Clarinet.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Clarinet.toml b/Clarinet.toml index 7f9bd005..cd5ae9d8 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -92,8 +92,8 @@ path = "contracts/extensions/ccd002-treasury.clar" [contracts.ccd002-treasury-mia-mining-v2] path = "contracts/extensions/ccd002-treasury-v2.clar" -[contracts.ccd002-treasury-mia-mining-v3] -path = "contracts/extensions/ccd002-treasury-v3.clar" +# [contracts.ccd002-treasury-mia-mining-v3] +# path = "contracts/extensions/ccd002-treasury-v3.clar" [contracts.ccd002-treasury-mia-stacking] path = "contracts/extensions/ccd002-treasury.clar" @@ -104,8 +104,8 @@ path = "contracts/extensions/ccd002-treasury.clar" [contracts.ccd002-treasury-nyc-mining-v2] path = "contracts/extensions/ccd002-treasury-v2.clar" -[contracts.ccd002-treasury-nyc-mining-v3] -path = "contracts/extensions/ccd002-treasury-v3.clar" +# [contracts.ccd002-treasury-nyc-mining-v3] +# path = "contracts/extensions/ccd002-treasury-v3.clar" [contracts.ccd002-treasury-nyc-stacking] path = "contracts/extensions/ccd002-treasury.clar" From 100fbc421bedd8beb97cb6f356cab919b753c507 Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 02:26:07 +0200 Subject: [PATCH 11/17] fix: use correct import --- tests/proposals/ccip021-extend-sunset-period-2.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/proposals/ccip021-extend-sunset-period-2.test.ts b/tests/proposals/ccip021-extend-sunset-period-2.test.ts index b1835849..b3042a5f 100644 --- a/tests/proposals/ccip021-extend-sunset-period-2.test.ts +++ b/tests/proposals/ccip021-extend-sunset-period-2.test.ts @@ -2,7 +2,7 @@ import { Account, Clarinet, Chain, types, assertEquals } from "../../utils/deps. import { constructAndPassProposal, mia, nyc, passProposal, PROPOSALS } from "../../utils/common.ts"; import { CCD006CityMining } from "../../models/extensions/ccd006-citycoin-mining.model.ts"; import { CCD007CityStacking } from "../../models/extensions/ccd007-citycoin-stacking.model.ts"; -import { CCIP021ExtendDirectExecuteSunsetPeriod } from "../../models/proposals/ccip021-extend-direct-execute-sunset-period.model.ts"; +import { CCIP021ExtendDirectExecuteSunsetPeriod } from "../../models/proposals/ccip021-extend-direct-execute-sunset-period-2.model.ts"; const TARGET_SUNSET_BLOCK = 147828; From 526668d27b6568ff39a182a1b248752a8246d88b Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 02:29:15 +0200 Subject: [PATCH 12/17] feat: add hash hash from https://github.com/citycoins/governance/commit/3af7199173df90463a0ba65541b53fa74e0914db --- contracts/proposals/ccip021-extend-sunset-period-2.clar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/proposals/ccip021-extend-sunset-period-2.clar b/contracts/proposals/ccip021-extend-sunset-period-2.clar index 3c59e600..48e09498 100644 --- a/contracts/proposals/ccip021-extend-sunset-period-2.clar +++ b/contracts/proposals/ccip021-extend-sunset-period-2.clar @@ -20,7 +20,7 @@ (define-constant CCIP_021 { name: "Extend Direct Execute Sunset Period 2", link: "https://github.com/citycoins/governance/blob/feat/ccip-21/ccips/ccip-021/ccip-021-extend-direct-execute-sunset-period-2.md", - hash: "", ;; TODO + hash: "3af7199173df90463a0ba65541b53fa74e0914db", }) (define-constant SUNSET_BLOCK u173748) From 84f77f840f48de5d99a94b67658e958a9a61c290 Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 02:38:59 +0200 Subject: [PATCH 13/17] chore: add ccip 21 contract to Clarinet.toml --- Clarinet.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Clarinet.toml b/Clarinet.toml index cd5ae9d8..42fae9f4 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -164,6 +164,11 @@ path = "contracts/proposals/ccip017-extend-sunset-period.clar" clarity_version = 2 epoch = 2.4 +[contracts.ccip021-extend-sunset-period-2] +path = "contracts/proposals/ccip021-extend-sunset-period-2.clar" +clarity_version = 2 +epoch = 2.4 + # CITYCOINS PROTOCOL TRAITS [contracts.extension-trait] From 76ccaa9ffd994bc048e8c40760eac091585526ab Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 02:51:04 +0200 Subject: [PATCH 14/17] fix: use correct votes --- tests/proposals/ccip021-extend-sunset-period-2.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/proposals/ccip021-extend-sunset-period-2.test.ts b/tests/proposals/ccip021-extend-sunset-period-2.test.ts index b3042a5f..967e4e29 100644 --- a/tests/proposals/ccip021-extend-sunset-period-2.test.ts +++ b/tests/proposals/ccip021-extend-sunset-period-2.test.ts @@ -157,9 +157,9 @@ Clarinet.test({ ccip021ExtendDirectExecuteSunsetPeriod.isVoteActive().result.expectSome().expectBool(true); // check proposal info const proposalInfo = { - hash: types.ascii("7ddbf6152790a730faa059b564a8524abc3c70d3"), - link: types.ascii("https://github.com/citycoins/governance/blob/feat/add-ccip-021/ccips/ccip-021/ccip-021-extend-direct-execute-sunset-period.md"), - name: types.ascii("Extend Direct Execute Sunset Period"), + hash: types.ascii("3af7199173df90463a0ba65541b53fa74e0914db"), + link: types.ascii("https://github.com/citycoins/governance/blob/feat/ccip-21/ccips/ccip-021/ccip-021-extend-direct-execute-sunset-period-2.md"), + name: types.ascii("Extend Direct Execute Sunset Period 2"), }; assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getProposalInfo().result.expectSome().expectTuple(), proposalInfo); // check vote period is not set (end unknown) @@ -513,9 +513,9 @@ Clarinet.test({ // overall totals assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoteTotals().result.expectSome().expectTuple(), { - noTotal: types.uint(941), + noTotal: types.uint(945), noVotes: types.uint(1), - yesTotal: types.uint(470), + yesTotal: types.uint(472), yesVotes: types.uint(1), }); From 262ff14431603394cf40fad7ca4dad934b02a14e Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 02:54:15 +0200 Subject: [PATCH 15/17] fix: use correct sunset block --- tests/proposals/ccip021-extend-sunset-period-2.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/proposals/ccip021-extend-sunset-period-2.test.ts b/tests/proposals/ccip021-extend-sunset-period-2.test.ts index 967e4e29..63150f99 100644 --- a/tests/proposals/ccip021-extend-sunset-period-2.test.ts +++ b/tests/proposals/ccip021-extend-sunset-period-2.test.ts @@ -4,7 +4,7 @@ import { CCD006CityMining } from "../../models/extensions/ccd006-citycoin-mining import { CCD007CityStacking } from "../../models/extensions/ccd007-citycoin-stacking.model.ts"; import { CCIP021ExtendDirectExecuteSunsetPeriod } from "../../models/proposals/ccip021-extend-direct-execute-sunset-period-2.model.ts"; -const TARGET_SUNSET_BLOCK = 147828; +const TARGET_SUNSET_BLOCK = 173748; Clarinet.test({ name: "ccip-021: execute() fails with ERR_VOTE_FAILED if there are no votes", From 04a3dad44d0f3fc129444b9b8dadc8191dc493f9 Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 02:56:54 +0200 Subject: [PATCH 16/17] fix: use correct new mia votes --- .../proposals/ccip021-extend-sunset-period-2.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/proposals/ccip021-extend-sunset-period-2.test.ts b/tests/proposals/ccip021-extend-sunset-period-2.test.ts index 63150f99..b17e8cfc 100644 --- a/tests/proposals/ccip021-extend-sunset-period-2.test.ts +++ b/tests/proposals/ccip021-extend-sunset-period-2.test.ts @@ -526,9 +526,9 @@ Clarinet.test({ stacked: types.uint(500), }); assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user1Id).result.expectSome().expectTuple(), { - mia: types.uint(441), + mia: types.uint(445), nyc: types.uint(500), - total: types.uint(941), + total: types.uint(945), vote: types.bool(false), }); @@ -553,9 +553,9 @@ Clarinet.test({ // overall totals assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoteTotals().result.expectSome().expectTuple(), { - noTotal: types.uint(470), + noTotal: types.uint(472), noVotes: types.uint(1), - yesTotal: types.uint(941), + yesTotal: types.uint(945), yesVotes: types.uint(1), }); // user 1 @@ -564,9 +564,9 @@ Clarinet.test({ stacked: types.uint(500), }); assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user1Id).result.expectSome().expectTuple(), { - mia: types.uint(441), + mia: types.uint(445), nyc: types.uint(500), - total: types.uint(941), + total: types.uint(945), vote: types.bool(true), }); // user 2 From 01b13a2c951f4f60833535bbe27fd467c9bd2c68 Mon Sep 17 00:00:00 2001 From: friedger Date: Sat, 6 Apr 2024 03:03:57 +0200 Subject: [PATCH 17/17] fix: more new mia votes --- tests/proposals/ccip021-extend-sunset-period-2.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/proposals/ccip021-extend-sunset-period-2.test.ts b/tests/proposals/ccip021-extend-sunset-period-2.test.ts index b17e8cfc..712dc134 100644 --- a/tests/proposals/ccip021-extend-sunset-period-2.test.ts +++ b/tests/proposals/ccip021-extend-sunset-period-2.test.ts @@ -538,9 +538,9 @@ Clarinet.test({ stacked: types.uint(250), }); assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user2Id).result.expectSome().expectTuple(), { - mia: types.uint(220), + mia: types.uint(222), nyc: types.uint(250), - total: types.uint(470), + total: types.uint(472), vote: types.bool(true), }); @@ -575,9 +575,9 @@ Clarinet.test({ stacked: types.uint(250), }); assertEquals(ccip021ExtendDirectExecuteSunsetPeriod.getVoterInfo(user2Id).result.expectSome().expectTuple(), { - mia: types.uint(220), + mia: types.uint(222), nyc: types.uint(250), - total: types.uint(470), + total: types.uint(472), vote: types.bool(false), });