-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathram.defi.cpp
320 lines (242 loc) · 11.4 KB
/
ram.defi.cpp
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#include <eosio.system/exchange_state.cpp>
#include <ram.defi.hpp>
using eosiosystem::rammarket;
using std::make_tuple;
[[eosio::action]]
void ram::create(const name& issuer, const asset& maximum_supply) {
require_auth(get_self());
check(issuer == get_self(), "issuer must be ram.defi");
auto sym = maximum_supply.symbol;
check(sym.is_valid(), "invalid symbol name");
check(maximum_supply.is_valid(), "invalid supply");
check(maximum_supply.amount > 0, "max-supply must be positive");
stats statstable(get_self(), sym.code().raw());
auto existing = statstable.find(sym.code().raw());
check(existing == statstable.end(), "token with symbol already exists");
statstable.emplace(get_self(), [&](auto& s) {
s.supply.symbol = maximum_supply.symbol;
s.max_supply = maximum_supply;
s.issuer = issuer;
});
}
[[eosio::action]]
void ram::transfer(const name& from, const name& to, const asset& quantity, const string& memo) {
check(from != to, "cannot transfer to self");
require_auth(from);
check(is_account(to), "to account does not exist");
auto sym = quantity.symbol.code();
stats statstable(get_self(), sym.raw());
const auto& st = statstable.get(sym.raw());
if (from != get_self()) {
require_recipient(from);
}
if (to != get_self()) {
require_recipient(to);
}
check(quantity.is_valid(), "invalid quantity");
check(quantity.amount > 0, "must transfer positive quantity");
check(quantity.symbol == st.supply.symbol, "symbol precision mismatch");
check(memo.size() <= 256, "memo has more than 256 bytes");
asset from_balance;
asset to_balance = asset(0, quantity.symbol);
// from -> to
auto payer = has_auth(to) ? to : from;
from_balance = sub_balance(from, quantity);
to_balance = add_balance(to, quantity, payer);
if (to == get_self()) {
if(memo == "ram"){
do_withdraw_ram(from, quantity);
}else {
do_withdraw_eos(from, quantity);
}
accounts to_acnts(get_self(), to.value);
auto to_itr = to_acnts.require_find(quantity.symbol.code().raw(), "no balance object found");
to_balance = to_itr->balance;
}
action(permission_level{_self, "active"_n}, _self, "transferlog"_n, make_tuple(from, to, quantity, from_balance, to_balance)).send();
}
[[eosio::action]]
void ram::updatestatus(bool disabled_deposit, bool disabled_withdraw) {
require_auth(ADMIN_ACCOUNT);
ram::config_table _config(get_self(), get_self().value);
config_row config = _config.get_or_default();
config.disabled_deposit = disabled_deposit;
config.disabled_withdraw = disabled_withdraw;
_config.set(config, get_self());
}
[[eosio::action]]
void ram::updateratio(uint16_t deposit_fee_ratio, uint16_t withdraw_fee_ratio) {
require_auth(ADMIN_ACCOUNT);
check(deposit_fee_ratio <= 5000, "ram.defi::updateratio: deposit_fee_ratio must be <= 5000");
check(withdraw_fee_ratio <= 5000, "ram.defi::updateratio: withdraw_fee_ratio must be <= 5000");
ram::config_table _config(get_self(), get_self().value);
config_row config = _config.get_or_default();
config.withdraw_fee_ratio = withdraw_fee_ratio;
config.deposit_fee_ratio = deposit_fee_ratio;
_config.set(config, get_self());
}
[[eosio::on_notify("*::ramtransfer")]]
void ram::on_ramtransfer(const name& from, const name& to, int64_t bytes, const std::string& memo ) {
// ignore transfers
if (to != get_self())
return;
const name contract = get_first_receiver();
check(contract == EOSIO_ACCOUNT, "ram.defi::depositram: only the eosio contract may send RAM bytes to this contract.");
do_deposit_ram(from, bytes);
}
[[eosio::on_notify("*::transfer")]]
void ram::on_transfer(const name& from, const name& to, const asset& quantity, const string& memo) {
// ignore transfers
if (to != get_self())
return;
const name contract = get_first_receiver();
check(contract == EOS_CONTRACT && quantity.symbol == EOS, "ram.defi::deposit: only transfer [eosio.token/EOS,ram.defi/BRAM] ");
do_deposit_eos(from, quantity);
}
void ram::do_deposit_ram(const name& owner, const int64_t bytes) {
check( bytes > 0, "ram.defi::deposit: cannot reduce negative byte");
ram::config_row _config = get_config();
check(!_config.disabled_deposit, "ram.defi::deposit: deposit has been suspended");
// transfer to rambank
ram_transfer(get_self(), RAM_BANK_ACCOUNT, bytes, "deposit ram");
asset output = {bytes, BRAM};
issue(get_self(), output);
auto deposit_fee = output * _config.deposit_fee_ratio / RATIO_PRECISION;
auto buy_ram_bytes = output - deposit_fee;
if (deposit_fee.amount > 0) {
token_transfer(get_self(), RAM_FEES_ACCOUNT, extended_asset(deposit_fee, get_self()), "deposit fee");
}
token_transfer(get_self(), owner, extended_asset(buy_ram_bytes, get_self()), "deposit ram");
// log
ram::depositram_action depositram(get_self(), {get_self(), "active"_n});
depositram.send(owner, bytes, deposit_fee, buy_ram_bytes);
}
void ram::do_deposit_eos(const name& owner, const asset& quantity) {
ram::config_row _config = get_config();
check(!_config.disabled_deposit, "ram.defi::deposit: deposit has been suspended");
auto deposit_fee = quantity * _config.deposit_fee_ratio / RATIO_PRECISION;
auto buy_ram_quantity = quantity - deposit_fee;
if (deposit_fee.amount > 0) {
token_transfer(get_self(), RAM_FEES_ACCOUNT, extended_asset(deposit_fee, EOS_CONTRACT), "deposit fee");
}
// buy ram
auto output_amount = buyram(get_self(), RAM_BANK_ACCOUNT, buy_ram_quantity);
auto output_bram = asset(output_amount.amount, BRAM);
// issue
issue(owner, output_bram);
// log
ram::depositlog_action depositlog(get_self(), {get_self(), "active"_n});
depositlog.send(owner, quantity, deposit_fee, output_bram);
}
void ram::do_withdraw_ram(const name& owner, const asset& quantity) {
ram::config_row _config = get_config();
check(!_config.disabled_withdraw, "ram.defi::withdraw: withdraw has been suspended");
auto withdraw_fee = quantity * _config.withdraw_fee_ratio / RATIO_PRECISION;
auto to_account_bytes = quantity - withdraw_fee;
// retire
retire(get_self(), to_account_bytes);
// transfer fee
if(withdraw_fee.amount > 0){
token_transfer(get_self(), RAM_FEES_ACCOUNT, extended_asset(withdraw_fee, get_self()), "withdraw fee");
}
// transfer ram
ram_transfer(RAM_BANK_ACCOUNT, owner, to_account_bytes.amount, "withdraw ram");
// log
ram::withdrawram_action withdrawram(get_self(), {get_self(), "active"_n});
withdrawram.send(owner, quantity, withdraw_fee, to_account_bytes.amount);
}
void ram::do_withdraw_eos(const name& owner, const asset& quantity) {
ram::config_row _config = get_config();
check(!_config.disabled_withdraw, "ram.defi::withdraw: withdraw has been suspended");
// retire
retire(get_self(), quantity);
// sellram
asset output_amount = sellram(RAM_BANK_ACCOUNT, quantity.amount);
auto withdraw_fee = output_amount * _config.withdraw_fee_ratio / RATIO_PRECISION;
auto to_account = output_amount - withdraw_fee;
// collect fee
if (withdraw_fee.amount > 0) {
token_transfer(RAM_BANK_ACCOUNT, RAM_FEES_ACCOUNT, extended_asset(withdraw_fee, EOS_CONTRACT), "withdraw fee");
}
// transfer to owner
token_transfer(RAM_BANK_ACCOUNT, owner, extended_asset(to_account, EOS_CONTRACT), "withdraw");
// log
ram::withdrawlog_action withdrawlog(get_self(), {get_self(), "active"_n});
withdrawlog.send(owner, quantity, withdraw_fee, to_account);
}
asset ram::buyram(const name& payer, const name& receiver, const asset& quantity) {
auto fee = quantity;
fee.amount = (fee.amount + 199) / 200; /// .5% fee (round up)
// fee.amount cannot be 0 since that is only possible if quant.amount is 0 which is not allowed by the assert above.
// If quant.amount == 1, then fee.amount == 1,
// otherwise if quant.amount > 1, then 0 < fee.amount < quant.amount.
auto quantity_after_fee = quantity;
quantity_after_fee.amount -= fee.amount;
auto output_amount = convert(quantity_after_fee, RAM);
action(permission_level{payer, "active"_n}, EOSIO_ACCOUNT, name("buyram"), make_tuple(payer, receiver, quantity)).send();
return output_amount;
}
asset ram::sellram(const name& account, int64_t bytes) {
auto output_amount = convert(asset(static_cast<uint64_t>(bytes), RAM), EOS);
auto fee = (output_amount.amount + 199) / 200; /// .5% fee (round up)
output_amount.amount -= fee;
action(permission_level{account, "active"_n}, EOSIO_ACCOUNT, name("sellram"), make_tuple(account, bytes)).send();
return output_amount;
}
void ram::issue(const name& to, const asset& quantity) {
auto sym = quantity.symbol;
stats statstable(_self, sym.code().raw());
auto existing = statstable.find(sym.code().raw());
check(existing != statstable.end(), "token with symbol does not exist, create token before issue");
const auto& st = *existing;
check(quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply");
statstable.modify(st, same_payer, [&](auto& s) { s.supply += quantity; });
add_balance(st.issuer, quantity, st.issuer);
if (to != st.issuer) {
ram::transfer_action transfer_act(get_self(), {get_self(), "active"_n});
transfer_act.send(st.issuer, to, quantity, "issue");
}
}
void ram::retire(const name& owner, const asset& quantity) {
auto sym = quantity.symbol;
stats statstable(get_self(), sym.code().raw());
auto existing = statstable.find(sym.code().raw());
check(existing != statstable.end(), "token with symbol does not exist");
const auto& st = *existing;
statstable.modify(st, same_payer, [&](auto& s) { s.supply -= quantity; });
sub_balance(owner, quantity);
}
asset ram::sub_balance(const name& owner, const asset& value) {
accounts from_acnts(get_self(), owner.value);
auto from = from_acnts.require_find(value.symbol.code().raw(), "no balance object found");
check(from->balance.amount >= value.amount, "overdrawn balance");
from_acnts.modify(from, owner, [&](auto& a) { a.balance -= value; });
return from->balance;
}
asset ram::add_balance(const name& owner, const asset& value, const name& ram_payer) {
accounts to_acnts(get_self(), owner.value);
auto to = to_acnts.find(value.symbol.code().raw());
if (to == to_acnts.end()) {
to = to_acnts.emplace(ram_payer, [&](auto& a) { a.balance = value; });
} else {
to_acnts.modify(to, same_payer, [&](auto& a) { a.balance += value; });
}
return to->balance;
}
void ram::token_transfer(const name& from, const name& to, const extended_asset& value, const string& memo) {
ram::transfer_action transfer(value.contract, {from, "active"_n});
transfer.send(from, to, value.quantity, memo);
}
void ram::ram_transfer(const name& from, const name& to, const int64_t bytes, const string& memo) {
action(permission_level{from, "active"_n}, EOSIO_ACCOUNT, "ramtransfer"_n, make_tuple(from, to, bytes, memo)).send();
}
asset ram::convert(const asset& from, const symbol& to) {
rammarket _rammarket(EOSIO_ACCOUNT, EOSIO_ACCOUNT.value);
auto itr = _rammarket.find(RAMCORE.raw());
auto itr_tmp = *itr;
return itr_tmp.convert(from, to);
}
ram::config_row ram::get_config() {
ram::config_table _config(get_self(), get_self().value);
return _config.get_or_default();
}