wallet-v3cr3/contracts/wallet.fc

118 lines
3.6 KiB
Plaintext

;; Wallet V3 Custom R3
;; (c) 2024 oscux
;; TL-B scheme:
;; storage#_ seqno:uint32 public_key:uint256 subwallet_id:uint32 trusted_hashpart:uint256 = Storage;
;;
;; new_internal#0000aac1 send_mode:uint8 message:^InternalMessage = SpendRequest;
;; upgrade#0000aaa0 new_code:^Cell new_data:^Cell = UpgradeRequest;
#include "imports/stdlib.fc";
const int op::new_internal = 0xAAC1;
const int op::upgrade = 0xAAA0;
const int error::not_allowed = 105;
const int error::expired = 108;
() execute_command(slice request, int sudo?) impure {
int op = request~load_uint(32);
if (op == op::new_internal) {
int mode = request~load_uint(8);
cell message = request~load_ref();
send_raw_message(message, mode);
return ();
}
if (op == op::upgrade) {
throw_unless(error::not_allowed, sudo?);
cell new_code = request~load_ref();
cell new_data = request~load_ref();
set_code(new_code);
set_c3(new_code.begin_parse().bless());
set_data(new_data);
;; but next commands executes with old code
return ();
}
if (op == 0) {
return ();
}
throw(0xffff);
}
() execute_commands(slice requests, int sudo?) impure {
while (requests.slice_refs() > 0) {
execute_command(requests~load_ref().begin_parse(), sudo?);
}
}
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
slice cs = in_msg_full.begin_parse();
cs~skip_bits(4);
slice sender_address = cs~load_msg_addr();
(_, int sender_hashpart) = parse_std_addr(sender_address);
slice ds = get_data().begin_parse();
ds~skip_bits(32 + 256 + 32);
int trusted_hashpart = ds~load_uint(256);
if (sender_hashpart == trusted_hashpart) {
throw_if(error::not_allowed, trusted_hashpart == 0);
execute_commands(in_msg_body~load_ref().begin_parse(), true);
}
return ();
}
() recv_external(slice in_msg) impure {
slice signature = in_msg~load_bits(512);
slice cs = in_msg;
(int subwallet_id, int valid_until, int msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
throw_if(error::expired, valid_until <= now());
slice ds = get_data().begin_parse();
(int stored_seqno, int public_key, int stored_subwallet, int trusted_hashpart) = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_uint(256));
throw_unless(error::not_allowed, subwallet_id == stored_subwallet);
throw_unless(error::not_allowed, msg_seqno == stored_seqno);
throw_unless(error::not_allowed, check_signature(slice_hash(in_msg), signature, public_key));
accept_message();
cs~touch();
int exit_code = 0;
try {
execute_commands(in_msg~load_ref().begin_parse(), trusted_hashpart == 0 ? true : false);
} catch (x, n) {
exit_code = n;
}
set_data(
begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(public_key, 256)
.store_uint(stored_subwallet, 32)
.store_uint(trusted_hashpart, 256)
.store_uint(exit_code, 16)
.end_cell()
);
return ();
}
;; Get methods
int seqno() method_id {
slice ds = get_data().begin_parse();
return ds~load_uint(32);
}
int get_public_key() method_id {
slice ds = get_data().begin_parse();
ds~skip_bits(32);
return ds~load_uint(256);
}
int get_trusted_hashpart() method_id {
slice ds = get_data().begin_parse();
ds~skip_bits(32 + 256 + 32);
return ds~load_uint(256);
}
int prev_exit_code() method_id {
slice ds = get_data().begin_parse();
ds~skip_bits(32 + 256 + 32 + 256);
return ds~load_uint(16);
}