universal-wallet-contract/contracts/universal_wallet_contract.fc

141 lines
5.2 KiB
Plaintext

;; Universal Wallet Contract v1
;; github.com/delpydoc | t.me/delpydoc
;;
;; Scheme
;; message_states#_ public_key:uint256 state_cleaned:uint64 old_queries:(HashmapE 14 ^Cell) = MessageStates;
;; storage#_ subwallet_id:uint32 admin:MsgAddressInt message_states:MessageStates = Storage;
#include "imports/stdlib.fc";
int gas_consumed() asm "GASCONSUMED";
const int opcode::admin::provide_message = 0xA173;
const int opcode::admin::edit_code = 0xA183;
const int opcode::any::provide_message = 0xA174;
const int error::privileges_violation = 72;
const int error::no_gas = 76;
const int error::expired_request = 78;
const int error::unknown_op = 0xffff;
const int env::query_timeout = 600;
(cell) storage::pack(int subwallet_id, slice admin_address, cell message_states) inline_ref {
return (
begin_cell()
.store_uint(subwallet_id, 32)
.store_slice(admin_address)
.store_ref(message_states)
.end_cell()
);
}
(cell) message_states::pack(int public_key, int state_cleaned, cell old_queries) inline_ref {
return (
begin_cell()
.store_uint(public_key, 256)
.store_uint(state_cleaned, 64)
.store_dict(old_queries)
.end_cell()
);
}
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
var cs = in_msg_full.begin_parse();
var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
if (flags & 1) {
;; ignore all bounced messages
return ();
}
if (in_msg_body.slice_bits() < 32) {
;; ignore simple transfers
return ();
}
slice ctx::sender = cs~load_msg_addr();
slice ds = get_data().begin_parse();
(int c4::subwallet_id, slice c4::admin_address) = (ds~load_uint(32), ds~load_msg_addr());
int ctx::op = in_msg_body~load_uint(32);
if (ctx::op == opcode::admin::provide_message) {
throw_unless(error::privileges_violation, equal_slices(c4::admin_address, ctx::sender));
in_msg_body~skip_bits(64); ;; query_id
while (in_msg_body.slice_bits()) {
int msg_mode = in_msg_body~load_uint(8);
cell raw_msg = in_msg_body~load_ref();
send_raw_message(raw_msg, msg_mode);
}
return ();
}
if (ctx::op == opcode::admin::edit_code) {
throw_unless(error::privileges_violation, equal_slices(c4::admin_address, ctx::sender));
cell new_code = in_msg_body~load_ref();
cell new_data = in_msg_body~load_ref();
set_code(new_code);
set_data(new_data);
return ();
}
if (ctx::op == opcode::any::provide_message) {
slice c4::messages_state = ds~load_ref().begin_parse();
(int c4::public_key, int c4::state_cleaned, cell c4::old_queries) = (
c4::messages_state~load_uint(256), c4::messages_state~load_uint(64), c4::messages_state~load_dict()
);
slice ctx::request_signature = in_msg_body~load_bits(512);
int ctx::request_hash = slice_hash(in_msg_body);
throw_if(error::privileges_violation - 2, c4::public_key == 0);
throw_unless(error::privileges_violation - 3, check_signature(ctx::request_hash, ctx::request_signature, c4::public_key));
int ctx::query_id = in_msg_body~load_uint(64);
int bound = (now() << 32);
~dump([34, ctx::query_id >> 32, bound >> 32]);
throw_if(error::expired_request, ctx::query_id < bound);
(_, int known_query?) = c4::old_queries.udict_get?(64, ctx::query_id);
throw_if(error::expired_request + 1, known_query?);
int ctx::subwallet_id = in_msg_body~load_uint(32);
throw_unless(error::privileges_violation - 4, ctx::subwallet_id == c4::subwallet_id);
int ctx::request_gas = in_msg_body~load_coins();
throw_if(error::no_gas, msg_value < ctx::request_gas);
accept_message();
while (in_msg_body.slice_bits()) {
int msg_mode = in_msg_body~load_uint(8);
cell raw_msg = in_msg_body~load_ref();
send_raw_message(raw_msg, (msg_mode | 2)); ;; maybe dont provide msg_mdoe
}
c4::old_queries~udict_set_builder(64, ctx::query_id, begin_cell());
bound -= (env::query_timeout << 32);
var queries = c4::old_queries;
do {
var (old_queries', i, _, f) = c4::old_queries.udict_delete_get_min(64);
f~touch();
if (f) {
f = (i < bound);
}
if (f) {
c4::old_queries = old_queries';
c4::state_cleaned = i;
}
} until (~ f);
cell new_message_states = message_states::pack(c4::public_key, c4::state_cleaned, c4::old_queries);
set_data(storage::pack(c4::subwallet_id, c4::admin_address, new_message_states));
return ();
}
return ();
}
(int, slice) get_wallet_data() method_id {
slice ds = get_data().begin_parse();
return (ds~load_uint(32), ds~load_msg_addr());
}
(int, int, cell) get_message_states() method_id {
slice ds = get_data().begin_parse();
slice messages_state = ds~load_ref().begin_parse();
return (messages_state~load_uint(256), messages_state~load_uint(64), messages_state~load_dict());
}