Skip to content

Intents

The intent vocabulary - high-level descriptions of what a strategy wants to do. The framework compiles these into executable transactions.

Intent

Factory class for creating intents.

almanak.framework.intents.Intent

Factory class for creating intents with a fluent API.

This class provides static factory methods for creating intents, making strategy code more readable and ergonomic.

Example

Instead of:

intent = SwapIntent(from_token="USDC", to_token="ETH", amount_usd=Decimal("1000"))

You can write:

intent = Intent.swap(from_token="USDC", to_token="ETH", amount_usd=Decimal("1000"))

swap staticmethod

swap(
    from_token: str,
    to_token: str,
    amount_usd: Decimal | None = None,
    amount: ChainedAmount | None = None,
    max_slippage: Decimal = Decimal("0.005"),
    protocol: str | None = None,
    chain: str | None = None,
    destination_chain: str | None = None,
) -> SwapIntent

Create a swap intent.

Parameters:

Name Type Description Default
from_token str

Symbol or address of the token to swap from

required
to_token str

Symbol or address of the token to swap to

required
amount_usd Decimal | None

Amount to swap in USD terms

None
amount ChainedAmount | None

Amount to swap in token terms, or "all" to use previous step output

None
max_slippage Decimal

Maximum acceptable slippage (default 0.5%)

Decimal('0.005')
protocol str | None

Preferred protocol for the swap

None
chain str | None

