Forensic Token (Forest) is a directed acyclic graph (DAG) inspired token model designed to enhance traceability and regulatory compliance in digital currency or e-Money systems. By introducing hierarchical token tracking, it enables efficient enforcement on any token linked to suspicious activity with level/root. Enforcement actions, such as freezing specific tokens or partitioning all tokens with relational links, are optimized to operate at $O(1)$ complexity.
The Central Bank Digital Currency and Private Money concept aim to utilize the advantages of Blockchain or Distributed Ledger Technology that provide immutability, transparency, and security, and it adopts smart contracts, which play a key role in creating programmable money. However, technology itself gives an advantage and eliminates the ideal problem of compliance with the regulator and the Anti-Money Laundering and Countering the Financing of Terrorism (AML/CFT) standard, but it does not seem practical to be done in the real world and is not efficiently responsible for the financial crime or incidents that occur in the open network of economics.
Financial crime incident response actions, like freezing accounts or funds, typically necessitate further analysis to pinpoint illicit transactions. This process is off-chain; it can be slow and inefficient. Many existing solutions focus primarily on prevention by attempting to predict bad actors in advance; however, human behavior changes over time, sometimes immediately, especially during periods of economic stress, which may make such approaches unreliable.
Therefore, preventive controls alone cannot fully eliminate bad actors, an inevitable risk in open financial systems. Rather than attempting to predict malicious behavior, there is a need for systems that can respond to incidents faster and more precisely once they occur. The Forensic Token (Forest) is designed to address this need by providing native, on-chain traceability and enforcement at the token level, enabling targeted actions that reduce operational metrics such as Mean Time To Resolve (MTTR) and Mean Time To Fix (MTTF) while preserving on-chain programmability.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Compatible implementations MUST implement the IERC8047 interface and MUST inherit from ERC-1155 and ERC-5615 interfaces. All functions defined in the interface MUST be present and all function behavior MUST meet the behavior specification requirements below.
mint function.
A mint operation is identified by intent: any operation that
creates a new token, thereby adding to the total circulating supply, is considered a mint. Implementations MAY expose a mint function or
integrate minting logic within another operation, provided
the resulting token satisfies the properties defined below.value MUST NOT be zero. If value is zero, the mint operation MUST revert.id MUST NOT be supplied by the minter; the id MUST be generated via a contract-side mechanism. See Contract-side ID Generation for the reasoning behind this requirement.root property of the new token MUST be set to its own id and the parent property of the new token MUST be set to zero to explicitly indicate that the token serves as the root of a new DAG.TokenCreated MUST be emitted when the minting token operation is successful.TokenCreated event MUST be emitted with root set to zero when minting a new root token, enabling off-chain indexers to identify and enumerate all DAG origins by filtering on root equal to zero.burn function.
A burn operation is identified by intent: any operation that removes value from the total circulating supply by reducing a token's value is considered a burn. Implementations MAY expose a burn function or integrate burning logic within another operation, provided the resulting state satisfies the properties defined below.id MUST NOT be removed from the DAG. Instead, its value MUST be reduced by the burn amount (e.g., a token with a value of 1000 burned by 1000 results in a value of zero — the token id remains in the DAG with its full lineage intact).TokenSpent event MUST be emitted when the burning operation is successful.value is reduced by the burn amount. The token remains in the DAG with its remaining value.value is reduced to zero. The token id remains in the DAG with its lineage intact but is no longer spendable.exists function MUST return true for any id that has been created, even if its value is zero. Implementations MUST determine existence by verifying that the root of the id is not zero. Checking the token's value MUST NOT be used as an existence check, as a burned token retains its id in the DAG with a value of zero. See Soft Delete and Forensic Persistence for the reasoning behind this requirement.safeTransferFrom MUST verify that the id exists. If it does not, the function MUST revert.safeTransferFrom MUST revert if the from address is equal to the to address.from MUST be the owner of the id or an approved operator.value to be spent MUST NOT be zero.value to be spent MUST NOT exceed the value of the id. If it does, the function MUST revert.safeTransferFrom function MUST mint a new id as a child of the id being spent. The new id MUST have its parent set to the id that was spent and its level MUST be incremented by one relative to the parent.value is less than the token's current value, the operation is considered a partial spend. The parent token's value MUST be reduced by the spent value.value is equal to the token's current value, the operation is considered a full spend. The parent token's value MUST be set with zero.safeTransferFrom MUST emit two TransferSingle events on full spend to reflect the parent–child token behavior.
TransferSingle(operator, from, address(0), id, value).TransferSingle(operator, address(0), to, newId, value).safeTransferFrom MUST emit two TransferSingle events. The first reflects the reduction of the parent token's value. The parent token remains in the DAG with a reduced value.
value TransferSingle(operator, from, address(0), id, value).TransferSingle(operator, address(0), to, newId, value).safeBatchTransferFrom MUST emit two TransferBatch events, preserving token order.
ids array.ids as the parent batch.TokenSpent event MUST be emitted with the spent amount whenever the token is spent, whether partial or full.TokenCreated event MUST be emitted whenever a new child token is successfully created.To maintain compatibility with standard indexers and wallets that support ERC-1155, implementations MUST emit the standard TransferBatch event transferring the consumed ids from the owner to the zero address to reflect their consumption. Additionally, a standard TransferSingle and TokenCreated event MUST be emitted for the newly minted merged token id.
To merge two or more ids into a new id, the default merging operation REQUIRE that all tokens be part of the same DAG to maintain a clean lineage (i.e., share the same root).
The level and parent of the new id from merging will be formalized as
$$k = \max_{i}(level_i), \quad i \in ids$$ $$parent_k = ids\bigl[\min{i : level_i = k}\bigr]$$
$$newTokenIdLevel = k + 1$$ $$newTokenIdParent = parent_k$$
The TokenMerged event MUST be emitted, including all ids involved in the merge, when the merging operation is successful.
Implementations MAY allow merging tokens from different root. If a merge occurs across different DAGs, the implementation MUST define a deterministic rule for assigning the root of the new token. (e.g., inheriting the root of the token with the highest value or the lowest level). Implementers MUST carefully consider the consequences of cross-DAG merging, as it combines previously independent asset lineages. This makes the lineage less clean and complicates forensic tracking, as enforcement actions or risk profiles associated with any of the original root will now propagate to the newly merged token id. Before executing a cross-DAG merge, implementations MAY enforce rules ensuring sufficient transaction confirmations or adequate confidence levels. Furthermore, when signaling this custom behavior via the TokenMerged event, implementations MUST use a mergeType flag strictly greater than zero, as zero is reserved exclusively for the default same DAG merge operation.
The validation step MAY be implemented before the merging logic executes. This leaves room for implementation-specific rules, such as gatekeeper, limit amount, etc.
In this proposal, each token has a unique id to track its movement in the DAG (like serial numbers), but all tokens representing the same asset share a single metadata URI. This reflects the fungible nature of the asset (like fiat currency).
id.A complete JSON Schema reference for ERC-8047 metadata is provided below for validation and implementation guidance.
The token ID is generated dynamically by the contract-side upon execution, rather than supplied by the caller. Because employs a Unspent Transaction Output (UTXO)-like mechanism where each transfer effectively spends an existing token and mints a new one to continue the DAG lineage, allowing caller-supplied IDs for these newly spawned tokens introduces critical attack vectors. Such vulnerabilities include ID collisions, unauthorized overwriting of lineage records, or root impersonation. Enforcing deterministic, contract-side ID generation at the time of the call guarantees global uniqueness and preserves the structural integrity of the lineage.
Unlike the UTXO model, the Forest architecture permits stateful mutations of existing tokens while enforcing strict parent–child lineage. Tokens support fractional, iterative expenditures until depletion. By natively embedding parent references within each token, the architecture optimizes for reverse topological traversal. This enables highly efficient back-to-root queries—isolating a specific token's lineage up to its origin without the computational overhead of full DAG traversal. This continuous topology inextricably links all child nodes back to their roots, guaranteeing deterministic forensic traceability that traditional, aggregated account-based standards like ERC-20 or ERC-3643 fundamentally lack this granular traceability, as they obfuscate individual token flows into aggregated account balances.
The forest token-based model it natively supports reverse topological traversal. Each token stores a reference to its parent token, allowing to efficiently iterate from any given token back to its root token of the DAG. This back-to-root traversal differs from a full DAG traversal. It only follows the lineage of a specific token ID up to its root, rather than visiting all tokens in the DAG.
The property level returns uint96 as this offers the maximum possible precision that fits within the same storage slot as the owner address. Since an address occupies 160 bits, exactly 96 bits remain available in the 256 bits word. Utilizing uint96 ensures zero wasted space.
From a functional perspective, uint96 allows for a tree depth of , which is for all practical purposes infinite. Even in an extreme scenario on a high-performance network or Layer 2 with a 250ms block time that produces 4 blocks per second, assuming a transaction increases the tree depth every single block
$$\text{Network block time} = 250 \text{ ms}$$ $$\text{Seconds per year} \approx 31{,}536{,}000 \text{ seconds}$$ $$\text{Blocks per year} = 4 \times 31{,}536{,}000 = 126{,}144{,}000 \text{ blocks}$$ $$\text{Years to overflow} = \frac{2^{96}}{126{,}144{,}000} = \frac{79{,}228{,}162{,}514{,}264{,}337{,}593{,}543{,}950{,}336}{126{,}144{,}000} \approx 6.2 \times 10^{20} \text{ years}$$
This timeframe is orders of magnitude longer than the current known age of the universe ($\approx 1.38 \times 10^{10} \text{ years}$). Therefore, limiting the level to uint96 to achieve storage packing imposes no realistic constraint on the system's longevity or throughput.
Tokens are never removed from the DAG when it's create. Removing a burned token would destroy its lineage record, breaking the forensic chain between parent and child tokens. Any enforcement action applied to a root or level must remain traceable to all tokens that were ever part of that DAG family, including those that have been fully spent. Hard deletion is therefore incompatible with the forensic guarantees this standard provides.
Traditional systems are enforced at the account level. This often means freezing an entire wallet just to stop one bad transaction, which unfairly locks up a user's legitimate funds. Forest solves this by applying rules to both the account and the individual tokens. It works like pruning a tree rather than chopping it down. This precision allows authorities to target only the specific illicit assets while leaving the rest of the user's portfolio untouched and fully operational.
The constant-time enforcement (i.e., $O(1)$ complexity) claim refers to the cost of applying an enforcement action relative to the size of the DAG, total token count, or number of tokens sharing the same root. Tokens sharing the same root form a single DAG family. Enforcement actions applied at the root or level propagate implicitly to all linked tokens within that family without iteration. Regardless of how large the DAG grows, enforcement cost remains constant. For a reference implementation, see Token Policy Enforcement (TPEn).
On-chain iteration to retrieve spendable balance can be gas-intensive and inefficient, especially for large DAGs or multiple sets of DAGs. To address this, the current spendable balance of account can be determined off-chain by deploying a service that subscribes to events emitted by the contract. This service calculates the spendable balance by reconciling the account’s total balance of with any tokens that have been frozen or restricted due to hierarchical or forensic rules, providing an accurate representation of the amount available for spend.
This standard is fully compatible with ERC-1155 and ERC-5615.
The following abstract contract provides a reference implementation of the TPEn. It demonstrates the gas-optimized logic required to evaluate and apply topological DAG quarantines using 256-bit storage packing and bitwise operations. Furthermore, this bucket-based design natively enables mass-quarantine capabilities, laying the groundwork for regulators to simultaneously freeze or unfreeze up to 256 distinct topological levels in a single transaction by passing a pre-computed bitmask.
Each DAG level maps to a 256-bit storage bucket and a specific bit position within that bucket using bitwise operations:
| Operation | Formula | Example level = 300 |
|---|---|---|
| bucket | level >> 8 (i.e., level / 256) | 300 >> 8 = 1 |
| bitIndex | level & 0xFF (i.e., level % 256) | 300 & 0xFF = 44` |
Each bucket covers 256 consecutive levels. A single uint256 storage slot represents
levels bucket^256 to (bucket + 1)^256 - 1.
| Bucket | Levels Covered |
|---|---|
0 | 0 – 255 |
1 | 256 – 511 |
2 | 512 – 767 |
n | n^256 – (n + 1)^256 - 1 |
Freezing a level sets the corresponding bit to 1 via bitwise OR. Unfreezing sets it to 0 via bitwise AND NOT. Checking freeze status reads the bit via bitwise AND.
Denial Of Service (DoS)
A potential out-of-gas issue may occur due to the transaction gas limit cap introduced in EIP-7825, Operations such as safeBatchTransferFrom may consume more gas than permitted by the transaction gas limit introduced in EIP-7825, leading to transaction revert. For private networks that do not adopt EIP-7825 the transaction may exceed the block gas limit if the required gas is higher than the network’s configured maximum. To mitigate this, implementations should enforce a maximum limit on the number of input IDs allowed per transaction.
State Growth
The token-based model tracks all assets within the system, formalized as
$$A_{\text{ids}} = A_{\text{totalSupply}} \times 10^{A_{\text{decimals}}}$$
where:
While this ensures precision, high granularity can increase storage needs. Traditional finance often uses simpler decimals (2, 4, or 6) to avoid excessive fragmentation. Adopting similar constraints such as capping decimals or enforcing a minimum token value before spending could help balance granularity with efficiency.
Coin Selection and Risk Propagation
Implementers have the flexibility to design automated coin selection algorithms tailored to user needs, such as First-In-First-Out (FIFO)
, Last-In-First-Out (LIFO) or other optimization strategies base on business need e.g. transaction fees optimization.
This introduces a risk of account linking, where legitimate and illicit token IDs are combined in a single batch transfer. Because traditional compliance frameworks rely on account-level heuristics, they may incorrectly penalize a user's creditworthiness due to this association or the presence of isolated frozen tokens. To prevent unwarranted financial exclusion, compliance infrastructure must be updated to derive reputation from Net Spendable Equity (clean assets) rather than the aggregate portfolio state.
Confidentiality and Privacy
Unlike opaque account-based models, this proposal treats every token as a traceable lineage, explicitly prioritizing forensic auditability. By preserving parent-child links on-chain, the protocol exposes the full transaction graph to observers.
The proposal itself remains strictly pseudonymous. It tracks the relationships between assets, not the identities of owners, and the core specification stores no Personally Identifiable Information (PII).
However, implementations of this standard may differ. Issuers are free to layer identity requirements such as whitelists or Soulbound Tokens (SBT) on top of the base protocol. Therefore, while the data structure is pseudonymous, a specific deployment may enforce real-world identity bindings.
Copyright and related rights waived via CC0.