Actions & Signing

Every on-chain action follows the same Universal Action Pattern: you sign an EIP-712 intent, sign a gas transaction that is cryptographically bound to that intent, and submit them together. The backend verifies the binding, broadcasts the gas payment, and executes the action.

The pattern

  1. Sign the intent — EIP-712 typed data for the action → intentSignature.
  2. Sign a gasTx — a raw EIP-1559 ETH transfer to the executor wallet. Its data field must equal keccak256(intentSignature). This binds the ETH payment to that exact intent.
  3. Sign an assetTx (deploy only, when initialBuyAmount > 0) — a raw ERC-20 transfer of the counter asset to the executor wallet, at nonce gasTx + 1.
  4. Sign an actionTx (liquidity actions and claimRewards) — the pre-signed contract call the backend relays, at nonce gasTx + 1.
  5. Submit everything to /execute/:action (or /verify/:action for a dry run).

Intent binding is mandatory

gasTx.data must be keccak256(intentSignature) for every action. Requests without this binding are rejected.

EIP-712 domain

Read the live values from GET /infoeip712Domain and eip712Types. The domain is:

{
  "name": "Machima Agent Protocol",
  "version": "1",
  "chainId": 8453,
  "verifyingContract": "0x0000000000000000000000000000000000000000"
}

Transaction fields per action

| Action | gasTx | assetTx | actionTx | |--------|---------|-----------|------------| | deploy | ETH fee | ERC-20 counter asset (if initialBuyAmount > 0) | — | | buy | ETH fee | — | — | | sell | ETH fee | — | — | | addLiquidity | ETH fee | — | signed position-manager call | | increaseLiquidity | ETH fee | — | signed position-manager call | | removeLiquidity | ETH fee | — | signed position-manager call | | collectFees | ETH fee | — | signed position-manager call | | withdrawFees | ETH fee | — | — | | claimRewards | ETH fee | — | signed DeployHandler.release call |

Nonce rule: gasTx at nonce N; assetTx or actionTx at nonce N+1.

Field names

Preferred names are gasTx and assetTx. The older feeTx and counterAssetTx are still accepted for backward compatibility.

Fees

Each action reads its own fee from on-chain protocol config (deploy, swap, lp, withdraw, claim are independent). Never hardcode them — read GET /infofees. Values are human strings like "0.000003949975364004 ETH":

const feeStr = '0.000003949975364004 ETH'
const wei = parseUnits(feeStr.split(' ')[0], 18)

Verify, then execute

Always dry-run with /verify/:action (same body as execute). It checks the signature, fee, deadline, and parameters without spending anything.

curl -X POST https://elixir-api.machima.ai/api/base/agent/verify/buy \
  -H "Authorization: Bearer {your-api-key}" \
  -H "Content-Type: application/json" \
  -d '{ "intent": { ... }, "intentSignature": "0x...", "gasTx": "0x..." }'

Failed actions still cost gas

/execute broadcasts the gasTx before the action runs. If the action reverts (bad params, insufficient balance, contract error), the gas fee is consumed and unrecoverable. Verify first, every time.

Buy / Sell

BuyIntent and SellIntent share the same shape:

{ token: address, counterAsset: address, amount: uint256, minOutput: uint256, deadline: uint256 }

Buy and sell only need gasTx — no assetTx. Instead, the signing wallet must approve MachimaSwapAdapter:

  • Before buy: approve intent.counterAsset to the adapter.
  • Before sell: approve intent.token to the adapter.

Read the live adapter address from GET /infobuy.approvalTarget / sell.approvalTarget.

curl -X POST https://elixir-api.machima.ai/api/base/agent/execute/buy \
  -H "Authorization: Bearer {your-api-key}" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": {
      "token": "0xTokenAddress",
      "counterAsset": "0x4200000000000000000000000000000000000006",
      "amount": "10000000000000000",
      "minOutput": "0",
      "deadline": 1771834000
    },
    "intentSignature": "0x...",
    "gasTx": "0x..."
  }'

For sell, amount is the token amount (18 decimals) and minOutput is the minimum counter asset to accept. Response: { gasTxHash, action, agentAddress, transactionHash, amountOut, blockNumber }.

Deploy

DeployIntent:

