Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Interact with Euler

Use the EVC when an integration needs authenticated batching, sub-accounts, collateral and controller management, operators, permits, simulation, or cross-vault actions. The examples below are building blocks; production integrations must resolve deployed interfaces, approvals, account state, and target-chain addresses. For offchain TypeScript integrations, the Euler SDK abstracts these EVC patterns — batching, sub-accounts, permits, and simulation — behind typed helpers; this guide shows the underlying contract-level calls.

1. Batching operations

Batching lets you group multiple actions into a single atomic transaction. Use the batch function and the BatchItem struct:

IEVC.BatchItem[] memory items = new IEVC.BatchItem[](2);
 
// Example: Deposit collateral and then borrow in a single batch.
// Assumes the collateral asset is approved and the collateral/controller
// vaults are already enabled for myAccount.
items[0] = IEVC.BatchItem({
    targetContract: collateralVault,
    onBehalfOfAccount: myAccount,
    value: 0,
    data: abi.encodeWithSelector(IVault.deposit.selector, depositAmount, myAccount)
});
 
items[1] = IEVC.BatchItem({
    targetContract: borrowVault,
    onBehalfOfAccount: myAccount,
    value: 0,
    data: abi.encodeWithSelector(IVault.borrow.selector, borrowAmount, myAccount)
});
 
connector.batch(items);

All operations succeed or fail together. You can batch across multiple vaults and even external contracts.

2. Using sub-accounts

Sub-accounts allow you to separate positions and strategies. Each address prefix has 256 derived account addresses. Token allowances, vault collateral/controller enablement, and operator permissions are separate mechanisms and can still be required for a workflow.

Address structure and derivation

All 256 sub-accounts belonging to the same owner share the first 19 bytes of their Ethereum address. The only difference between them is the very last byte. This means that, on-chain, you can easily recognize which sub-accounts belong to the same owner: they will look almost identical, except for the final two characters.

A sub-account address is created by XOR-ing your main address with a number from 0 to 255 (as a uint8).

  • Your main address is sub-account 0 (no change).
  • Sub-account 1 is your address XOR 1, sub-account 2 is your address XOR 2, and so on up to 255.

This structure makes sub-accounts easy to group and identify onchain while preserving unique addresses for each sub-account.

Example: deriving a sub-account address in Solidity

function getAccount(address owner, uint8 accountId) public pure returns (address) {
    return address(uint160(owner) ^ uint160(accountId));
}
 
// Usage:
address myAddress = /* your main address */;
address account3 = getAccount(myAddress, 3); // This is your 4th sub-account

For each operation, distinguish the account authenticated through onBehalfOfAccount from the address that receives assets or shares:

  • Deposits:
    • onBehalfOfAccount must be an address that actually controls the ERC-20 balance and allowance—commonly the main wallet. A typical XOR-derived EOA sub-account has no separate signer.
    • The receiver parameter (in the deposit function) can be a sub-account address, so the deposited shares are credited to the sub-account.
  • Borrows:
    • onBehalfOfAccount should be the sub-account for which you want to create the debt position.
    • The receiver should be an address that can control the received ERC-20 tokens—commonly the main wallet—not an unsigned derived address.

Example: deposit to a sub-account

// Deposit tokens from your main address, but credit the shares to subAccount3
IEVC.BatchItem[] memory items = new IEVC.BatchItem[](1);
items[0] = IEVC.BatchItem({
    targetContract: vault,
    onBehalfOfAccount: myAddress, // tokens are pulled from here
    value: 0,
    data: abi.encodeWithSelector(IVault.deposit.selector, depositAmount, subAccount3) // shares go to sub-account
});
connector.batch(items);

Example: borrow from a sub-account

// Borrow on behalf of subAccount3, but send tokens to your main address
IEVC.BatchItem[] memory items = new IEVC.BatchItem[](1);
items[0] = IEVC.BatchItem({
    targetContract: vault,
    onBehalfOfAccount: subAccount3, // debt is created here
    value: 0,
    data: abi.encodeWithSelector(IVault.borrow.selector, borrowAmount, myAddress) // tokens sent to main address
});
connector.batch(items);

This distinction is about control, not a protocol rule that ERC-20 balances always live at a main address. Most ERC-20 contracts cannot authenticate EVC ownership of a derived sub-account, so tokens sent there are generally not recoverable with the owner's normal wallet signature unless a reviewed EVC-aware flow exists.

3. Account ownership and registration

The EVC maintains a mapping of account owners, which is important for resolving the relationship between sub-accounts and their primary (EOA or smart contract) owner. This is especially relevant for integrations, analytics, and off-chain systems.

How ownership is registered

An account's owner is registered in the EVC the first time the owner interacts with the EVC (for example, by enabling collateral or a controller, or performing EVC batch or call). Until this happens, the EVC does not know who the owner is.

Direct vault interactions and edge cases

Accounts can interact with vaults directly, bypassing the EVC. This means:

  • An account may not have an owner registered in the EVC until it finally interacts with the EVC (e.g., to enable collateral or a controller for borrowing).
  • If an account receives vault shares (e.g., as a transfer) before its owner has ever interacted with the EVC, the EVC will not have an owner registered for that account.

Integration pattern and limitations

A typical integration pattern is:

  • Use the EVC's getAccountOwner(address account) function to resolve the owner.
  • If the owner is not registered (i.e., the function returns address(0)), assume that the account is its own owner.
