Closed BlinkyStitt closed 4 years ago
Hey @WyseNynja, typically we want to keep contract sizes down and instead refer developers to querying the contract for these kinds of things, calculating values off-chain using on-chain information as reference.
Right now synthetix-js is the best place to find JavaScript-based functionality to query the Synthetix contracts without having to load the ABIs yourself.
For example, to figure out how much SNX is left in the ArbRewarder:
const { SynthetixJs } = require('synthetix-js'); // assumes node.js - if using browser then SynthetixJs is attached to `window`
const snxjs = new SynthetixJs();
const arbRewarder = '0xA6B5E74466eDc95D0b6e65c5CBFcA0a676d893a4';
snxjs.Synthetix.balanceOf(arbRewarder).then(value => {
const snxRemaining = snxjs.utils.formatEther(value);
console.log('The ArbRewarder has', snxRemaining, 'SNX remaining');
snxjs.ExchangeRates.ratesForCurrencies(['SNX','ETH'].map(snxjs.utils.toUtf8Bytes32).then(([snx, eth]) => {
const snxRate = snxjs.utils.formatEther(snx);
const ethRate = snxjs.utils.formatEther(eth);
// now you have the SNXUSD and ETHUSD rates, figure out how much ETH you can send
const maxInputEther = snxRemaining * snxRate / ethRate;
// huge success
});
});
Thank you. This is helpful.
However, I don't have a javascript environment setup. Can you please tell me what ['SNX','ETH'].map(snxjs.utils.toUtf8Bytes32)
returns?
Nevermind. I figured it out:
assert_eq!(snx_bytes32, [83, 78, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(eth_bytes32, [69, 84, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
Are you sure const maxInputEther = snxRemaining * snxRate / ethRate
is correct?
The arbSynthRate function does a lot more things and it includes the uniswap sETH exchange balance:
/**
* Here the caller gives us some ETH. We convert the ETH->sETH and reward the caller with SNX worth
* the value of the sETH received from the earlier swap.
*/
function arbSynthRate() public payable
rateNotStale("ETH")
rateNotStale("SNX")
notPaused
returns (uint reward_tokens)
{
/* Ensure there is enough more sETH than ETH in the Uniswap pool */
uint seth_in_uniswap = synth.balanceOf(uniswapAddress);
uint eth_in_uniswap = uniswapAddress.balance;
require(eth_in_uniswap.divideDecimal(seth_in_uniswap) < uint(divisor-off_peg_min).divideDecimal(divisor), "sETH/ETH ratio is too high");
/* Get maximum ETH we'll convert for caller */
uint max_eth_to_convert = maxConvert(eth_in_uniswap, seth_in_uniswap, divisor, divisor-off_peg_min);
uint eth_to_convert = min(msg.value, max_eth_to_convert);
uint unspent_input = msg.value - eth_to_convert;
/* Actually swap ETH for sETH */
uint min_seth_bought = expectedOutput(uniswapExchange, eth_to_convert);
uint tokens_bought = uniswapExchange.ethToTokenSwapInput.value(eth_to_convert)(min_seth_bought, now + max_delay);
/* Reward caller */
reward_tokens = rewardCaller(tokens_bought, unspent_input);
}
function maxConvert(uint a, uint b, uint n, uint d) private pure returns (uint result) {
result = (sqrt((a * (9*a*n + 3988000*b*d)) / n) - 1997*a) / 1994;
}
function rewardCaller(uint bought, uint unspent_input)
private
returns
(uint reward_tokens)
{
uint snx_rate = exchangeRates.rateForCurrency("SNX");
uint eth_rate = exchangeRates.rateForCurrency("ETH");
reward_tokens = eth_rate.multiplyDecimal(bought).divideDecimal(snx_rate);
synthetix.transfer(msg.sender, reward_tokens);
if(unspent_input > 0) {
msg.sender.transfer(unspent_input);
}
}
No, I just threw something together to get you started.
To get the uniswap ETH balance, you'll need to query the Uniswap contract (via it's address and ABI), for balance
- or you can use the Uniswap Graph's endpont.
If you want more help, please jump into the #dev-portal
channel in our Discord - there are more people in there who can help.
I'm looking into building some automation that calls arbSynthRate on the ArbRewarder contract. The first thing I'm wanting to build is a dashboard that shows the maximum input ether along with the tokens + reward. Maybe I'm missing it, but I'm not seeing a way to get this information out of the contract since most of the methods are marked private. For now, I'm essentially re-implementing most of the calls in my own code.
I think it would be very helpful if there was a function that had a signature something like this:
Then, every time a new block arrives, I can use web3 to query arbitrageAvailable.
Am I missing a better way to do this?
I also want to say that this contract is my favorite example of permissionless defi composability yet. Awesome work.