{ name: string, symbol: string, creator: address, feeManager: address,
  counterAsset: uint8, initialBuyAmount: uint256, minTokensOut: uint256,
  buyTaxBps: uint16, sellTaxBps: uint16, tokenType: uint8, deadline: uint256 }

Constraints (validated server-side and on-chain):

  • A logo is mandatory — upload it first via POST /upload-logo, then pass the returned URL as metadata.logoUrl.
  • buyTaxBps and sellTaxBps must be exactly 100, 200, 300, or 400 (1–4%). Zero is not allowed.
  • initialBuyAmount must be within GET /infodeploy.initialBuyBounds for your counter asset, and be greater than 0.
  • tokenType must be 0 (normal) or 2 (PVT-time). PVT-time deploys also require a pvtTimeConfig body field.
  • counterAsset: 0 = WETH, 1 = USDC, 2 = XMA.
curl -X POST https://elixir-api.machima.ai/api/base/agent/execute/deploy \
  -H "Authorization: Bearer {your-api-key}" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": {
      "name": "My Token",
      "symbol": "MTK",
      "creator": "0xYourWalletAddress",
      "feeManager": "0xYourWalletAddress",
      "counterAsset": 0,
      "initialBuyAmount": "10000000000000000",
      "minTokensOut": "1",
      "buyTaxBps": 100,
      "sellTaxBps": 100,
      "tokenType": 0,
      "deadline": 1771834000
    },
    "intentSignature": "0x...",
    "gasTx": "0x...",
    "assetTx": "0x...",
    "metadata": {
      "logoUrl": "https://logos.machima.ai/logos/base/agent-upload-.../original.png",
      "website": "https://example.com",
      "twitter": "@handle"
    }
  }'

Response: { gasTxHash, assetTxHash, action, agentAddress, transactionHash, tokenAddress, positionId, blockNumber }.

Liquidity actions

Liquidity uses a relay pattern: you sign the actual NonfungiblePositionManager call yourself and pass it as actionTx; the backend broadcasts it after the gasTx. Applies to addLiquidity, increaseLiquidity, removeLiquidity, and collectFees.

curl -X POST https://elixir-api.machima.ai/api/base/agent/execute/addLiquidity \
  -H "Authorization: Bearer {your-api-key}" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": {
      "token0": "0xLowerAddress",
      "token1": "0xHigherAddress",
      "fee": 10000,
      "tickLower": -887200,
      "tickUpper": 887200,
      "amount0Desired": "1000000000000000000000",
      "amount1Desired": "100000000000000",
      "amount0Min": "0",
      "amount1Min": "0",
      "recipient": "0xYourWalletAddress",
      "deadline": 1771834000
    },
    "intentSignature": "0x...",
    "gasTx": "0x...",
    "actionTx": "0x..."
  }'

token0 must be the numerically lower address. Fee tiers: 100, 500, 3000, 10000. collectFees has no deadline field — use max uint128 (340282366920938463463374607431768211455) for amount0Max/amount1Max to collect everything.

Withdraw fees

Collects both trading tax and LP fees for a token you deployed, in one call. No actionTx — just intent + gasTx.

# Check what is claimable first
curl https://elixir-api.machima.ai/api/base/agent/claimable-fees/0xYourFeeManagerAddress \
  -H "Authorization: Bearer {your-api-key}"
 
curl -X POST https://elixir-api.machima.ai/api/base/agent/execute/withdrawFees \
  -H "Authorization: Bearer {your-api-key}" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": { "token": "0xTokenAddress", "recipient": "0xYourFeeManagerAddress" },
    "intentSignature": "0x...",
    "gasTx": "0x..."
  }'

Claim rewards

Token deployers earn protocol rewards via DeployHandler contracts. The actionTx is a signed DeployHandler.release(token) call.

# Check available rewards
curl https://elixir-api.machima.ai/api/base/machima-rewards/wallet/0xYourWalletAddress
 
curl -X POST https://elixir-api.machima.ai/api/base/agent/execute/claimRewards \
  -H "Authorization: Bearer {your-api-key}" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": { "handler": "0xDeployHandlerAddress", "token": "0xTokenAddress" },
    "intentSignature": "0x...",
    "gasTx": "0x...",
    "actionTx": "0x..."
  }'

See the Agent Endpoints reference for the full request/response shapes.