-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlib.rs
207 lines (177 loc) · 6.44 KB
/
lib.rs
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
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
mod synthetics {
use ink_storage::traits::SpreadLayout;
/// ERC20 token like to represent long and shorts, TODO maybe use main ERC20
#[derive(Debug, SpreadLayout)]
#[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout)
)]
struct Token {
total_supply: Balance,
/// The balance of each user.
balances: ink_storage::collections::HashMap<AccountId, Balance>
}
// TODO: refactor by using ERC20 trait?
impl Token {
pub fn new(initial_supply: Balance, owner: AccountId) -> Self {
let mut balances = ink_storage::collections::HashMap::new();
balances.insert(owner, initial_supply);
Self {
total_supply: initial_supply,
balances
}
}
pub fn total_supply(&self) -> Balance {
self.total_supply
}
pub fn balance_of(&self, owner: AccountId) -> Balance {
self.balance_of_or_zero(&owner)
}
fn balance_of_or_zero(&self, owner: &AccountId) -> Balance {
*self.balances.get(owner).unwrap_or(&0)
}
}
/// The actual contract
#[ink(storage)]
pub struct Synthetics {
/// The short token, you first mint this to create the short
short: Token,
/// The long token, you ming this when you buy the corresponding short
/// long total balance has to be less than or equal than the total balance of short
long: Token,
/// Collateral held by each accont
margin: ink_storage::collections::HashMap<AccountId, Balance>
}
#[ink(event)]
/// Emitted when a Token is minted
pub struct TokenMinted {
#[ink(topic)]
creator: AccountId,
#[ink(topic)]
value: Balance
}
/// The error types.
#[ink(event)]
pub struct Error {
/// Message
message: ink_prelude::string::String,
}
// TODO: make this call an oracle, check return type, add asset type
pub fn get_price() -> u128 {
1
}
impl Synthetics {
#[ink(constructor, selector = "0xCAFEBABE")]
/// When a contract is first created, a short is minted with the balance given
/// # Panics
///
/// If not enough IM is sent
pub fn new(initial_supply: Balance) -> Self {
// TODO: how do I add the margin, start by taking the margin here
let caller = Self::env().caller();
// Take the IM, if this fails don't continue the rest
// TODO make the HC percentage a parameter
let im = initial_supply * get_price() / 5;
// TODO Should I panic?
let margin_sent = Self::env().transferred_balance();
if im > margin_sent {
let message = ink_prelude::format!("Not enough IM is posted, we need a minimum of {} and {} was given.", margin_sent, im);
// TODO ugly
let m = message.clone();
Self::env().emit_event(Error { message });
panic!("{}", m);
}
let short = Token::new(initial_supply, caller);
Self::env().emit_event(TokenMinted {
creator: caller,
value: initial_supply,
});
let long = Token::new(0, caller);
let mut margin = ink_storage::collections::HashMap::new();
margin.insert(caller, margin_sent);
Self{ short, long, margin }
}
#[ink(message)]
pub fn short_total_balance(&self) -> Balance {
self.short.total_supply()
}
#[ink(message)]
pub fn long_total_balance(&self) -> Balance {
self.long.total_supply()
}
#[ink(message)]
pub fn short_balance_of(&self, owner: AccountId) -> Balance {
self.short.balance_of(owner)
}
#[ink(message)]
pub fn long_balance_of(&self, owner: AccountId) -> Balance {
self.long.balance_of(owner)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ink_env::{
call,
test,
};
use ink_lang as ink;
/// We test if the constructor does its job.
#[ink::test]
fn constructur_works() {
let account = create_contract(100);
let synthetics = Synthetics::new(100);
// TODO test events
assert_eq!(synthetics.short_total_balance(), 100);
assert_eq!(synthetics.long_total_balance(), 0);
assert_eq!(synthetics.short_balance_of(account), 100);
assert_eq!(synthetics.long_balance_of(account), 0);
}
/// Failure for not enough balance
#[ink::test]
#[should_panic]
fn constructur_fails_no_balance() {
create_contract(1);
Synthetics::new(100);
}
// Helper functions
fn default_accounts(
) -> ink_env::test::DefaultAccounts<ink_env::DefaultEnvironment> {
ink_env::test::default_accounts::<ink_env::DefaultEnvironment>()
.expect("Off-chain environment should have been initialized already")
}
fn contract_id() -> AccountId {
ink_env::test::get_current_contract_account_id::<ink_env::DefaultEnvironment>(
)
.expect("Cannot get contract id")
}
fn set_sender(sender: AccountId) {
let callee = ink_env::account_id::<ink_env::DefaultEnvironment>()
.unwrap_or([0x0; 32].into());
test::push_execution_context::<Environment>(
sender,
callee,
1000000,
1000000,
test::CallData::new(call::Selector::new([0x00; 4])), // dummy
);
}
fn create_contract(initial_balance: Balance) -> AccountId {
let accounts = default_accounts();
set_sender(accounts.alice);
let mut data = ink_env::test::CallData::new(ink_env::call::Selector::new([
0xCA, 0xFE, 0xBA, 0xBE,
]));
data.push_arg(&accounts.alice);
ink_env::test::push_execution_context::<ink_env::DefaultEnvironment>(
accounts.alice,
contract_id(),
1000000,
initial_balance,
data,
);
accounts.alice
}
}
}