Definition: [
"autonomous agent",
{
"doc_url": "https://oswap.io/v1-v2-arb.json",
"getters": "{
}",
"init": "{
$v1_aa = params.oswap_v1_aa;
$v2_aa = params.oswap_v2_aa;
$v1_params = definition[$v1_aa][1].params;
$v2_params = definition[$v2_aa][1].params;
$x_asset = $v2_params.x_asset;
$y_asset = $v2_params.y_asset;
$non_base = $x_asset != 'base' AND $y_asset != 'base';
require(
$v1_params.asset0 == $x_asset AND $v1_params.asset1 == $y_asset
OR $v1_params.asset0 == $y_asset AND $v1_params.asset1 == $x_asset,
'non-matching assets'
);
$get_param = ($name, $default) => {
$value = var[$v2_aa][$name];
exists($value) ? $value : (exists($v2_params[$name]) ? $v2_params[$name] : $default)
};
$get_shifts = () => {
$mid_price = $get_param('mid_price', 0); // price of x_asset in terms of y_asset
if ($mid_price){
$alpha = $get_param('alpha', 0.5);
$beta = 1 - $alpha;
$gamma = $get_param('price_deviation', 0);
$lp_shares = var[$v2_aa]['lp_shares'];
$s_curve = $lp_shares.linear * $lp_shares.coef;
$x0 = $s_curve / $mid_price^$beta / $gamma;
$y0 = $x0 * $mid_price;
}
// else{
// $x0 = 0;
// $y0 = 0;
// }
{x0: $x0, y0: $y0}
};
$get_denom = ($balances, $l_balances, $shifts, $pxy, $beta) => {
$leverages = [2, 5, 10, 20, 50, 100];
// account for leveraged positions
$L_sums = {x: 0};
foreach($leverages, 6, $L => {
$L_sums.x = $L_sums.x + ($L - 1) * ($l_balances[$L||'x'].balance + $l_balances[-$L||'x'].balance / $pxy);
});
$denom = $beta * ($balances.x + $shifts.x0) - $L_sums.x;
// log({denom: $denom, sum: $L_sums.x, share: $L_sums.x/$denom});
require($denom > 0, "negative denom " || $denom);
$denom
};
$get_available_balance = ($asset) => balance[$asset] - ($asset == 'base' ? 10000 : 0);
$scale_amounts_down_if_necessary = ($amounts, $x_balance, $y_balance) => {
if ($amounts.x > $x_balance AND $amounts.y > $y_balance){
$x_factor = $x_balance/$amounts.x;
$y_factor = $y_balance/$amounts.y;
if ($x_factor >= $y_factor){
$amounts.x = $x_balance;
$amounts.y = $x_factor * $amounts.y;
}
else {
$amounts.y = $y_balance;
$amounts.x = $y_factor * $amounts.x;
}
log('scaled down to', $amounts);
}
};
}",
"messages": {
"cases": [
{
"if": "{ trigger.data.arb}",
"init": "{
$share = trigger.data.share OTHERWISE 1; // for incomplete arb
$precalculated = trigger.data.amount AND trigger.data.from;
if (!$precalculated) {
$v1_fee = $v1_params.swap_fee / 1e11;
$v2_fee = $get_param('swap_fee', 0.003);
$v1_x_balance = balance[$v1_aa][$x_asset];
$v1_y_balance = balance[$v1_aa][$y_asset];
$p1 = $v1_y_balance / $v1_x_balance;
$balances = var[$v2_aa]['balances'];
$l_balances = var[$v2_aa]['leveraged_balances'];
$shifts = $get_shifts();
$alpha = $get_param('alpha', 0.5);
$beta = 1 - $alpha;
$p2 = $alpha/$beta * ($balances.y + $shifts.y0) / ($balances.x + $shifts.x0);
$denom1 = $v1_x_balance/2; // beta = 1/2
$denom2 = $get_denom($balances, $l_balances, $shifts, $p2, $beta);
$Lambda = $get_param('pool_leverage', 1);
$x_balance = $get_available_balance($x_asset);
$y_balance = $get_available_balance($y_asset);
$amounts = {x: 0, y: 0};
// profitable to buy x on v2 and sell on v1
if ($p2 * (1 + $v2_fee) < $p1 * (1 - $v1_fee)){
$p2_plus_fee = $p2 * (1 + $v2_fee);
$p1_minus_fee = $p1 * (1 - $v1_fee);
$amounts.x = ($p1_minus_fee - $p2_plus_fee)/$p1_minus_fee / (1/$denom2 + 1/$denom1);
$amounts.y = $p1_minus_fee * $amounts.x; // overestimate
$scale_amounts_down_if_necessary($amounts, $x_balance, $y_balance);
$final_p = $p2 * (1 + $amounts.x/$denom2 * $share);
// log({p1: $p1, p2: $p2, p1_minus_fee: $p1_minus_fee, p2_plus_fee: $p2_plus_fee, final_p: $final_p, denom2: $denom2, amounts: $amounts});
if ($amounts.y <= $y_balance){
$from = 'v2y';
$asset = $y_asset;
$amount = ceil($amounts.y);
$address = $v2_aa;
$data = {
final_price: $final_p,
hops: [
{ // next oswap in the chain
address: $v1_aa,
change_address: this_address,
data: {to: this_address},
},
]
};
}
else{
$from = 'v1x';
$asset = $x_asset;
$amount = ceil($amounts.x);
$address = $v1_aa;
$data = {to: this_address}; // v1 sends y back to us, and we then send it to v2
$stored_data = {
final_price: $final_p,
};
}
}
// profitable to buy x on v1 and sell on v2
else if ($p2 * (1 - $v2_fee) > $p1 * (1 + $v1_fee)){
$p2_minus_fee = $p2 * (1 - $v2_fee);
$p1_plus_fee = $p1 * (1 + $v1_fee);
$amounts.x = ($p2_minus_fee - $p1_plus_fee)/$p2_minus_fee / (1/$denom2 + 1/$denom1);
$amounts.y = $amounts.x * $p2_minus_fee; // overestimate
$scale_amounts_down_if_necessary($amounts, $x_balance, $y_balance);
$final_pyx = 1/$p2 * (1 + $amounts.x/($denom2 + $beta * $Lambda * $amounts.x) * $share);
// log({p1: $p1, p2: $p2, p1_plus_fee: $p1_plus_fee, p2_minus_fee: $p2_minus_fee, final_pyx: $final_pyx, denom2: $denom2, amounts: $amounts});
if ($amounts.x <= $x_balance){
$from = 'v2x';
$asset = $x_asset;
$amount = ceil($amounts.x);
$address = $v2_aa;
$data = {
final_price: $final_pyx,
hops: [
{ // next oswap in the chain
address: $v1_aa,
change_address: this_address,
data: {to: this_address},
},
]
};
}
else {
$from = 'v1y';
$asset = $y_asset;
$amount = ceil($amounts.y);
$address = $v1_aa;
$data = {to: this_address}; // v1 sends x back to us, and we then send it to v2
$stored_data = {
final_price: $final_pyx,
};
}
}
else
bounce("no arb opportunity exists");
}
else { // overridden in the request, don't calc
$amount = trigger.data.amount;
$from = trigger.data.from;
if ($from == 'v2y'){
$asset = $y_asset;
$address = $v2_aa;
$data = {
final_price: trigger.data.final_p,
hops: [
{ // next oswap in the chain
address: $v1_aa,
change_address: this_address,
data: {to: this_address},
},
]
};
}
else if ($from == 'v1x'){
$asset = $x_asset;
$address = $v1_aa;
$data = {to: this_address}; // v1 sends y back to us, and we then send it to v2
$stored_data = {
final_price: trigger.data.final_p,
};
}
else if ($from == 'v2x'){
$asset = $x_asset;
$address = $v2_aa;
$data = {
final_price: trigger.data.final_pyx,
hops: [
{ // next oswap in the chain
address: $v1_aa,
change_address: this_address,
data: {to: this_address},
},
]
};
}
else if ($from == 'v1y'){
$asset = $y_asset;
$address = $v1_aa;
$data = {to: this_address}; // v1 sends x back to us, and we then send it to v2
$stored_data = {
final_price: trigger.data.final_pyx,
};
}
}
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "{$asset}",
"outputs": [
{
"address": "{$address}",
"amount": "{ $amount }"
}
]
}
},
{
"if": "{$non_base}",
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$address}",
"amount": 3000
}
]
}
},
{
"app": "data",
"payload": "{$data}"
},
{
"app": "state",
"state": "{
$arb = {
from: $from,
avg_price: ($p1 + $p2) / 2,
sent: {},
received: {},
};
$arb.sent[$asset] = $amount;
if ($stored_data)
$arb.stored_data = $stored_data;
var['arb'] = $arb;
// log({arb: $arb});
response['sent_amount'] = $amount;
if ($from == 'v2y')
response['message'] = 'will arb by buying X from v2 and selling to v1';
else if ($from == 'v1x')
response['message'] = 'will arb by selling X to v1 and buying from v2';
else if ($from == 'v2x')
response['message'] = 'will arb by selling X to v2 and buying from v1';
else if ($from == 'v1y')
response['message'] = 'will arb by buying X from v1 and selling to v2';
}"
}
]
},
{
"if": "{
$received_x = trigger.output[[asset=$x_asset]];
$received_y = trigger.output[[asset=$y_asset]];
$arb = var['arb'];
($received_x > 0 OR $received_y > 0) AND (trigger.address == $v1_aa OR trigger.address == $v2_aa) AND $arb
}",
"init": "{
$from_v1 = $arb.from == 'v1x' OR $arb.from == 'v1y';
if ($from_v1)
require($arb.stored_data, 'no data for v2');
$forward = $from_v1 AND trigger.address == $v1_aa;
if ($forward){
if ($received_x > 0 AND $received_y > 0)
bounce('received both assets from v1');
$received_asset = $received_x > 0 ? $x_asset : $y_asset;
}
}",
"messages": [
{
"if": "{$forward}",
"app": "payment",
"payload": {
"asset": "{$received_asset}",
"outputs": [
{
"address": "{$v2_aa}",
"amount": "{ $received_x OTHERWISE $received_y }"
}
]
}
},
{
"if": "{$forward AND $non_base}",
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$v2_aa}",
"amount": 3000
}
]
}
},
{
"if": "{$forward}",
"app": "data",
"payload": "{$arb.stored_data}"
},
{
"app": "state",
"state": "{
if ($forward){
response['message'] = 'forwarded to v2';
// log('forwarded to v2', $received_x, $received_y);
return;
}
$arb.received[$x_asset] = $arb.received[$x_asset] + $received_x;
$arb.received[$y_asset] = $arb.received[$y_asset] + $received_y;
$change = !$from_v1 AND trigger.address == $v2_aa;
if ($change){
var['arb'] = $arb;
response['message'] = 'received the change from v2';
// log('received the change from v2', $received_x, $received_y);
return;
}
// log('received proceeds', $received_x, $received_y);
$sent = $arb.sent[$y_asset] + $arb.sent[$x_asset] * $arb.avg_price;
$received = $arb.received[$y_asset] + $arb.received[$x_asset] * $arb.avg_price;
$profit = $received - $sent;
// log({p1: balance[$v1_aa][$y_asset]/balance[$v1_aa][$x_asset], p2: $v2_aa#28.$get_price('x'), arb: $arb});
require($profit > 0, "unprofitable " || $arb.from || ": " || $received || " < " || $sent);
response['profit'] = $profit; // in Y
var['arb'] = false;
}"
}
]
},
{
"if": "{ trigger.data.withdraw AND trigger.data.asset AND trigger.address == params.owner }",
"init": "{
if (trigger.data.asset == 'x')
$asset = $x_asset;
else if (trigger.data.asset == 'y')
$asset = $y_asset;
else
$asset = trigger.data.asset;
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "{$asset}",
"outputs": [
{
"address": "{params.owner}",
"amount": "{ trigger.data.amount OTHERWISE '' }"
}
]
}
}
]
},
{
"if": "{ trigger.output[[asset=$x_asset]] > 0 OR trigger.output[[asset=$y_asset]] > 0 }",
"messages": [
{
"app": "state",
"state": "{
response['message'] = 'added';
}"
}
]
}
]
}
}
]