# On-Chain Tracking - Adding an Identifier to Your Safe Accounts

Attribute active users and UserOperations to your project by tagging each userOp's `callData` with a 32-byte marker. Your indexer filters on the marker, no extra infrastructure, no off-chain correlation.

## Generate an On-Chain Identifier[​](#generate-an-on-chain-identifier "Direct link to Generate an On-Chain Identifier")

Pass `onChainIdentifierParams` when you initialize the Safe account. The SDK appends the marker to every userOp's `callData`.

```
import { SafeMultiChainSigAccountV1 as SafeAccount } from "abstractionkit";



const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress], {

  onChainIdentifierParams: {

    project: "YourProject",        // required — the thing you key analytics off

    platform: "Web",               // optional — 'Web' | 'Mobile' | 'Safe App' | 'Widget'

    tool: "abstractionkit",        // optional — which SDK

    toolVersion: "0.3.2",          // optional — SDK version

  },

});
```

Only `project` is required. `platform`, `tool`, and `toolVersion` refine attribution (Web vs Mobile, which SDK, which version). Use the same values everywhere, analytics key off this exact string.

### Already-deployed accounts[​](#already-deployed-accounts "Direct link to Already-deployed accounts")

For accounts that already exist on-chain, pass the same params to the constructor. New userOps will carry the marker from that point on; historical userOps are not retroactively tagged.

```
const smartAccount = new SafeAccount(accountAddress, {

  onChainIdentifierParams: { project: "YourProject" },

});
```

## Getting the On-Chain Identifier[​](#getting-the-on-chain-identifier "Direct link to Getting the On-Chain Identifier")

```
const onchainIdentifier = smartAccount.onChainIdentifier;

// "0x5afe00..."
```

View the identifier generation in the [generateOnChainIdentifier](https://github.com/candidelabs/abstractionkit/blob/v0.3.4/src/account/Safe/SafeAccount.ts#L3124) source code.

## Indexer patterns[​](#indexer-patterns "Direct link to Indexer patterns")

### Exact match: `UserOperationEvent`[​](#exact-match-useroperationevent "Direct link to exact-match-useroperationevent")

The EntryPoint emits a `UserOperationEvent` log for every included userOp. Decode it, pull the userOp's `callData`, and check the suffix. This is the right approach: it's per-userOp and the marker sits exactly at the tail.

```
const endsWithId = userOp.callData

  .toLowerCase()

  .endsWith(identifier.toLowerCase());
```

Aggregate:

* Unique `sender` values → active users
* Total matching events → userOp volume
* Group by the identifier's trailing hashes → split by platform / tool / version

### Fuzzy match: `handleOps` tx input[​](#fuzzy-match-handleops-tx-input "Direct link to fuzzy-match-handleops-tx-input")

The bundler wraps userOps in `EntryPoint.handleOps(ops[], beneficiary)`. The ABI-encoded tx calldata is laid out as:

```
selector (4 bytes)

│

├─ head

│    offset-to-ops  (32 bytes)

│    beneficiary    (32 bytes)

│

└─ ops data (dynamic: length + each op, with callData inlined as dynamic bytes)
```

The marker is inside each op's `callData`, so it appears somewhere in the dynamic tail of the tx input, not strictly at the end. With multiple userOps batched into one `handleOps` call, each op's callData contributes its own marker occurrence at a different offset. Suffix matching on `tx.input` is not reliable. Use substring match, or decode the ops array and inspect each `callData`:

```
const tagged = tx.input

  .toLowerCase()

  .includes(identifier.toLowerCase());
```

Good enough for quick dashboards, but batches of multiple userOps in one `handleOps` call collapse into one match. For per-userOp attribution, decode the ops array (ABI-decode the tx input) and check each op's `callData` individually, or prefer the event-based approach above.

## Full example[​](#full-example "Direct link to Full example")

A complete working script (Safe v0.3.0 on Arbitrum Sepolia, with sponsored gas and a mint transaction) is available on GitHub: [onchain-identifier.ts](https://github.com/candidelabs/abstractionkit-examples/blob/main/onchain-identifier/onchain-identifier.ts).

## Marker layout[​](#marker-layout "Direct link to Marker layout")

What's the format of the identifier?

The identifier is 32 bytes and follows the format below:

`5afe` `00` `6363643438383836663461336661366162653539` `646561` `393238` `653366`

Check the last 32 bytes of the `callData` field in a `UserOperationEvent` log, or inside the `handleOps` tx input, to see how the identifier appears after the transaction is executed.

```
5afe │ 00 │ project(20) │ platform(3) │ tool(3) │ toolVersion(3)

└─prefix  │

   version
```

Each variable-content field is `keccak256(value)` truncated to its byte width.

#### Prefix hash[​](#prefix-hash "Direct link to Prefix hash")

* Type: 2 bytes
* Example: `5afe`

Static prefix to identify the Safe on-chain identifier.

#### Version hash[​](#version-hash "Direct link to Version hash")

* Type: 1 byte
* Example: `00`

Version number of the Safe on-chain identifier format.

#### Project hash[​](#project-hash "Direct link to Project hash")

* Type: 20 bytes
* Example: `6363643438383836663461336661366162653539`

Truncated hash of the project's name (for example, "Gnosis", "CoW Swap").

#### Platform hash[​](#platform-hash "Direct link to Platform hash")

* Type: 3 bytes
* Example: `646561`

Truncated hash of the platform's name (for example, "Web", "Mobile", "Safe App", "Widget").

#### Tool hash[​](#tool-hash "Direct link to Tool hash")

* Type: 3 bytes
* Example: `393238`

Truncated hash of the tool's name (for example, "protocol-kit", "relay-kit", or any custom tool built by projects).

#### Tool version hash[​](#tool-version-hash "Direct link to Tool version hash")

* Type: 3 bytes
* Example: `653366`

Truncated hash of the tool's version (for example, "1.0.0", "1.0.1").

## Submission Form[​](#submission-form "Direct link to Submission Form")

The Safe team aims to better understand and recognise key contributors who are driving the adoption of smart accounts within the ecosystem. By submitting your on-chain identifiers through the provided form, you will help Safe accurately attribute activity.

You can fill out the form by clicking [this link](https://forms.gle/NYkorYebc6Fz1fMW6).