address owner = evc.getAccountOwner(account);
if (owner == address(0)) {
    // Owner not registered; assume the account is its own owner
    owner = account;
}

4. Operator delegation

Operators delegate EVC-authenticated authority to another address, such as a reviewed bot or automation contract. setAccountOperator(account, operator, true) toggles one account. setOperator(prefix, operator, bitField) sets a bitmap across the 256 account IDs sharing that prefix.

// Grant operator rights for a specific sub-account
evc.setAccountOperator(subAccount, operatorAddress, true);
 
// Grant operator rights for ALL sub-accounts of the owner
// The prefix groups the owner's 256 account IDs.
// Each set bit grants the operator authority for that account ID.
// type(uint256).max sets all 256 bits.
evc.setOperator(evc.getAddressPrefix(owner), operatorAddress, type(uint256).max);

5. Liquidations with controlCollateral

To perform a liquidation, the controller vault calls controlCollateral to seize collateral from a borrower's sub-account. Typically, this is done by transferring vault shares from the violator's sub-account to the liquidator using the transfer function:

// Example: Controller vault seizes collateral shares from violator's sub-account
address collateralVault = /* address of the collateral vault */;
address violator = /* sub-account in violation */;
address liquidator = /* address of the liquidator */;
uint256 seizeShares = /* number of shares to seize */;
 
// Prepare transfer call data
bytes memory transferData = abi.encodeWithSelector(
    IVault.transfer.selector,
    liquidator,    // recipient of the shares
    seizeShares    // amount of shares to transfer
);
 
// Call controlCollateral
connector.controlCollateral(
    collateralVault,
    violator,
    0, // value
    transferData
);

controlCollateral is accepted only from the account's enabled controller in the EVC's collateral-control context. Within that context the controller can call the enabled collateral vault on behalf of the account, including a withdrawal or share transfer that would not require the account's ordinary ERC-20 allowance. Collateral-vault implementations must authenticate this exact EVC context; controller implementations determine when and how the authority is used.

6. Simulating transactions

You can simulate batches before execution using batchSimulation:

(
    IEVC.BatchItemResult[] memory results,
    IEVC.StatusCheckResult[] memory accountChecks,
    IEVC.StatusCheckResult[] memory vaultChecks
) = connector.batchSimulation(items);
 
// Analyze results before sending a real transaction

This is useful for diagnostics in frontends, bots, and risk-management systems. Simulation mode is observable by called contracts, so an untrusted target can behave differently in simulation and execution. Do not treat a successful simulation as a security guarantee.

7. Relayed transactions with permits

The EVC supports EIP-712 permits. A user can sign an authorization offchain and an allowed relayer can submit it; the relayer pays execution gas, although an application may charge or recover that cost separately.

Permits are useful for relayed transactions and automation on the chain where the permit is valid. The EVC's permit function supports both ECDSA (EOA) and ERC-1271 (smart-contract wallet) signatures.

How to use EVC permits

  1. Construct the permit message, specifying the signer, sender, nonce, deadline, value, and calldata (typically a batch of operations).
  2. Sign the message off-chain using EIP-712.
  3. Submit the permit to the EVC's permit function:
evc.permit(
    signer,         // The user authorizing the action
    sender,         // The relayer or executor
    nonceNamespace, // For replay protection
    nonce,          // For replay protection
    deadline,       // Expiry timestamp
    value,          // ETH value to forward (usually 0)
    data,           // Encoded calldata (e.g., batch)
    signature       // EIP-712 signature
);

The EVC will verify the signature, check the nonce and deadline, and then execute the requested operations as if they were sent by the signer.

Understanding nonce namespace

The EVC's permit system uses a nonceNamespace and nonce for replay protection and sequencing. The nonceNamespace allows you to have multiple independent streams of permits for the same account. This is useful if you want to:

  • Allow parallel workflows (e.g., one for regular actions, one for high-priority or emergency actions)
  • Cancel or replace a specific stream of permits without affecting others

For most simple use cases, you can use a single namespace (e.g., nonceNamespace = 0) and increment the nonce for each new permit. For more advanced scenarios, you can assign different namespaces to different workflows or applications.

8. Emergency modes: lockdown and permit disabled

The EVC provides two emergency modes that can help users limit account activity in case of compromise or suspicious activity:

Lockdown mode

  • When enabled, Lockdown Mode restricts all operations for the affected address prefix (all 256 sub-accounts), except for managing operators and nonces.
  • No external contract calls or value transfers are allowed, but controllers can still control collateral for the accounts.
  • Useful if you suspect a malicious operator or permit has been added.

Enable with:

evc.setLockdownMode(evc.getAddressPrefix(owner), true);

Permit disabled mode

  • When enabled, this mode prevents execution of any permits signed by the owner for the affected address prefix.
  • Useful if you believe a harmful permit message has been signed.

Enable with:

evc.setPermitDisabledMode(evc.getAddressPrefix(owner), true);

9. Best practices

  • Use batching for related operations to save gas and make the operation atomic
  • Use sub-accounts for position and strategy separation
  • Delegate with operators for automation only after reviewing the delegated contract or account
  • Simulate complex transactions before execution
  • Review EVC security features such as Lockdown Mode and Permit Disabled Mode as part of your account response plan

Read next