Skip to content

Commit 0177d82

Browse files
authored
Create minimarket.move
1 parent 4c9319c commit 0177d82

1 file changed

Lines changed: 183 additions & 0 deletions

File tree

minimarket/sources/minimarket.move

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
module @minimarket::minimarket {
2+
use std::signer;
3+
use std::vector;
4+
5+
/// Ошибки
6+
const E_ALREADY_INITIALIZED: u64 = 1;
7+
const E_NOT_INITIALIZED: u64 = 2;
8+
const E_BAD_FEE_BPS: u64 = 3;
9+
const E_ONLY_ADMIN: u64 = 4;
10+
const E_BAD_ITEM_ID: u64 = 5;
11+
const E_ALREADY_LISTED: u64 = 6;
12+
const E_NOT_LISTED: u64 = 7;
13+
const E_PRICE_MISMATCH: u64 = 8;
14+
15+
/// Простая запись о лоте
16+
struct Item has drop, store {
17+
seller: address,
18+
price: u64,
19+
listed: bool,
20+
}
21+
22+
/// Баланс внутри модуля (псевдо-коины для демо)
23+
struct Balance has drop, store {
24+
owner: address,
25+
amount: u64,
26+
}
27+
28+
/// Синглтон маркетплейса, хранится под адресом администратора (инициатора init).
29+
struct Market has key {
30+
admin: address,
31+
fee_bps: u64, // 0..=10_000
32+
fees_accumulated: u64, // Накопленные комиссии
33+
items: vector<Item>, // По индексу = item_id
34+
balances: vector<Balance>, // Простая "карта": поиск по адресу линейный
35+
initialized: bool,
36+
}
37+
38+
/// === helpers ===
39+
40+
fun assert_initialized(market_addr: address) {
41+
assert!(exists<Market>(market_addr), E_NOT_INITIALIZED);
42+
}
43+
44+
fun only_admin(market: &Market, caller: address) {
45+
assert!(market.admin == caller, E_ONLY_ADMIN);
46+
}
47+
48+
fun get_mut(market_addr: address): &mut Market acquires Market {
49+
let m = borrow_global_mut<Market>(market_addr);
50+
m
51+
}
52+
53+
fun get(market_addr: address): &Market acquires Market {
54+
let m = borrow_global<Market>(market_addr);
55+
m
56+
}
57+
58+
/// Найти индекс баланса по адресу, либо вернуть опционально None.
59+
fun find_balance_index(balances: &vector<Balance>, owner: address): (bool, u64) {
60+
let i = 0u64;
61+
let len = vector::length(balances);
62+
while (i < len) {
63+
let b = vector::borrow(balances, i);
64+
if (b.owner == owner) {
65+
return (true, i)
66+
};
67+
i = i + 1;
68+
};
69+
(false, 0)
70+
}
71+
72+
/// Кредитовать внутренний баланс.
73+
fun credit_balance(balances: &mut vector<Balance>, owner: address, amount: u64) {
74+
let (found, idx) = find_balance_index(balances, owner);
75+
if (found) {
76+
let b = vector::borrow_mut(balances, idx);
77+
b.amount = b.amount + amount;
78+
} else {
79+
vector::push_back(balances, Balance { owner, amount });
80+
}
81+
}
82+
83+
/// === entry ===
84+
85+
/// Инициализация маркетплейса под адресом администратора (signer).
86+
public entry fun init(admin: &signer, fee_bps: u64) acquires Market {
87+
let admin_addr = signer::address_of(admin);
88+
assert!(!exists<Market>(admin_addr), E_ALREADY_INITIALIZED);
89+
assert!(fee_bps <= 10000, E_BAD_FEE_BPS);
90+
91+
move_to(admin, Market {
92+
admin: admin_addr,
93+
fee_bps,
94+
fees_accumulated: 0,
95+
items: vector::empty<Item>(),
96+
balances: vector::empty<Balance>(),
97+
initialized: true
98+
});
99+
}
100+
101+
/// Размещение лота. Если item_id == len, пушим новый; если меньше len — ячейка должна быть свободна.
102+
public entry fun list(seller: &signer, market_addr: address, item_id: u64, price: u64) acquires Market {
103+
assert_initialized(market_addr);
104+
let seller_addr = signer::address_of(seller);
105+
let m = get_mut(market_addr);
106+
107+
let len = vector::length(&m.items);
108+
if (item_id == len) {
109+
vector::push_back(&mut m.items, Item { seller: seller_addr, price, listed: true });
110+
} else {
111+
assert!(item_id < len, E_BAD_ITEM_ID);
112+
let it = vector::borrow_mut(&mut m.items, item_id);
113+
// Разрешаем перезалистинг только если ячейка свободна
114+
assert!(!it.listed, E_ALREADY_LISTED);
115+
it.seller = seller_addr;
116+
it.price = price;
117+
it.listed = true;
118+
}
119+
}
120+
121+
/// Покупка лота. Внутренняя «оплата»: увеличиваем баланс продавца, удерживаем комиссию в пуле.
122+
public entry fun buy(buyer: &signer, market_addr: address, item_id: u64, pay_amount: u64) acquires Market {
123+
assert_initialized(market_addr);
124+
let m = get_mut(market_addr);
125+
let len = vector::length(&m.items);
126+
assert!(item_id < len, E_BAD_ITEM_ID);
127+
128+
let it = vector::borrow_mut(&mut m.items, item_id);
129+
assert!(it.listed, E_NOT_LISTED);
130+
assert!(pay_amount == it.price, E_PRICE_MISMATCH);
131+
132+
// комиссия: (price * fee_bps) / 10000
133+
let fee = (it.price * m.fee_bps) / 10000;
134+
let seller_amount = it.price - fee;
135+
136+
// начисляем продавцу, накапливаем комиссию
137+
credit_balance(&mut m.balances, it.seller, seller_amount);
138+
m.fees_accumulated = m.fees_accumulated + fee;
139+
140+
// снимаем лот
141+
it.listed = false;
142+
}
143+
144+
/// Вывод комиссий админом во внутренний баланс админа
145+
public entry fun withdraw_fees(admin: &signer, market_addr: address) acquires Market {
146+
assert_initialized(market_addr);
147+
let caller = signer::address_of(admin);
148+
let m = get_mut(market_addr);
149+
only_admin(m, caller);
150+
151+
let amount = m.fees_accumulated;
152+
if (amount > 0) {
153+
m.fees_accumulated = 0;
154+
credit_balance(&mut m.balances, caller, amount);
155+
}
156+
}
157+
158+
/// Просмотр баланса (для тестов/демо)
159+
public fun view_balance(market_addr: address, owner: address): u64 acquires Market {
160+
let m = get(market_addr);
161+
let (found, idx) = find_balance_index(&m.balances, owner);
162+
if (!found) { 0 } else { vector::borrow(&m.balances, idx).amount }
163+
}
164+
165+
/// Демонстрационная процедура (сценарий happy-path)
166+
public entry fun entry_demo(admin: &signer) acquires Market {
167+
// 1) init с 250 bps = 2.5%
168+
init(admin, 250);
169+
let admin_addr = signer::address_of(admin);
170+
171+
// 2) Продавец = админ для простоты. Листинг item_id=0 по цене 10_000
172+
list(admin, admin_addr, 0, 10_000);
173+
174+
// 3) Покупка тем же аккаунтом для демо (в реале будет другой signer)
175+
buy(admin, admin_addr, 0, 10_000);
176+
177+
// 4) Вывод комиссии админом
178+
withdraw_fees(admin, admin_addr);
179+
// Теперь у админа во внутреннем балансе 10_000 (от продажи) + 250 (комиссия) не будет,
180+
// комиссия не прибавляется к продавцу; продавцу ушло 9_750, комиссия 250 ушла в пул и затем на баланс админа.
181+
// Проверки оставляем в юнит-тестах.
182+
}
183+
}

0 commit comments

Comments
 (0)