forked from SO4-Markets/contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
139 lines (124 loc) · 5.01 KB
/
Copy pathlib.rs
File metadata and controls
139 lines (124 loc) · 5.01 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
//! Order vault — holds collateral and LP tokens during order lifecycle.
//! Mirrors GMX's OrderVault pattern (same balance-snapshot pattern as deposit/withdrawal vaults).
//!
//! Collateral for market/limit increase orders and LP tokens for decrease orders
//! are held here between create_order and execute_order.
#![no_std]
#![allow(dependency_on_unit_never_type_fallback)]
use gmx_keys::roles;
use soroban_sdk::{
contract, contracterror, contractimpl, contracttype, panic_with_error, token, Address, BytesN,
Env,
};
// ─── Errors ───────────────────────────────────────────────────────────────────
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
AlreadyInitialized = 1,
NotInitialized = 2,
Unauthorized = 3,
NegativeAmount = 4,
}
// ─── Storage keys ─────────────────────────────────────────────────────────────
#[contracttype]
enum InstanceKey {
Initialized,
RoleStore,
}
#[contracttype]
enum DataKey {
TokenBalance(Address),
}
// ─── Role-store client ────────────────────────────────────────────────────────
#[allow(dead_code)]
#[soroban_sdk::contractclient(name = "RoleStoreClient")]
trait IRoleStore {
fn has_role(env: Env, account: Address, role: BytesN<32>) -> bool;
}
fn require_controller(env: &Env, caller: &Address) {
let rs: Address = env
.storage()
.instance()
.get(&InstanceKey::RoleStore)
.unwrap_or_else(|| panic_with_error!(env, Error::NotInitialized));
if !RoleStoreClient::new(env, &rs).has_role(caller, &roles::controller(env)) {
panic_with_error!(env, Error::Unauthorized);
}
}
// ─── Contract ─────────────────────────────────────────────────────────────────
#[contract]
pub struct OrderVault;
#[contractimpl]
impl OrderVault {
/// One-time setup: store admin and role_store addresses.
pub fn initialize(env: Env, admin: Address, role_store: Address) {
admin.require_auth();
if env.storage().instance().has(&InstanceKey::Initialized) {
panic_with_error!(&env, Error::AlreadyInitialized);
}
env.storage()
.instance()
.set(&InstanceKey::Initialized, &true);
env.storage()
.instance()
.set(&InstanceKey::RoleStore, &role_store);
}
/// Snapshot the balance of `token` in this vault and return the received delta.
///
/// # Balance invariant (issue #47)
///
/// The returned delta is `current_on_chain_balance − last_recorded_balance`.
/// A positive delta means tokens arrived since the last snapshot; the caller
/// (order_handler.create_order) treats this as the collateral amount and
/// reverts the transaction if the delta is ≤ 0.
///
/// After every transfer-out the vault re-snapshots in the same call, so the
/// recorded balance always equals the actual on-chain balance. This prevents
/// double-counting: a second `record_transfer_in` before any new deposit
/// returns 0, which order_handler will reject.
pub fn record_transfer_in(env: Env, token: Address) -> i128 {
let current = token::Client::new(&env, &token).balance(&env.current_contract_address());
let recorded: i128 = env
.storage()
.persistent()
.get(&DataKey::TokenBalance(token.clone()))
.unwrap_or(0);
let delta = current - recorded;
env.storage()
.persistent()
.set(&DataKey::TokenBalance(token), ¤t);
delta
}
/// Transfer `amount` of `token` out to `receiver`. CONTROLLER-gated.
pub fn transfer_out(
env: Env,
caller: Address,
token: Address,
receiver: Address,
amount: i128,
) {
caller.require_auth();
if amount <= 0 {
panic_with_error!(&env, Error::NegativeAmount);
}
require_controller(&env, &caller);
token::Client::new(&env, &token).transfer(
&env.current_contract_address(),
&receiver,
&amount,
);
// Sync recorded balance
let new_bal = token::Client::new(&env, &token).balance(&env.current_contract_address());
env.storage()
.persistent()
.set(&DataKey::TokenBalance(token), &new_bal);
}
/// Return the last recorded balance for a token.
pub fn get_recorded_balance(env: Env, token: Address) -> i128 {
env.storage()
.persistent()
.get(&DataKey::TokenBalance(token))
.unwrap_or(0)
}
}