sbt-ethereum 0.5.0

Update: There’s a potentially dangerous bug in my reworking of the ABI representation for this release. The ordering of arguments to multiargument functions may become scrambled, leading usually to execution failures but potentially to costly mistakes. Please do not use sbt-ethereum releases v0.5.0 and v0.5.1. If you have imported ABIs under either of those releases, use ethContractAbiDefaultDrop to delete and reimport them, and ethContractAbiAliasDrop to delete any aliases pointing to them. If you’ve compilations in you shoebox database produced with these version, please get in touch with me and I’ll try to help resolve those. This issue will be fixed in sbt-ethereum v0.5.2.

sbt-ethereum v0.5.0 has been released!

Several of the changes were motivated by a desire to more conveniently support EIP-1967 transparent proxies.

Some Highlights

  • The task ethContractAbiImport can be executed with no arguments, in which case the imported ABI won’t be bound to an address, but an ABI alias will be solicited. (Since there is no address associated with the ABI, it cannot be automatically downloaded from etherscan, but must be copied and pasted into the console, in a compact form without newlines.)
  • The task ethContractAbiDefaultImport (or ethContractAbiImport with an address argument supplied) will check to see if the address for which you are attempting do download an ABI via Etherscan appears to be an EIP-1967 proxy. If so, it will prompt if you wish to asssociate the proxied ABI, rather than the (usually less useful) compiler-generated ABI of the proxy itself.
  • Added new utility ethUtilFunctionIdentifier for quickly computing the four byte identifier of a solidity function from its name and types.
  • Added utility ethContractStorageLookup that permits looking up the data stored in a smart contract’s address space, which is organized as numbered “slots”.
  • Added support (via solcj-compat) for Solidity compiler versions 0.6.12, 0.7.6, and 0.8.1.
  • Under the hood (via consuela), this release contains a more flexible representation of Ethereum smart contracts’ JSON ABIs, in preparation for support of ABIs accepting tuple-typed inputs.

Examples

Get an ABI and associate it permanently with an alias

> ethContractAbiImport
You are importing an ABI unattached to any contract address. You must provide an alias, so you can refer to it later.
Contract ABI: [{"constant":false,"inputs":[{"name":"fortune","type":"string"}],"name":"addFortune","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"fortunes","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"drawFortune","outputs":[{"name":"fortune","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"countFortunes","outputs":[{"name":"count","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initialFortune","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"author","type":"address"},{"indexed":false,"name":"fortune","type":"string"}],"name":"FortuneAdded","type":"event"}]
Please enter an alias for this ABI: fortune
[info] The ABI has been successfully imported, with alias 'abi:fortune'.
[success] Total time: 4 s, completed Feb 18, 2021, 9:52:36 PM

Check the association

> ethContractAbiAliasList
+--------------------------+--------------------------------------------------------------------+
| ABI Alias                | ABI Hash                                                           |
+--------------------------+--------------------------------------------------------------------+
| abi:aave-lending-pool-v2 | 0x3f5b9d5ba0e3fff0e84f5939757a75ddb515e9e32f81c206a3fbd1b881cd8593 |
| abi:ens-registrar        | 0xf79b59bc8ab1c1f1788c1f821c04d0afe9f8bc670e8ca1f9d4898be5be74386f |
| abi:fortune              | 0x9aa7db23ebf1aaac18537b713e03c030d9cb3cab880f17918ec845a27b736140 |
| abi:standard:erc20       | 0x718552976884b21da6b7cd7f5aa17c5347377d1715ed92fa166e0bf89bcf60c2 |
| abi:whoami               | 0x093def1bd67f0c4c3f6d578cd27f5135d1fa62e2f652fab0ee79933b23a37c27 |
+--------------------------+--------------------------------------------------------------------+
[success] Total time: 0 s, completed Feb 18, 2021, 9:53:13 PM

Temporarily associate the aliased ABI with an address and work with it

