Calling Huff code auditors

Hello! So Ramana.eth has been so kind as to push forward on an idle thought of mine, essentially engineering a low gas smart contract to batch ETH->rETH mints and stETH->rETH swaps on L1. My thesis is that reducing gas costs for rocket pool aligned individuals means more rETH created, and more stETH burned. Ultimately the plan is to make it a public good, if successful.

Contract specifications:

I asked Ramana to craft a contract that had 2 basic functions:

  1. accept up to 1.0 stETH or ETH and send back rETH based on the current rETH/ETH ratio
  2. drain the stETH and ETH balances into one specified address

The order of priorities for the contract are:

  1. Trustlessness for stakers (ie, depositing stETH or ETH always gets you the appropriate rETH, or reverts)
  2. Security of honeypot (deposited assets can only be drained to a single specified address)
  3. Gas efficiency (bare bones simplicity to lower gas costs for end users)

I’m looking for people who are literate in Huff to look through the code. The bug bounty is not great (my discretion, probably something like 0.5-1 ETH for fatal bug [ways to empty either user or smart contract funds], 0.1-0.3 for griefing or sub-maximal theft exploits, and 0.01-0.1 for implemented improvements in gas efficiency), but the code isn’t complex and the amount at risk is small.

Contract documents:

Huff contract:
contracts/epineph/src/Contract.huff at main · xrchz/contracts · GitHub

launched Goerli contract:

API that we used:
[{“name”: “Deposit”, “inputs”: [{“name”: “who”, “type”: “address”, “indexed”: true}, {“name”: “stETH”, “type”: “uint256”, “indexed”: false}, {“name”: “ETH”, “type”: “uint256”, “indexed”: false}, {“name”: “rETH”, “type”: “uint256”, “indexed”: false}], “anonymous”: false, “type”: “event”}, {“name”: “Drain”, “inputs”: [{“name”: “who”, “type”: “address”, “indexed”: true}, {“name”: “to”, “type”: “address”, “indexed”: true}, {“name”: “stETH”, “type”: “uint256”, “indexed”: false}, {“name”: “ETH”, “type”: “uint256”, “indexed”: false}], “anonymous”: false, “type”: “event”}, {“stateMutability”: “nonpayable”, “type”: “constructor”, “inputs”: [{“name”: “_rocketStorage”, “type”: “address”}, {“name”: “_stETH”, “type”: “address”}, {“name”: “_owner”, “type”: “address”}], “outputs”: []}, {“stateMutability”: “payable”, “type”: “function”, “name”: “deposit”, “inputs”: [{“name”: “stETH”, “type”: “uint256”}], “outputs”: []}, {“stateMutability”: “payable”, “type”: “function”, “name”: “depositETH”, “inputs”: [], “outputs”: []}, {“stateMutability”: “nonpayable”, “type”: “function”, “name”: “depositStETH”, “inputs”: [{“name”: “stETH”, “type”: “uint256”}], “outputs”: []}, {“stateMutability”: “nonpayable”, “type”: “function”, “name”: “drain”, “inputs”: [], “outputs”: []}]

Test scenario transactions:

Deposit 0 ETH, 0 stETH
Success: 0xa9a8dd4a92969b35df61f512bfda0657fa038ec0b483af7ff0f309118d132bf2

Deposit 0.1 ETH, 0 stETH
Success: 0xb1eacf28c9dda22b1d5ca574b19f2faa60c1bbba3893b125bf3d716dec36dff4

Deposit 0.11 ETH, 0.10 stETH
Success: 0xd2855daed959545d817223b07aab93517021b4c844ed1434824e18d7a7ac0f1e

Deposit 0 ETH, 0.11 stETH
Success: 0xb6788a0cdf403c061f5d21f1e6b6a7988bc1cf973265aab2f178a22c718f7fbe

Deposit 1.1 ETH, 0.12 stETH
Revert: 0xf95a27af421fbf964db5ae610ac52a9faabca60d8655bb3bc4ccb202c9f60493

