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
- Sign the intent — EIP-712 typed data for the action →
intentSignature. - Sign a
gasTx— a raw EIP-1559 ETH transfer to the executor wallet. Itsdatafield must equalkeccak256(intentSignature). This binds the ETH payment to that exact intent. - Sign an
assetTx(deploy only, wheninitialBuyAmount > 0) — a raw ERC-20 transfer of the counter asset to the executor wallet, at noncegasTx + 1. - Sign an
actionTx(liquidity actions andclaimRewards) — the pre-signed contract call the backend relays, at noncegasTx + 1. - Submit everything to
/execute/:action(or/verify/:actionfor 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 /info → eip712Domain 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 /info → fees. 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: approveintent.counterAssetto the adapter. - Before
sell: approveintent.tokento the adapter.
Read the live adapter address from GET /info → buy.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 asmetadata.logoUrl. buyTaxBpsandsellTaxBpsmust be exactly100,200,300, or400(1–4%). Zero is not allowed.initialBuyAmountmust be withinGET /info→deploy.initialBuyBoundsfor your counter asset, and be greater than 0.tokenTypemust be0(normal) or2(PVT-time). PVT-time deploys also require apvtTimeConfigbody 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.