forked from MyFanss/MyFans
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
243 lines (201 loc) · 6.48 KB
/
Copy pathlib.rs
File metadata and controls
243 lines (201 loc) · 6.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#![no_std]
use soroban_sdk::{
contract, contracterror, contractimpl, contracttype, panic_with_error, token, Address, Env,
Symbol,
};
#[contracttype]
pub enum DataKey {
Admin,
Token,
Balance(Address),
AuthorizedDepositor(Address),
}
/// Per-contract error codes for the **creator-earnings** contract.
///
/// These discriminants are stable and form part of the public client API.
/// Do **not** renumber existing variants; add new ones at the end.
///
/// | Code | Variant |
/// |------|---------|
/// | 1 | `NotInitialized` |
/// | 2 | `NotAuthorized` |
/// | 3 | `InsufficientBalance` |
/// | 4 | `AlreadyInitialized` |
/// | 5 | `InvalidAmount` |
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
/// Code 1 – contract was never initialized.
NotInitialized = 1,
/// Code 2 – caller is not the admin or an authorized depositor.
NotAuthorized = 2,
/// Code 3 – creator balance is less than the requested withdrawal amount.
InsufficientBalance = 3,
/// Code 4 – contract was already initialized.
AlreadyInitialized = 4,
/// Code 5 – deposit or withdrawal amount must be strictly positive.
InvalidAmount = 5,
}
/// -------- Events --------
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InitializedEvent {
pub admin: Address,
pub token: Address,
}
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AuthorizedAddedEvent {
pub depositor: Address,
}
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DepositEvent {
pub from: Address,
pub creator: Address,
pub amount: i128,
pub token: Address,
}
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WithdrawEvent {
pub creator: Address,
pub amount: i128,
pub token: Address,
}
/// Avoid magic strings
const INITIALIZED_EVENT: &str = "initialized";
const AUTHORIZED_ADDED_EVENT: &str = "authorized_added";
const DEPOSIT_EVENT: &str = "deposit";
const WITHDRAW_EVENT: &str = "withdraw";
#[contract]
pub struct CreatorEarnings;
#[contractimpl]
impl CreatorEarnings {
/// Initialize contract with admin and accepted token
pub fn initialize(env: Env, admin: Address, token_address: Address) {
if env.storage().instance().has(&DataKey::Admin) {
panic_with_error!(&env, Error::AlreadyInitialized);
}
admin.require_auth();
env.storage().instance().set(&DataKey::Admin, &admin);
env.storage()
.instance()
.set(&DataKey::Token, &token_address);
env.events().publish(
(Symbol::new(&env, INITIALIZED_EVENT),),
InitializedEvent {
admin,
token: token_address,
},
);
}
/// Add authorized depositor contract (admin only)
pub fn add_authorized(env: Env, contract: Address) {
let admin: Address = Self::get_admin(&env);
admin.require_auth();
env.storage()
.instance()
.set(&DataKey::AuthorizedDepositor(contract.clone()), &true);
env.events().publish(
(Symbol::new(&env, AUTHORIZED_ADDED_EVENT),),
AuthorizedAddedEvent {
depositor: contract,
},
);
}
/// Deposit earnings for creator
/// Callable by authorized contracts or admin
pub fn deposit(env: Env, from: Address, creator: Address, amount: i128) {
if amount <= 0 {
panic_with_error!(&env, Error::InvalidAmount);
}
from.require_auth();
Self::require_authorized(&env, &from);
let token_address: Address = Self::get_token(&env);
let token_client = token::Client::new(&env, &token_address);
token_client.transfer(&from, &env.current_contract_address(), &amount);
let balance = Self::balance(env.clone(), creator.clone());
let new_balance = balance + amount;
env.storage()
.instance()
.set(&DataKey::Balance(creator.clone()), &new_balance);
env.events().publish(
(Symbol::new(&env, DEPOSIT_EVENT),),
DepositEvent {
from,
creator,
amount,
token: token_address,
},
);
}
/// Get creator balance
pub fn balance(env: Env, creator: Address) -> i128 {
env.storage()
.instance()
.get(&DataKey::Balance(creator))
.unwrap_or(0)
}
/// Withdraw earnings
pub fn withdraw(env: Env, creator: Address, amount: i128) {
if amount <= 0 {
panic_with_error!(&env, Error::InvalidAmount);
}
creator.require_auth();
let current_balance = Self::balance(env.clone(), creator.clone());
if current_balance < amount {
panic_with_error!(&env, Error::InsufficientBalance);
}
let token_address = Self::get_token(&env);
let token_client = token::Client::new(&env, &token_address);
// transfer first (fail-fast if token fails)
token_client.transfer(&env.current_contract_address(), &creator, &amount);
// safe subtraction
let new_balance = current_balance
.checked_sub(amount)
.unwrap_or_else(|| panic_with_error!(&env, Error::InsufficientBalance));
env.storage()
.instance()
.set(&DataKey::Balance(creator.clone()), &new_balance);
env.events().publish(
(Symbol::new(&env, WITHDRAW_EVENT),),
WithdrawEvent {
creator,
amount,
token: token_address,
},
);
}
// -------- Internal helpers --------
fn get_admin(env: &Env) -> Address {
env.storage()
.instance()
.get(&DataKey::Admin)
.unwrap_or_else(|| panic_with_error!(env, Error::NotInitialized))
}
fn get_token(env: &Env) -> Address {
env.storage()
.instance()
.get(&DataKey::Token)
.unwrap_or_else(|| panic_with_error!(env, Error::NotInitialized))
}
fn require_authorized(env: &Env, caller: &Address) {
let admin = Self::get_admin(env);
if caller == &admin {
return;
}
if env
.storage()
.instance()
.has(&DataKey::AuthorizedDepositor(caller.clone()))
{
return;
}
panic_with_error!(env, Error::NotAuthorized);
}
}
#[cfg(test)]
mod test;
#[cfg(test)]
mod property_tests;