←Back to all guides

EIP-7702 and CoW Protocol Solvers: Who Holds the Keys

Solver Guides | Updated

Why our solver's submitter addresses return bytecode, what an EIP-7702 delegation proves about who holds the key, and how to check your own in one call.

We called eth_getCode on an address we believed we controlled, and it came back with bytecode.

Externally owned accounts do not have bytecode. That is close to the definition of one. So either we had misunderstood our own setup, or something had happened to the account, and on Ethereum the second explanation is usually the one that ruins your week. A delegation appearing on an address you did not delegate is the exact shape of a wallet drainer.

It was not a drainer. What it was turned out to be more interesting, and it changed how we describe our own operation.

What we believed

Before we looked properly, our picture of Kaisersolver, the CoW Protocol solver we operate, was four kinds of wallet:

  1. A rewards address that collects weekly COW.
  2. An operating wallet per chain, which we funded with ETH.
  3. A separate submitter address per chain, which put settlements on chain.
  4. All of them ours.

Every part of that except the first was wrong.

What EIP-7702 does

EIP-7702 arrived with the Pectra upgrade under the title "Set Code for EOAs." It lets an externally owned account point at a contract, so that calls into the account run that contract's code in the account's own context. The account keeps its address, nonce and balance, and gains contract-style behaviour.

Two details matter for everything below.

The delegation lives in the account's code slot as a designator: the three bytes 0xef0100 followed by the twenty-byte address of the contract it points at. That is what eth_getCode returns, and it is why an EOA can suddenly appear to have code.

Only the account itself can set it. A SetCode transaction carries authorization tuples, and each tuple is signed by the private key of the account being delegated. There is no way for a third party to set a delegation on an account whose key it does not hold. The signature is the authorization.

That second property is the whole article.

The reasoning that closes it

Put the two facts together.

A delegation designator exists on our submitter address. Only the holder of that account's private key could have created it. We do not hold that key, and we have never signed a SetCode authorization for that account.

So somebody else holds the key, and they set it.

That is not an inference from documentation or from something we were told. It proves itself from chain state. The delegation could not exist under any other arrangement.

The party holding the key is the protocol's own operations, and the reason is architectural rather than sinister.

The actual map

The operating wallet and the submitter we thought were separate are the same account on each chain, doing two jobs: quote attribution and settlement submission. That one fact explains most of our original confusion.

There are four relevant addresses. We hold the key to one. The protocol's operations hold the keys to the other three.

AddressChainDelegates toKey held by
0x689d90B257722edB0759e1D07ED956494DFc805eRewards, Ethereum mainnetNoneFiftysix
0xDd5AEcDd8BA8498706E2583f6e2Ff90E08E1c01bArbitrum submitter0xf2076Ad911785622d7A09FA5E874A1eC1EffC547Protocol operations
0x4C7Bdd2d75050C4Fdb84AD47dd328dc5C07d7743Base submitter0xd102ACE6350Feec29934432929F2b816477510F8Protocol operations
0x4914895fE025D44b2FAfbe65f7D988c6eF0c1378BNB Chain submitter0xb8872EC50ca3ff8EB57bC039c96C62Bf4DAAD922Protocol operations

The rewards address receives weekly COW. It is the only address whose key Fiftysix holds, and it has never held user funds or trading inventory.

Each submitter delegates to a different contract on its own chain. They look like the protocol's settlement submission contracts, which is the obvious thing for them to be. We have not yet matched them against a published deployment, and until we have, we would rather say so than round it up to certainty.

“The chain corrected us in one call, and the answer was better than the story we had been telling.”

What the architecture actually is

Kaisersolver takes part through the CoW DAO bonding pool under what is known as the hosted driver arrangement. Once you know who holds which key, the division of labour reads cleanly.

The engine is the brain. It receives the solve request, works out routing and pricing, and returns a proposed settlement. That is the part that competes, and it is on our side of the line.

The protocol runs the driver. It takes the solution, simulates it, signs it, and submits it on chain from the submitter address. That is the part that touches the chain, and it is not ours.

The EIP-7702 delegation is what makes the second half work. Pointing each EOA at a settlement submission contract gives the driver contract-style execution from an address that is still an EOA, which is what you would build if you wanted a separate address per solver for attribution while running the submission machinery centrally.

It is a reasonable design. The protocol did nothing unexpected. We had inherited an operating model without checking which half of it we held.

What funding the submitter actually means

Every so often we send ETH to an address whose key we do not control. Written that way, it sounds like paying a fee.

It is not a fee. It is a gas tank. The ETH is spent only as gas, and only when the driver lands one of our winning settlements, and the weekly rewards accounting pays the gas back. Economically the balance is ours the whole time, even though the key is not.

That makes it working capital rather than an expense. We go into what that does to a solver's numbers in what it costs to run a CoW Protocol solver.

Three consequences

Non-custody here is structural, not a promise. We are not promising to keep our accounts empty. We do not hold the keys to any account that could hold user value. There is one key, and it receives rewards already earned. The delegations prove the rest, because they could not exist if we held those keys.

Control and cost are separate. We cannot cancel, replace or pause a transaction on the submission path. When a settlement fails, the penalty still lands on us, because we are the registered and bonded operator of record.

Monitoring is evidence, not enforcement. Watching an address whose key you do not hold cannot stop anything. It tells you what happened. So we treat ours that way: alerts on any token balance at a submitter, any change to a delegation designator and any standing token approval, plus a real outflow watch on the one address we do control.

How to check an address for an EIP-7702 delegation

None of this needs special access. It is public chain state, and it takes about a minute.

Call eth_getCode on the address. A plain EOA returns 0x. A delegated one returns twenty-three bytes starting with 0xef0100. Strip that prefix, and the remaining twenty bytes are the contract it delegates to.

cast code 0xDd5AEcDd8BA8498706E2583f6e2Ff90E08E1c01b --rpc-url $ARB_RPC
# 0xef0100f2076ad911785622d7a09fa5e874a1ec1effc547

Without Foundry, the same call over plain JSON-RPC:

curl -s -X POST $RPC -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_getCode","params":["<address>","latest"]}'

Then ask two questions about the result. Do you recognise the delegate as a published, verified contract you can read? And did you ever sign an authorization for that account?

If the answer to the second is no, you do not hold the key to that address, whatever your internal documents say. Better to know that before someone asks you about custody than after.

The general point

If you are the company behind a solver you did not build, the first thing to establish is not what your addresses hold. It is which keys you actually have.

We had that wrong for weeks. No documentation would have corrected us, because documentation describes how a system is meant to work, not what your deployment looks like right now. The chain corrected us in one call, and the answer was better than the story we had been telling.

Kaisersolver is operated by Fiftysix LLC, a US company; the solver engine and tooling are built by William (kaisersnipes). Addresses and chain state are as observed at the dates stated. Nothing here is legal, tax or investment advice.

Share on:

Next Guide →