Source chain for execution (defaults to strategy's primary chain)

None
destination_chain str | None

Destination chain for cross-chain swaps (None for same-chain)

None

Returns:

Name Type Description
SwapIntent SwapIntent

The created swap intent

Example

Swap $1000 worth of USDC to ETH

intent = Intent.swap("USDC", "ETH", amount_usd=Decimal("1000"))

Swap 0.5 ETH to USDC on Base

intent = Intent.swap("ETH", "USDC", amount=Decimal("0.5"), chain="base")

Swap all ETH from previous step output

intent = Intent.swap("ETH", "USDC", amount="all", chain="base")

Cross-chain swap: Base USDC -> Arbitrum WETH via Enso

intent = Intent.swap("USDC", "WETH", amount_usd=Decimal("1000"), chain="base", destination_chain="arbitrum", protocol="enso")

lp_open staticmethod

lp_open(
    pool: str,
    amount0: Decimal,
    amount1: Decimal,
    range_lower: Decimal,
    range_upper: Decimal,
    protocol: str = "uniswap_v3",
    chain: str | None = None,
) -> LPOpenIntent

Create an LP open intent.

Parameters:

Name Type Description Default
pool str

Pool address or identifier

required
amount0 Decimal

Amount of token0 to provide

required
amount1 Decimal

Amount of token1 to provide

required
range_lower Decimal

Lower price bound for concentrated liquidity

required
range_upper Decimal

Upper price bound for concentrated liquidity

required
protocol str

LP protocol (default "uniswap_v3")

'uniswap_v3'
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
LPOpenIntent LPOpenIntent

The created LP open intent

Example

Open an ETH/USDC LP position around the current price

intent = Intent.lp_open( pool="0x8ad...", amount0=Decimal("1"), # 1 ETH amount1=Decimal("2000"), # 2000 USDC range_lower=Decimal("1800"), range_upper=Decimal("2200"), )

lp_close staticmethod

lp_close(
    position_id: str,
    pool: str | None = None,
    collect_fees: bool = True,
    protocol: str = "uniswap_v3",
    chain: str | None = None,
) -> LPCloseIntent

Create an LP close intent.

Parameters:

Name Type Description Default
position_id str

Identifier of the position to close

required
pool str | None

Pool address (optional, for validation)

None
collect_fees bool

Whether to collect accumulated fees (default True)

True
protocol str

LP protocol (default "uniswap_v3")

'uniswap_v3'
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
LPCloseIntent LPCloseIntent

The created LP close intent

Example

Close an LP position and collect fees

intent = Intent.lp_close(position_id="12345")

Close without collecting fees

intent = Intent.lp_close(position_id="12345", collect_fees=False)

borrow staticmethod

borrow(
    protocol: str,
    collateral_token: str,
    collateral_amount: ChainedAmount,
    borrow_token: str,
    borrow_amount: Decimal,
    interest_rate_mode: InterestRateMode | None = None,
    market_id: str | None = None,
    chain: str | None = None,
) -> BorrowIntent

Create a borrow intent.

Parameters:

Name Type Description Default
protocol str

Lending protocol (e.g., "aave_v3", "morpho_blue")

required
collateral_token str

Token to use as collateral

required
collateral_amount ChainedAmount

Amount of collateral to supply, or "all" for previous step output

required
borrow_token str

Token to borrow

required
borrow_amount Decimal

Amount to borrow

required
interest_rate_mode InterestRateMode | None

Interest rate mode for Aave ('variable' | 'stable'). Only applies to protocols that support rate mode selection. For Aave V3, defaults to 'variable' if not specified.

None
market_id str | None

Market identifier for isolated lending protocols (e.g., Morpho Blue). Required for morpho/morpho_blue, ignored for aave_v3.

None
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
BorrowIntent BorrowIntent

The created borrow intent

Example

Supply ETH as collateral and borrow USDC on Arbitrum with variable rate

intent = Intent.borrow( protocol="aave_v3", collateral_token="ETH", collateral_amount=Decimal("1"), borrow_token="USDC", borrow_amount=Decimal("1500"), interest_rate_mode="variable", chain="arbitrum", )

Borrow on Morpho Blue (requires market_id)

intent = Intent.borrow( protocol="morpho_blue", collateral_token="wstETH", collateral_amount=Decimal("0"), # Already supplied borrow_token="USDC", borrow_amount=Decimal("1500"), market_id="0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc", chain="ethereum", )

repay staticmethod

repay(
    protocol: str,
    token: str,
    amount: ChainedAmount,
    repay_full: bool = False,
    market_id: str | None = None,
    chain: str | None = None,
) -> RepayIntent

Create a repay intent.

Parameters:

Name Type Description Default
protocol str

Lending protocol (e.g., "aave_v3", "morpho_blue")

required
token str

Token to repay

required
amount ChainedAmount

Amount to repay, or "all" to use previous step output

required
repay_full bool

If True, repay the full outstanding debt

False
market_id str | None

Market identifier for isolated lending protocols (e.g., Morpho Blue). Required for morpho/morpho_blue, ignored for aave_v3.

None
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
RepayIntent RepayIntent

The created repay intent

Example

Repay 500 USDC on Aave

intent = Intent.repay( protocol="aave_v3", token="USDC", amount=Decimal("500"), )

Repay full debt on Morpho Blue

intent = Intent.repay( protocol="morpho_blue", token="USDC", amount=Decimal("0"), # Ignored when repay_full=True repay_full=True, market_id="0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc", )

supply staticmethod

supply(
    protocol: str,
    token: str,
    amount: ChainedAmount,
    use_as_collateral: bool = True,
    market_id: str | None = None,
    chain: str | None = None,
) -> SupplyIntent

Create a supply intent.

Parameters:

Name Type Description Default
protocol str

Lending protocol (e.g., "aave_v3", "morpho_blue")

required
token str

Token to supply

required
amount ChainedAmount

Amount to supply, or "all" to use previous step output

required
use_as_collateral bool

Whether to enable as collateral (default True)

True
market_id str | None

Market identifier for isolated lending protocols (e.g., Morpho Blue). Required for morpho/morpho_blue, ignored for aave_v3.

None
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
SupplyIntent SupplyIntent

The created supply intent

Example

Supply 1 ETH to Aave V3 on Arbitrum

intent = Intent.supply( protocol="aave_v3", token="WETH", amount=Decimal("1"), chain="arbitrum", )

Supply wstETH to Morpho Blue market

intent = Intent.supply( protocol="morpho_blue", token="wstETH", amount=Decimal("1"), market_id="0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc", chain="ethereum", )

withdraw staticmethod

withdraw(
    protocol: str,
    token: str,
    amount: ChainedAmount,
    withdraw_all: bool = False,
    market_id: str | None = None,
    chain: str | None = None,
) -> WithdrawIntent

Create a withdraw intent.

Parameters:

Name Type Description Default
protocol str

Lending protocol (e.g., "aave_v3", "morpho_blue")

required
token str

Token to withdraw

required
amount ChainedAmount

Amount to withdraw, or "all" to use previous step output

required
withdraw_all bool

If True, withdraw all available balance

False
market_id str | None

Market identifier for isolated lending protocols (e.g., Morpho Blue). Required for morpho/morpho_blue, ignored for aave_v3.

None
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
WithdrawIntent WithdrawIntent

The created withdraw intent

Example

Withdraw 0.5 ETH from Aave V3

intent = Intent.withdraw( protocol="aave_v3", token="WETH", amount=Decimal("0.5"), )

Withdraw all collateral from Morpho Blue

intent = Intent.withdraw( protocol="morpho_blue", token="wstETH", amount=Decimal("0"), # Ignored when withdraw_all=True withdraw_all=True, market_id="0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc", )

perp_open staticmethod

perp_open(
    market: str,
    collateral_token: str,
    collateral_amount: ChainedAmount,
    size_usd: Decimal,
    is_long: bool = True,
    leverage: Decimal = Decimal("1"),
    max_slippage: Decimal = Decimal("0.01"),
    protocol: str = "gmx_v2",
    chain: str | None = None,
) -> PerpOpenIntent

Create a perpetual position open intent.

Parameters:

Name Type Description Default
market str

Market identifier (e.g., "ETH/USD") or market address

required
collateral_token str

Token symbol or address for collateral

required
collateral_amount ChainedAmount

Amount of collateral in token terms, or "all" for previous step output

required
size_usd Decimal

Position size in USD terms

required
is_long bool

True for long, False for short (default True)

True
leverage Decimal

Target leverage (default 1x)

Decimal('1')
max_slippage Decimal

Maximum acceptable slippage (default 1%)

Decimal('0.01')
protocol str

Perpetuals protocol (default "gmx_v2")

'gmx_v2'
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
PerpOpenIntent PerpOpenIntent

The created perp open intent

Example

Open a 5x long ETH position with 0.1 ETH collateral on Arbitrum

intent = Intent.perp_open( market="ETH/USD", collateral_token="WETH", collateral_amount=Decimal("0.1"), size_usd=Decimal("1750"), # ~5x at $3500 ETH is_long=True, leverage=Decimal("5"), chain="arbitrum", )

Use all collateral from previous step

intent = Intent.perp_open( market="ETH/USD", collateral_token="WETH", collateral_amount="all", # Use previous step output size_usd=Decimal("1750"), is_long=True, chain="arbitrum", )

perp_close staticmethod

perp_close(
    market: str,
    collateral_token: str,
    is_long: bool,
    size_usd: Decimal | None = None,
    max_slippage: Decimal = Decimal("0.01"),
    protocol: str = "gmx_v2",
    chain: str | None = None,
) -> PerpCloseIntent

Create a perpetual position close intent.

Parameters:

Name Type Description Default
market str

Market identifier (e.g., "ETH/USD") or market address

required
collateral_token str

Token symbol or address for collateral

required
is_long bool

Position direction

required
size_usd Decimal | None

Amount to close in USD (None = close full position)

None
max_slippage Decimal

Maximum acceptable slippage (default 1%)

Decimal('0.01')
protocol str

Perpetuals protocol (default "gmx_v2")

'gmx_v2'
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
PerpCloseIntent PerpCloseIntent

The created perp close intent

Example

Close entire long ETH position

intent = Intent.perp_close( market="ETH/USD", collateral_token="WETH", is_long=True, )

Close $500 of position

intent = Intent.perp_close( market="ETH/USD", collateral_token="WETH", is_long=True, size_usd=Decimal("500"), )

bridge staticmethod

bridge(
    token: str,
    amount: Decimal | Literal["all"],
    from_chain: str,
    to_chain: str,
    max_slippage: Decimal = Decimal("0.005"),
    preferred_bridge: str | None = None,
) -> Any

Create a bridge intent for cross-chain asset transfer.

Bridge intents represent cross-chain token transfers. They can be used standalone or as part of an IntentSequence for complex multi-step operations like swap -> bridge -> supply.

When amount="all", the bridge will use the entire output from the previous step in a sequence. This is useful for chaining operations.

Parameters:

Name Type Description Default
token str

Token symbol to bridge (e.g., "ETH", "USDC", "WBTC")

required
amount Decimal | Literal['all']

Amount to bridge (Decimal) or "all" to use previous step's output

required
from_chain str

Source chain identifier (e.g., "base", "arbitrum")

required
to_chain str

Destination chain identifier (e.g., "arbitrum", "optimism")

required
max_slippage Decimal

Maximum acceptable slippage (default 0.5%)

Decimal('0.005')
preferred_bridge str | None

Optional preferred bridge adapter name (e.g., "across", "stargate")

None

Returns:

Name Type Description
BridgeIntent Any

The created bridge intent

Example

Bridge 1000 USDC from Base to Arbitrum

intent = Intent.bridge( token="USDC", amount=Decimal("1000"), from_chain="base", to_chain="arbitrum", )

Bridge all ETH from previous step (in a sequence)

sequence = Intent.sequence([ Intent.swap("USDC", "ETH", amount=Decimal("1000"), chain="base"), Intent.bridge( token="ETH", amount="all", # Use output from swap from_chain="base", to_chain="arbitrum", ), Intent.supply(protocol="aave_v3", token="WETH", amount="all", chain="arbitrum"), ])

Bridge with preferred bridge

intent = Intent.bridge( token="USDC", amount=Decimal("5000"), from_chain="arbitrum", to_chain="optimism", preferred_bridge="across", # Prefer Across for fast finality )

flash_loan staticmethod

flash_loan(
    provider: Literal["aave", "balancer", "auto"],
    token: str,
    amount: Decimal,
    callback_intents: list[FlashLoanCallbackIntent],
    chain: str | None = None,
) -> FlashLoanIntent

Create a flash loan intent with callback operations.

A flash loan allows borrowing assets without collateral, provided the borrowed amount plus fees is repaid within the same transaction.

Parameters:

Name Type Description Default
provider Literal['aave', 'balancer', 'auto']

Flash loan provider ("aave", "balancer", or "auto") - "aave": 0.09% fee, high liquidity - "balancer": 0% fee, lower liquidity - "auto": Automatically select based on availability and fees

required
token str

Token to borrow via flash loan

required
amount Decimal

Amount to borrow

required
callback_intents list[FlashLoanCallbackIntent]

List of intents to execute with borrowed funds. Must return sufficient funds to repay loan + fees.

required
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
FlashLoanIntent FlashLoanIntent

The created flash loan intent

Example

Flash loan arbitrage: borrow USDC, swap through two DEXs

intent = Intent.flash_loan( provider="aave", token="USDC", amount=Decimal("100000"), callback_intents=[ Intent.swap("USDC", "WETH", amount=Decimal("100000"), protocol="uniswap_v3"), Intent.swap("WETH", "USDC", amount="all", protocol="curve"), ], chain="ethereum" )

hold staticmethod

hold(
    reason: str | None = None, chain: str | None = None
) -> HoldIntent

Create a hold intent (no action).

Parameters:

Name Type Description Default
reason str | None

Optional reason for holding (for logging/debugging)

None
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
HoldIntent HoldIntent

The created hold intent

Example

Hold with no reason

intent = Intent.hold()

Hold with a reason for logging

intent = Intent.hold(reason="RSI in neutral zone, waiting for signal")

stake staticmethod

stake(
    protocol: str,
    token_in: str,
    amount: ChainedAmount,
    receive_wrapped: bool = True,
    chain: str | None = None,
) -> StakeIntent

Create a stake intent for liquid staking protocols.

Parameters:

Name Type Description Default
protocol str

Staking protocol (e.g., "lido", "ethena")

required
token_in str

Token to stake (e.g., "ETH" for Lido, "USDe" for Ethena)

required
amount ChainedAmount

Amount to stake, or "all" to use previous step output

required
receive_wrapped bool

Whether to receive wrapped version (default True). For Lido: True = wstETH (non-rebasing), False = stETH (rebasing) For Ethena: Always receives sUSDe regardless of this flag

True
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
StakeIntent StakeIntent

The created stake intent

Example

Stake 1 ETH with Lido on Ethereum, receive wstETH

intent = Intent.stake( protocol="lido", token_in="ETH", amount=Decimal("1"), receive_wrapped=True, chain="ethereum", )

Stake USDe with Ethena

intent = Intent.stake( protocol="ethena", token_in="USDe", amount=Decimal("10000"), chain="ethereum", )

Stake all ETH from previous step in a sequence

intent = Intent.stake( protocol="lido", token_in="ETH", amount="all", chain="ethereum", )

unstake staticmethod

unstake(
    protocol: str,
    token_in: str,
    amount: ChainedAmount,
    chain: str | None = None,
) -> UnstakeIntent

Create an unstake intent for withdrawing from liquid staking protocols.

Parameters:

Name Type Description Default
protocol str

Staking protocol (e.g., "lido", "ethena")

required
token_in str

Staked token to unstake (e.g., "wstETH" for Lido, "sUSDe" for Ethena)

required
amount ChainedAmount

Amount to unstake, or "all" to use previous step output

required
chain str | None

Target chain for execution (defaults to strategy's primary chain)

None

Returns:

Name Type Description
UnstakeIntent UnstakeIntent

The created unstake intent

Example

Unstake 1 wstETH with Lido on Ethereum

intent = Intent.unstake( protocol="lido", token_in="wstETH", amount=Decimal("1"), chain="ethereum", )

Unstake sUSDe with Ethena (initiates cooldown)

intent = Intent.unstake( protocol="ethena", token_in="sUSDe", amount=Decimal("10000"), chain="ethereum", )

Unstake all tokens from previous step in a sequence

intent = Intent.unstake( protocol="lido", token_in="wstETH", amount="all", chain="ethereum", )

ensure_balance staticmethod

ensure_balance(
    token: str,
    min_amount: Decimal,
    target_chain: str,
    max_slippage: Decimal = Decimal("0.005"),
    preferred_bridge: str | None = None,
) -> Any

Create an ensure_balance intent for automatic cross-chain balance management.

EnsureBalanceIntent expresses the goal of having at least a certain amount of tokens on a specific chain. When resolved (via resolve() method), the system will automatically determine the appropriate action:

  1. If target chain has sufficient balance -> HoldIntent (no action)
  2. If another chain has sufficient balance -> BridgeIntent (transfer)
  3. If no single chain has enough -> InsufficientBalanceError

This simplifies strategy development by abstracting away the complexity of cross-chain balance management.

Parameters:

Name Type Description Default
token str

Token symbol to ensure (e.g., "ETH", "USDC", "WBTC")

required
min_amount Decimal

Minimum amount required on target chain

required
target_chain str

Chain where the balance is needed (e.g., "arbitrum", "base")

required
max_slippage Decimal

Maximum acceptable slippage for bridging (default 0.5%)

Decimal('0.005')
preferred_bridge str | None

Optional preferred bridge adapter name for transfer

None

Returns:

Name Type Description
EnsureBalanceIntent Any

The created ensure_balance intent

Example

Ensure at least 1000 USDC on Arbitrum before opening a position

intent = Intent.ensure_balance( token="USDC", min_amount=Decimal("1000"), target_chain="arbitrum", )

Ensure at least 2 ETH on Base with custom slippage

intent = Intent.ensure_balance( token="ETH", min_amount=Decimal("2"), target_chain="base", max_slippage=Decimal("0.01"), # 1% max slippage preferred_bridge="across", # Prefer Across bridge )

Using ensure_balance in a strategy

def decide(self, market: MultiChainMarketSnapshot) -> DecideResult: # First ensure we have enough USDC on Arbitrum ensure_intent = Intent.ensure_balance( token="USDC", min_amount=Decimal("5000"), target_chain="arbitrum", )

# Resolve to concrete intent based on current balances
target_balance = market.balance("USDC", chain="arbitrum").balance
chain_balances = {
    chain: market.balance("USDC", chain=chain).balance
    for chain in market.chains
    if chain != "arbitrum"
}
resolved_intent = ensure_intent.resolve(target_balance, chain_balances)

# If resolved to HoldIntent, we can proceed with other actions
# If resolved to BridgeIntent, execute the bridge first
return resolved_intent

prediction_buy staticmethod

prediction_buy(
    market_id: str,
    outcome: Literal["YES", "NO"],
    amount_usd: Decimal | None = None,
    shares: Decimal | None = None,
    max_price: Decimal | None = None,
    order_type: Literal["market", "limit"] = "market",
    time_in_force: Literal["GTC", "IOC", "FOK"] = "GTC",
    expiration_hours: int | None = None,
    protocol: str = "polymarket",
    chain: str | None = None,
    exit_conditions: PredictionExitConditions | None = None,
) -> PredictionBuyIntent

Create a prediction buy intent for purchasing outcome shares.

Buy outcome tokens (YES or NO) on a prediction market like Polymarket. Prices represent implied probability (e.g., 0.65 = 65% chance).

Parameters:

Name Type Description Default
market_id str

Polymarket market ID or slug (e.g., "will-bitcoin-exceed-100000")

required
outcome Literal['YES', 'NO']

Which outcome to buy ("YES" or "NO")

required
amount_usd Decimal | None

USDC amount to spend (mutually exclusive with shares)

None
shares Decimal | None

Number of shares to buy (mutually exclusive with amount_usd)

None
max_price Decimal | None

Maximum price per share (0.01-0.99) for limit orders

None
order_type Literal['market', 'limit']

Order type ("market" or "limit", default "market")

'market'
time_in_force Literal['GTC', 'IOC', 'FOK']

How long order remains active ("GTC", "IOC", "FOK")

'GTC'
expiration_hours int | None

Hours until order expires (None = no expiry)

None
protocol str

Protocol to use (defaults to "polymarket")

'polymarket'
chain str | None

Target chain (defaults to "polygon" for Polymarket)

None
exit_conditions PredictionExitConditions | None

Optional exit conditions for automatic position monitoring (stop-loss, take-profit, trailing stop, pre-resolution exit)

None

Returns:

Name Type Description
PredictionBuyIntent PredictionBuyIntent

The created prediction buy intent

Example

Buy $100 worth of YES shares at market price

intent = Intent.prediction_buy( market_id="will-bitcoin-exceed-100000", outcome="YES", amount_usd=Decimal("100"), )

Buy 50 YES shares with limit order at max price of $0.65

intent = Intent.prediction_buy( market_id="will-bitcoin-exceed-100000", outcome="YES", shares=Decimal("50"), max_price=Decimal("0.65"), order_type="limit", )

Buy NO shares with IOC (immediate or cancel)

intent = Intent.prediction_buy( market_id="will-bitcoin-exceed-100000", outcome="NO", amount_usd=Decimal("200"), time_in_force="IOC", )

prediction_sell staticmethod

prediction_sell(
    market_id: str,
    outcome: Literal["YES", "NO"],
    shares: Decimal | Literal["all"],
    min_price: Decimal | None = None,
    order_type: Literal["market", "limit"] = "market",
    time_in_force: Literal["GTC", "IOC", "FOK"] = "GTC",
    protocol: str = "polymarket",
    chain: str | None = None,
) -> PredictionSellIntent

Create a prediction sell intent for selling outcome shares.

Sell outcome tokens (YES or NO) on a prediction market like Polymarket. Use shares="all" to sell your entire position.

Parameters:

Name Type Description Default
market_id str

Polymarket market ID or slug

required
outcome Literal['YES', 'NO']

Which outcome to sell ("YES" or "NO")

required
shares Decimal | Literal['all']

Number of shares to sell, or "all" to sell entire position

required
min_price Decimal | None

Minimum price per share (0.01-0.99) for limit orders

None
order_type Literal['market', 'limit']

Order type ("market" or "limit", default "market")

'market'
time_in_force Literal['GTC', 'IOC', 'FOK']

How long order remains active ("GTC", "IOC", "FOK")

'GTC'
protocol str

Protocol to use (defaults to "polymarket")

'polymarket'
chain str | None

Target chain (defaults to "polygon" for Polymarket)

None

Returns:

Name Type Description
PredictionSellIntent PredictionSellIntent

The created prediction sell intent

Example

Sell all YES shares at market price

intent = Intent.prediction_sell( market_id="will-bitcoin-exceed-100000", outcome="YES", shares="all", )

Sell 25 NO shares with limit order at min $0.40

intent = Intent.prediction_sell( market_id="will-bitcoin-exceed-100000", outcome="NO", shares=Decimal("25"), min_price=Decimal("0.40"), order_type="limit", )

prediction_redeem staticmethod

prediction_redeem(
    market_id: str,
    outcome: Literal["YES", "NO"] | None = None,
    shares: Decimal | Literal["all"] = "all",
    protocol: str = "polymarket",
    chain: str | None = None,
) -> PredictionRedeemIntent

Create a prediction redeem intent for redeeming winning positions.

Redeem winning outcome tokens after a market has resolved. Winning positions redeem for $1 per share in USDC.

Parameters:

Name Type Description Default
market_id str

Polymarket market ID or slug

required
outcome Literal['YES', 'NO'] | None

Which outcome to redeem ("YES", "NO", or None for both)

None
shares Decimal | Literal['all']

Number of shares to redeem, or "all" (default)

'all'
protocol str

Protocol to use (defaults to "polymarket")

'polymarket'
chain str | None

Target chain (defaults to "polygon" for Polymarket)

None

Returns:

Name Type Description
PredictionRedeemIntent PredictionRedeemIntent

The created prediction redeem intent

Note

Redemption is only possible after the market has resolved. Losing positions are worthless and cannot be redeemed.

Example

Redeem all winning positions from a resolved market

intent = Intent.prediction_redeem( market_id="will-bitcoin-exceed-100000", )

Redeem only YES shares (if YES won)

intent = Intent.prediction_redeem( market_id="will-bitcoin-exceed-100000", outcome="YES", )

Redeem specific number of shares

intent = Intent.prediction_redeem( market_id="will-bitcoin-exceed-100000", outcome="YES", shares=Decimal("50"), )

sequence staticmethod

sequence(
    intents: list[AnyIntent], description: str | None = None
) -> IntentSequence

Create an intent sequence for dependent actions that must execute in order.

Use this when you have a series of intents where each step depends on the previous step's output. For example: - Swap USDC -> ETH, then bridge ETH to another chain - Bridge tokens, then supply to lending protocol

The intents in a sequence will always execute sequentially. If any step fails, subsequent steps will not execute.

Parameters:

Name Type Description Default
intents list[AnyIntent]

List of intents to execute in order

required
description str | None

Optional description of what this sequence accomplishes

None

Returns:

Name Type Description
IntentSequence IntentSequence

The created intent sequence

Raises:

Type Description
InvalidSequenceError

If the intents list is empty

Example

Create a sequence: swap -> bridge -> supply

return Intent.sequence([ Intent.swap("USDC", "ETH", amount=Decimal("1000"), chain="base"), Intent.supply(protocol="aave_v3", token="WETH", amount=Decimal("0.5"), chain="arbitrum"), ], description="Move funds from Base to Arbitrum and deposit")

In decide(), return multiple sequences for parallel execution

return [ Intent.sequence([swap1, supply1]), # Execute as sequence Intent.sequence([swap2, supply2]), # Execute in parallel with above ]

serialize staticmethod

serialize(intent: AnyIntent) -> dict[str, Any]

Serialize any intent to a dictionary.

Parameters:

Name Type Description Default
intent AnyIntent

The intent to serialize

required

Returns:

Name Type Description
dict dict[str, Any]

The serialized intent

deserialize staticmethod

deserialize(data: dict[str, Any]) -> Any

Deserialize a dictionary to the appropriate intent type.

Parameters:

Name Type Description Default
data dict[str, Any]

The serialized intent data

required

Returns:

Type Description
Any

The deserialized intent (AnyIntent or BridgeIntent)

Raises:

Type Description
ValueError

If the intent type is unknown

get_type staticmethod

get_type(intent: AnyIntent) -> IntentType

Get the type of an intent.

Parameters:

Name Type Description Default
intent AnyIntent

The intent to get the type of

required

Returns:

Name Type Description
IntentType IntentType

The type of the intent

validate_chain staticmethod

validate_chain(
    intent: AnyIntent,
    configured_chains: Sequence[str],
    default_chain: str | None = None,
) -> str

Validate and resolve the chain for an intent.

Validates that the intent's chain (if specified) is in the list of configured chains. If no chain is specified on the intent, returns the default chain.

Parameters:

Name Type Description Default
intent AnyIntent

The intent to validate

required
configured_chains Sequence[str]

List of chains configured for the strategy

required
default_chain str | None

Default chain to use if intent has no chain specified. If None, uses the first configured chain.

None

Returns:

Name Type Description
str str

The resolved chain name (lowercase)

Raises:

Type Description
InvalidChainError

If the intent's chain is not in configured_chains

ValueError

If no default chain can be determined

Example

Validate an intent against strategy's configured chains

resolved_chain = Intent.validate_chain( intent, configured_chains=["arbitrum", "optimism"], default_chain="arbitrum", )

get_chain staticmethod

get_chain(intent: AnyIntent) -> str | None

Get the chain specified on an intent.

Parameters:

Name Type Description Default
intent AnyIntent

The intent to get the chain from

required

Returns:

Type Description
str | None

Optional[str]: The chain name if specified, None otherwise

is_sequence staticmethod

is_sequence(item: AnyIntent | IntentSequence) -> bool

Check if an item is an IntentSequence.

Parameters:

Name Type Description Default
item AnyIntent | IntentSequence

Intent or IntentSequence to check

required

Returns:

Name Type Description
bool bool

True if item is an IntentSequence

normalize_decide_result staticmethod

normalize_decide_result(
    result: DecideResult,
) -> list[AnyIntent | IntentSequence]

Normalize a decide() result to a list of items to execute.

This helper converts any valid decide() return value into a normalized list that the executor can process: - None -> empty list (no action) - Single intent -> list with one intent - IntentSequence -> list with one sequence - List -> returned as-is

Parameters:

Name Type Description Default
result DecideResult

The return value from decide()

required

Returns:

Type Description
list[AnyIntent | IntentSequence]

List of intents and/or sequences to execute.

list[AnyIntent | IntentSequence]

Items in the list can execute in parallel.

list[AnyIntent | IntentSequence]

Intents within a sequence execute sequentially.

count_intents staticmethod

count_intents(result: DecideResult) -> int

Count the total number of intents in a decide() result.

Parameters:

Name Type Description Default
result DecideResult

The return value from decide()

required

Returns:

Type Description
int

Total number of intents (counting all intents within sequences)

serialize_result staticmethod

serialize_result(
    result: DecideResult,
) -> dict[str, Any] | None

Serialize a decide() result to a dictionary.

Parameters:

Name Type Description Default
result DecideResult

The return value from decide()

required

Returns:

Type Description
dict[str, Any] | None

Serialized result, or None if result was None

deserialize_result staticmethod

deserialize_result(
    data: dict[str, Any] | None,
) -> DecideResult

Deserialize a decide() result from a dictionary.

Parameters:

Name Type Description Default
data dict[str, Any] | None

Serialized result data

required

Returns:

Type Description
DecideResult

Deserialized DecideResult

has_chained_amount staticmethod

has_chained_amount(intent: AnyIntent) -> bool

Check if an intent uses a chained amount from a previous step.

An intent has a chained amount when its amount field is set to "all", meaning it should use the actual received amount from the previous step in a sequence (post-slippage, post-fees).

Parameters:

Name Type Description Default
intent AnyIntent

The intent to check

required

Returns:

Type Description
bool

True if the intent uses amount="all", False otherwise

validate_chained_amounts staticmethod

validate_chained_amounts(sequence: IntentSequence) -> None

Validate that chained amounts are used correctly in a sequence.

Validates that: 1. amount="all" is NOT used on the first step of a sequence 2. The sequence has proper dependencies for amount resolution

Parameters:

Name Type Description Default
sequence IntentSequence

The intent sequence to validate

required

Raises:

Type Description
InvalidAmountError

If amount="all" is used on the first step

get_amount_field staticmethod

get_amount_field(intent: AnyIntent) -> ChainedAmount | None

Get the amount field value from an intent for chaining purposes.

This returns the amount that flows to the next step in a sequence. Different intents output different amounts: - SwapIntent: amount (token output) or amount_usd - SupplyIntent: amount (what was supplied) - RepayIntent: amount (what was repaid) - WithdrawIntent: amount (what was withdrawn) - BorrowIntent: borrow_amount (NOT collateral_amount - this is what's borrowed) - PerpOpenIntent: collateral_amount (what was deposited) - BridgeIntent: amount (what was bridged)

Parameters:

Name Type Description Default
intent AnyIntent

The intent to get the amount from

required

Returns:

Type Description
ChainedAmount | None

The amount value (Decimal or "all"), or None if not applicable

set_resolved_amount staticmethod

set_resolved_amount(
    intent: AnyIntent, resolved_amount: Decimal
) -> AnyIntent

Create a copy of an intent with the amount resolved from "all" to a concrete value.

This is used at execution time to resolve amount="all" to the actual received amount from the previous step.

Parameters:

Name Type Description Default
intent AnyIntent

The intent to update

required
resolved_amount Decimal

The concrete amount to use

required

Returns:

Type Description
AnyIntent

A new intent instance with the resolved amount

Note

This creates a new intent instance; it does not mutate the original.

IntentType

almanak.framework.intents.IntentType

Bases: Enum

Types of intents that strategies can express.

HoldIntent

almanak.framework.intents.HoldIntent

Bases: AlmanakImmutableModel

Intent to take no action (wait).

This is useful when a strategy explicitly decides not to act, as opposed to returning None which might indicate an error.

Attributes:

Name Type Description
reason str | None

Optional reason for holding (for logging/debugging)

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

intent_type property

intent_type: IntentType

Return the type of this intent.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> HoldIntent

Deserialize a dictionary to a HoldIntent.

SwapIntent

almanak.framework.intents.SwapIntent

Bases: AlmanakImmutableModel

Intent to swap one token for another.

Attributes:

Name Type Description
from_token str

Symbol or address of the token to swap from

to_token str

Symbol or address of the token to swap to

amount_usd OptionalSafeDecimal

Amount to swap in USD terms (mutually exclusive with amount)

amount OptionalChainedAmount

Amount to swap in token terms, or "all" to use output from previous step

max_slippage SafeDecimal

Maximum acceptable slippage (e.g., 0.005 = 0.5%)

protocol str | None

Preferred protocol for the swap (e.g., "uniswap_v3", "enso")

chain str | None

Source chain for execution (defaults to strategy's primary chain)

destination_chain str | None

Destination chain for cross-chain swaps (None for same-chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note

When amount="all", the swap will use the entire output from the previous step in a sequence. This is useful for chaining operations like: bridge -> swap -> supply. The actual amount is resolved at execution time.

For cross-chain swaps, set destination_chain to the target chain name. Cross-chain swaps require protocol="enso" as Enso handles the bridging.

Example

Same-chain swap

Intent.swap("USDC", "WETH", amount_usd=1000, chain="arbitrum")

Cross-chain swap: Base USDC -> Arbitrum WETH

Intent.swap("USDC", "WETH", amount_usd=1000, chain="base", destination_chain="arbitrum", protocol="enso")

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

is_cross_chain property

is_cross_chain: bool

Check if this is a cross-chain swap.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_swap_intent

validate_swap_intent() -> SwapIntent

Validate that either amount_usd or amount is provided.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

Backward compatible with existing serialization format.

deserialize classmethod

deserialize(data: dict[str, Any]) -> SwapIntent

Deserialize a dictionary to a SwapIntent.

Backward compatible with existing serialization format.

LPOpenIntent

almanak.framework.intents.LPOpenIntent

Bases: AlmanakImmutableModel

Intent to open a liquidity position.

Attributes:

Name Type Description
pool str

Pool address or identifier

amount0 SafeDecimal

Amount of token0 to provide

amount1 SafeDecimal

Amount of token1 to provide

range_lower SafeDecimal

Lower price bound for concentrated liquidity

range_upper SafeDecimal

Upper price bound for concentrated liquidity

protocol str

LP protocol (e.g., "uniswap_v3", "camelot")

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_lp_open_intent

validate_lp_open_intent() -> LPOpenIntent

Validate LP open parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> LPOpenIntent

Deserialize a dictionary to an LPOpenIntent.

LPCloseIntent

almanak.framework.intents.LPCloseIntent

Bases: AlmanakImmutableModel

Intent to close a liquidity position.

Attributes:

Name Type Description
position_id str

Identifier of the position to close (e.g., NFT token ID)

pool str | None

Pool address (optional, for validation)

collect_fees bool

Whether to collect accumulated fees

protocol str

LP protocol (e.g., "uniswap_v3", "camelot")

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

intent_type property

intent_type: IntentType

Return the type of this intent.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> LPCloseIntent

Deserialize a dictionary to an LPCloseIntent.

BorrowIntent

almanak.framework.intents.BorrowIntent

Bases: AlmanakImmutableModel

Intent to borrow tokens from a lending protocol.

Attributes:

Name Type Description
protocol str

Lending protocol (e.g., "aave_v3", "morpho")

collateral_token str

Token to use as collateral

collateral_amount ChainedAmount

Amount of collateral to supply, or "all" for previous step output

borrow_token str

Token to borrow

borrow_amount SafeDecimal

Amount to borrow

interest_rate_mode InterestRateMode | None

Interest rate mode for protocols that support it (Aave: 'variable' | 'stable')

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note

When collateral_amount="all", the borrow will use the entire output from the previous step in a sequence as collateral.

The interest_rate_mode parameter is protocol-specific: - Aave V3: Supports 'variable' (default) and 'stable' modes - Morpho: Does not support rate mode selection (parameter is ignored) - Compound V3: Does not support rate mode selection (parameter is ignored)

The market_id parameter is required for protocols with isolated markets: - Morpho Blue: Required - identifies the specific lending market - Aave V3: Not used - uses unified pool

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_borrow_intent

validate_borrow_intent() -> BorrowIntent

Validate borrow parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> BorrowIntent

Deserialize a dictionary to a BorrowIntent.

RepayIntent

almanak.framework.intents.RepayIntent

Bases: AlmanakImmutableModel

Intent to repay borrowed tokens.

Attributes:

Name Type Description
protocol str

Lending protocol (e.g., "aave_v3", "morpho")

token str

Token to repay

amount ChainedAmount

Amount to repay, or "all" to use output from previous step

repay_full bool

If True, repay the full outstanding debt

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note

When amount="all", the repay will use the entire output from the previous step in a sequence.

The market_id parameter is required for protocols with isolated markets: - Morpho Blue: Required - identifies the specific lending market - Aave V3: Not used - uses unified pool

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_repay_intent

validate_repay_intent() -> RepayIntent

Validate repay parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> RepayIntent

Deserialize a dictionary to a RepayIntent.

SupplyIntent

almanak.framework.intents.SupplyIntent

Bases: AlmanakImmutableModel

Intent to supply tokens to a lending protocol.

Attributes:

Name Type Description
protocol str

Lending protocol (e.g., "aave_v3")

token str

Token to supply

amount ChainedAmount

Amount to supply, or "all" to use output from previous step

use_as_collateral bool

Whether to enable the asset as collateral (default True). Also known as 'enable_as_collateral' - this is an Aave-specific parameter that controls whether the supplied asset can be used as collateral for borrowing.

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note

When amount="all", the supply will use the entire output from the previous step in a sequence. This is useful for chaining operations like: swap -> supply or bridge -> supply.

The use_as_collateral parameter is protocol-specific: - Aave V3: Supports collateral toggle (default True) - Compound V3: Supports collateral toggle - Morpho: Does not support collateral toggle (all supplied assets are collateral)

The market_id parameter is required for protocols with isolated markets: - Morpho Blue: Required - identifies the specific lending market - Aave V3: Not used - uses unified pool

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_supply_intent

validate_supply_intent() -> SupplyIntent

Validate supply parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> SupplyIntent

Deserialize a dictionary to a SupplyIntent.

WithdrawIntent

almanak.framework.intents.WithdrawIntent

Bases: AlmanakImmutableModel

Intent to withdraw tokens from a lending protocol.

Attributes:

Name Type Description
protocol str

Lending protocol (e.g., "aave_v3")

token str

Token to withdraw

amount ChainedAmount

Amount to withdraw, or "all" to use output from previous step

withdraw_all bool

If True, withdraw all available balance

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note

When amount="all", the withdraw will use the entire output from the previous step in a sequence. This is different from withdraw_all which withdraws all available balance from the protocol.

The market_id parameter is required for protocols with isolated markets: - Morpho Blue: Required - identifies the specific lending market - Aave V3: Not used - uses unified pool

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_withdraw_intent

validate_withdraw_intent() -> WithdrawIntent

Validate withdraw parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> WithdrawIntent

Deserialize a dictionary to a WithdrawIntent.

PerpOpenIntent

almanak.framework.intents.PerpOpenIntent

Bases: AlmanakImmutableModel

Intent to open a perpetual futures position.

Attributes:

Name Type Description
market str

Market identifier (e.g., "ETH/USD") or market address

collateral_token str

Token symbol or address for collateral

collateral_amount ChainedAmount

Amount of collateral in token terms, or "all" for previous step output

size_usd SafeDecimal

Position size in USD terms

is_long bool

True for long position, False for short

leverage SafeDecimal

Target leverage for the position (protocol-specific limits apply)

max_slippage SafeDecimal

Maximum acceptable slippage (e.g., 0.01 = 1%)

protocol str

Perpetuals protocol (default "gmx_v2")

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note

When collateral_amount="all", the perp open will use the entire output from the previous step in a sequence. This is useful for chaining operations like: swap -> perp_open.

The leverage parameter is validated against protocol-specific limits: - GMX V2: Supports leverage from 1.1x to 100x - Hyperliquid: Supports leverage from 1x to 50x

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_perp_open_intent

validate_perp_open_intent() -> PerpOpenIntent

Validate perp open parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> PerpOpenIntent

Deserialize a dictionary to a PerpOpenIntent.

PerpCloseIntent

almanak.framework.intents.PerpCloseIntent

Bases: AlmanakImmutableModel

Intent to close a perpetual futures position.

Attributes:

Name Type Description
market str

Market identifier (e.g., "ETH/USD") or market address

collateral_token str

Token symbol or address for collateral

is_long bool

Position direction

size_usd OptionalSafeDecimal

Amount to close in USD (None = close full position)

max_slippage SafeDecimal

Maximum acceptable slippage (e.g., 0.01 = 1%)

protocol str

Perpetuals protocol (default "gmx_v2")

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

intent_type property

intent_type: IntentType

Return the type of this intent.

close_full_position property

close_full_position: bool

Check if this intent is to close the full position.

validate_perp_close_intent

validate_perp_close_intent() -> PerpCloseIntent

Validate perp close parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> PerpCloseIntent

Deserialize a dictionary to a PerpCloseIntent.

StakeIntent

almanak.framework.intents.StakeIntent

Bases: AlmanakImmutableModel

Intent to stake tokens with a liquid staking protocol.

StakeIntent represents staking tokens (like ETH) with a liquid staking protocol (like Lido or Ethena) to receive a liquid staking derivative (like stETH or sUSDe).

Attributes:

Name Type Description
protocol str

Staking protocol (e.g., "lido", "ethena")

token_in str

Token to stake (e.g., "ETH" for Lido, "USDe" for Ethena)

amount ChainedAmount

Amount to stake, or "all" to use output from previous step

receive_wrapped bool

Whether to receive the wrapped version (e.g., wstETH instead of stETH). Default is True for better DeFi composability.

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note

When amount="all", the stake will use the entire output from the previous step in a sequence. This is useful for chaining operations like: swap -> stake or bridge -> stake.

Protocol-specific behavior: - Lido: Stakes ETH, receives stETH (rebasing) or wstETH (non-rebasing) - Ethena: Stakes USDe, receives sUSDe (ERC4626 vault)

Example

Stake 1 ETH with Lido, receive wstETH (wrapped, non-rebasing)

intent = Intent.stake( protocol="lido", token_in="ETH", amount=Decimal("1"), receive_wrapped=True, # Get wstETH chain="ethereum", )

Stake USDe with Ethena, receive sUSDe

intent = Intent.stake( protocol="ethena", token_in="USDe", amount=Decimal("10000"), chain="ethereum", )

Stake all ETH from previous step

intent = Intent.stake( protocol="lido", token_in="ETH", amount="all", chain="ethereum", )

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_stake_intent

validate_stake_intent() -> StakeIntent

Validate stake parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> StakeIntent

Deserialize a dictionary to a StakeIntent.

UnstakeIntent

almanak.framework.intents.UnstakeIntent

Bases: AlmanakImmutableModel

Intent to unstake/withdraw tokens from a liquid staking protocol.

UnstakeIntent represents withdrawing staked tokens from a liquid staking protocol (like Lido or Ethena) to receive back the underlying tokens.

Attributes:

Name Type Description
protocol str

Staking protocol (e.g., "lido", "ethena")

token_in str

Staked token to unstake (e.g., "wstETH" for Lido, "sUSDe" for Ethena)

amount ChainedAmount

Amount to unstake, or "all" to use output from previous step

chain str | None

Optional target chain for execution (defaults to strategy's primary chain)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note

When amount="all", the unstake will use the entire output from the previous step in a sequence. This is useful for chaining operations.

Protocol-specific behavior: - Lido: Unwrap wstETH to stETH, or request withdrawal from stETH - Ethena: Initiates cooldown on sUSDe (unstaking has a cooldown period)

Example

Unstake 1 wstETH with Lido

intent = Intent.unstake( protocol="lido", token_in="wstETH", amount=Decimal("1"), chain="ethereum", )

Unstake sUSDe with Ethena (starts cooldown)

intent = Intent.unstake( protocol="ethena", token_in="sUSDe", amount=Decimal("10000"), chain="ethereum", )

Unstake all tokens from previous step

intent = Intent.unstake( protocol="lido", token_in="wstETH", amount="all", chain="ethereum", )

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_unstake_intent

validate_unstake_intent() -> UnstakeIntent

Validate unstake parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> UnstakeIntent

Deserialize a dictionary to an UnstakeIntent.

BridgeIntent

almanak.framework.intents.BridgeIntent

Bases: AlmanakImmutableModel

Intent to bridge tokens from one chain to another.

BridgeIntent represents a cross-chain asset transfer. It can be used standalone or as part of an IntentSequence for complex multi-step operations.

When amount="all", the bridge will use the entire output from the previous step in a sequence. This is useful for chaining operations like swap -> bridge -> supply.

Attributes:

Name Type Description
token str

Token symbol to bridge (e.g., "ETH", "USDC", "WBTC")

amount ChainedAmount

Amount to bridge (Decimal or "all" for chained amounts)

from_chain str

Source chain identifier (e.g., "base", "arbitrum")

to_chain str

Destination chain identifier (e.g., "arbitrum", "optimism")

max_slippage SafeDecimal

Maximum acceptable slippage (e.g., 0.005 = 0.5%)

preferred_bridge str | None

Optional preferred bridge adapter name

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Example

Bridge 1000 USDC from Base to Arbitrum with 0.5% max slippage

intent = BridgeIntent( token="USDC", amount=Decimal("1000"), from_chain="base", to_chain="arbitrum", max_slippage=Decimal("0.005"), )

Bridge all ETH from previous step output

intent = BridgeIntent( token="ETH", amount="all", from_chain="optimism", to_chain="arbitrum", )

intent_type property

intent_type: IntentType

Return the type of this intent.

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

chain property

chain: str

Return the source chain for compatibility with Intent.get_chain().

For bridge intents, the 'chain' property returns the source chain (from_chain) since that's where the transaction originates.

validate_token classmethod

validate_token(v: str) -> str

Validate token is non-empty.

normalize_chain classmethod

normalize_chain(v: str) -> str

Validate and normalize chain names to lowercase.

validate_bridge_intent

validate_bridge_intent() -> BridgeIntent

Validate bridge intent parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> BridgeIntent

Deserialize a dictionary to a BridgeIntent.

validate_chains staticmethod

validate_chains(
    from_chain: str,
    to_chain: str,
    configured_chains: Sequence[str],
) -> None

Validate that both chains are configured for the strategy.

Parameters:

Name Type Description Default
from_chain str

Source chain identifier

required
to_chain str

Destination chain identifier

required
configured_chains Sequence[str]

List of chains configured for the strategy

required

Raises:

Type Description
BridgeChainError

If either chain is not configured

PredictionBuyIntent

almanak.framework.intents.PredictionBuyIntent

Bases: AlmanakImmutableModel

Intent to buy shares in a prediction market.

This intent is used to buy outcome tokens (YES or NO) on Polymarket or similar prediction market platforms.

Attributes:

Name Type Description
market_id str

Polymarket market ID or slug (e.g., "will-bitcoin-exceed-100000")

outcome PredictionOutcome

Which outcome to buy ("YES" or "NO")

amount_usd OptionalSafeDecimal

USDC amount to spend (mutually exclusive with shares)

shares OptionalSafeDecimal

Number of shares to buy (mutually exclusive with amount_usd)

max_price OptionalSafeDecimal

Maximum price per share (0.01-0.99) for limit orders

order_type PredictionOrderType

Order type ("market" or "limit")

time_in_force PredictionTimeInForce

How long order remains active ("GTC", "IOC", "FOK")

expiration_hours int | None

Hours until order expires (None = no expiry)

protocol str

Protocol to use (defaults to "polymarket")

chain str | None

Target chain (defaults to "polygon" for Polymarket)

exit_conditions PredictionExitConditions | None

Optional exit conditions for automatic position monitoring (stop-loss, take-profit, trailing stop, pre-resolution exit)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note
  • Prices represent implied probability (0.65 = 65% chance of YES)
  • Market orders use aggressive pricing for immediate execution
  • Limit orders rest in the orderbook until matched or cancelled
  • GTC (Good Till Cancelled) orders remain until filled or cancelled
  • IOC (Immediate or Cancel) fills what it can immediately, cancels rest
  • FOK (Fill or Kill) must fill entirely or is cancelled
Example

Buy $100 worth of YES shares at market price

intent = Intent.prediction_buy( market_id="will-bitcoin-exceed-100000", outcome="YES", amount_usd=Decimal("100"), )

Buy 50 YES shares with limit order at max price of $0.65

intent = Intent.prediction_buy( market_id="will-bitcoin-exceed-100000", outcome="YES", shares=Decimal("50"), max_price=Decimal("0.65"), order_type="limit", )

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_prediction_buy_intent

validate_prediction_buy_intent() -> PredictionBuyIntent

Validate prediction buy parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> PredictionBuyIntent

Deserialize a dictionary to a PredictionBuyIntent.

PredictionSellIntent

almanak.framework.intents.PredictionSellIntent

Bases: AlmanakImmutableModel

Intent to sell shares in a prediction market.

This intent is used to sell outcome tokens (YES or NO) on Polymarket or similar prediction market platforms.

Attributes:

Name Type Description
market_id str

Polymarket market ID or slug

outcome PredictionOutcome

Which outcome to sell ("YES" or "NO")

shares PredictionShareAmount

Number of shares to sell, or "all" to sell entire position

min_price OptionalSafeDecimal

Minimum price per share (0.01-0.99) for limit orders

order_type PredictionOrderType

Order type ("market" or "limit")

time_in_force PredictionTimeInForce

How long order remains active ("GTC", "IOC", "FOK")

protocol str

Protocol to use (defaults to "polymarket")

chain str | None

Target chain (defaults to "polygon" for Polymarket)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note
  • Use shares="all" to sell your entire position
  • Market orders execute immediately at best available price
  • Limit orders only execute at min_price or better
Example

Sell all YES shares at market price

intent = Intent.prediction_sell( market_id="will-bitcoin-exceed-100000", outcome="YES", shares="all", )

Sell 25 NO shares with limit order at min $0.40

intent = Intent.prediction_sell( market_id="will-bitcoin-exceed-100000", outcome="NO", shares=Decimal("25"), min_price=Decimal("0.40"), order_type="limit", )

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_prediction_sell_intent

validate_prediction_sell_intent() -> PredictionSellIntent

Validate prediction sell parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> PredictionSellIntent

Deserialize a dictionary to a PredictionSellIntent.

PredictionRedeemIntent

almanak.framework.intents.PredictionRedeemIntent

Bases: AlmanakImmutableModel

Intent to redeem winning prediction market positions.

This intent is used to redeem outcome tokens after a market has resolved. Winning tokens can be redeemed for $1 each (in USDC).

Attributes:

Name Type Description
market_id str

Polymarket market ID or slug

outcome PredictionOutcome | None

Which outcome to redeem ("YES", "NO", or None for both)

shares PredictionShareAmount

Number of shares to redeem, or "all" (default)

protocol str

Protocol to use (defaults to "polymarket")

chain str | None

Target chain (defaults to "polygon" for Polymarket)

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Note
  • Redemption is only possible after the market has resolved
  • Winning positions redeem for $1 per share
  • Losing positions are worthless
  • Use outcome=None to redeem all winning positions
Example

Redeem all winning positions from a market

intent = Intent.prediction_redeem( market_id="will-bitcoin-exceed-100000", )

Redeem only YES shares (if YES won)

intent = Intent.prediction_redeem( market_id="will-bitcoin-exceed-100000", outcome="YES", shares="all", )

is_chained_amount property

is_chained_amount: bool

Check if this intent uses a chained amount from previous step.

intent_type property

intent_type: IntentType

Return the type of this intent.

validate_prediction_redeem_intent

validate_prediction_redeem_intent() -> (
    PredictionRedeemIntent
)

Validate prediction redeem parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> PredictionRedeemIntent

Deserialize a dictionary to a PredictionRedeemIntent.

EnsureBalanceIntent

almanak.framework.intents.EnsureBalanceIntent

Bases: AlmanakImmutableModel

Intent to ensure a minimum token balance on a target chain.

EnsureBalanceIntent is a high-level intent that expresses the goal of having at least a certain amount of tokens on a specific chain. The framework will automatically determine how to achieve this goal:

  1. Check current balance on target chain
  2. If balance >= min_amount: Return HoldIntent (no action needed)
  3. If balance < min_amount: Find a source chain with sufficient balance
  4. Generate a BridgeIntent to transfer the needed tokens

This simplifies strategy development by abstracting away the complexity of cross-chain balance management.

Attributes:

Name Type Description
token str

Token symbol to ensure (e.g., "ETH", "USDC", "WBTC")

min_amount SafeDecimal

Minimum amount required on target chain

target_chain str

Chain where the balance is needed (e.g., "arbitrum", "base")

max_slippage SafeDecimal

Maximum acceptable slippage for bridging (e.g., 0.005 = 0.5%)

preferred_bridge str | None

Optional preferred bridge adapter name for transfer

intent_id str

Unique identifier for this intent

created_at datetime

Timestamp when the intent was created

Example

Ensure at least 1000 USDC on Arbitrum before opening a position

intent = EnsureBalanceIntent( token="USDC", min_amount=Decimal("1000"), target_chain="arbitrum", )

Ensure at least 2 ETH on Base with specific slippage

intent = EnsureBalanceIntent( token="ETH", min_amount=Decimal("2"), target_chain="base", max_slippage=Decimal("0.01"), # 1% max slippage )

intent_type property

intent_type: EnsureBalanceIntentType

Return the type of this intent.

chain property

chain: str

Return the target chain for compatibility with Intent.get_chain().

For ensure_balance intents, the 'chain' property returns the target chain since that's where the balance needs to be ensured.

validate_token classmethod

validate_token(v: str) -> str

Validate token is non-empty.

normalize_chain classmethod

normalize_chain(v: str) -> str

Validate and normalize chain name to lowercase.

validate_ensure_balance_intent

validate_ensure_balance_intent() -> EnsureBalanceIntent

Validate ensure balance intent parameters.

serialize

serialize() -> dict[str, Any]

Serialize the intent to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> EnsureBalanceIntent

Deserialize a dictionary to an EnsureBalanceIntent.

validate_target_chain staticmethod

validate_target_chain(
    target_chain: str, configured_chains: Sequence[str]
) -> None

Validate that the target chain is configured for the strategy.

Parameters:

Name Type Description Default
target_chain str

Target chain identifier

required
configured_chains Sequence[str]

List of chains configured for the strategy

required

Raises:

Type Description
InvalidEnsureBalanceError

If target chain is not configured

resolve

resolve(
    target_balance: Decimal,
    chain_balances: dict[str, Decimal],
) -> Union[HoldIntent, BridgeIntent]

Resolve this ensure_balance intent to a concrete intent.

This method determines the appropriate action based on current balances: 1. If target chain has sufficient balance -> HoldIntent 2. If a source chain has sufficient balance -> BridgeIntent 3. If no single chain has sufficient balance -> raise InsufficientBalanceError

Parameters:

Name Type Description Default
target_balance Decimal

Current balance of the token on the target chain

required
chain_balances dict[str, Decimal]

Dict of {chain: balance} for all configured chains (excluding the target chain)

required

Returns:

Type Description
Union[HoldIntent, BridgeIntent]

HoldIntent if no action needed, BridgeIntent if transfer required

Raises:

Type Description
InsufficientBalanceError

If no single chain has sufficient balance

IntentSequence

almanak.framework.intents.IntentSequence dataclass

IntentSequence(
    intents: list[AnyIntent],
    sequence_id: str = (lambda: str(uuid.uuid4()))(),
    created_at: datetime = (lambda: datetime.now(UTC))(),
    description: str | None = None,
)

A sequence of intents that must execute in order (dependent actions).

IntentSequence wraps a list of intents that have dependencies between them and must execute sequentially. This is used when the output of one intent feeds into the input of the next (e.g., swap output -> bridge input).

Intents that are NOT in a sequence can execute in parallel if they are independent (e.g., two swaps on different chains).

Attributes:

Name Type Description
intents list[AnyIntent]

List of intents to execute in order

sequence_id str

Unique identifier for this sequence

created_at datetime

Timestamp when the sequence was created

description str | None

Optional description of the sequence purpose

Example

Create a sequence of dependent actions

sequence = Intent.sequence([ Intent.swap("USDC", "ETH", amount=Decimal("1000"), chain="base"), Intent.bridge(token="ETH", amount="all", from_chain="base", to_chain="arbitrum"), Intent.supply(protocol="aave_v3", token="WETH", amount="all", chain="arbitrum"), ])

Return from decide() - will execute sequentially

return sequence

first property

first: AnyIntent

Get the first intent in the sequence.

last property

last: AnyIntent

Get the last intent in the sequence.

__post_init__

__post_init__() -> None

Validate the sequence.

__len__

__len__() -> int

Return the number of intents in the sequence.

__iter__

__iter__()

Iterate over intents in the sequence.

__getitem__

__getitem__(index: int) -> AnyIntent

Get intent at index.

serialize

serialize() -> dict[str, Any]

Serialize the sequence to a dictionary.

deserialize classmethod

deserialize(data: dict[str, Any]) -> IntentSequence

Deserialize a dictionary to an IntentSequence.

Note: This requires the Intent.deserialize function to be available, which creates a circular dependency. The actual deserialization is done in the Intent class.

ChainedAmount

almanak.framework.intents.ChainedAmount module-attribute

ChainedAmount = Decimal | Literal['all']