SITIR 3

🏈 The Athlete That Gave Up Millions To Build A Sports Media Empire 💰 – Been enjoying the Huddle Up newsletter and this was an awesome story (the YouTube at the end was cool).

⚖️ Proof Of Liquidity, Joel Moegro. I’ve been thinking about uses of Balancer Pool Tokens (BPT) and it was interesting to see this – When you add assets to a pool, you receive ERC-20 BPT tokens in that pool proportional to your contribution. In Joels example you use these tokens to stake. The staking service can ‘look through’ the BPT to see how much of the required token you’re staking. In the meantime the pooled tokens are providing liquidity and the stakers assets remain balanced to the original pool weights. Seems like a lot can be done with this kind of idea…

I’m an NHS doctor – and I’ve had enough of people clapping for me – Strange, confusing times and this captures a bit of how I feel.

6 JavaScript console methods like Taylor Swift folklore lyrics – Twilio have some decent dev articles and this one had some nice new info I hadn’t heard.

⚖️ “Honestly, there’s another way to look at it from the low cap perspective. If you hold onto the low cap, and it moons on very low volume/liquidity, then you likely won’t even be able to sell it at the top. There just won’t be enough liquidity. But if you provide liquidity in a pool, and your pool comprises most of the global liquidity for that low-cap token, then when it moons you can simply withdraw your liquidity and you’ll have way more ETH or USDC or whatever than you started with. It’s basically a unique way of selling a bunch of those tokens on the way up whereas you may not have even been able to do that if you were trading on someone else’s illiquid market.

⬆️ Balancer community member, @rabmurat posted that message on Discord and it provided some interesting confirmation for something I had been thinking –

  • Assume stable DAI price
  • 📈💩⬆️ Market shitcoin price goes up 10%
  • 👀 Arbitrageur sees opportunity
  • 💲➡️⚖ ️➡️💩 DAI in to pool shitcoin out
  • ⚖️💩⬇️ Bal pool shitcoin price goes up to market price
  • Pool now has more token and less DAI
  • How to choose weights? High shit % means keep more shit but high % also means less of pair (DAI) required
  • Compared to HODL?
    • Impermanent loss does happen but remember HODL only works if you can actually sell at that price – is there liquidity/volume/slippage, etc?

Along similar lines this tweet was an interesting way to think about the BAL incentive mechanism – “…people can deposit their illiquid shitcoins and convert it to a new coin which might be better…”

How to create an Ethereum DeFi realtime dashboard – I didn’t realise Google scripting was so advanced.

🐂 This was a good Tweet storm by Chris Burniske with some tips on how to handle the Bull market (if it comes…)

🐂 Also some more good Bull market/general advice from Taylor Monahan.

Ballet is so cool 🩰 and this from Ballet Opera de Paris shows that working from home is easier for some!

An explainer video about the Opyn hack that shows some useful tooling. Opyn handled it pretty well I thought but also thought it kind of showed they don’t have much traction.

Memeconomics – thought this summed up the current situation pretty well but I guess time will tell.

Photo by Patrick Tomasso on Unsplash

Buidler, Waffle & Ethers

Lately at Balancer we’ve moved from the Truffle development environment to using Buidler, Waffle and Ethers. The main benefit is being able to use console.log in Solidity during debugging – it’s amazing how much of a difference this makes and for this alone the change over is worth it. Here’s some notes I made during the switch over.

Ethers

The ethers.js library aims to be a complete and compact library for interacting with the Ethereum Blockchain and its ecosystem.

Documentation is here: https://docs.ethers.io and this Web3.js vs Ethers.js guide was useful.

The following gist demonstrates some basic usage of Ethers that creates an instance of a deployed contract and then running some calls against it:

Buidler & Waffle

Buidler is described as a ‘task runner’. I think its easiest to see it as a swap for Truffle/Ganache. It has lots of different plugins that make it really useful and its documentation was refreshingly good.

The Quickstart shows you how to install and how to run common tasks. It also uses Waffle for testing. Waffle is a simple smart contract testing library built on top of Ethers.js. Tests in Waffle are written using Mocha alongside with Chai and from my experience everything just worked. The docs are here. And its worth digging in to see some of the useful things it offers such as Chai Matchers which allow you to test things like reverts, events, etc.

