Definition: [
"autonomous agent",
{
"doc_url": "https://friends.obyte.org/governance.json",
"init": "{
$year = 31536000; // 365 * 24 * 3600;
$day = 86400; // 24 * 3600;
$challenging_period = params.challenging_period OTHERWISE $day; // 1 day
$friend_aa = params.friend_aa;
require($friend_aa, "no friend_aa");
$names = [
'rewards_aa',
'messaging_attestors',
'real_name_attestors',
'referrer_deposit_reward_share',
'followup_reward_share',
'min_balance_instead_of_real_name',
'deposit_asset',
];
$count_names = 7;
require(length($names) == $count_names, "wrong number of governable parameters");
$is_allowed_name = $name => {
length(filter($names, $count_names, $n => $n == $name)) == 1
};
$get_sqrt_balance = ($address, $bThrow) => {
$user = var[$friend_aa]['user_'||$address];
require($user, "you are not a user here");
$min_unlock_date = timestamp_to_string(timestamp + $day, 'date');
if ($user.unlock_date < $min_unlock_date){
if ($bThrow)
bounce("your unlock date must be "||$min_unlock_date||" or later");
return 0;
}
// balances in deposit assets don't count
$balance = $user.balances.frd + $user.balances.base / $friend_aa#2.$get_ceiling_price();
if (!$balance){
if ($bThrow)
bounce("you have no locked balance and cannot vote");
return 0;
}
sqrt($balance)
};
$get_value_key = $value => $value;
}",
"messages": {
"cases": [
{
"if": "{ trigger.data.name AND trigger.data.commit }",
"init": "{
$name = trigger.data.name;
if ($name == 'deposit_asset'){
$deposit_asset = trigger.data.deposit_asset;
require(typeof($deposit_asset) == 'string' AND length($deposit_asset) == 44, "invalid deposit asset");
$full_name = $name||'_'||$deposit_asset;
}
else
$full_name = $name;
$leader = var['leader_' || $full_name];
require(exists($leader), "no leader"); // can be 0
$current_value = var[$full_name];
if (exists($current_value) AND $leader == $current_value)
bounce("already equal to leader");
require(var['challenging_period_start_ts_' || $full_name] + $challenging_period < timestamp, "challenging period not expired yet");
$payload = {
name: $name,
value: $leader,
};
if ($name == 'deposit_asset')
$payload.deposit_asset = $deposit_asset;
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$friend_aa}",
"amount": 5000
}
]
}
},
{
"app": "data",
"payload": "{$payload}"
},
{
"app": "state",
"state": "{
var[$full_name] = $leader;
}"
}
]
},
{
"if": "{ trigger.data.name }",
"init": "{
$name = trigger.data.name;
$value = trigger.data.value; // can be empty to remove one's vote
require($is_allowed_name($name), "unknown name: " || $name);
if ($name == 'deposit_asset'){
$deposit_asset = trigger.data.deposit_asset;
require(typeof($deposit_asset) == 'string' AND length($deposit_asset) == 44, "invalid deposit asset");
$full_name = $name||'_'||$deposit_asset;
}
else
$full_name = $name;
$sqrt_balance = $get_sqrt_balance(trigger.address, true);
if (exists($value)){
if ($name == 'rewards_aa' OR $name == 'messaging_attestors' OR $name == 'real_name_attestors' OR $name == 'deposit_asset'){
require(typeof($value) == 'string', "must be a string");
if ($name == 'rewards_aa' OR $name == 'deposit_asset'){
require(is_aa($value), "not an AA");
if ($name == 'deposit_asset') // just check that it doesn't throw
$friend_aa#2.$get_deposit_asset_exchange_rates_on_aa($deposit_asset, $value);
}
else{
$arr = split($value, ':');
require(length($arr) > 0, "no attestors");
foreach($arr, 5, $addr => { require(is_valid_address($addr), "invalid attestor address"); });
}
}
else{
require(typeof($value) == 'number' AND $value >= 0, "must be a nonnegative number");
if ($name == 'min_balance_instead_of_real_name')
require(is_integer($value), "value must be integer");
else
require($value < 1, "value must be < 1");
}
}
}",
"messages": [
{
"app": "state",
"state": "{
$votes = var['votes_'||trigger.address] OTHERWISE {};
$prev_choice = $votes[$full_name];
$leader = var['leader_' || $full_name];
var['choice_' || trigger.address || '_' || $full_name] = $value;
if (exists($prev_choice)){
$prev_choice_key = $get_value_key($prev_choice.value);
var['support_' || $full_name || '_' || $prev_choice_key] -= $prev_choice.sqrt_balance;
delete($votes, $full_name);
}
if (exists($value)){
$value_key = $get_value_key($value);
var['support_' || $full_name || '_' || $value_key] += $sqrt_balance;
$votes[$full_name] = { value: $value, sqrt_balance: $sqrt_balance };
if (!exists($leader) OR var['support_' || $full_name || '_' || $value_key] > var['support_' || $full_name || '_' || $get_value_key($leader)]){
var['leader_' || $full_name] = $value;
var['challenging_period_start_ts_' || $full_name] = timestamp;
}
}
var['votes_'||trigger.address] = $votes;
}"
}
]
},
{
"if": "{ trigger.data.update_user_balance AND trigger.data.address }",
"messages": [
{
"app": "state",
"state": "{
$votes = var['votes_'||trigger.data.address];
if (!$votes) return;
$sqrt_balance = $get_sqrt_balance(trigger.data.address, false);
foreach($votes, $count_names, ($name, $vote) => {
$value_key = $get_value_key($vote.value);
var['support_' || $name || '_' || $value_key] += $sqrt_balance - $vote.sqrt_balance;
$votes[$name].sqrt_balance = $sqrt_balance;
});
var['votes_'||trigger.data.address] = $votes;
}"
}
]
}
]
}
}
]