Skip to content

Commit 496cdcc

Browse files
authored
Create vesting.move
1 parent ead927a commit 496cdcc

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

vesting/sources/vesting.move

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
module Vesting::vesting {
2+
use std::signer;
3+
use std::vector;
4+
5+
// --- Ошибки ---
6+
const E_ALREADY_INIT: u64 = 1;
7+
const E_NOT_INIT: u64 = 2;
8+
const E_BAD_TIME: u64 = 3;
9+
const E_NOT_BENEFICIARY: u64 = 4;
10+
const E_BAD_ID: u64 = 5;
11+
const E_NOTHING_TO_CLAIM: u64 = 6;
12+
13+
// --- Модель данных ---
14+
struct Schedule has store {
15+
beneficiary: address,
16+
total: u64,
17+
start: u64,
18+
end: u64,
19+
claimed: u64,
20+
}
21+
22+
struct Balance has drop, store { owner: address, amount: u64 }
23+
24+
struct State has key {
25+
admin: address,
26+
schedules: vector<Schedule>,
27+
balances: vector<Balance>,
28+
initialized: bool,
29+
}
30+
31+
// --- Внутренние утилиты ---
32+
fun is_inited(addr: address): bool { exists<State>(addr) }
33+
34+
fun assert_inited(addr: address) {
35+
assert!(exists<State>(addr), E_NOT_INIT);
36+
}
37+
38+
fun find_balance_index(bals: &vector<Balance>, owner: address): (bool, u64) {
39+
let i = 0u64;
40+
let n = vector::length(bals);
41+
while (i < n) {
42+
let b = vector::borrow(bals, i);
43+
if (b.owner == owner) return (true, i);
44+
i = i + 1;
45+
};
46+
(false, 0)
47+
}
48+
49+
fun credit_balance(bals: &mut vector<Balance>, owner: address, amount: u64) {
50+
let (found, idx) = find_balance_index(bals, owner);
51+
if (found) {
52+
let b = vector::borrow_mut(bals, idx);
53+
b.amount = b.amount + amount;
54+
} else {
55+
vector::push_back(bals, Balance { owner, amount });
56+
}
57+
}
58+
59+
fun vested_at(s: &Schedule, now: u64): u64 {
60+
if (now <= s.start) {
61+
0
62+
} else if (now >= s.end) {
63+
s.total
64+
} else {
65+
// линейная разблокировка
66+
let elapsed = now - s.start;
67+
let dur = s.end - s.start;
68+
(s.total * elapsed) / dur
69+
}
70+
}
71+
72+
fun claimable_at(s: &Schedule, now: u64): u64 {
73+
let v = vested_at(s, now);
74+
if (v <= s.claimed) 0 else v - s.claimed
75+
}
76+
77+
// --- Публичное API ---
78+
79+
/// Разворачивает состояние под адресом администратора
80+
public entry fun init(admin: &signer) {
81+
let a = signer::address_of(admin);
82+
assert!(!is_inited(a), E_ALREADY_INIT);
83+
move_to(admin, State {
84+
admin: a,
85+
schedules: vector::empty<Schedule>(),
86+
balances: vector::empty<Balance>(),
87+
initialized: true,
88+
});
89+
}
90+
91+
/// Создаёт новую вестинг-выплату.
92+
/// Требования: end > start, total > 0, init уже вызван.
93+
public entry fun create(admin: &signer, beneficiary: address, total: u64, start: u64, end: u64) acquires State {
94+
let a = signer::address_of(admin);
95+
assert_inited(a);
96+
assert!(end > start && total > 0, E_BAD_TIME);
97+
98+
let st = borrow_global_mut<State>(a);
99+
vector::push_back(&mut st.schedules, Schedule {
100+
beneficiary,
101+
total,
102+
start,
103+
end,
104+
claimed: 0,
105+
});
106+
}
107+
108+
/// Бенефициар клеймит доступную на момент `now` сумму.
109+
public entry fun claim(ben: &signer, admin_addr: address, sched_id: u64, now: u64) acquires State {
110+
assert_inited(admin_addr);
111+
let st = borrow_global_mut<State>(admin_addr);
112+
113+
let n = vector::length(&st.schedules);
114+
assert!(sched_id < n, E_BAD_ID);
115+
116+
let s = vector::borrow_mut(&mut st.schedules, sched_id);
117+
let caller = signer::address_of(ben);
118+
assert!(caller == s.beneficiary, E_NOT_BENEFICIARY);
119+
120+
let amt = claimable_at(s, now);
121+
assert!(amt > 0, E_NOTHING_TO_CLAIM);
122+
123+
s.claimed = s.claimed + amt;
124+
credit_balance(&mut st.balances, caller, amt);
125+
}
126+
127+
/// Просмотр внутреннего баланса бенефициара (накопленные клеймы)
128+
public fun view_balance(admin_addr: address, owner: address): u64 acquires State {
129+
let st = borrow_global<State>(admin_addr);
130+
let (found, idx) = find_balance_index(&st.balances, owner);
131+
if (!found) { 0 } else { vector::borrow(&st.balances, idx).amount }
132+
}
133+
134+
/// Хелпер для тест-демо: создаёт 1 расписание и делает 3 клейма
135+
public entry fun entry_demo(admin: &signer) acquires State {
136+
use std::signer;
137+
let a = signer::address_of(admin);
138+
139+
init(admin);
140+
create(admin, a, 100, /*start*/ 0, /*end*/ 100);
141+
142+
// клейм на t=30 (30%), затем t=80 (+50), затем t=200 (+20 = всё)
143+
claim(admin, a, 0, 30);
144+
claim(admin, a, 0, 80);
145+
claim(admin, a, 0, 200);
146+
147+
let bal = view_balance(a, a);
148+
assert!(bal == 100, 0);
149+
}
150+
}

0 commit comments

Comments
 (0)