You can now deploy proposals for instances & instances yourself! 🥳

Hi guys! :wave:

Tornado Cash wouldn’t be anything without the on-chain instances, but up until now there was the problem that it wasn’t easy for anyone to deploy instances and propose them. For this reason, two new contracts have been deployed to Mainnet and verified. These are the (both links link to Etherscan!) MinimalInstanceFactory and InstanceProposalFactory together with two implementation contracts, namely ETHTornadoCloneable and ERC20TornadoCloneable.

I’ll call the “MinimalInstanceFactory” the “Clone factory :factory:” and the “InstanceProposalFactory” the “Proposal factory :factory:” going further!

Essentially, now you will be able to deploy proposals (and new instances) by yourself, starting via manual contract calls via Etherscan (or other methods, we will discuss this later) until UI’s and other stuff implement methods for this to be done from other interfaces as well!

Addresses:

MinimalInstanceFactory = 0x9d00007c0f5037157b5be8bff174b194a99118d0
InstanceProposalFactory = 0x10715a092e160793C278a9830F0f8D4417B94D71
ERC20TornadoCloneable (implementation) = 0xED2c9A637379DBF045982335db34Ea948F5FDB10
ETHTornadoCloneable (implementation) = 0xFDE7d58A869B7D4B11cA57A35a10D3D95B1683B2

Disclaimer

Obviously you should not just believe me but also check out the code of the contracts out by yourself. For example, you could take the older contracts and put them into a file, then compare them with the ones from the Github tornado-core and other repositories. Do note that the relative paths of the import statements might be different in some contract files, but comb through the code and you will see it is exactly the same logic, other than the stuff that was added on top to make things work (the most important part security wise, you can imagine, is that the cloneable instances are safe).

How it works

The Instance factory :factory: allows you to create an instance for any token of any denomination, you just have to call the createInstanceClone(uint256 _denomination, address _token) function with a wanted denomination and a token address, and voila - the instance will be created, no strings attached! Note - a new instance won’t be deployed if there already is one for some token and denomination, it will just return the address.

This created instance is immediately ready for depositing, the source code on Etherscan will refer to the implementation (because these are cloned contracts) source code. Let’s continue on to the Proposal factory, which is where things get spicy.

The Proposal factory :factory: deploys proposals to add Instances to the TORN staking system. The proposed instances can be any token or denomination, but with a catch - since the staking system uses Uniswap V3 to get some TWAP (Time Weighted Average Price) data, we need a Uniswap V3 pool with a reliable history price history. This means, that the Proposal factory :factory: will not consider every token to be eligible to be added to the staking system. It automatically checks this.

Otherwise, the Proposal factory :factory: allows you to create a proposal for any amount of tokens and denominations as long as it fits within a single block! You just have to call one function (createProposalContract, more on it later), take the address, and propose that address as a new proposal, done - your instance are proposed!

Details

Now for some necessary details when it comes to deploying the proposals, because this can get just a little bit complicated! :grimacing:

In future, future-ish, soon (I don’t know!) this should be handled automatically (and not via :frog: REEEEtherscan), but for now start noting down! :writing_hand:

The most complicated part is choosing the denominations wisely!


The denominations MUST be in the following format:

p = power, d = denomination, d * 10^p where 1 <= d <= 1099511627775 && 0 <= p <= 65535

(2^16 - 1 === 65535 , 2^40 - 1 === 1099511627775)

This is because we are storing the data efficiently to reduce gas costs! In MANY CASES:

p = 18

Because 2^40 - 1 will be enough to represent how large the denomination is, if not, bump p!

For each token, there will be ONE SET of denominations and ONE power.


The rest of the construction goes as follows:

  1. Choose the tokens and for each one a set of denominations and one power.
  2. For each token, choose a UNISWAP POOL SWAPPING FEE (the usual ones: 10000 == 1%, 3000 == 0.3%, 500 = 0.05%).
  3. For each DENOMINATION choose a PROTOCOL FEE (fee on withdraw, 100 == 1%, max 3%).
  4. Count the total number of denominations for all tokens.

YOU ARE ALMOST DONE.

Filling in the function goes as follows:

createProposalContract(
	[token_address_1, token_address_2, ..., token_address_t],
	[swapping_fee_1, swapping_fee_2, ..., swapping_fee_t],
	[power_1, power_2, ..., power_t],
	[[denomination_1_1, ..., denomination_1_d], ..., [..., denomination_t_d]],
	[[fee_1_1, ..., fee_1_d], ..., [..., fee_t_d]],
	d (the number of denominations we used to index above)
)

YOU ARE DONE!


A mini note on the Instance factory :factory::

The above doesn’t count for deploying instances, when using the createInstanceClone function, you just use the full denomination number (with decimals and all of that) and an address.


Obviously you can use Tor Browser to access Etherscan and connect with say… a Metamask hot wallet (because it doesn’t recognize cold wallets because it blocks access to your devices, but you would obviously use a hot wallet), and then just call the function from there.

Furthermore if you’re a dev for now grab the ABI off Etherscan or encode according to the function signature, this should be easy since there is no bytes data involved.


Why, THANK YOU ~ VERY MUCH ~ for reading this through! :wave:

2 Likes