# Authorizing an Agent

A one-time onchain transaction from the trader's main wallet that grants a signing key permission to
submit trade intents on their behalf.

Skip this page entirely if you are [self-signing](/gasless/overview#integration-modes) —
when `signer == trader`, the contract accepts intents without any authorization.

All functions are on the Diamond (`0xea1b8E4aB7f14F7dCA68c5B214303B13078FC5ec`).

## Which route do you need

| You are building | How the agent gets authorized |
| :--- | :--- |
| A frontend or wallet | Your app calls `authorizeAgent` during onboarding — [below](#authorize). Users should never leave your product to set this up. |
| A bot trading **its own** wallet | Nothing to do. [Self-sign](/gasless/overview#integration-modes) instead. |
| A bot trading **someone else's** wallet | That trader authorizes your bot's address once. They can do it from the [Agent Wallet Manager](#authorize-from-the-app) without you shipping any UI. |

## Authorize from the app

The LeverUp app has a UI for this, at
[app.leverup.xyz/agent-wallets](https://app.leverup.xyz/agent-wallets). Use it to onboard a
counterparty who has no interface of their own, or to inspect and revoke authorizations while
debugging your integration.

1. Open the page and connect the **trader's main wallet**.
2. **+ Add Agent**.
3. Paste the **agent address** — the public address of the signing key you generated. The private
   key stays with you; the app never asks for it.
4. Set a **name** (unique per trader) and **permissions** — `ALL`, or tick individual actions.
5. **Authorize**, and confirm the one transaction.

The agent then appears as `ACTIVE`, with **Update** for name and permissions and **Revoke** to
remove it. Every button maps to a contract call documented on this page, so an authorization made
here and one made from your own code are indistinguishable onchain.

## Authorize

```solidity
function authorizeAgent(address agent, bytes32 name, uint256 permissions) external;
```

| Parameter | Notes |
| :--- | :--- |
| `agent` | The signing key's address. Must not be zero or the trader's own address. |
| `name` | A `bytes32` label, unique per trader. Must not be zero. |
| `permissions` | Bitmask — bit *n* grants action *n*. `type(uint256).max` grants everything. |

```ts
import { stringToHex, maxUint256 } from 'viem'
import { publicClient, walletClient, DIAMOND } from './config'

const AUTHORIZE_ABI = [{
  type: 'function',
  name: 'authorizeAgent',
  stateMutability: 'nonpayable',
  inputs: [
    { name: 'agent', type: 'address' },
    { name: 'name', type: 'bytes32' },
    { name: 'permissions', type: 'uint256' },
  ],
  outputs: [],
}] as const

const hash = await walletClient.writeContract({
  address: DIAMOND,
  abi: AUTHORIZE_ABI,
  functionName: 'authorizeAgent',
  args: [
    agentAddress,
    stringToHex('LeverUp 1CT', { size: 32 }),
    maxUint256,
  ],
})

await publicClient.waitForTransactionReceipt({ hash })
```

Calling `authorizeAgent` again for an address that is already authorized **updates** it — changing
the name and/or permissions — rather than creating a duplicate.

A trader can have several agents. `name` must be unique within a trader's set; reusing one reverts
with `OneClickV2: name taken`.

## Choosing permissions

`permissions` is a `uint256` where **bit number = action value**:

```ts
import { maxUint256 } from 'viem'

const openOnly  = 1n << 0n                    // MARKET_OPEN
const tradeOnly = (1n << 0n) | (1n << 1n)     // MARKET_OPEN + MARKET_CLOSE
const everything = maxUint256                 // wildcard
```

The full bit map is in [Actions Reference](/gasless/actions#permission-bits).

::: warning `maxUint256` is a wildcard, not a bit pattern
The contract special-cases `type(uint256).max` and skips per-bit checking entirely. Any other value
is checked bit by bit. This means "everything except one action" must be written as an explicit
mask, and that mask does **not** behave as a wildcard:

```ts
const noWithdraw = maxUint256 & ~(1n << 6n)   // every action except REMOVE_MARGIN
```

It also means an explicit mask is a snapshot. Action numbers are **appended** as the protocol gains
operations, and an agent authorized before an action existed does not hold its bit — intents for
that action come back `skipReason = INVALID` until the trader calls
[`updateAgentPermissions`](#update-and-revoke). A wildcard agent picks up new actions with no
action required.
:::

## Query

```solidity
function isAgentAuthorized(address trader, address agent) external view returns (bool);
function hasPermission(address trader, address agent, uint8 action) external view returns (bool);
function getAgentAuth(address trader, address agent) external view returns (AgentAuth memory);
function getAgentByName(address trader, bytes32 name) external view returns (address);
function getAgentCount(address trader) external view returns (uint256);
function getAgents(address trader, uint256 offset, uint256 limit) external view returns (AgentAuth[] memory);
function getLastNonce(address trader, address signer) external view returns (uint64);
```

```solidity
struct AgentAuth {
    address agent;
    bytes32 name;
    uint256 permissions;
    uint32 authorizedAt;
}
```

To check whether 1CT is enabled for a user, read `getAgentAuth(user, agentAddress)` and treat a
non-zero `agent` field as authorized:

```ts
const AGENT_AUTH_ABI = [{
  type: 'function',
  name: 'getAgentAuth',
  stateMutability: 'view',
  inputs: [{ name: 'trader', type: 'address' }, { name: 'agent', type: 'address' }],
  outputs: [{
    type: 'tuple', name: '', components: [
      { name: 'agent', type: 'address' },
      { name: 'name', type: 'bytes32' },
      { name: 'permissions', type: 'uint256' },
      { name: 'authorizedAt', type: 'uint32' },
    ],
  }],
}] as const

const auth = await publicClient.readContract({
  address: DIAMOND,
  abi: AGENT_AUTH_ABI,
  functionName: 'getAgentAuth',
  args: [user, agentAddress],
})

const isEnabled = auth.agent !== '0x0000000000000000000000000000000000000000'
```

`getAgents` is paginated — use `getAgentCount` to size the query.

## Update and revoke

```solidity
function updateAgentPermissions(address agent, uint256 permissions) external;
function updateAgentName(address agent, bytes32 newName) external;
function revokeAgent(address agent) external;
function revokeAgentByName(bytes32 name) external;
function revokeAllAgents() external;
```

```ts
const REVOKE_ABI = [{
  type: 'function',
  name: 'revokeAgent',
  stateMutability: 'nonpayable',
  inputs: [{ name: 'agent', type: 'address' }],
  outputs: [],
}] as const

const hash = await walletClient.writeContract({
  address: DIAMOND,
  abi: REVOKE_ABI,
  functionName: 'revokeAgent',
  args: [agentAddress],
})
await publicClient.waitForTransactionReceipt({ hash })
```

::: warning
When resetting a user's 1CT setup, revoke onchain and wait for the receipt **before** deleting the
local key material. The reverse order strands a live authorization on a key nobody holds.
:::

## Token approvals

Authorization grants permission to *trade*, not to move tokens. The trader must still approve the
Diamond for:

1. The **collateral token** they intend to trade with (USDC, WMON, LVUSD, LVMON).
2. The **execution fee token** — see [Execution Fee](/gasless/fees).

Without those approvals, intents are accepted by the endpoint and then skipped onchain with
`skipReason = "FEE"` or a collateral transfer failure.

Next: [Actions Reference](/gasless/actions).
