[
"autonomous agent",
{
"doc_url": "https://obyte.org/bank.json",
"getters": "{
$get_balance = ($address, $asset) => {
var['balance_' || $address || '_' || $asset] OTHERWISE 0
};
$get_payment_messages = ($payments) => {
$payment_messages_by_asset = {};
$buffer_recipients = [];
$payment_messages = [];
foreach($payments, 5, $payment => {
if (!$payment.amount)
return;
if ($payment.is_aa){
delete($payment, 'is_aa');
$buffer_recipients[] = $payment;
$address = this_address;
}
else
$address = $payment.address;
if ($payment_messages_by_asset[$payment.asset])
$payment_messages_by_asset[$payment.asset].payload.outputs[] = {address: $address, amount: $payment.amount};
else
$payment_messages_by_asset[$payment.asset] = {
app: 'payment',
payload: {
asset: $payment.asset,
outputs: [
{address: $address, amount: $payment.amount}
]
}
};
});
foreach($payment_messages_by_asset, 5, $payment_message => {
$payment_messages[] = $payment_message;
});
{
payment_messages: $payment_messages,
buffer_recipients: $buffer_recipients,
}
};
}",
"init": "{
$withdrawal_fee = 2000;
if (exists(trigger.data.to) AND !is_valid_address(trigger.data.to))
bounce("bad to-address");
$get_amount = ($amount, $balance) => {
if ($amount == 'all')
return $balance;
if (!is_integer($amount) OR $amount <= 0)
bounce("bad amount: " || $amount);
$amount
};
// a command to transfer or withdraw can be signed by an authorized key and submitted by anybody
$get_owner = ($message_to_sign) => {
if (!trigger.data.owner)
return trigger.address;
if (!trigger.data.pubkey)
bounce("no pubkey");
if (!trigger.data.signature)
bounce("no signature");
if (!trigger.data.nonce) // to avoid replay
bounce("no nonce");
$pubkey_hash = sha256(trigger.data.pubkey);
if (!var['key_' || trigger.data.owner || '_' || $pubkey_hash])
bounce("this key is not auhorized to sign for this owner");
if (var['nonce_' || $pubkey_hash || '_' || trigger.data.nonce])
bounce("this nonce was already used");
$full_message_to_sign = $message_to_sign || " with nonce " || trigger.data.nonce;
if (!is_valid_sig($full_message_to_sign, trigger.data.pubkey, trigger.data.signature))
bounce("invalid signature");
trigger.data.owner
};
$write_nonce = () => {
if (trigger.data.owner)
var['nonce_' || sha256(trigger.data.pubkey) || '_' || trigger.data.nonce] = 1;
};
}",
"messages": {
"cases": [
{
"if": "{trigger.data.withdraw AND trigger.data.asset AND trigger.data.amount}",
"init": "{
$owner = $get_owner("withdraw " || trigger.data.amount || " " || trigger.data.asset || " to " || (trigger.data.to OTHERWISE "self"));
$to = trigger.data.to OTHERWISE $owner;
$base_amount = trigger.output[[asset=base]];
$key = 'balance_' || $owner || '_' || trigger.data.asset;
$base_key = 'balance_' || $owner || '_base';
$fee = (trigger.data.asset == 'base') ? $withdrawal_fee : 0;
$balance = var[$key];
if (!$balance)
bounce("you have no balance in this asset");
$amount = $get_amount(trigger.data.amount, $balance - $fee);
$required_amount = $amount + $fee;
if ($required_amount > $balance)
bounce("trying to withdraw more than you have: " || $required_amount || " > " || $balance);
if ($withdrawal_fee > var[$base_key] + $base_amount)
bounce("not enough bytes to pay the withdrawal fee, please add some bytes first");
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "{trigger.data.asset}",
"outputs": [
{
"address": "{$to}",
"amount": "{$amount}"
}
]
}
},
{
"app": "state",
"state": "{
var[$key] -= $amount;
var[$base_key] += - $withdrawal_fee + $base_amount;
$write_nonce();
}"
}
]
},
{
"if": "{trigger.data.withdraw AND trigger.data.recipients}",
"init": "{
$owner = trigger.address;
if (!is_array(trigger.data.recipients))
bounce("recipients must be array");
if (length(trigger.data.recipients) > 5)
bounce("too many recipients, max 5");
$fee = $withdrawal_fee * length(trigger.data.recipients);
$totals = { base: $fee };
foreach(trigger.data.recipients, 5, $recipient => {
if (!is_integer($recipient.amount) OR $recipient.amount < 0)
bounce("bad amount: " || $recipient.amount);
if (!is_valid_address($recipient.address))
bounce("bad address: " || $recipient.address);
if ($recipient.is_aa)
bounce("is_aa not allowed here");
$totals[$recipient.asset] = $totals[$recipient.asset] + $recipient.amount;
});
foreach($totals, 5, ($asset, $total) => {
$balance = var['balance_' || $owner || '_' || $asset];
if (!$balance)
bounce("you have no balance in " || $asset);
if ($total > $balance)
bounce("not enough balance in " || $asset);
});
$res = $get_payment_messages(trigger.data.recipients);
$payment_messages = $res.payment_messages;
$data = trigger.data;
delete($data, 'recipients');
delete($data, 'withdraw');
}",
"messages": [
"{$payment_messages[0] OTHERWISE ''}",
"{$payment_messages[1] OTHERWISE ''}",
"{$payment_messages[2] OTHERWISE ''}",
"{$payment_messages[3] OTHERWISE ''}",
"{$payment_messages[4] OTHERWISE ''}",
{
"if": "{length($data) > 0}",
"app": "data",
"payload": "{$data}"
},
{
"app": "state",
"state": "{
foreach($totals, 5, ($asset, $total) => {
var['balance_' || $owner || '_' || $asset] -= $total;
});
}"
}
]
},
{
"if": "{trigger.data.transfer AND trigger.data.to AND trigger.data.asset AND trigger.data.amount}",
"init": "{
$owner = $get_owner("transfer " || trigger.data.amount || " " || trigger.data.asset || " to " || trigger.data.to);
$base_amount = trigger.output[[asset=base]];
$base_key = 'balance_' || $owner || '_base';
$key = 'balance_' || $owner || '_' || trigger.data.asset;
$balance = var[$key];
if (!$balance)
bounce("you have no balance in this asset");
$amount = $get_amount(trigger.data.amount, $balance);
if ($amount > $balance)
bounce("trying to transfer more than you have: " || $balance);
}",
"messages": [
{
"app": "state",
"state": "{
var[$key] -= $amount;
var['balance_' || trigger.data.to || '_' || trigger.data.asset] += $amount;
var[$base_key] += $base_amount;
$write_nonce();
}"
}
]
},
{
"if": "{trigger.data.authorize AND trigger.data.pubkey}",
"init": "{
if (length(trigger.data.pubkey) > 1000)
bounce("pubkey is too long");
}",
"messages": [
{
"app": "state",
"state": "{
var['key_' || trigger.address || '_' || sha256(trigger.data.pubkey)] = 1;
response['message'] = "authorized the key to sign transactions on your behalf";
}"
}
]
},
{
"if": "{trigger.data.revoke AND trigger.data.pubkey}",
"init": "{
if (length(trigger.data.pubkey) > 1000)
bounce("pubkey is too long");
}",
"messages": [
{
"app": "state",
"state": "{
var['key_' || trigger.address || '_' || sha256(trigger.data.pubkey)] = false;
response['message'] = "revoked the key";
}"
}
]
},
{
"if": "{trigger.data.recipients}",
"init": "{
$totals = {};
foreach(trigger.data.recipients, 10, $recipient => {
if (!is_integer($recipient.amount) OR $recipient.amount < 0)
bounce("bad amount: " || $recipient.amount);
if (!is_valid_address($recipient.address))
bounce("bad address: " || $recipient.address);
if (!trigger.output[[asset=$recipient.asset]])
bounce("nothing received in asset " || $recipient.asset);
$totals[$recipient.asset] = $totals[$recipient.asset] + $recipient.amount;
});
foreach($totals, 10, ($asset, $total) => {
if ($total != trigger.output[[asset=$asset]])
bounce("expected " || $total || " for asset " || $asset);
});
// if we receive more assets than is mentioned in recipients, we silently eat them
}",
"messages": [
{
"app": "state",
"state": "{
foreach(trigger.data.recipients, 10, $recipient => {
$asset_key = 'balance_' || $recipient.address || '_' || $recipient.asset;
var[$asset_key] += $recipient.amount;
});
}"
}
]
},
{
"messages": [
{
"app": "state",
"state": "{
$asset = trigger.output[[asset!=base]].asset;
if ($asset == 'ambiguous')
bounce('ambiguous asset');
$to = trigger.data.to OTHERWISE trigger.address;
$base_amount = trigger.output[[asset=base]];
$base_key = 'balance_' || $to || '_base';
var[$base_key] += $base_amount;
$response_base = $base_amount || ' bytes\
';
if ($asset != 'none'){
$asset_key = 'balance_' || $to || '_' || $asset;
var[$asset_key] += trigger.output[[asset=$asset]];
$response_asset = trigger.output[[asset=$asset]] || ' of ' || $asset || '\
';
}
response['message'] = 'accepted coins:\
' || ($response_base otherwise '') || ($response_asset otherwise '');
}"
}
]
}
]
}
}
]