Buidler commands I found I used a lot:

  • Run the local Buidler EVM: $ npx buidler node
  • Compile project contracts: $ npx buidler compile
  • Run tests: $ npx buidler test ./test/testfile.ts

Here’s an example test file I used that demonstrates a few useful things:

Static Calls

let poolAddr = await factory.callStatic.newBPool(); – The contract callStatic pretends that a call is not state-changing and returns the result. This does not actually change any state and is free.

Connecting Different Accounts

await _pools[1].connect(newUserSigner).approve(PROXY, MAX); – Using contract connect(signer) calls the contract via the signer specified.

Gas Costs

await proxy.connect(newUserSigner).exitswapExternAmountOut(
            _POOLS[1],
            MKR,
            amountOut,
            startingBptBalance,
            {
              gasPrice: 0
            }
        );

Setting the gasPrice to 0 like above allows me to run the transaction without spending any Eth on it. This was useful when checking Eth balance changes without having to worry about gas costs.

Custom accounts & balances

const config: BuidlerConfig = {
  solc: {
    version: "0.5.12",
    optimizer: {
      enabled: true,
      runs: 200,
    },
  },
  networks: {
    buidlerevm: {
      blockGasLimit: 20000000,
      accounts: [
        { privateKey: '0xPrefixedPrivateKey1', balance: '1000000000000000000000000000000' },
        { privateKey: '0xPrefixedPrivateKey2', balance: '1000000000000000000000000000000' }
      ]
    },
  },
};

I needed the test accounts to have more than the 1000Eth balance set by default. In buidler.config.ts you can add accounts with custom balances like above.

Deploying

Deploying is done using scripts. First I updated my buidler.config.ts with the account/key for Kovan that will be used to deploy (i.e. must have Eth):

const config: BuidlerConfig = {
  solc: {
    version: "0.5.12",
    optimizer: {
      enabled: true,
      runs: 200,
    },
  },
  networks: {
    buidlerevm: {
      blockGasLimit: 20000000,
    }
    kovan: {
      url: `https://kovan.infura.io/v3/${process.env.INFURA}`,
      accounts: [`${process.env.KEY}`]
    }
  },
};

Then I wrote a deploy-script.js:

