Skip to main content
A strategy described in one sentence becomes transactions on several chains, running for hours or days on an account only you own, without you holding anything open. This page is what does that: which processes run, what each is allowed to do, and where the boundaries are. The work is not the transactions. It is knowing where to send them. A strategy that reads as one sentence: borrow against this, move it, put it to work: is a dozen sites in practice. Which chain has the rate today, which bridge to cross with and what it costs, which market has room, what to approve, and in what order any of it can happen without stranding money halfway. That knowledge is the barrier, not the clicking, and it is why DeFi stays with the people who already have it. Here you name the outcome. The route is worked out for you: the chain, the market, the pool, the approvals, and the order they have to happen in. Then it runs whether or not you are there. Some steps settle in seconds and some do not: a limit order might fill this afternoon, or halfway, or never; a transaction can sit unmined and need sending again at a higher fee. So a run is not a request that returns an answer. It has to survive you closing the tab, and survive a deploy, a restart, or a crash nine steps into a fourteen-step plan. A program holding the plan in its memory would lose it at the first of those. Every step is written down as it happens, and a run is rebuilt from that record rather than remembered. That is the whole design, and it is why this runs as its own infrastructure with its own database rather than inside the 0xequity backend: the only coupling is HTTP reads from that API.

What a request actually becomes

A person says something in words. Six translations later it is a transaction on a chain, and each one narrows what is possible: The narrowing is the product. Each stage is allowed to refuse, and a refusal is recorded as carefully as an approval, because a refusal is evidence. See Adapters for the vocabulary that second row is written in, and Kernel for what the last two rows mean about whose money it is.

What runs, and what each part is trusted with

The parts worth knowing are the ones that hold something. Around those sit the console and its server side, the tool server the model talks to, a quoting service, a simulator that runs every non-bridge step before it is broadcast, and the durable-execution engine holding the workflow histories. One container is reachable from outside; everything else answers only on the internal network. The Go services are built from one image with a variable choosing which one runs, so they share every compiled constant: adapter addresses, the intent schema, the chain tables. That is why they are deployed together and never singly: two of them on different builds derive account addresses two different ways, and every group is then refused.

Why a forked mainnet, not a testnet

Testnets lack the dependencies this system needs: CCTP, lending markets, DEX depth. So the substrate is a fork pinned to a block: a run spends fork money and settles against real mainnet state, against the same contracts and the same liquidity that production would meet.

Durable waiting, and the two things it buys

Stuck transactions are replaced, never re-sent. A transaction that does not mine within its window is replaced at a higher fee reusing its nonce, up to a bump limit, and then goes to a STUCK state for a human. Reusing the nonce is the whole point: a “retry” on a fresh nonce would leave the original still valid and still pending, both could confirm, and the recipient would be paid twice. Measured on a fork with mining disabled: four broadcasts, one nonce, exactly one winner. Retries cannot double-send, because three layers have to agree: the nonce lease is keyed on the task id so a retry rebuilds an identical transaction, the submission claim returns the original hash before any broadcast, and the node’s “already known” is treated as success.

Amending a plan that already moved money

Step 1 lands, step 2 fails, somebody proposes a change, steps 2 and 3 run. That is a normal outcome, not an exception. Automatic retry is deliberately minimal: a fee bump for a stuck transaction, and a couple of attempts for a revert whose cause can clear by itself. Beyond that the system stops guessing and parks, because most real failures need a decision rather than another attempt at the same thing. An amendment may change the future and never the past. Every step in the execution log must reappear, unchanged, in order. Altering or dropping an executed step is refused; so is reusing its task id, and so is resubmitting the failed step unchanged. Who proposed it is recorded for attribution after the fact, not authorisation. An owner can be socially engineered and a model can be prompt-injected, so the validation is identical either way: nothing is trusted because of who sent it. Two details that prevent surprises at approval time:
  • Step ids are names, not positions. If they were indices, inserting a step during an amendment would silently repoint every later reference.
  • Symbolic amounts resolve to actual, not requested. Step 1 asked for 1000 and delivered 997; step 2 gets 997. A diff reports which steps a human edited and which merely followed, because “you edited one step and three others moved” is the sentence that has to be said out loud.

Where the boundaries are drawn, and where they are not yet

The calldata invariant is enforced today: the relayer builds calldata from typed parameters and there is no input to that function capable of expressing an arbitrary call. The signing invariant is now load-bearing on the paths that move money: the adapter recovers the agent’s signature on chain and refuses anything not signed by the account’s appointed agent, and the relayer is sender of the outer transaction only rather than holder of the assets. Two gaps remain named rather than rounded up, because a security document that describes the design as though it were the build is worse than no document. The agent guard is a denylist, not an allowlist; it blocks control takeover but not every value movement, and nothing constrains what the relayer key signs once someone holds it. Kernel says exactly where both bite.