Skip to content

Commit 8774a0f

Browse files
committed
feat: implement logic for monthly vesting math
1 parent 44c0752 commit 8774a0f

File tree

5 files changed

+170
-2
lines changed

5 files changed

+170
-2
lines changed

contrib/core-contract-tests/Clarigen.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ clarinet = "./Clarinet.toml"
55
output = "tests/clarigen-types.ts"
66

77
# `types.after` - script to run after TypeScript types are generated.
8-
after = "npx -y prettier -w ./tests/clarigen-types.ts"
8+
after = "npx prettier -w ./tests/clarigen-types.ts"
99

1010
watch_folders = [
1111
"../../stackslib/src/chainstate/stacks/boot"

contrib/core-contract-tests/deployments/default.simnet-plan.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,43 @@ genesis:
77
- name: deployer
88
address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM
99
balance: "100000000000000"
10+
sbtc-balance: "1000000000"
1011
- name: wallet_1
1112
address: ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5
1213
balance: "100000000000000"
14+
sbtc-balance: "1000000000"
1315
- name: wallet_2
1416
address: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG
1517
balance: "100000000000000"
18+
sbtc-balance: "1000000000"
1619
- name: wallet_3
1720
address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC
1821
balance: "100000000000000"
22+
sbtc-balance: "1000000000"
1923
- name: wallet_4
2024
address: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND
2125
balance: "100000000000000"
26+
sbtc-balance: "1000000000"
2227
- name: wallet_5
2328
address: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB
2429
balance: "100000000000000"
30+
sbtc-balance: "1000000000"
2531
- name: wallet_6
2632
address: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0
2733
balance: "100000000000000"
34+
sbtc-balance: "1000000000"
2835
- name: wallet_7
2936
address: ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ
3037
balance: "100000000000000"
38+
sbtc-balance: "1000000000"
3139
- name: wallet_8
3240
address: ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP
3341
balance: "100000000000000"
42+
sbtc-balance: "1000000000"
3443
- name: wallet_9
3544
address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6
3645
balance: "100000000000000"
46+
sbtc-balance: "1000000000"
3747
contracts:
3848
- costs
3949
- pox

contrib/core-contract-tests/tests/clarigen-types.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4577,12 +4577,39 @@ export const contracts = {
45774577
[newRecipient: TypedAbiArg<string, "newRecipient">],
45784578
Response<boolean, bigint>
45794579
>,
4580+
calcVestedAmount: {
4581+
name: "calc-vested-amount",
4582+
access: "read_only",
4583+
args: [{ name: "burn-height", type: "uint128" }],
4584+
outputs: { type: { response: { ok: "uint128", error: "none" } } },
4585+
} as TypedAbiFunction<
4586+
[burnHeight: TypedAbiArg<number | bigint, "burnHeight">],
4587+
Response<bigint, null>
4588+
>,
4589+
getDeployBlockHeight: {
4590+
name: "get-deploy-block-height",
4591+
access: "read_only",
4592+
args: [],
4593+
outputs: { type: "uint128" },
4594+
} as TypedAbiFunction<[], bigint>,
4595+
getLastVestingClaimBlock: {
4596+
name: "get-last-vesting-claim-block",
4597+
access: "read_only",
4598+
args: [],
4599+
outputs: { type: { optional: "uint128" } },
4600+
} as TypedAbiFunction<[], bigint | null>,
45804601
getRecipient: {
45814602
name: "get-recipient",
45824603
access: "read_only",
45834604
args: [],
45844605
outputs: { type: "principal" },
45854606
} as TypedAbiFunction<[], string>,
4607+
getVestedClaimedAmount: {
4608+
name: "get-vested-claimed-amount",
4609+
access: "read_only",
4610+
args: [],
4611+
outputs: { type: "uint128" },
4612+
} as TypedAbiFunction<[], bigint>,
45864613
},
45874614
maps: {},
45884615
variables: {
@@ -4591,15 +4618,65 @@ export const contracts = {
45914618
type: "uint128",
45924619
access: "constant",
45934620
} as TypedAbiVariable<bigint>,
4621+
INITIAL_MINT_AMOUNT: {
4622+
name: "INITIAL_MINT_AMOUNT",
4623+
type: "uint128",
4624+
access: "constant",
4625+
} as TypedAbiVariable<bigint>,
4626+
INITIAL_MINT_IMMEDIATE_AMOUNT: {
4627+
name: "INITIAL_MINT_IMMEDIATE_AMOUNT",
4628+
type: "uint128",
4629+
access: "constant",
4630+
} as TypedAbiVariable<bigint>,
4631+
INITIAL_MINT_VESTING_AMOUNT: {
4632+
name: "INITIAL_MINT_VESTING_AMOUNT",
4633+
type: "uint128",
4634+
access: "constant",
4635+
} as TypedAbiVariable<bigint>,
4636+
INITIAL_MINT_VESTING_ITERATIONS: {
4637+
name: "INITIAL_MINT_VESTING_ITERATIONS",
4638+
type: "uint128",
4639+
access: "constant",
4640+
} as TypedAbiVariable<bigint>,
4641+
INITIAL_MINT_VESTING_ITERATION_BLOCKS: {
4642+
name: "INITIAL_MINT_VESTING_ITERATION_BLOCKS",
4643+
type: "uint128",
4644+
access: "constant",
4645+
} as TypedAbiVariable<bigint>,
4646+
deployBlockHeight: {
4647+
name: "deploy-block-height",
4648+
type: "uint128",
4649+
access: "variable",
4650+
} as TypedAbiVariable<bigint>,
4651+
lastVestingClaimBlock: {
4652+
name: "last-vesting-claim-block",
4653+
type: {
4654+
optional: "uint128",
4655+
},
4656+
access: "variable",
4657+
} as TypedAbiVariable<bigint | null>,
45944658
recipient: {
45954659
name: "recipient",
45964660
type: "principal",
45974661
access: "variable",
45984662
} as TypedAbiVariable<string>,
4663+
vestedClaimedAmount: {
4664+
name: "vested-claimed-amount",
4665+
type: "uint128",
4666+
access: "variable",
4667+
} as TypedAbiVariable<bigint>,
45994668
},
46004669
constants: {
46014670
ERR_NOT_ALLOWED: 101n,
4671+
INITIAL_MINT_AMOUNT: 200_000_000_000_000n,
4672+
INITIAL_MINT_IMMEDIATE_AMOUNT: 100_000_000_000_000n,
4673+
INITIAL_MINT_VESTING_AMOUNT: 100_000_000_000_000n,
4674+
INITIAL_MINT_VESTING_ITERATIONS: 24n,
4675+
INITIAL_MINT_VESTING_ITERATION_BLOCKS: 4_383n,
4676+
deployBlockHeight: 3n,
4677+
lastVestingClaimBlock: null,
46024678
recipient: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM",
4679+
vestedClaimedAmount: 0n,
46034680
},
46044681
non_fungible_tokens: [],
46054682
fungible_tokens: [],

contrib/core-contract-tests/tests/sip-031/sip-031.test.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { project, accounts } from '../clarigen-types'; // where your [types.output] was specified
22
import { CoreNodeEventType, projectFactory } from '@clarigen/core';
3-
import { filterEvents, rov, txErr, txOk } from '@clarigen/test';
3+
import { filterEvents, rov, rovOk, txErr, txOk } from '@clarigen/test';
44
import { test, expect } from 'vitest';
55

66
const contracts = projectFactory(project, 'simnet');
@@ -72,4 +72,53 @@ test('updated recipient can claim', () => {
7272
expect(event.data.amount).toBe(`${100000000n}`);
7373
expect(event.data.recipient).toBe(accounts.wallet_1.address);
7474
expect(event.data.sender).toBe(contract.identifier);
75+
});
76+
77+
test('calculating vested amounts at a block height', () => {
78+
const deployBlockHeight = rov(contract.getDeployBlockHeight());
79+
80+
const initialMintAmount = 200_000_000n * 1000000n; // 200,000,000 STX
81+
const immediateAmount = 100_000_000n * 1000000n; // 100,000,000 STX
82+
const vestingAmount = initialMintAmount - immediateAmount;
83+
84+
function expectedAmount(burnHeight: bigint) {
85+
const diff = burnHeight - deployBlockHeight;
86+
const iterations = diff / 4383n;
87+
const stxPerIteration = (initialMintAmount - immediateAmount) / 24n;
88+
const vestingAmount = stxPerIteration * iterations;
89+
return immediateAmount + vestingAmount;
90+
}
91+
92+
expect(rovOk(contract.calcVestedAmount(deployBlockHeight))).toBe(immediateAmount);
93+
94+
function expectAmount(month: bigint) {
95+
const burnHeight = deployBlockHeight + month * 4383n;
96+
expect(rovOk(contract.calcVestedAmount(burnHeight))).toBe(expectedAmount(burnHeight));
97+
}
98+
expectAmount(1n);
99+
expectAmount(2n);
100+
expectAmount(3n);
101+
expectAmount(4n);
102+
expectAmount(5n);
103+
expectAmount(6n);
104+
expectAmount(7n);
105+
expectAmount(8n);
106+
expectAmount(9n);
107+
expectAmount(10n);
108+
expectAmount(11n);
109+
expectAmount(12n);
110+
expectAmount(13n);
111+
expectAmount(14n);
112+
expectAmount(15n);
113+
expectAmount(16n);
114+
expectAmount(17n);
115+
expectAmount(18n);
116+
expectAmount(19n);
117+
expectAmount(20n);
118+
expectAmount(21n);
119+
expectAmount(22n);
120+
expectAmount(23n);
121+
expectAmount(24n);
122+
123+
expect(rovOk(contract.calcVestedAmount(deployBlockHeight + 25n * 4383n))).toBe(initialMintAmount);
75124
});

stackslib/src/chainstate/stacks/boot/sip-031.clar

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
(define-constant ERR_NOT_ALLOWED u101)
22

3+
(define-constant INITIAL_MINT_AMOUNT u200000000000000) ;; 200,000,000 STX
4+
(define-constant INITIAL_MINT_VESTING_ITERATIONS u24) ;; 24 months
5+
(define-constant INITIAL_MINT_VESTING_ITERATION_BLOCKS u4383) ;; ~1 month of BTC blocks
6+
(define-constant INITIAL_MINT_IMMEDIATE_AMOUNT u100000000000000) ;; 100,000,000 STX
7+
(define-constant INITIAL_MINT_VESTING_AMOUNT (- INITIAL_MINT_AMOUNT INITIAL_MINT_IMMEDIATE_AMOUNT))
8+
39
(define-data-var recipient principal tx-sender)
410

11+
(define-data-var deploy-block-height uint burn-block-height)
12+
13+
(define-data-var last-vesting-claim-block (optional uint) none)
14+
15+
(define-data-var vested-claimed-amount uint u0)
16+
517
(define-read-only (get-recipient) (var-get recipient))
618

19+
(define-read-only (get-deploy-block-height) (var-get deploy-block-height))
20+
21+
(define-read-only (get-last-vesting-claim-block) (var-get last-vesting-claim-block))
22+
23+
(define-read-only (get-vested-claimed-amount) (var-get vested-claimed-amount))
24+
725
;; Update the recipient of the funds.
826
;;
927
;; May only be called by the `recipient`.
@@ -34,3 +52,17 @@
3452
(define-private (validate-caller)
3553
(ok (asserts! (is-eq contract-caller (var-get recipient)) (err ERR_NOT_ALLOWED)))
3654
)
55+
56+
(define-read-only (calc-vested-amount (burn-height uint))
57+
(let
58+
(
59+
(diff (- burn-height (var-get deploy-block-height)))
60+
(iterations (/ diff INITIAL_MINT_VESTING_ITERATION_BLOCKS))
61+
(stx-per-iteration (/ INITIAL_MINT_VESTING_AMOUNT INITIAL_MINT_VESTING_ITERATIONS))
62+
(vesting-multiple (* stx-per-iteration iterations))
63+
(vesting-amount (if (> vesting-multiple INITIAL_MINT_VESTING_AMOUNT) INITIAL_MINT_VESTING_AMOUNT vesting-multiple))
64+
(total-amount (+ INITIAL_MINT_IMMEDIATE_AMOUNT vesting-amount))
65+
)
66+
(ok (- total-amount (var-get vested-claimed-amount)))
67+
)
68+
)

0 commit comments

Comments
 (0)