|
1 | | -# Vesting — linear time-based vesting (Aptos Move) |
2 | | - |
3 | | -**Модуль:** `Vesting::vesting` |
4 | | -**Статус:** компилируется; CI зелёный; есть юнит-тесты |
5 | | -**Юзкейсы:** эмиссия и разлок токенов по времени, тим-вестинг, инвест-расписания |
6 | | - |
7 | | ---- |
8 | | - |
9 | | -## Идея |
10 | | -Простейший вестинг с **линейным разлоком** между `[start, end]`. Денежной логики тут нет — это **учётная модель**: всё считается во внутренних числовых балансах (u64), чтобы было легко интегрировать в реальный токен позже. |
11 | | - |
12 | | -Формула: |
13 | | -vested(now) = |
14 | | -0, if now <= start |
15 | | -total, if now >= end |
16 | | -floor(total * (now-start) / (end-start)), otherwise |
17 | | - |
18 | | -claimable(now) = vested(now) - already_claimed |
19 | | - |
20 | | -## Данные |
21 | | -- `struct Schedule { ben: address, total: u64, start: u64, end: u64, claimed: u64, active: bool }` |
22 | | -- `vector<Schedule>` — хранится под адресом админа (инициатора) |
23 | | -- Внутренние «балансы» по адресам для демо-учёта: `view_balance(admin_addr, owner) -> u64` |
24 | | - |
25 | | ---- |
26 | | - |
27 | | -## Публичный API (entries) |
28 | | -- `entry init(admin: &signer)` |
29 | | - - Однократная инициализация хранилища под `address_of(admin)`. |
30 | | - |
31 | | -- `entry create(admin: &signer, ben: address, total: u64, start: u64, end: u64)` |
32 | | - - Добавляет расписание. Первый созданный идёт под `id = 0`, далее по инкременту. |
33 | | - - Валидация времени: `start < end`. |
34 | | - |
35 | | -- `entry claim(ben: &signer, admin_addr: address, id: u64, now: u64)` |
36 | | - - Клеймит дельту `claimable(now)` для расписания `id` и увеличивает внутренний баланс бенефициара. |
37 | | - - Проверяет, что `ben` == назначенный бенефициар. |
| 1 | +# Vesting — linear token vesting (Aptos Move) |
| 2 | + |
| 3 | +Simple, gas-friendly linear vesting with on-chain accounting of claimed amounts. |
| 4 | + |
| 5 | +**Module:** `Vesting::vesting` |
| 6 | +**Status:** compiles; CI green |
| 7 | +**Use cases:** team/seed vesting, grants, OTC unlocks |
| 8 | + |
| 9 | +## Data model |
| 10 | +struct Schedule { |
| 11 | +beneficiary: address, |
| 12 | +total: u64, |
| 13 | +start: u64, |
| 14 | +end: u64, |
| 15 | +claimed: u64, |
| 16 | +} |
| 17 | +struct Balance { owner: address, amount: u64 } |
| 18 | +struct State { admin, schedules, balances, initialized } |
| 19 | + |
| 20 | +`vested_at(now)` = linear piecewise (0 → total). |
| 21 | +`claimable_at(now)` = `vested_at - claimed` (floor math, no remainders). |
| 22 | + |
| 23 | +## Public API |
| 24 | +- `entry init(admin)` — one-time init under `address_of(admin)`. |
| 25 | +- `entry create(admin, beneficiary, total, start, end)` — append schedule. |
| 26 | +- `entry claim(ben, admin_addr, sched_id, now)` — claim to internal balance. |
| 27 | +- `view_balance(admin_addr, owner)` — read internal balance (for demos/tests). |
| 28 | + |
| 29 | +Errors: |
| 30 | +`E_ALREADY_INIT=1, E_NOT_INIT=2, E_BAD_TIME=3, E_NOT_BENEFICIARY=4, E_BAD_ID=5, E_NOTHING_TO_CLAIM=6`. |
| 31 | + |
| 32 | +## Devnet |
| 33 | +- **Package address:** `0x225b...5278` |
| 34 | +- **Publish tx:** `0xedfb5d7e52...b62c2` |
| 35 | +- **Demo tx (entry_demo):** `0xc74e87de5f...b4f8` |
| 36 | + |
| 37 | +## Quick start |
| 38 | +```bash |
| 39 | +# publish (from your Aptos profile) |
| 40 | +aptos move publish --assume-yes |
38 | 41 |
|
39 | | -- `fun view_balance(admin_addr: address, owner: address): u64` |
40 | | - - Возвращает внутренний «накопленный» баланс адреса в рамках хранилища `admin_addr`. |
| 42 | +# init |
| 43 | +aptos move run --function-id <PKG>::vesting::init |
41 | 44 |
|
42 | | -> ⚠️ Денежные переводов нет — это осознанно. В реальном проекте сюда можно подвязать `Coin`/трансфер NFT или экспортировать учтённую величину в другой модуль. |
| 45 | +# create schedule (total=100, [0..100]) for <PKG> |
| 46 | +aptos move run --function-id <PKG>::vesting::create \ |
| 47 | + --args address:<PKG> u64:100 u64:0 u64:100 |
43 | 48 |
|
44 | | ---- |
| 49 | +# claims at t=30,80,200 |
| 50 | +aptos move run --function-id <PKG>::vesting::claim --args address:<PKG> u64:0 u64:30 |
| 51 | +aptos move run --function-id <PKG>::vesting::claim --args address:<PKG> u64:0 u64:80 |
| 52 | +aptos move run --function-id <PKG>::vesting::claim --args address:<PKG> u64:0 u64:200 |
45 | 53 |
|
46 | | -## Errors (major) |
47 | | -- creation with an invalid time (e.g., `start >= end`) |
48 | | -- labels non-beneficiary |
49 | | -- accessing a non-existent schedule/uninitialized storage |
| 54 | +Inspect via REST |
| 55 | +REST="https://fullnode.devnet.aptoslabs.com" |
| 56 | +curl -s "$REST/v1/accounts/<PKG>/resource/<PKG>::vesting::State" | jq . |
| 57 | +curl -s "$REST/v1/accounts/<PKG>/resource/<PKG>::vesting::State" \ |
| 58 | + | jq -r '.data.schedules[] | {beneficiary,total,start,end,claimed}' |
50 | 59 |
|
51 | | ---- |
| 60 | +Contacts |
52 | 61 |
|
53 | | -## Unit tests (`tests/vesting_tests.move`) |
54 | | -- **Линейный разлок**: последовательные клеймы увеличивают баланс до `total`. |
55 | | -- **Bad time**: создание с неправильным временем падает. |
56 | | -- **Wrong beneficiary**: клейм не тем адресом падает. |
| 62 | +Discord: @rootmhz_ |
57 | 63 |
|
58 | | ---- |
| 64 | +Telegram: @Nikolai_Rootmhz |
59 | 65 |
|
60 | | -## Quick start (локально) |
| 66 | +Email: 007rootmhz@gmail.com |
61 | 67 |
|
62 | | -```bash |
63 | | -cd vesting |
64 | | -aptos move compile |
65 | | -aptos move test --filter . |
66 | | - |
67 | | -## Contacts |
68 | | -- Discord: [@rootmhz_](https://discord.com/users/1047911417396875264) |
69 | | -- Telegram: [@Nikolai_Rootmhz](https://t.me/Nikolai_Rootmhz) |
70 | | -- Email: [007rootmhz@gmail.com](mailto:007rootmhz@gmail.com) |
71 | | -- Hire me: [issue form](https://github.com/root28root/move-projects/issues/new?template=hire-me.yml) |
| 68 | +Hire me: issue form |
0 commit comments