(When doing this example, the parser’s cache of ABI aliases had not been refreshed after the step above. It should have been. I’ll need to fix it. The workaround is to quit and restart the command line after setting the ABI alias.)

First, we temporarily associate the new, aliased ABI with an address.

> ethContractAbiOverride 0xBce652a5065fCA396DdEdcA3494Eefb4B781c165 abi:fortune
[info] ABI override successfully set.
[info] Refreshing caches.
[success] Total time: 0 s, completed Feb 18, 2021, 9:55:01 PM

Then, let’s use the ABI.

> ethTransactionView 0xBce652a5065fCA396DdEdcA3494Eefb4B781c165 drawFortune
[warn] Found multiple candidates when looking up the ABI for 0xBce652a5065fCA396DdEdcA3494Eefb4B781c165. Using a user-set ABI override, which shadows both a compilation ABI.
[info] The function 'drawFortune' yields 1 result.
[info]  + Result 1 of type 'string', named 'fortune', is "Ask again later."
[success] Total time: 1 s, completed Feb 18, 2021, 9:55:18 PM

Find the four-byte identifier of a Solidity function

> ethUtilFunctionIdentifier transfer address uint
[info] Canonical signature: transfer(address,uint256)
[info] Identifier: 0xa9059cbb
[success] Total time: 0 s, completed Feb 18, 2021, 10:06:19 PM

Install and use a Solidity 0.8.1 compiler

Install…

> ethLanguageSolidityCompilerInstall 0.8.1
[info] Installing local solidity compiler into the sbt-ethereum shoebox. This may take a few minutes.
[info] Installed local solcJ compiler, version 0.8.1 in '/Users/swaldman/Library/Application Support/sbt-ethereum/solcJ'.
[info] Testing newly installed compiler... ok.
[info] Refreshing compiler list.
[info] Updating available solidity compiler set.
[success] Total time: 3 s, completed Feb 18, 2021, 10:02:58 PM

Select…

> ethLanguageSolidityCompilerSelect local-shoebox-solc-v0.8.1
[info] Set compiler to 'local-shoebox-solc-v0.8.1'
[success] Total time: 0 s, completed Feb 18, 2021, 10:03:11 PM

Lookup a contract storage slot

> ethContractStorageLookup 0x6810e776880C02933D47DB1b9fc05908e5386b96 2
[info] For contract at address '0x6810e776880C02933D47DB1b9fc05908e5386b96' (with aliases ['GNO'] on chain with ID 1)...
[info]   as of the most recent block...
[info]     storage slot 0x0000000000000000000000000000000000000000000000000000000000000002 contains value 0x000000000000000000000000000000000000000000084595161401484a000000.
[success] Total time: 0 s, completed Feb 19, 2021, 6:47:32 AM

Download the proxied ABI of an EIP-1967 proxy