Deposit 0.12 ETH, 1.1 stETH
Revert: 0xd1fe1e1cc5bb5f400e425e65e133c0391354d23daebdbfcd2af1bc44aa256549

DepositEth 0.13 ETH
Success: 0x08508be6e6ceb29b7e8b5d4e7b5547a0440b4c840bb457b7ae8e8932b1034c14

DepositEth 0 ETH
Success: 0xb942a2681a133796ce19bcbb9ace254a62f3d8f2970fe3609b3f13e1301db7e5

DepositEth 1.2 ETH
Revert: 0x51eb2e2e46d6587842d56970733ff9a8227079623d02c204eabaa80b2db4cb92

DepositStETH 0 stETH
Success: 0x1a8c7f7a5394163a533612e30edd7b5039bb805a78034dcc53f977314de8bd98

DepositStETH 0.13 stETH
Success: 0x4b8574325c5dc649a3deb230cad100beaa8889ff71947a3e3a35a7472e0a3942

DepositStETH 1.2 stETH
Revert: 0x2904232709baf654a2e2127a494d888de4ac31ae3ac5d643eed242a86f7c2b93

Drain: total balance of eth/stETH
Success: 0xcc0f4f6a52d617d2a7a3fb9a698afdcc8d21a8e1ebc35c5d0c8e576d79081315

Drain: 0 ETH 0 stETH
Success: 0x43a5efdfe34825c070ac29d09a2a62fe888d994e9f61b3e30d043aad707ae995

Deposit 11 stETH (when rETH balance low)
Revert: 0x43d2804433d2cac8c2faf4b8196dd99cd8e22c009b5e928ad8f85a7b3e4cb6a6

Deposit 10 ETH (when rETH balance low)
Revert: 0x3cc28b8525f28a7ebac988d26bd7cd1e3ad6e172e12ba0b7bae1df77dfea7fdb

Deposit 0.93 ETH (when reth balance low)
Revert: 0x7a4ee6263fbd77a32ffd6dcaf768280ea1ae1bf39e56c704065ae441dd1fd858

Deposit 0.93 stETH (when reth balance low)
revert 0x3f109f270b65ac07cfc9a34f7f5f20522ef92bd98ed5ef19a8018c546d6135d5

This is the first draft of the auditor anouncement, so it may change.

1 Like

So Rocket Rebate is open for business. This is a non-upgradable smart contract written in Huff which is intended to maximize simplicity and gas efficiency in the conversion of ETH and stETH to Rocket Pool staked ETH (rETH).


Contract address: 0xfAaBbE302750635E3F918385a1aEb4A9eb45977a

Smart contract author: @ramana

Permalink for contract open source code:

Function of the contract:
ETH, stETH, or any combination summing less than 1.0 can be exchanged, and users receive rETH based on the official rETH price. Users benefit from the gas savings, as well as no rETH premium, no slippage. After sufficient deposits of ETH and stETH are made, the deposits ETH/stETH are drained to a wallet controlled by me (epineph). The stETH is burned manually through the normal Lido method into ETH, and the combined ETH amount is used to mint rETH in bulk when the DP has room. This rETH is sent back to the Rocket Rebate contract, and the cycle continues.

Maximum deposit amount:
This contract is intended for smaller rETH purchases that would otherwise be squeezed out of L1. Its benefits over uniswap decrease near 1ETH, and are greatest at lower amounts.
The amount that can be exchanged is limited by 1 ETH/stETH equivalent, or by the amount in the contract (for example, if it has recently been used a lot and hasn’t been manually refilled, or if the DP has been full and rETH can’t be minted).

The contract exacts a (1,000,000/32*[deposit amount]*gas price) fee on each transaction in the form of decreased rETH returned; this is already accounted for in the estimated savings and estimated rETH returned on the webiste. This works out to about 1/2th of the gas savings for a 1ETH deposit, 1/20 of the transaction cost for a 0.1 ETH deposit, etc- effectively this fee pays gas of the various operations to burn stETH and mint rETH (or buy on an exchange if under peg).

