# SC
```c
;; Constants:
;; CANCEL_CODE = 0x01;
;; START_CODE = 0x02;
;; FAST_EXIT_CODE = 0x03;
;; SEND_STATE_CODE = 0x04;
;; SEND_SIG_CODE = 0x05;
;; SEND_PAYADDR_CODE = 0x06;
(int, int, int, int, int, int, int, int, int) get_contract_state() {
var ds = get_data().begin_parse();
var (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb) = (
ds~load_uint(1),
ds~load_uint(32),
ds~load_uint(48),
ds~load_uint(256),
ds~load_uint(256),
ds~load_uint(1),
ds~load_uint(1),
ds~load_uint(214),
ds~load_uint(214)
);
ds.end_parse();
return (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb);
}
() set_contract_state(int init, int timeout, int timestamp, int pubA, int pubB, int lockA, int lockB, int Xa, int Xb) {
var ds = begin_cell()
.store_uint(init, 1) ;; init 1 bit flag to initialize contract
.store_uint(timeout, 32) ;; timeout 32 bits timeout in second for exit
.store_uint(timestamp, 48) ;; timestamp 48 bits unix time in seconds
.store_uint(pubA, 256) ;; pubA 256 bits pub key of user A
.store_uint(pubB, 256) ;; pubB 256 bits pub key of user B
.store_uint(lockA, 1) ;; lockA 1 bit flag registry for A
.store_uint(lockB, 1) ;; lockB 1 bit flag registry for B
.store_uint(Xa, 214) ;; Xa - amount that first user is going to send
.store_uint(Xb, 214) ;; Xb - amount that second user is going to send
.end_cell();
set_data(ds);
}
() send_grams(int dest_addr, int amount) {
send_raw_message(begin_cell()
.store_uint(0xc400, 17)
.store_uint(dest_addr, 256)
.store_grams(amount)
.store_uint(0, 106)
.store_uint(0x444f4e45, 33)
.end_cell(), 3);
}
(int) get_balance() {
return 0;
}
;; deposit funds during initialization
() deposit(int pubkey, int msg_value) {
var (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb) = get_contract_state();
throw_unless(1, init == 0);
if (pubkey == pubA) {
Xa += msg_value;
} elseif (pubkey == pubB) {
Xb += msg_value;
} else {
throw(2);
}
}
;; withdraw funds before payment channel started
() cancel(slice signature) {
var (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb) = get_contract_state();
throw_unless(1, init == 0);
var checking_slice = begin_cell()
.store_uint(0x01, 4)
.store_slice(my_address())
.end_cell();
var data_slice_hash = slice_hash(checking_slice.begin_parse());
throw_unless(201, check_signature(data_slice_hash, signature, pubA) | check_signature(data_slice_hash, signature, pubB));
accept_message();
init = 1;
send_grams(Xa, pubA);
send_grams(Xb, pubB);
Xa = 0;
Xb = 0;
set_contract_state(init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb);
}
;; finish initialization and start the channel
() start(slice sig_a, slice sig_b, int Ua, int Ub) {
var (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb) = get_contract_state();
throw_unless(1, init == 0);
var checking_slice = begin_cell()
.store_uint(0x02, 4)
.store_slice(my_address())
.store_uint(Ua, 214)
.store_uint(Ub, 214)
.end_cell();
var data_slice_hash = slice_hash(checking_slice.begin_parse());
throw_unless(202, check_signature(data_slice_hash, sig_a, pubA));
throw_unless(203, check_signature(data_slice_hash, sig_b, pubB));
if ((Xa < Ua) | (Xb < Ub)) {
throw(5);
}
accept_message();
if (Xa > Ua) {
send_grams(pubA, Ua - Xa);
}
if (Xb > Ub) {
send_grams(pubB, Ub - Xb);
}
Xa = 0;
Xb = 0;
init = 1;
set_contract_state(init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb);
}
;; exit with no delay if both parties signed the exit message
() fast_exit(int _amountA, slice sig_a, slice sig_b) {
var (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb) = get_contract_state();
throw_unless(1, init == 1);
var checking_slice = begin_cell()
.store_uint(0x03, 4)
.store_slice(my_address())
.store_uint(_amountA, 214)
.end_cell();
var data_slice_hash = slice_hash(checking_slice.begin_parse());
throw_unless(204, check_signature(data_slice_hash, sig_a, pubA));
throw_unless(205, check_signature(data_slice_hash, sig_b, pubB));
accept_message();
var amountA = min(_amountA, get_balance());
var amountB = get_balance() - amountA;
send_grams(amountA, pubA);
send_grams(amountB, pubB);
}
;; initialize a withdraw and start challenge period
() send_state(slice sig_a, int _Xa, slice sig_b, int _Xb, slice sig_sender) {
var (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb) = get_contract_state();
throw_unless(7, init == 1);
throw_if(8, get_balance() == 0);
var checking_slice_a = begin_cell()
.store_uint(0x04, 4)
.store_slice(my_address())
.store_uint(_Xa, 214)
.end_cell();
var checking_slice_b = begin_cell()
.store_uint(0x04, 4)
.store_slice(my_address())
.store_uint(_Xb, 214)
.end_cell();
var checking_slice_a_b = begin_cell()
.store_uint(0x04, 4)
.store_slice(my_address())
.store_uint(_Xa, 214)
.store_uint(_Xb, 214)
.end_cell();
var data_slice_hash_a = slice_hash(checking_slice_a.begin_parse());
var data_slice_hash_b = slice_hash(checking_slice_b.begin_parse());
var data_slice_hash_a_b = slice_hash(checking_slice_a_b.begin_parse());
throw_unless(206, check_signature(data_slice_hash_a, sig_a, pubA));
throw_unless(207, check_signature(data_slice_hash_b, sig_b, pubB));
var senderA = 0;
if (check_signature(data_slice_hash_a_b, sig_sender, pubA)) {
senderA = 0x1;
} elseif (check_signature(data_slice_hash_a_b, sig_sender, pubB)) {
senderA = 0x0;
} else {
throw(10);
}
accept_message();
if (timestamp == 0) {
timestamp = now() + timeout;
}
if((lockA == 0) & (Xa < _Xa)) {
Xa = _Xa;
if (senderA == false) {
lockA = 1;
}
}
if((lockB == 0) & (Xb < _Xb)) {
Xb = _Xb;
if (senderA) {
lockB = 1;
}
}
set_contract_state(init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb);
}
;; exit by timeout
() finalize(slice sig_a, int addrA, slice sig_b, int addrB) {
var (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb) = get_contract_state();
throw_unless(11, init == 1);
throw_if(12, timestamp < now() & ((lockA & lockB) == false));
var checking_slice_a = begin_cell()
.store_uint(0x06, 4)
.store_slice(my_address())
.store_uint(addrA, 256)
.end_cell();
var checking_slice_b = begin_cell()
.store_uint(0x06, 4)
.store_slice(my_address())
.store_uint(addrB, 256)
.end_cell();
var data_slice_hash_a = slice_hash(checking_slice_a.begin_parse());
var data_slice_hash_b = slice_hash(checking_slice_b.begin_parse());
throw_unless(207, check_signature(data_slice_hash_a, sig_a, pubA));
throw_unless(208, check_signature(data_slice_hash_b, sig_b, pubB));
accept_message();
var a = Xa - Xb + get_balance() / 2;
var b = get_balance() - a;
if (a < 0) {
a = 0;
}
if (a > get_balance()) {
a = get_balance();
}
send_grams(pubA, a);
send_grams(pubB, b);
}
() challenge(slice signature, int X) {
var (init, timeout, timestamp, pubA, pubB, lockA, lockB, Xa, Xb) = get_contract_state();
throw_unless(13, init == 1);
throw_if(14, get_balance() == 0);
accept_message();
var checking_slice = begin_cell()
.store_uint(0x05, 4)
.store_slice(my_address())
.store_uint(X, 214)
.end_cell();
var data_slice_hash = slice_hash(checking_slice.begin_parse());
if (check_signature(data_slice_hash, signature, pubA)) {
if ((X > Xa) & lockA & (lockB == false)) {
;; send_grams(pubB, get_balance());
Xb = get_balance() / 2;
Xa = 0;
lockB = 1;
}
} elseif (check_signature(data_slice_hash, signature, pubB)) {
if ((X > Xb) & lockB & (lockA == false)) {
;; send_grams(pubA, get_balance());
Xa = 0;
Xb = get_balance() / 2;
flagA = 1;
}
} else {
throw(15);
}
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
;; probably standard transfers have different serialization format
var cs = in_msg_cell.begin_parse();
var pubkey = cs~load_uint(256);
deposit(pubkey, msg_value);
cs.end_parse();
}
;; 0x4e43ef05 -> "() change_elector_code(slice cs)"
() recv_external(slice in_msg) impure {
var cs = in_msg;
;; tag = hash(method_name)[0:4]
var tag = cs~load_uint(4);
if (tag == 0x00000000) {
var signature = cs~load_bits(512);
cs.end_parse();
cancel(signature);
} elseif (tag == 0x00000001) {
var sig_a = cs~load_bits(512);
var Ua = cs~load_uint(214);
var sig_b = cs~load_bits(512);
var Ub = cs~load_uint(214);
cs.end_parse();
start(sig_a, sig_b, Ua, Ub);
} elseif (tag == 0x00000002) {
var _amountA = cs~load_uint(214);
var sig_a = cs~load_bits(512);
var sig_b = cs~load_bits(512);
cs.end_parse();
fast_exit(_amountA, sig_a, sig_b);
} elseif (tag == 0x00000003) {
var sig_a = cs~load_bits(512);
var _Xa = cs~load_uint(214);
var sig_b = cs~load_bits(512);
var _Xb = cs~load_uint(214);
var sender_sig = cs~load_bits(512);
cs.end_parse();
send_state(sig_a, _Xa, sig_b, _Xb, sender_sig);
} elseif (tag == 0x00000004) {
var signature = cs~load_bits(512);
var X = cs~load_uint(214);
cs.end_parse();
challenge(signature, X);
}
}
```