199 lines
7.5 KiB
Plaintext
199 lines
7.5 KiB
Plaintext
;; Universal Wallet Contract v1
|
|
;; github.com/delpydoc | t.me/delpydoc
|
|
;;
|
|
;; Scheme
|
|
;; message_states#_ public_key:uint256 mem_code:^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 opcode::mem::status_new = 0xBACA10;
|
|
const int opcode::mem::status_exist = 0xBACA20;
|
|
|
|
const int opcode::mem::status_new = 0xBACA10;
|
|
const int opcode::mem::status_exist = 0xBACA20;
|
|
|
|
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;
|
|
const int env::workchain = 0;
|
|
|
|
(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, cell mem_code) inline_ref {
|
|
return (
|
|
begin_cell()
|
|
.store_uint(public_key, 256)
|
|
.store_ref(mem_code)
|
|
.end_cell()
|
|
);
|
|
}
|
|
|
|
(cell) mem_query::calculate_state_init(cell mem_code, int query_id) {
|
|
return (
|
|
begin_cell()
|
|
.store_uint(6, 5)
|
|
.store_ref(mem_code)
|
|
.store_ref(
|
|
begin_cell()
|
|
.store_slice(my_address())
|
|
.store_uint(query_id, 256)
|
|
.end_cell()
|
|
)
|
|
.end_cell()
|
|
);
|
|
}
|
|
|
|
(slice) calculate_contract_address(cell mem_state_init) {
|
|
return (
|
|
begin_cell()
|
|
.store_uint(4, 3)
|
|
.store_int(env::workchain, 8)
|
|
.store_uint(cell_hash(mem_state_init), 256)
|
|
.end_cell().begin_parse()
|
|
);
|
|
}
|
|
|
|
() 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::mem::status_new) {
|
|
int mem::query_id = in_msg_body~load_uint(256);
|
|
slice c4::messages_state = ds~load_ref().begin_parse();
|
|
(int c4::public_key, cell c4::mem_contract_code) = (c4::messages_state~load_uint(256), c4::messages_state~load_ref());
|
|
cell mem_state_init = mem_query::calculate_state_init(c4::mem_contract_code, mem::query_id);
|
|
slice mem_contract_address = calculate_contract_address(mem_state_init);
|
|
throw_unless(error::privileges_violation, equal_slices(mem_contract_address, ctx::sender));
|
|
in_msg_body = in_msg_body~load_ref().begin_parse();
|
|
slice ctx::request_signature = in_msg_body~load_bits(512);
|
|
int ctx::request_hash = slice_hash(in_msg_body);
|
|
throw_if(error::privileges_violation - 1, c4::public_key == 0);
|
|
throw_unless(error::privileges_violation - 2, check_signature(ctx::request_hash, ctx::request_signature, c4::public_key));
|
|
int ctx::query_id = in_msg_body~load_uint(64);
|
|
throw_unless(error::privileges_violation - 3, ctx::query_id == mem::query_id);
|
|
int bound = (now() << 32);
|
|
throw_if(error::expired_request, ctx::query_id < bound);
|
|
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_mode
|
|
}
|
|
return ();
|
|
}
|
|
|
|
if (ctx::op == opcode::mem::status_exist) {
|
|
int mem::query_id = in_msg_body~load_uint(256);
|
|
slice c4::messages_state = ds~load_ref().begin_parse();
|
|
(int c4::public_key, cell c4::mem_contract_code) = (c4::messages_state~load_uint(256), c4::messages_state~load_ref());
|
|
cell mem_state_init = mem_query::calculate_state_init(c4::mem_contract_code, mem::query_id);
|
|
slice mem_contract_address = calculate_contract_address(mem_state_init);
|
|
throw_unless(error::privileges_violation, equal_slices(mem_contract_address, ctx::sender));
|
|
slice ret_address = in_msg_body~load_msg_addr();
|
|
send_raw_message(
|
|
begin_cell()
|
|
.store_uint(0x10, 6)
|
|
.store_slice(ret_address)
|
|
.store_coins(0)
|
|
.store_uint(0, 1 + 4 + 4 + 32 + 64 + 1 + 1)
|
|
.end_cell(), 64 + 2
|
|
);
|
|
return ();
|
|
}
|
|
|
|
if (ctx::op == opcode::any::provide_message) {
|
|
slice raw_payload = in_msg_body;
|
|
in_msg_body~skip_bits(512);
|
|
int ctx::query_id = in_msg_body~load_uint(64);
|
|
slice c4::messages_state = ds~load_ref().begin_parse();
|
|
(_, cell c4::mem_contract_code) = (c4::messages_state~load_uint(256), c4::messages_state~load_ref());
|
|
cell mem_state_init = mem_query::calculate_state_init(c4::mem_contract_code, ctx::query_id);
|
|
slice mem_contract_address = calculate_contract_address(mem_state_init);
|
|
send_raw_message(
|
|
begin_cell()
|
|
.store_uint(0x18, 6)
|
|
.store_slice(mem_contract_address)
|
|
.store_coins(0)
|
|
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 32 + 64 + 1 + 1 + 1)
|
|
.store_ref(mem_state_init)
|
|
.store_ref(
|
|
begin_cell()
|
|
.store_slice(ctx::sender)
|
|
.store_ref(begin_cell().store_slice(raw_payload).end_cell())
|
|
.end_cell()
|
|
)
|
|
.end_cell(), 64
|
|
);
|
|
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, cell) get_message_states() method_id { ;; -> (public_key, mem_code)
|
|
slice ds = get_data().begin_parse();
|
|
slice messages_state = ds~load_ref().begin_parse();
|
|
return (messages_state~load_uint(256), messages_state~load_ref());
|
|
}
|
|
|
|
|