async function main() {
  // We get the contract to deploy
  const ExchangeProxy = await ethers.getContractFactory("ExchangeProxy");
  const WETH = '0xd0A1E359811322d97991E03f863a0C30C2cF029C';
  const exchangeProxy = await ExchangeProxy.deploy(WETH);

  await exchangeProxy.deployed();

  console.log("Proxy deployed to:", exchangeProxy.address);
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

Then run this using: npx buidler run --network kovan deploy-script.js

🎉 Console Logging 🎉

One of the holy grails of Solidity development and so easy to setup in this case! There are also Solidity stack traces and error messages but unfortunately there was a bug that caused this not to work for our contracts.

To get this going all you need to do is add: import "@nomiclabs/buidler/console.sol"; at the top of your contract then use console.log. More details on what kind of outputs, etc it supports are here. Lifesaver!

Hope some of this was helpful and you enjoy using it as much as me.

(Photo by Kevin Jarrett on Unsplash)

SITIR 2

Some interesting things I read lately that I don’t really know what to do with but might be interesting to look back on.

68 bits of advice from Kevin Kelly – some good ones in there and definitely worth a look every now and then.

Considering tonights Bojo bullshit press announcement it’ll be interesting to see how the post, Invisible Enemy, about Covid-19 ages. This in particular makes a lot of sense to me: “if the overwhelming majority of experts on the topic think we should do all we can to prevent the spread of Covid-19, it’s crazy not to listen to them.”

Daniel Radcliffes Desert Island Disc was interesting and enjoyable. He seems like a decent dude so positive vibes to him.

Semaphore, a privacy gadget built on Ethereum feels stuck to the back of my mind! I don’t fully get it but feel like there’s something there that’s going to be valuable/useful someday.

NFT Art – I like art, the idea of collecting art and crypto so NFT/Digital art seems like an obvious interest point. Tried a bid on SuperRare for this piece which I liked but I lost :(. I really like Trevor Jones stuff and it was cool to see his Bull go for > $10k!

I’ve had Justin Kans post about Feeling Good saved for ages now as it coincided with me stopping drinking and it gave me some inspiration. Been well over 1 year now so feel I can keep it here if required.

Eth2.0 Staking on test net is a hot topic in general at the moment and I’ve been tempted to get involved. Trying to decide best approach hardware wise at the moment and this post had some useful info. I’m feeling pulled towards setting up a PI 4 but have a few concerns that’s stopping me going for it so far.

Some interesting info on using Echidna on to find high Eth gas cost transactions.

The Beastie Boys Story – this should be worth a watch!

Photo by Road Trip with Raj on Unsplash

SITIR 1

Some interesting things I read lately that I don’t really know what to do with but might be interesting to look back on.

Lendf.me $25million hack. ERC-777 tokens, explanations of reentrancy and basic breakdown of transactions.

Post from Paul Graham about Credibility during the Coronavirus. This really sounds familiar to me from past experience especially this:

These people constantly make false predictions, and get away with it, because the things they make predictions about either have mushy enough outcomes that they can bluster their way out of trouble, or happen so far in the future that few remember what they said.

Mastering the Mempool – I just found this interesting and intriguining. Helped to solidify in my mind how things go from say, MetaMask, to the actual blockchain. I liked the suggestions to think of this as the “waiting area” for transactions to be accepted into a block. There’s a whole series available and it looks like it would be interesting to build on.

I wanted to dive into this a bit more but haven’t had time yet. Seems like you link a Solidity fallback function to an ENS domain. You can then do things like send Eth to the address and get PNK via a UniSwap swap or send Eth to an address (CHAIUnipool.DeFiZap.eth) that then adds to UniSwap liquidity pool.

This discussion about the Coronavirus Market Crash from the JL Collins and Madfientist is a good sanity check and bit of reassurance and I think it should mostly be true

Every time there’s a bear market, it has the feeling that it’s something major and it’s something different and it’s terrifying. And almost, if you think about it, that’s by definition, because if it were not those things, there would not be a bear market.

(Photo by Ashes Sitoula on Unsplash)

Making The Draft

There was a nice piece of advice from one of the pro basketball players in the movie High Flying Bird, something along the lines of:

The worst thing a rookie can do when he makes the draft is think he’s made it. Take the pats on the back for a day then get to work.

So it’s time to get the head down, work hard, learn hard and lets see if I can help my team win the 🏆

(Photo by Isaiah Rustad on Unsplash)

Setting Up Testing – Typescript with Mocha/Chai

This is more a reminder post incase I ever have to do something like this again so its kind of boring!

For pure Javascript testing with Mocha/Chai:

$ yarn add mocha
$ yarn add chai

In package.json:

"scripts":{
    "test":"mocha"
}

Add a test dir:

$ mkdir test

Add first test file:

var expect  = require('chai').expect;
var request = require('request');

it('Main page content', function(done) {
    request('http://localhost:8080' , function(error, response, body) {
        expect(body).to.equal('Hello World');
        done();
    });
});

Now for Typescript:

$ yarn add typescript
$ yarn add ts-node --dev
$ yarn add @types/chai --dev
$ yarn add @types/mocha --dev

 Replace test script:

"test": "mocha -r ts-node/register test/*.spec.ts"

And make sure test is in root/test/example.spec.ts

import { expect, assert } from 'chai';
import 'mocha';
import { Pool } from '../src/types';
import { smartOrderRouter } from '../src/sor';
import { BigNumber } from '../src/utils/bignumber';
import { getSpotPrice, BONE } from '../src/helpers';

const errorDelta = 10 ** -8;

function calcRelativeDiff(expected: BigNumber, actual: BigNumber): BigNumber {
    return expected
        .minus(actual)
        .div(expected)
        .abs();
}

// These example pools are taken from python-SOR SOR_method_comparison.py
let balancers: Pool[] = [
    {
        id: '0x165021F95EFB42643E9c3d8677c3430795a29806',
        balanceIn: new BigNumber(1.341648768830377422).times(BONE),
        balanceOut: new BigNumber(84.610322835523687996).times(BONE),
        weightIn: new BigNumber(0.6666666666666666),
        weightOut: new BigNumber(0.3333333333333333),
        swapFee: new BigNumber(0.005).times(BONE),
    },
    {
        id: '0x31670617b85451E5E3813E50442Eed3ce3B68d19',
        balanceIn: new BigNumber(14.305796722007608821).times(BONE),
        balanceOut: new BigNumber(376.662367824920653194).times(BONE),
        weightIn: new BigNumber(0.6666666666666666),
        weightOut: new BigNumber(0.3333333333333333),
        swapFee: new BigNumber(0.000001).times(BONE),
    },
];

describe('Two Pool Tests', () => {
    it('should test spot price', () => {
        var sp1 = getSpotPrice(balancers[0]);
        var sp2 = getSpotPrice(balancers[1]);

        // Taken form python-SOR, SOR_method_comparison.py
        var sp1Expected = new BigNumber(7968240028251420);
        var sp2Expected = new BigNumber(18990231371439040);

        var relDif = calcRelativeDiff(sp1Expected, sp1);
        assert.isAtMost(
            relDif.toNumber(),
            errorDelta,
            'Spot Price Balancer 1 Incorrect'
        );

        relDif = calcRelativeDiff(sp2Expected, sp2);
        assert.isAtMost(
            relDif.toNumber(),
            errorDelta,
            'Spot Price Balancer 2 Incorrect'
        );
    });

    it('should test two pool SOR swap amounts', () => {
        var amountIn = new BigNumber(0.7).times(BONE);
        var swaps = smartOrderRouter(
            balancers,
            'swapExactIn',
            amountIn,
            10,
            new BigNumber(0)
        );

        // console.log(swaps[0].amount.div(BONE).toString())
        // console.log(swaps[1].amount.div(BONE).toString())
        assert.equal(swaps.length, 2, 'Should be two swaps for this example.');

        // Taken form python-SOR, SOR_method_comparison.py
        var expectedSwap1 = new BigNumber(635206783664651400);
        var relDif = calcRelativeDiff(expectedSwap1, swaps[0].amount);
        assert.isAtMost(relDif.toNumber(), errorDelta, 'First swap incorrect.');

        var expectedSwap2 = new BigNumber(64793216335348570);
        relDif = calcRelativeDiff(expectedSwap2, swaps[1].amount);
        assert.isAtMost(
            relDif.toNumber(),
            errorDelta,
            'Second swap incorrect.'
        );
    });

    it('should test two pool SOR swap amounts highestEpNotEnough False branch.', () => {
        var amountIn = new BigNumber(400).times(BONE);
        var swaps = smartOrderRouter(
            balancers,
            'swapExactIn',
            amountIn,
            10,
            new BigNumber(0)
        );

        // console.log(swaps[0].amount.div(BONE).toString())
        // console.log(swaps[1].amount.div(BONE).toString())
        assert.equal(swaps.length, 2, 'Should be two swaps for this example.');
        assert.equal(
            swaps[0].pool,
            '0x31670617b85451E5E3813E50442Eed3ce3B68d19',
            'First pool.'
        );
        assert.equal(
            swaps[1].pool,
            '0x165021F95EFB42643E9c3d8677c3430795a29806',
            'Second pool.'
        );

        // Taken form python-SOR, SOR_method_comparison.py with input changed to 400
        var expectedSwap1 = new BigNumber(326222020689680300000);
        var relDif = calcRelativeDiff(expectedSwap1, swaps[0].amount);
        assert.isAtMost(relDif.toNumber(), errorDelta, 'First swap incorrect.');

        var expectedSwap2 = new BigNumber(73777979310319780000);
        relDif = calcRelativeDiff(expectedSwap2, swaps[1].amount);
        assert.isAtMost(
            relDif.toNumber(),
            errorDelta,
            'Second swap incorrect.'
        );
    });

    // Check case mentioned in Discord
});

Photo by René Porter on Unsplash

Should I Stay Or Should I Go?

He who jumps into the void owes no explanation to those who stand and watch.

Jean-Luc Godard

Money vs satisfaction. The known vs the unknown. Safe(ish) vs safe. These are the kind of trade-offs that go through my mind when I evaluate whether to stay in my current job or not.

It’s all about the money, money, money – until it’s not! Getting paid reasonably well to work in an unsatisfactory environment isn’t enough.

So what is enough 🤔? There’s been some interesting information on this subject, most recently one that struck a chord was by Fred Wilson in his post What To Work On. A checklist of his suggestions are projects that:

  • Interests
  • Motivates
  • Inspires
  • Impactful

Realistically in my current role the last three no longer apply. The last could be a yes. The available options all appear to be a yes although there’s always the risk that the vision is different from the reality.

Some more advice to help zero in on specifics are from this post and include:

  • Do you know what you want to get out of the next chapter – mmmh, specifics are tricky! I think I’d like to build my knowledge and skills in the blockchain and development world. It’s also important to gain access to people and opportunities in this industry. A sketchy final goal is to do something myself some day.
  • What are your side constraints – Ideally I want to stay in my current location, remote work would be fine. I’d like to work with professional, inspirational people that are focused and knowledgeable and are willing to let me soak up as much as I can! Getting paid well would be a bonus but I will accept below market rates for the right opportunity.
  • How much risk will I take – I guess I’m pretty open to risk really. I would probably prefer a startup because there’s more opportunity to make an impact and learn. I’m also considering being a student. Joining a post product-market fit start-up that is already scaling rapidly is the advice but I don’t think there’s many of these available right now. One of my biggest risk fears is trading one bad for another! Another is being ‘all in’ on blockchain – what if it ALL goes tits up?! I don’t think either are big enough risks to emergency stop.

As I type it becomes clear – mentally I’m already gone! I just hope I ‘go’ to the right thing. How awesome, inspirational and cool is the below quote from Preston Van Loon – that sounds like the right thing to me!

We are strong believers in Ethereum for a major reason – it is a fully permissionless blockchain that allows anyone to build unstoppable, censorship-resistant applications. In the beginning, we were all independent software engineers that shared a passion for the Ethereum blockchain, searching for the best way to contribute to its future. Now, we work full time on making the next iteration of the Ethereum project a reality. The fact that such a story is allowed to happen is a testament to the Ethereum project’s meritocracy and its community. We love to code, and being able to wake up each morning and work on fun, challenging, and meaningful problems is one of the best things anyone in this field could ask for. This is the best possible time to shape the future of Ethereum, and we’re privileged to have the support of you all to do so.
We believe in code quality, maintainability, and good design, and tests as our key pillars over anything else. Additionally, we believe there is a lack of industry best practice and sound software design in Ethereum development. A lot of the pain eth1 suffered came from reinventing many key pieces of its architecture, such as a custom json-rpc, a completely new, custom p2p library, etc. instead of adopting practices present in industry for years. An open source project needs to have longevity, and having easy readability + great tests ensures new contributors can pick up where we left off and keep the project growing at the same pace. When building the infrastructure that will maintain a major, multi-billion dollar project, every line of code we write must be considered with great care and consideration. Protocol development for Ethereum should be the opposite of what “hackathon”-style code looks like, and more importantly, we believe every developer should spend twice as much time reviewing code as writing it. We see many other projects write code that is really clever at the cost of being unintelligible to most, and that goes against our ethos. We chose Go because it is a language that prefers being clear and simple over being witty and complex, and Go as a language resonates well with our philosophy towards software engineering. Every time someone aims to make some code more clever or “concise” using fancy tricks or attempting to cram key logic into as few lines of code as possible, we avoid doing the same, as that’s not what good software should be about.
Ethereum 2.0 will eventually just be Ethereum, otherwise, the entire effort would have failed massively! We definitely see ourselves as a key team within the Ethereum community, building important tooling and infrastructure for its users, companies, and dApps building on the protocol. We have a responsibility to increase its adoption and keep improving the project over time. We believe Ethereum will keep creating a lot more valuable projects on top of it, and perhaps in the future we will create our own product on top of Ethereum itself, but with the key goal of always furthering its vision.

EthFinanace AMA Series with Prysmatic Labs

🤓 Geeking Out On Uniswap 🦄

Uniswap is a simple smart contract interface for swapping ERC20 tokens and in general it’s pretty awesome. The story behind it is really inspirational to me and just recently Hayden Adams tweeted that $10million of liquidity was added in a 24 hour period – surely pretty succesful by any measure!

Some of the features of Uniswap include:

  • Supplies on-chain liquidity to other smart contracts
  • Ease of use
  • Gas efficiency (10x less than Bancor)
  • Decentralised/censorship resistant

And just as a reminder:

Liquidity describes the degree to which an asset or security can be quickly bought or sold in the market at a price reflecting its intrinsic value.

Investopedia

Each Uniswap pool holds Eth and another token and trades are exectuted against these reserves.

By supplying tokens to the pooled reserve (being a liquidity provider) you get a proportional share of transaction fees via a liquidity token.

Prices are set automatically using eqn: x * y = k or in terms of tokens: eth_pool * token_pool = invariant.

The invariant is constant during trades but DOES change when liquidity is added or removed from pool. (So not really an invariant?!)

Liquidity Tokens

Liquidity tokens are minted to track the relative proportion of total reserves that each liquidity provider has supplied.

Fees are taken during a token swap and are added to the liquidity reserves. Since total reserves are increased without adding any additional share tokens, this increases that value of all liquidity tokens equally. This functions as a payout to liquidity providers that can be collected by burning shares. (It’s also the reason that the invariant increases at the end of every trade)

When a liquidity provider joins the pool the amount of liquidity tokens minted are calcualted by:

(Initial pool liquidity is equal to initial Eth value provided)
total_liquidity = self.totalSupply
eth_reserve = self.balance - msg.value
liquidity_minted = msg.value * total_liquidity / eth_reserve

Liqudity tokens can be burned at any time to return a proportional share of the markets liquidity to the provider:

removeLiquidity(amount...):

total_liquidity = self.totalSupply
token_reserve = self.token.balanceOf(self)
eth_amount = amount * self.balance / total_liquidity
token_amount = amount * token_reserve / total_liquidity

self.balances[msg.sender] -= amount
self.totalSupply = total_liquidity - amount
send(msg.sender, eth_amount)
return eth_amount, token_amount

Exchanging

Eth -> Token: Traders Eth is added to pool and Token is removed. So Eth amount increases, token amount decreases. Token becomes more expensive.

Token -> Eth: Traders Token is added to pool and Eth is removed. So Eth amount decreases, token amound decreases. Eth becomes more expensive.

Arbitrage

Arbitrage seems to be the key to so many DeFi applications! I think the following is nice description of what it is:

Arbitrage trading is a strategy that can be best understood as a trader that takes advantage of the price differential that exists between two markets. In the case of cryptocurrency, this price differential can be found in the differences in price of a digital asset between cryptocurrency exchanges. If a trader identified an opportunity for arbitrage trading, then they would purchase a digital asset in one exchange, and then sell it on another cryptocurrency exchange.

Mycryptopedia

To me it’s all about icentives – basically in the form of greed! There’s always someone looking to make money from an opportunity. In this case they execute a trade to make a profit but by making they trade they change the price which basically corrects it to the market price – that’s cool!

For example:

  • the global price of ETH-USD moves enough away from the pool price, an arbitrage opportunity exists and is corrected
  • When the initial liquidity is provided the exchange rate is set. If the ratio of liquidity provided isn’t realistic arbitrage traders will correct at the expense of initial liquidity provider.

Some Examples With Numbers

Invariant is set on initial deposit to a new pool. For example 10 ETH and 500 FUN are deposited into new ETH/FUN pool. Invariant is set to:

ETH_pool * FUN_pool = invariant
10 * 500 = 5000

Now for an ETH -> FUN trade:

Buyer sends 1ETH

Fee = 1 ETH / 400 = 0.0025 ETH (0.25% fee)

ETH_pool = 10 + 1 - 0.0025 = 10.9975

FUN_pool = 5000/10.9975 = 454.65 (invariant/ETH_pool)

Buyer receives: 500 - 454.65 = 45.35 FUN

Fee is added back to pool:

ETH_pool = 10.9975 + 0.0025 = 11

FUN_pool = 454.65

New invariant = 11 * 454.65 = 5001.15

Executed price = 45.35 FUN/ETH

But now price has changed:

Fee = 0.0025 ETH
ETH_pool = 11.9975
FUN_pool = 5001.15/11.9975 = 416.85
Buyer receives: 454.65 - 416.85 = 37.8

Executed price = 37.8 FUN/ETH

Price Slippage

Price slippage refers to the difference between the expected price before a transaction is executed and the actual price at which it is executed.

Bancor Support

Easiest for me to think of spot price and actual price.

A trade that is large relative to the size of the total size of the liquidity pool will cause price slippage.

Same example as above: 
ETH_pool * FUN_pool = invariant
10 * 500 = 5000

Spot price for ETH -> FUN = 500/10 = 50

1ETH Purchase: Executed price = 45.35 FUN/ETH

10ETH Purchase: Executed price = 24.969 FUN/ETH

Fee = 10 ETH / 400 = 0.025 ETH (0.25% fee)

ETH_pool = 10 + 10 - 0.025 = 19.975

FUN_pool = 5000/19.975 = 250.31

Buyer receives: 500 - 250.31 = 249.69 FUN

Fee is added back to pool:

ETH_pool = 19.975 + 0.025 = 20

FUN_pool = 250.31

New invariant = 20 * 250.31 = 5006.2

Executed price = 24.969 FUN/ETH

Final Thought

I found this comment from Vitalik on this EthResearch post interesting:

The point is not for this kind of exchange to be the only exchange; the point is for it to be one type among many. It offers the benefits of executing a complete trade in one transaction, and extreme user-friendliness even to smart contracts, which are very real benefits and will at least sometimes exceed the costs of slippage to some users. I am ok with just accepting that this kind of approach will not be acceptable to whales who want to liquidate large amounts of things; that’s not the target market.

(A lot of this stuff came from the Uniswap White paper. Photo by James Lee on Unsplash)

Solidity – How To Estimate Gas Costs

I’ve been working on optimizing the gas cost for deploying a smart contract. Turns out truffle has some pretty useful ways of estimating gas used.

Good Old Migrate

The most obvious way is to just run $ truffle migrate and see what the output says. When the project has quite a large build this can take some time so it’s not that efficient for seeing the difference some code changes make. I do find it useful to run this every now and again though just to confirm the estimates are correct.

estimateGas()

The other way I found useful is to use the estimateGas function. As is pretty self explanatory it is just an estimate and could change depending on the contract state. I found this was nice and quick to use on a truffle cli and I found two useful estimations to look at:

Contract deployment

Start the local ganache with a large gas limit:

$ ganache-cli --gasLimit 10000000 --allowUnlimitedContractSize

Start a truffle cli that connects to above chain:

$ truffle console

Estimate gas for deploying your contract:

truffle(develop)> YourContract.new.estimateGas()

Calling Contract Method

Start with the same set-up steps as above. Then:

truffle(develop)>  YourContract.testMethod.estimateGas()

And that’s it – another useful trick!

Migrating SAI to DAI

I was using Dharma.io to earn interest on my SAI (formerly DAI). I found Dharma really nice to use and originally offered a good interest rate. Recently the rate had become less competitive and I was getting concerned (maybe unneccesarily?) about the lack of comms about migrating the SAI to DAI so I decided to take matters into my own hands especially now that the DAI Savings Rate has kicked in.

When I swapped I was getting 3.22% for SAI on Dharma.io. The DAI savings rate is 4% and looks like it might get raised to 6% soon so definitely worth the swap.

  1. Visit https://migrate.makerdao.com/
  2. Unlock SAI. This calls the approve function for the token. (Nice explanation here.)
  3. This had a gas cost of 0.002637ETH, $0.35.
  4. That was it – once I checked my account I could see DAI.
  5. Not to earn some interest.
  6. Click earn savings. This navigates to https://oasis.app/save.
  7. Deploy Proxy – Setting up your proxy will bundle multiple transactions into one, saving transaction time and gas costs. This only has to be done once.
  8. Gas cost: 0.007344ETH, $0.97
  9. Now wait for 10 confirmations.
  10. Approve Oasis for DAI.
  11. 0.00055ETH, $0.07
  12. Finally deposit the DAI.
  13. 0.002713ETH, $0.36

Total cost $1.75. And now it’s quite satisfying to watch my depost earn interest in real time!

(Photo by Fabian Blank on Unsplash)