Composer deploys a small set of contracts on every supported EVM chain. Your wallet sends the
compiled transaction to your per-user proxy (or, on the proxy’s first use, to the ProxyFactory,
which deploys the proxy and runs the flow atomically). The proxy delegatecalls the VM — the shared
logic contract — so the flow executes in your proxy’s own balance and storage context. The remaining
contracts support arithmetic and invariant checks during execution.
| Contract | Role |
|---|
| VirtualMachine | Shared logic that executes a compiled Flow. Each proxy delegatecalls the VM — it is not the transactionRequest.to. |
| ProxyFactory | Deterministically derives each signer’s per-signer execution proxy, and is the transactionRequest.to on the proxy’s first use (deployAndExecute). See Account model. |
| ArithmeticProcessor | Evaluates core.* arithmetic instructions during execution. |
| InvariantChecker | Enforces guard assertions baked into the calldata. |
| FlashloanRegistry | Resolves flashloan providers for the flashloan materialiser. |
| LiFiFlashloanAdapter | Initiates flashloans and routes the provider callback so lifi.flashloanRepay can settle each leg. |
Verify before you hardcode. The most reliable source is the POST /compose response itself:
userProxy is your per-user proxy, and transactionRequest.to is that same proxy once it’s deployed
(or the ProxyFactory on first use). These always match the deployment that produced your calldata.
Treat the tables below as a convenience snapshot.
Canonical (production)
Served from https://composer.li.quest. These addresses are the same on every supported chain.
| Contract | Address |
|---|
| VirtualMachine | 0xb57Ce43Be47DF611C98EB0943e5D36EBDb36cc6D |
| ProxyFactory | 0xe174D02351656a883f6626497C86684e849efB35 |
| InvariantChecker | 0xe17006F4DfE8Aa2bf80589E497ad98D470f66fef |
| ArithmeticProcessor | 0x46C2c852E6FEfaF173dFe49457f0Fe61Dc3F587a |
| FlashloanRegistry | Not in production — ETHGlobal preview only |
| LiFiFlashloanAdapter | Not in production — ETHGlobal preview only |
Flashloans are not yet deployed to production; the FlashloanRegistry and LiFiFlashloanAdapter contracts exist only in the ETHGlobal preview deployment below.
ETHGlobal hackathon
Unaudited preview. The ETHGlobal hackathon deployment, and any contract surface that exists only there (flashloans included), is an unaudited preview intended for the hackathon and experimentation only. Do not point production applications at these addresses or route significant value through them. Use the canonical production deployment (composer.li.quest) for anything handling real money.
A separate deployment is used for the ETHGlobal hackathon. Point the SDK at the hackathon host:
const sdk = createComposeSdk({
baseUrl: 'https://ethglobal-composer.li.quest',
apiKey: process.env.LIFI_API_KEY,
});
| Contract | Address |
|---|
| VirtualMachine | 0x6Db1F048B899ADb75728A5976F871AF1426A9443 |
| ProxyFactory | 0xA0cA0957fa87d3Ed7243d8dfd442979fa5A0dd7a |
| InvariantChecker | 0xd31Ede9fD78d0f6Aa61c5De13849164c3874b8fd |
| ArithmeticProcessor | 0x5506E9fbf8168Ef6aEe346ED2Aa5412dD98554cD |
| FlashloanRegistry | 0x5032441C17A58D247e605f928c23De0BB011A7B9 |
| LiFiFlashloanAdapter | 0x2dD49Bd2E4afC5b48877fD1a598b0dA59e39e692 |
Addresses from the compile response
You rarely need to hardcode anything: every POST /compose response carries the two addresses an
integration actually touches, and they always match the deployment (canonical or hackathon) that
produced the calldata.
const result = await builder.compile({ signer, inputs, sweepTo });
result.transactionRequest.to; // your execution proxy (the ProxyFactory on first use)
result.userProxy; // your per-signer execution proxy address
The remaining contracts (arithmetic processor, invariant checker, flashloan registry, adapter) are
wired in by the compiler during lowering and don’t need to be referenced from integration code.