In a state channel network, multiple channels need to be funded on chain — be they user-to-user or user-to-hub connections. There are a few approaches that we know of:
- Escrowing assets in a singleton contract for multiple channels
- Escrowing assets in an ethereum account per channel.
a. Where the account is a contract
b. Where the account is not a contract
From a gas point of view, the main pros and cons are as follows:
1. Singleton escrow contract
- No contract creation costs are necessary per channel — the single contract need be deployed only once (per chain).
- Escrow of ETH and tokens is relatively expensive and cumbersome. Each deposit must update some storage in the escrow contract in order to declare which channel is being deposited into: in other words, a “layer 2 accounting system” needs to be implemented in the escrow contract.
- This further implies that deposits must be made via a contract call to the escrow contract. If the asset is an ERC20 token (far and away the most popular standard), this necessitates i) an
approve
call to the token itself followed by ii) thedeposit
call to the escrow contract (which internally callstransferFrom
on the token, and atomically updates the layer 2 accounting system). This flow causes excess reads/writes to storage in the token contract (e.g. theallowances
mapping). - Depending on how the layer 2 accounting system works, the costs can be partially mitigated by clearing storage slots when the channel is liquidated, but only so long as the relevant gas refunds remain a feature (see EIP 3529).
- Yet a further nuisance is the need to perform i) and ii) above in two separate transactions, implying poor UX due to longer waits and higher gas overheads due to the base transaction fee.
2a. Escrow contract account per-channel
- Contract creation and code deposit are gas expensive. This can be partially mitigated by having the contract be a very lightweight proxy such as an EIP-1167 Proxy. The proxy points to a singleton “Master Adjudicator” which need be deployed only once (per chain). The costs can also be partially mitigated by
SELFDESTRUCT
gas refunds, so long as they remain a feature of mainnet (not for much longer, see EIP 3529 scheduled for London). - Contract addresses deployed using
CREATE
have difficult-to-predict addresses. This is solved by having the escrow contract deployed withCREATE2
. There are benefits to having predictable escrow contract addresses: deployment can be delayed until the funds need to be liquidated, adding some extra privacy and security. - Escrow of ETH and tokens can be as cheap and easy as is possible: just send the assets to the escrow contract for the appropriate channel.
2b. Escrow (non-contract) account per-channel
- This architecture would be made possible by EIP 3074. See this comment.
- Escrow of ETH and tokens can be as cheap and easy as is possible, just send the assets to the escrow account (called a synthetic EOA) for the appropriate channel
- A singleton Invoker contract controls all of the synthetic EOAs. There is a unique synthetic EOA address for each channel
- No layer 2 accounting system is necessary
- No contract creation or code deposits are needed
- It’s the best of both worlds, having our cake and eating it too!
- There are some modest additional gas expenditures for
AUTH
andAUTH_CALL
when liquidating the channel.
More detail on synthetic-EOA-per-channel approach
-
A singleton
Adjudicator
contract is deployed ahead of time by the state channel protocol team, and plays the role of an EIP-3074Invoker
. -
Then, state channel participants generate a 32 bytes
channelId
in the established way: they hash the combination of their state channel wallet addresses with thechainId
and anonce
. -
The participants then derive a
channelAddress
from thechannelId
. They do this by interpreting thechannelId
as an EIP-3074 signature, i.e. a ECDSA signature on the digest:
digest = keccak256(MAGIC || padTo32Bytes(Adjudicator) || padTo32Bytes(0))
channelAddress = ecrecover(digest, syntheticSignature(channelId))
Note that we set the EIP-3074 commit
to 0.
-
Participants then send funds directly to
channelAddress
, for example viaERC20.transfer
. Noapprove
transaction is necessary. -
When it comes to liquidating the channel, any participant holding a “support proof” (state channel jargon for the requisite off-chain state and signatures to close a channel) may submit that proof to the Adjudicator. It will
- performs all of the usual checks according to the state channel protocol;
- do
auth(commit = 0, yParity = 0, r = channelId, s = channelId)
, which sets msg.sender to channelAddress; - do
authcall
to interact with token contracts and move tokens from channelAddress to the beneficiaries of the channel.
Note that channelAddress
is a “synthetic EOA”, meaning an EOA for which the private key is not known or used. In that sense it is similar to a contract; but a crucial difference is that no code or storage needs to (or even can) be deployed “under” the channelAddress.
This is because the “synthetic signature”, in this case the channelId
, confers the Adjudicator
the power to control precisely one Ethereum account: namely, the account with the channelAddress
address. I’m glossing over some details around how to construct a synthetic signature from the channelId
– we can discuss that over at Reliable and safe generation of synthetic signatures.
The term “synthetic EOA” is somewhat oxymoronic: the account is not externally owned by any single party, or even by a set of parties. It is controlled by a single contract only.
The collision resistance of the signature scheme makes it infeasible that any other contract can control the synthetic EOA.
Now it is important for funds in a state channel escrow to be liquidated only when strict conditions (set by the state channel protocol) are met. It would be possible in principle to make use of the commit
field in the AUTH
message format, to commit to the relevant cryptographic proof for liquidating the channel. Alternatively, the commit
field is set to zero, which gives the maximum level of control to the Adjudicator.
Since the Adjudicator
must be trusted (in the sense of having its behaviour verified or audited), there is no real change in the trust model by making it an EIP-3074 Invoker
and handing it complete control over a set of synthetic EOAs.