Proposing a design for a smart contract W
which provides these functionalities

W
is a smart contract which shields txn.  There is only one smart contract (
W
) for all addresses.  deposit any amount of ETH in
W
 transfer any amount of ETH to another address. Here, the eth remains in
W
, just the internal accounting changes so that the transferred ETH is now controlled by the recipient.  withdraw any amount of ETH from
W
to an address.
The design can easily be extended to shield ERC20, 721 and 1155 transfers as explained at the end.
Setting up the stage
A=aG
and B=bG
are ECC public keys, G
is the generator, a
and b
are private keys.
A
wants to deposit x
eth to W
.
A
generates a random secret s
and sends H(sA,x)
as commitment (leaf in a merkle tree), along with a proof and x
eth.
The proof proves that â€”

H(sA,x)
is a hash of(saG,msg.value)
.G, msg.value
are public values. Note that this can only be proved by someone knowing the private key toA
.
H(sA,x)
is added to the merkle tree as a leaf. We call this an unspent commitment as this deposit has been not been transferred or withdrawn.
A
wants to transfer y
eth to B
All the transfers happen inside the contract, so W.balance
doesnâ€™t change and using cryptography and ZK, observers arenâ€™t able to recognize the amount being transferred, and the addresses between which the transfer is happening.
A
wants to transfer y
eth to B
. A
generates another random secret r
, and computes B_r = rB
. It nows sends a proof to W
along with public inputs H(sA,x,1)
(nullifier), H(sA,xy), H(B_r, y)
.
The proof proves that â€”

H(sA,x)
is a hash of(saG,x)
, and it has been committed (using a merkle proof). 
H(sA,x,1)
is a hash of(saG,x,1)
. 
H(sA,xy)
is a hash of(saG,xy)
. 
H(B_r, y)
is a hash of(B_r,y)
.
W
maintains a boolean hashmap m
keyed on hash values. It first checks if m[H(sA,x,1)]
is true. If so, it rejects the transaction. Otherwise, it sets it to true, and then proceeds. This is done to prevent double spending. The commitment H(sA,x)
is now a spent commitment since it canâ€™t be used anymore.
Now, it adds H(sA,xy), H(B_r,y)
as commitments (leaves in the merkle tree). Again, these are unspent commitments.
At the same time, A
shares r,y
with B
privately offchain or by using (EC)DiffieHellman. A
can alternatively announce r
publicly.
A
can claim to transfery
eth torB
, but the protocol doesnâ€™t enforce it, soA
can send it to another secret. Similarly,B
can claim that no eth was tranferred to it.
SinceH(B_r,y)
is visible onchain, this conflict can be resolved. Both parties can compute this hash value to prove their claim or to refute other partyâ€™s claim.
B
wants to withdraw from W
Assume B
knows the secret s
, and it has x
eth associated with it. B
wants to withdraw y
eth from this secret. It now sends a proof to W
along with public inputs H(sG, x, 1), H(sG,xy)
along with public value y
.
The proof proves that â€”

H(sG,x)
is a hash of(sG,x)
, and it has been committed (using a merkle path). 
H(sG,x,1)
is a hash of(sG,x,1)
. 
H(sG,xy)
is a hash of(sG,xy)
.
W
first checks if H(sG,x,1)
is already used by checking m
. If not, then it first sets this hash to true in m
.
Now, it adds H(sG,xy)
as commitment, and then transfers x
eth to B
.
Merging two secrets
It can be a pain to have your eth split across different secrets. To withdraw eth from W
, you can only withdraw an amount included in just one secret. Hence, it would be nice if you can combine all your eth under one secret. Hereâ€™s the mechanism.
Suppose A
has two commitments corresponding to (s_1G,x)
and (s_2G,y)
, and you want to combine these eth amounts (x
and y
) to one secret (s_1G,x+y)
.
A
provides a proof to W
along with public inputs H(s_1G,x,1), H(s_2G,y,1), H(s_1G,x+y)
The proof proves that â€”

H(s_1G,x)
is a hash of(s_1G,x)
. 
H(s_1G,x,1)
is a hash of(s_1G,x,1)
. 
H(s_2G,y)
is a hash of(s_2G,y)
. 
H(s_2G,y,1)
is a hash of(s_2G,y,1)
. 
H(s_1G,x+y)
is a hash of(s_1G, x+y)
.
As above, W
first checks with m
, the values for the nullifiers values. If they are false, then it sets them to true, then sets H(s_1G,x+y)
as a commitment. s_2
can be discarded now.
Viewing keys
 A commitment in
W
is of the formH(sC,x)
wheres
is a secret,C=cG
is an ECC public key andx
is the amount of ETH associated with it.  Knowing
s,c,x
means controlling this ETH. You can transfer or withdraw it.  Knowing
s, C, x
means you can verify the hash commitment.  If you want to disclose a transfer which involves your address
C
, you can disclose the secrets
and the amountx
. This knowledge wonâ€™t give anyone the control of ETH, only the ability to verify that you sent or received this amount.  If you want to reveal your balance in
W
to someone, you can disclose the secrets
and amountx
for all your commitments.
Enable ERC20, 721 and 1155 transfers
 ERC20: Use
H(sA, ERC20_addr, amount)
as commitments.  ERC721: Use
H(sA, ERC721_addr, tokenId)
as commitments.  ERC1155: Use
H(sA, ERC1155_addr, tokenId, amount)
as commitments.
If we want all of this in the same contract, then a generalized commitment will be of the form H(sA, addr, tokenId, amount)
. We can use zero
where a field is not required.
Open Questions
 Is it possible to fill up the merkle tree blocking further interaction with the protocol?