> ethContractAbiDefaultImport 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9
An Etherscan API key has been set. Would you like to try to import the ABI for this address from Etherscan? [y/n] y
Checking to see if address '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9' is an EIP-1967 transparent proxy.
'0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9' appears to be an EIP-1967 transparent proxy for '0xC6845a5C768BF8D7681249f8927877Efda425baf'.
Import the ABI of the proxied contract instead of the apparent proxy? [y/n] y
Will attempt to import the ABI associated with proxied contract at '0xC6845a5C768BF8D7681249f8927877Efda425baf'.
Attempting to fetch ABI for address '0xC6845a5C768BF8D7681249f8927877Efda425baf' from Etherscan.
ABI found:
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowRateMode","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowRate","type":"uint256"},{"indexed":true,"internalType":"uint16","name":"referral","type":"uint16"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"uint16","name":"referral","type":"uint16"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":true,"internalType":"address","name":"initiator","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premium","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collateralAsset","type":"address"},{"indexed":true,"internalType":"address","name":"debtAsset","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"debtToCover","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidatedCollateralAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"bool","name":"receiveAToken","type":"bool"}],"name":"LiquidationCall","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"RebalanceStableBorrowRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"repayer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":false,"internalType":"uint256","name":"liquidityRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stableBorrowRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"variableBorrowRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidityIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"variableBorrowIndex","type":"uint256"}],"name":"ReserveDataUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"ReserveUsedAsCollateralDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"ReserveUsedAsCollateralEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"rateMode","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reserve","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"FLASHLOAN_PREMIUM_TOTAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LENDINGPOOL_REVISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_NUMBER_RESERVES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_STABLE_RATE_BORROW_SIZE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"interestRateMode","type":"uint256"},{"internalType":"uint16","name":"referralCode","type":"uint16"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"balanceFromBefore","type":"uint256"},{"internalType":"uint256","name":"balanceToBefore","type":"uint256"}],"name":"finalizeTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiverAddress","type":"address"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"modes","type":"uint256[]"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"bytes","name":"params","type":"bytes"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAddressesProvider","outputs":[{"internalType":"contract ILendingPoolAddressesProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getConfiguration","outputs":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.ReserveConfigurationMap","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveData","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.ReserveConfigurationMap","name":"configuration","type":"tuple"},{"internalType":"uint128","name":"liquidityIndex","type":"uint128"},{"internalType":"uint128","name":"variableBorrowIndex","type":"uint128"},{"internalType":"uint128","name":"currentLiquidityRate","type":"uint128"},{"internalType":"uint128","name":"currentVariableBorrowRate","type":"uint128"},{"internalType":"uint128","name":"currentStableBorrowRate","type":"uint128"},{"internalType":"uint40","name":"lastUpdateTimestamp","type":"uint40"},{"internalType":"address","name":"aTokenAddress","type":"address"},{"internalType":"address","name":"stableDebtTokenAddress","type":"address"},{"internalType":"address","name":"variableDebtTokenAddress","type":"address"},{"internalType":"address","name":"interestRateStrategyAddress","type":"address"},{"internalType":"uint8","name":"id","type":"uint8"}],"internalType":"struct DataTypes.ReserveData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveNormalizedIncome","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveNormalizedVariableDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReservesList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserAccountData","outputs":[{"internalType":"uint256","name":"totalCollateralETH","type":"uint256"},{"internalType":"uint256","name":"totalDebtETH","type":"uint256"},{"internalType":"uint256","name":"availableBorrowsETH","type":"uint256"},{"internalType":"uint256","name":"currentLiquidationThreshold","type":"uint256"},{"internalType":"uint256","name":"ltv","type":"uint256"},{"internalType":"uint256","name":"healthFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserConfiguration","outputs":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"}],"internalType":"struct DataTypes.UserConfigurationMap","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"aTokenAddress","type":"address"},{"internalType":"address","name":"stableDebtAddress","type":"address"},{"internalType":"address","name":"variableDebtAddress","type":"address"},{"internalType":"address","name":"interestRateStrategyAddress","type":"address"}],"name":"initReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILendingPoolAddressesProvider","name":"provider","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"},{"internalType":"address","name":"debtAsset","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"debtToCover","type":"uint256"},{"internalType":"bool","name":"receiveAToken","type":"bool"}],"name":"liquidationCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"user","type":"address"}],"name":"rebalanceStableBorrowRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rateMode","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"configuration","type":"uint256"}],"name":"setConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"val","type":"bool"}],"name":"setPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"rateStrategyAddress","type":"address"}],"name":"setReserveInterestRateStrategyAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"bool","name":"useAsCollateral","type":"bool"}],"name":"setUserUseReserveAsCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"rateMode","type":"uint256"}],"name":"swapBorrowRateMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Use this ABI? [y/n] y
[info] A default ABI is now known for the contract at address 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9
[info] Refreshing caches.
[success] Total time: 9 s, completed Feb 18, 2021, 9:44:04 PM