I like the proposed approach by @zac-williamson – especially over creating random single use addresses at the contract level.
If a dApp or wallet developer has to manage multiple addresses, the wallet UX will be poor → unusable. You will have to show your assets split between your public and private accounts, or attempt to aggregate them
The protocol could have a deterministic way to generate those addresses, or someone (us?) creates a very strong standard, however that seems like a bit of a minefield.
It could be worth removing the message sender all together for private > public transactions. A big move away from Solidity, and I am sure will have other negative consequences but some rational for discusion.
Let’s take two versions of a DEX that let’s a user add liquidity.
DEX A– Records positions in a public mapping
msg.sender > position
DEX B – Records the pool total in public state, but positions in the private state tree
IMO, end users will pick between two protocols as follows:
- I am using Aztec I have privacy.
- Protocol A is 1 tx, vs protocol B which is 2.
- Pick protocol A.
Some time later… all liquidity will be on protocol A as many users pick A.
If some liquidity still exists on B, B users will have a higher cost to using the private version (from multiple txs or slippage).
The contract developer devex without
msg.sender in the public world is actuably reasonable thanks to Note abstractions.
Note this relies heavily on a custom note implementation that works like AC claim notes.
// ignore the horrific rust / solidity mashup.
import privateToken from './token'
import note from './note'
// UTXO set mapping for storing private notes
Option(field: Option(field: note::UTXOSet)): privateBalances = ;
private function privateLP(Field: amount, Field: assetIdTo, Field: assetIdFrom, Field:
// send this contract a specific note (imagine this exists)
note::UTXO:note1 = privateToken::transferFromNote(
// contract owns the note, it can nullify
// create a peddersen encryption of the owner
field privateOwner = note::peddersenEncrypt(msg.sender);
// call a public function
public this::addLP(note1.value, assetIdTo, assetIdFrom, privateOwner)
public function addLP(Field: amount, Field: assetIdTo, Field: assetIdFrom, Field:
// get the price ( we can do this as we are in the public land)
(to, from) = this::computeAtoB(amount, assetIdTo, assetIdfrom);
// missing checks for slippage
poolTotals[assetIdTo] = poolTotals[assetIdTo] + to;
poolTotals[assetIdFrom] = poolTotals[assetIdFrom] + from;
// always return any state for the user to the data tree, likely using peddesen homomorphic hashing magic.
// this would be decided by the note implementation used
UTXO’s are the perfect throwaway addresses IMO.