Etherscan API

Etherscan is a really useful website but fairly recently I discovered they also have a pretty handy Developer API. I used it to analyse gas a contract was using over a period of time and thought it might be useful to record.

Getting Started

First of all an API key is required and this can be created for free by logging in and going to: https://etherscan.io/myapikey.

I’m going to paste my code with comments which should be easy enough to follow. The API covers a whole lot of other end point such as Logs, Tokens, etc that I didn’t use but the docs are good and worth a scan.

Example

Photo by Markus Spiske on Unsplash

SITIR 6

🀝 Eth Network Discord Community – I liked the idea about this from DeFi Dudes tweet.

πŸ₯§ I’ve used RaspberryPi a lot and the new Pi 400 seems cool.

🎧 Uncommon Core seems like a good podcast to add to the list and this was an interesting thread discussing some of points made in the last episode.

πŸ§‘β€πŸ’» Then new Ethereum.org dev portal was released and is probably a go to resource now.

πŸ₯ͺ Mmmmmhhhh sandwiches!

πŸ‘©β€πŸ« 40 things I learned by age 40 by Sunday Soother has some really good advice worth re-reading every now and then.

I don’t really know much about yield curves and things but fixed rate lending/borrowing on Ethereum would be πŸ‘Œ

And along similar lines this Rise of the Cryptodollar article from Bankless discusses savings rates for crypto dollars.

πŸŒοΈβ€β™‚οΈ Didn’t realise half this stuff about the Masters finances and thought it was pretty interesting.

🧘 This so true:

Working on a problem reduces the fear of it.

It’s hard to fear a problem when you are making progress on itβ€”even if progress is imperfect and slow.

Action relieves anxiety.

James Clear 3-2-1

SITIR 5

πŸ‘· Hardhat & Tenderly are two super useful smart contract dev tools and this guide shows how to get them setup together.

🧘 James Clear 3-2-1 Newsletter is probably the most consistently good one I read, it almost always makes you think. On kindness, real wealth, and earning love and respect is a really good one.

πŸ‘©β€πŸ« Good learning in Falsehoods that Ethereum programmers believe.

I read about Ocean Farming ages ago and haven’t been able to shake the crazy idea that this would be a cool thing to do. 🐟

πŸŽ₯ I was pretty proud of this 🦚 feels like I’ve done a circle and gone from hacker to judge, was also very cool to appear on the schedule next to the big names:

πŸ“– Bookmarked for the future – Ethereum Developer Interview Questions.

Look- here’s a table covered with red cloth. On it is a cage the size of a small fish aquarium. In the cage is a white rabbit with a pink nose and pink-rimmed eyes.

On its back, clearly marked in blue ink, is the numeral 8. […] The most interesting thing here isn’t even the carrot-munching rabbit in the cage, but the number on its back. Not a six, not a four, not nineteen-point-five. It’s an eight. This is what we’re looking at, and we all see it. I didn’t tell you. You didn’t ask me. I never opened my mouth and you never opened yours.

We’re not even in the same year together, let alone the same room… except we are together. We are close. We’re having a meeting of the minds. We’ve engaged in an act of telepathy. No mythy-mountain shit; real telepathy.

Stephen King – Books are portable magic. πŸ§™β€β™‚οΈ

βš–οΈ Clearing out some old bookmarks and this Tweet and this AMA with Bankless are from back when it all got started with Balancer!

Can rely on Chris Burniske tweets to reset the sentiment on a bad day πŸ“‰

Photo by Artur Tumasjan on Unsplash

SITIR 4

πŸ‘Ύ Dark Forest was a pretty fun game to play on the Ethereum network that used xkSNARKs and they did a pretty interesting intro to zk proofs.

No Edinburgh Festival this year πŸ˜₯ but people are still trying and here’s a celebration of the city.

πŸ›Ή Jackass was a big part of my younger days and Steve O was one of my main men. Demise and Rise is pretty inspiring, how he turns his life around should give hope.

Shared a lot but Ethereum is a Dark Forest and the follow up, Escaping The Dark Forest were pretty good – the mempool is cool 😎!

Following up on ☝️ this dives into front-running Bots. Also cool how its a ‘story of block 1088…’.

βš’οΈ Graphtage: A New Semantic Diffing Tool – could be a handy tool.

TxStreet.com – Love this ❀️‍πŸ”₯ a live cryptocurrency transaction visualizer featuring Bitcoin, Ethereum and Bitcoin Cash.

πŸ’Έ Pretty useful cheat sheet for seeing IL vs Fees vs Price action on Uniswap.

πŸ“» I’ve been enjoying listening to Justin Kans podcast, The Quest, lately – tech, happiness and the meaning of life!

β›½ Vitalik explains Gas costs.

🍱 I didn’t love the whole food farming thing but it was pretty wild and will go down in Ethereum history. Here’s some food for thought (🀦).

πŸ‘¨β€πŸ’» How to re-send a transaction with higher gas price using ethers.js – Something that might be useful.

Options are interesting and I’ve played a bit with Opyn. Some interesting Buidling ideas.πŸ’‘

πŸ“– Perpetual Contracts are another finance instrument thats new to me and here’s a bit of an explainer.

πŸ‘ One of the few posts I’ve seen that shows examples for EIP712. Also goes into meta-txs.

Photo by Clever Visuals on Unsplash

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