Further testing:
This is written in Huff which does not have a large number of fluent users, nor many tools for testing. The amount in the contract (my funds) is at risk; if you find a critical bug (ie, to drain or grief the contract deposits) you can submit a bug report for a bug bounty up to 1 rETH (for critical bugs). The less polite way would be to just steal the contract holdings and then write a bug report. Either would be fine. Please do not grief the funds and then expect a bug bounty.

Future plans:
This is a beta/late beta release. It needs many more eyes on it, use and exposure to be reasonably sure that the contract funds are safe. Ultimately, when larger amounts can safely stay on the contract, the goal is to reduce fees (by creating a new contract with different fee logic) as well as automating some of the manual minting/burning functions. My dream goal would be to integrate into the Rocket Pool site staking, so that if <1ETH was being staked, Rocket Rebate would be used; else if deposit pool was not full, rETH would be minted; else users directed to DEX to market buy.

1 Like

Can you please verify the contract on etherscan? I couldn’t recommend interacting with this contract, or any unverified contract, to anyone I knew.

ETA: hmm. Reading my first 3 mins about huff I wonder if that’s even possible to do. That’s a bummer if not.

More: ah yeah I guess it’s be fine since you just bind the source with the abi. Should be doable.

Yep, definitely agree with your points, and that we all need to stay safe out there!

Huff is a low level programming language and not able to be compiled on etherscan etc. Some would say not to use Huff for monetary purposes at all, and while being human readable may sometimes give an inflated sense of security, all things being equal it does give some additional protection.

From my standpoint, the consumer protections on the RocketRebate contract are:

  1. Immutable contract- so with address posted in multiple locations there won’t be subject to bait and switch
  2. Simplicity: pretty limited/targeted functionality, so not as much surface area for exploits
  3. Eyes upon it: Not yet. I expect some folks poking around it, and will post again on the huff dev discord. To me, this is not a ‘ready for Reddit’ project yet.
  4. Time - Not yet. the longer that occurs without an exploit, the less likely one will be to exist
  5. Targeted at small users: 1 ETH is a lot for much of the world, but in terms of crypto it’s small potatoes. The stETH function is mostly intended for liquidations of small residual amounts on a wallet ( the benefits are really greatest in <0.1 ETH), so leaving the stETH contract permission in place is low risk. People with larger amounts would probably use more mainstream contracts
  6. No user funds remain in the contract after access: The largest likelihood of an exploit is of the contract funds, which are ultimately mine to cover.

And honestly, I think the best feature is that you can do a simple send ETH message from your wallet, which will in turn return rETH; I believe this is effectively reduces risk to any asset except that ETH sent in that particular transaction.

Verification is not a solved problem for Huff? That’s interesting.

I’m not the target audience, but it’s a pass from me. In fact, I don’t see why anyone would even review the source given you can’t know that the code you’re reviewing is what got deployed.

All your points above are only valid if you can verify the statements. There’s no way to prove the deployed contract is not a proxy, doesn’t have changeable properties, etc. It may have my address in a list to divert, it might change behavior at a certain block, no way to know. The only thing that will give assurance is lindy, and a trust assumption on the developers. We trust the Rocket pool team, but they still verify the contracts, and even ensure that the deployed version is the audited commit.

I have to disagree that verification gives an inflated sense of security. It’s not whether you can simply read the contract on etherscan, it’s really about proving fidelity of the source to the deployed contract. It’s essential, not a nice to have.

So I will never try to convince someone to do something they think is risky (certainly not to save 4 USD!), but this is not my comfort zone so I don’t doubt that I’m communicating it poorly.

  1. The source code is certainly open source and viewable; it can be interpreted by people, just not if you only know solidity.
  2. the source code can be compiled on Foundry forks or other tools for verification/confirmation by anyone at home; just not the verification function on etherscan (which I think just recognizes solidity/vyper languages).

Just to confirm what epineph said: you can totally verify fidelity of the source code to the deployed contract. It’s just that etherscan doesn’t make it easy.

1 Like

Gotcha, yup this makes sense.