;; The validator has his own wallet in the masterchain, on which he holds his own coins for operating.
;; From this wallet he sends commands to this nominator pool (mostly `new_stake`, `update_validator_set` and `recover_stake`).
;; Register/vote_for complaints and register/vote_for config proposals are sent from validator's wallet.
;;
;; Pool contract must be in masterchain.
;; Nominators' wallets must be in the basechain.
;; The validator in most cases have two pools (for even and odd validation rounds).
#include "stdlib.fc";
int op::new_stake() asm "0x4e73744b PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L621
int op::new_stake_error() asm "0xee6f454c PUSHINT"; ;; return_stake https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L169
int op::new_stake_ok() asm "0xf374484c PUSHINT"; ;; send_confirmation https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L173
int op::recover_stake() asm "0x47657424 PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L625
int op::recover_stake_error() asm "0xfffffffe PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L407
int op::recover_stake_ok() asm "0xf96f7324 PUSHINT"; ;; https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L426
int ADDR_SIZE() asm "256 PUSHINT";
int BOUNCEABLE() asm "0x18 PUSHINT";
int NON_BOUNCEABLE() asm "0x10 PUSHINT";
int SEND_MODE_PAY_FEE_SEPARATELY() asm "1 PUSHINT"; ;; means that the sender wants to pay transfer fees separately
int SEND_MODE_IGNORE_ERRORS() asm "2 PUSHINT"; ;; means that any errors arising while processing this message during the action phase should be ignored
int SEND_MODE_REMAINING_AMOUNT() asm "64 PUSHINT"; ;; is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message
int ONE_TON() asm "1000000000 PUSHINT";
int MIN_TONS_FOR_STORAGE() asm "10000000000 PUSHINT"; ;; 10 TON
int DEPOSIT_PROCESSING_FEE() asm "1000000000 PUSHINT"; ;; 1 TON
int MIN_STAKE_TO_SEND() asm "500000000000 PUSHINT"; ;; 500 TON
int VOTES_LIFETIME() asm "2592000 PUSHINT"; ;; 30 days
int binary_log_ceil(int x) asm "UBITSIZE";
;; hex parse same with bridge https://github.com/ton-blockchain/bridge-func/blob/d03dbdbe9236e01efe7f5d344831bf770ac4c613/func/text_utils.fc
(slice, int) ~load_hex_symbol(slice comment) {
int n = comment~load_uint(8);
n = n - 48;
throw_unless(329, n >= 0);
if (n < 10) {
return (comment, (n));
}
n = n - 7;
throw_unless(329, n >= 0);
if (n < 16) {
return (comment, (n));
}
n = n - 32;
throw_unless(329, (n >= 0) & (n < 16));
return (comment, n);
}
(slice, int) ~load_text_hex_number(slice comment, int byte_length) {
int current_slice_length = comment.slice_bits() / 8;
(int validators_elected_for, int elections_start_before, int elections_end_before, int stake_held_for) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs.preload_uint(32));
cell pack_config(int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake) inline_ref {
() save_data(int state, int nominators_count, int stake_amount_sent, int validator_amount, cell config, cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) impure inline_ref {
if ((op == op::new_stake()) & (is_elector_address(sender_wc, sender_addr))) {
;; `new_stake` from nominator-pool should always be handled without throws by elector
;; because nominator-pool do `check_new_stake_msg` and `msg_value` checks before sending `new_stake`.
;; If the stake is not accepted elector will send `new_stake_error` response message.
;; Nevertheless we do process theoretically possible bounced `new_stake`.
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
if (op == 0) {
;; We use simple text comments for nominator operations so nominators can do it from any wallet app.
;; In other cases, they will need to put a stake on a browser extension, or use scripts, which can be inconvenient.
;; Throw on any unexpected request so that the stake is bounced back to the nominator in case of a typo.
int action = in_msg_body~load_uint(8);
int is_vote = (action == 121) | (action == 110); ;; "y" or "n"
throw_unless(64, (action == 100) | (action == 119) | is_vote); ;; "d" or "w" or "y" or "n"
if (~ is_vote) {
in_msg_body.end_parse();
throw_unless(61, sender_wc == 0); ;; nominators only in basechain
;; We need to be guaranteed to wait for unfreezing round and only then send `recover_stake`.
;; So we are waiting for the change of 3 validator sets.
;; ADDITIONAL NOTE:
;; In a special case (if the network was down), the config theoretically can refuse the elector to save a new round after election - https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/config-code.fc#L494
;; and the elector will start a new election - https://github.com/ton-blockchain/ton/blob/b38d227a469666d83ac535ad2eea80cb49d911b8/crypto/smartcont/elector-code.fc#L364
;; in this case, our pool will have to skip the round, but it will be able to recover stake later
send_msg(elector_address(), 0, payload, BOUNCEABLE(), SEND_MODE_REMAINING_AMOUNT()); ;; bounceable, carry all the remaining value of the inbound message, fee deducted from amount, revert on errors
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
(slice nominator, int found) = nominators.udict_get?(ADDR_SIZE(), nominator_address);
throw_unless(86, found);
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
(slice withdraw_request, int withdraw_found) = withdraw_requests.udict_get?(ADDR_SIZE(), nominator_address);
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
var list = null();
int address = -1;
do {
(address, slice nominator, int found) = nominators.udict_get_next?(ADDR_SIZE(), address);
if (found) {
(int amount, int pending_deposit_amount) = unpack_nominator(nominator);
(_, int withdraw_requested) = withdraw_requests.udict_get?(ADDR_SIZE(), address);
list = cons(tuple4(address, amount, pending_deposit_amount, withdraw_requested), list);
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
var list = null();
int proposal_hash = -1;
do {
(proposal_hash, slice votes_slice, int found) = config_proposal_votings.udict_get_next?(256, proposal_hash);
if (found) {
(cell votes_dict, int votes_create_time) = (votes_slice~load_dict(), votes_slice~load_uint(32));
list = cons(pair(proposal_hash, votes_create_time), list);
(int state, int nominators_count, int stake_amount_sent, int validator_amount, (int validator_address, int validator_reward_share, int max_nominators_count, int min_validator_stake, int min_nominator_stake), cell nominators, cell withdraw_requests, int stake_at, int saved_validator_set_hash, int validator_set_changes_count, int validator_set_change_time, int stake_held_for, cell config_proposal_votings) = load_data();
var list = null();
(slice votes_slice, int found) = config_proposal_votings.udict_get?(256, proposal_hash);
throw_unless(133, found);
cell votes_dict = votes_slice~load_dict();
int address = -1;
do {
(address, slice cs, int found) = votes_dict.udict_get_next?(ADDR_SIZE(), address);
if (found) {
(int support, int vote_time) = (cs~load_int(1), cs~load_uint(32));
list = cons(triple(address, support, vote_time), list);