If you are a developer following along, you’ll want to edit the metamask() test suite in tests/clevis.js to add in your address so you will receive a little ETH each time the contracts are deployed:

To receive your ETH you can do a full redeploy with:

clevis test full

If you inspect line 154 we are also minting 5000 SomeCoins to the Loan contract to provide us with tokens to loan to users. The frontend shows us the address of the Loan contract and the current balance of SomeCoins:

Awesome. Before we can actually issue loans, we will need to prepare our predictive deployment scripts. As we saw above, the counterfactual crafting script takes in an ABI and bytecode so let’s compile our Sweeper.sol to bytecode:

🗜️ Clevis:/dapp 🗜️ clevis compile Sweeper

Now we can launch our backend script that will handle everything for us with simple ajax calls from our frontend:

🗜️ Clevis:/dapp 🗜️ node backend.js

Now we are ready to issue a loan to our metamask user from the frontend:

When we hit issue a lot of things happen and we should see a new loan and the token balance of the metamask user should adjust to 100:

Notice that we also have a simple address to return our tokens to when we are done using them! Rad. Okay. Whoa. What all just happened?

First, the React app POSTed the loanAmount and loanRecipient to the backend server:

As you can see here in the backend.js it crafts the counterfactual transaction using the script demoed above by passing in the Sweeper:

The result of this crafting is returned to the frontend and that’s how it displays the address we need to return our tokens too. But, we can also query the data from the backend using the /tx/ endpoint and our loan id:

This is the output from our counterfactual/craft.js script we explored above. If the transaction is posted to the Ethereum network, our Sweeper contract will be deployed from account 0x69D3Ec… and its address will be 0x4D8e539…

But! We don’t want to deploy that contract yet. Maybe the loan will be repaid in the traditional way or maybe it won’t ever be repaid at all. Either way, we will have to wait and see. An off-chain script can run regularly and watch the state of our loan:

Let’s go ahead and transfer the 100 SomeCoins to the repayment address:

Notice we can use the Blockie (Identicon) as a hint that we have the address right.

And when the tokens arrive at the address, our frontend reflects the change:

The counterfactual address where the Sweeper will be deployed to now contains the correct amount of tokens and we are ready to deploy. Let’s hit the Collect button and fire off the repayment sweep:

Neato gang! The Loan contract now contains the original 5000 SomeCoins and the loan is marked as PAID on-chain. Let’s inspect what just happened:

The frontend tells the backend to collect the loan of a specific id. The backend first sends a little ETH to the keyless from address so it has enough funds to deploy the contract. Then, the backend runs the counterfactual/deploy.js which sends our transaction to the Ethereum network to deploy, approve, sweep, and destroy.

To further unpack all of this, we deployed the Sweeper.sol contract to the predicted location that was holding the 100 SomeCoins. The Sweeper then approved the Loan contract to move the tokens and called the repay() function:

Then the repay function marked the loan as repaid and collected the tokens:

Finally, the Sweeper destroyed itself to clean up on-chain storage.

Pitfalls

There are few things to mention that I’d love to get some feedback on from the community. First, the counterfactual transaction is very sensitive. The gas constraints and nonce are all signed and you can’t adjust them. If the transaction runs before there are enough tokens or the gasLimit is wrong, there is a chance that the transaction will fail and the tokens will be locked up forever in an account we will never know the private key of. 😪😪😪

A smaller but worth mentioning issue is the deploying from address will be left with some ETH that we will never get back. It is isn’t much, but that adds some overhead costs.

I bet some smartypants out there can figure out some possible solutions and how we can collect those leftover funds from the contract destroy?

Conclusion

Using this method, we were able to simplify the process for the end user by adding a little extra complexity on our side. Instead of the user having to approve() the Loan contract and call the repay() function themselves. They were able to just transfer() the tokens to a repayment address and our off-chain cron will automatically sweep the tokens. This is a much easier task for a new user in the space. If we are going to drive mass adoption in Ethereum, we need to make the UX as smooth as possible. But maybe it isn’t worth it? Maybe just a good UX around the approve() and repay() is good enough?

Thanks for kicking through this exploration with me. If you’d like to get in touch, please do! I’m @austingriffith on telegram/twitter and I try to keep some of my projects and articles posted up at: https://austingriffith.com Huge thanks to my dude Kevin Owocki for letting me rep Gitcoin Labs.

THANKS!