Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions ARCs/arc-1400.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
---
arc: 1400
title: Security Token Standard Suite
description: Composite specification aggregating core security token capabilities (issuance, partitioning, documents, controller)
author: Ludovit Scholtz (@scholtz)
discussions-to: https://github.com/algorandfoundation/ARCs/discussions
status: Draft
type: Standards Track
category: Interface
sub-category: Application
created: 2025-09-03
requires: 4, 88, 1594, 1410, 1643, 1644
replaces: ERC-1400
---

# ARC-1400: Security Token Standard Suite

Check failure on line 16 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 16 | # ARC-1400: Security Token Standard Suite | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 16 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 16 | # ARC-1400: Security Token Standard Suite | = info: the pattern in question: `(?i)ARC-[0-9]+`

## Abstract

ARC-1400 defines a composable suite of Algorand smart contract application layer specifications enabling compliant issuance, partitioned holdings, document disclosure, and controller / regulatory actions for tokenized securities. It adapts the semantics of the Ethereum ERC-1400 family (ERC-1594, ERC-1410, ERC-1643, ERC-1644) to Algorand primitives including ARC-200 (ERC-20 port), smart contract global/local state, boxes, logic signatures, and rekeyed multi-signature governance accounts.

Check failure on line 20 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 20 | ARC-1400 defines a composable suite of Algorand smart contract application layer specifications enabling compliant issuance, partitioned holdings, document disclosure, and controller / regulatory actions for tokenized securities. It adapts the semantics of the Ethereum ERC-1400 family (ERC-1594, ERC-1410, ERC-1643, ERC-1644) to Algorand primitives including ARC-200 (ERC-20 port), smart contract global/local state, boxes, logic signatures, and rekeyed multi-signature governance accounts. | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 20 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 20 | ARC-1400 defines a composable suite of Algorand smart contract application layer specifications enabling compliant issuance, partitioned holdings, document disclosure, and controller / regulatory actions for tokenized securities. It adapts the semantics of the Ethereum ERC-1400 family (ERC-1594, ERC-1410, ERC-1643, ERC-1644) to Algorand primitives including ARC-200 (ERC-20 port), smart contract global/local state, boxes, logic signatures, and rekeyed multi-signature governance accounts. | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 20 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 20 | ARC-1400 defines a composable suite of Algorand smart contract application layer specifications enabling compliant issuance, partitioned holdings, document disclosure, and controller / regulatory actions for tokenized securities. It adapts the semantics of the Ethereum ERC-1400 family (ERC-1594, ERC-1410, ERC-1643, ERC-1644) to Algorand primitives including ARC-200 (ERC-20 port), smart contract global/local state, boxes, logic signatures, and rekeyed multi-signature governance accounts. | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 20 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 20 | ARC-1400 defines a composable suite of Algorand smart contract application layer specifications enabling compliant issuance, partitioned holdings, document disclosure, and controller / regulatory actions for tokenized securities. It adapts the semantics of the Ethereum ERC-1400 family (ERC-1594, ERC-1410, ERC-1643, ERC-1644) to Algorand primitives including ARC-200 (ERC-20 port), smart contract global/local state, boxes, logic signatures, and rekeyed multi-signature governance accounts. | = info: the pattern in question: `(?i)ARC-[0-9]+`

## Motivation

Security tokens require:

- Transfer restriction checks (jurisdiction, KYC/AML, lock-up) with informative rejections.
- Partitioned balances (e.g., unrestricted, restricted, escrow, tranches) with atomic transfer semantics.
- Off-chain document disclosure with on-chain integrity proofs.
- Regulator / controller powers for forced transfers, seizures, or redemptions under predefined governance.

On Ethereum these are expressed via a set of interfaces across multiple ERCs composing ERC-1400. Algorand's architecture (ARC-200 base fungible token primitive plus AVM smart contracts) provides distinct affordances: fast finality, stateless/stateful TEAL, and logic signature / rekey features. ARC-1400 maps required behaviors into deterministic AVM logic while minimizing transaction overhead and preserving auditability.

Check failure on line 31 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 31 | On Ethereum these are expressed via a set of interfaces across multiple ERCs composing ERC-1400. Algorand's architecture (ARC-200 base fungible token primitive plus AVM smart contracts) provides distinct affordances: fast finality, stateless/stateful TEAL, and logic signature / rekey features. ARC-1400 maps required behaviors into deterministic AVM logic while minimizing transaction overhead and preserving auditability. | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 31 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 31 | On Ethereum these are expressed via a set of interfaces across multiple ERCs composing ERC-1400. Algorand's architecture (ARC-200 base fungible token primitive plus AVM smart contracts) provides distinct affordances: fast finality, stateless/stateful TEAL, and logic signature / rekey features. ARC-1400 maps required behaviors into deterministic AVM logic while minimizing transaction overhead and preserving auditability. | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 31 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 31 | On Ethereum these are expressed via a set of interfaces across multiple ERCs composing ERC-1400. Algorand's architecture (ARC-200 base fungible token primitive plus AVM smart contracts) provides distinct affordances: fast finality, stateless/stateful TEAL, and logic signature / rekey features. ARC-1400 maps required behaviors into deterministic AVM logic while minimizing transaction overhead and preserving auditability. | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 31 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 31 | On Ethereum these are expressed via a set of interfaces across multiple ERCs composing ERC-1400. Algorand's architecture (ARC-200 base fungible token primitive plus AVM smart contracts) provides distinct affordances: fast finality, stateless/stateful TEAL, and logic signature / rekey features. ARC-1400 maps required behaviors into deterministic AVM logic while minimizing transaction overhead and preserving auditability. | = info: the pattern in question: `(?i)ARC-[0-9]+`

## Specification Overview

ARC-1400 is an umbrella designation; compliance implies conformance with the component ARCs it aggregates:

Check failure on line 35 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 35 | ARC-1400 is an umbrella designation; compliance implies conformance with the component ARCs it aggregates: | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 35 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 35 | ARC-1400 is an umbrella designation; compliance implies conformance with the component ARCs it aggregates: | = info: the pattern in question: `(?i)ARC-[0-9]+`

- ARC-1594 (Core Security Token Operations & Transfer Validation)

Check failure on line 37 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 37 | - ARC-1594 (Core Security Token Operations & Transfer Validation) | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 37 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 37 | - ARC-1594 (Core Security Token Operations & Transfer Validation) | = info: the pattern in question: `(?i)ARC-[0-9]+`
- ARC-1410 (Partitioned Balances)

Check failure on line 38 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 38 | - ARC-1410 (Partitioned Balances) | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 38 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 38 | - ARC-1410 (Partitioned Balances) | = info: the pattern in question: `(?i)ARC-[0-9]+`
- ARC-1643 (Document Registry)

Check failure on line 39 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 39 | - ARC-1643 (Document Registry) | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 39 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 39 | - ARC-1643 (Document Registry) | = info: the pattern in question: `(?i)ARC-[0-9]+`
- ARC-1644 (Controller Operations)

Check failure on line 40 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 40 | - ARC-1644 (Controller Operations) | = info: the pattern in question: `(?i)ARC-[0-9]+`

Check failure on line 40 in ARCs/arc-1400.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ARCs/arc-1400.md | 40 | - ARC-1644 (Controller Operations) | = info: the pattern in question: `(?i)ARC-[0-9]+`

An implementation MAY provide a unified application (single App ID) coordinating all modules, or deploy multiple composable applications with standardized ABI method names and box/global state keys.

## Roles

- Issuer: Originates and manages supply, documents, compliance data.
- Controller: Authorized entity (could be same as Issuer) with limited override powers (ARC-1644).
- Investor: Holder of ARC-200 units.
- Registrar / Compliance Oracle: Off-chain / on-chain service that signs or writes eligibility data (e.g., KYC flags, jurisdiction codes) into contract state.
- Document Provider: Pushes document hashes / URIs (ARC-1643).
- Governance Multisig: Rekeyed address or LogicSig controlling privileged calls.

## Algorand Mapping

| Concept | Ethereum (ERC-1400) | Algorand (ARC-1400) |
| ------------------ | ------------------------------------- | ----------------------------------------------------------------------------------------- |
| Token Units | ERC-20 ledger | ARC-200 |
| Partition Balances | Mapped in contract storage | Boxes (partition -> uint balance) per holder or local state schema |
| Transfer Pre-Check | Smart contract hook or off-chain call | Group transaction including App call performing eligibility logic before ARC-200 transfer |
| Force Transfer | Controller function | App call + inner transaction transferring ARC-200 units using controller authority |
| Document Hashes | On-chain mapping | App global state / boxes storing (name -> struct {hash, URI, timestamp}) |
| Compliance Data | Contract storage | Local state (per address flags) + boxes for extended KYC metadata |

## Transaction Patterns

Implementations SHOULD leverage atomic groups:

1. Validate transfer: App call (method: `arc1594_validate_transfer(partition, from, to, amount, data)` ) returning success code via log or state write.
2. Execute ARC-200 transfer: Subsequent token transfer (or inner transaction) referencing validated parameters. App SHOULD enforce replay protection by tagging group ID / round.

Forced operations (ARC-1644) MUST be executed as: governance-signed App call + inner ARC-200 transfer using controller-managed authority.

### ARC-200 vs Partition Balance Interaction

To avoid ambiguity implementers MUST document and adhere to the following normative rules (recommended defaults provided):

- Invariant: For each holder `sum(partition_balances) == arc200_balance` at all times.
- Moving units between partitions (reclassification) DOES NOT change `arc200_balance`; it debits one partition and credits another atomically.
- Plain ARC-200 transfers without an accompanying partition parameter SHOULD operate solely on the default/unrestricted (zero-address) partition. If insufficient balance exists there, the transfer MUST fail rather than implicitly sourcing from other partitions.
- Implementations MAY disable plain ARC-200 transfers (enforcing `arc1410_transfer_by_partition` exclusively). If so, they MUST expose clear failure reason codes when a bare transfer attempt is made.
- Receiving units via a plain ARC-200 transfer credits only the default partition; no auto-allocation across other partitions occurs.
- Issuance / redemption via partition-aware methods adjusts both ARC-200 total supply and the specified partition balance in one atomic group.

Rationale: Explicit partition context prevents inadvertent regulatory circumvention and preserves audit clarity.

## Standard Keys and ABI

Recommended ABI method names (snake_case) with ARC-4 type signatures (arguments then return types where applicable). Unless specified otherwise `uint64` refers to ARC-4 `uint64`, `byte[]` to ARC-4 `bytes`, and addresses to ARC-4 `address`.

Divergence vs original ERC family: ERC-1410/1594 use `bytes32` for partition identifiers; ARC-1400 standardizes on `address` for partition identifiers to leverage native authorization semantics and zero-address default partition.

- `arc1594_issue(to: address, amount: uint64, partition: address, data: bytes)`
- `arc1594_redeem(from: address, amount: uint64, partition: address, data: bytes)`
- `arc1594_validate_transfer(partition: address, from: address, to: address, amount: uint64, data: bytes) -> (code: uint64, reason: bytes)`
- `arc1410_transfer_by_partition(from: address, to: address, partition: address, amount: uint64, data: bytes) -> (code: uint64, reason: bytes)`
- `arc1643_set_document(name: bytes, uri: bytes, hash: bytes32)`
- `arc1643_get_document(name: bytes) -> (uri: bytes, hash: bytes32, timestamp: uint64)`
- `arc1643_document_hash(name: bytes) -> (hash: bytes32)`
- `arc1643_document_names() -> (names: bytes[])`
- `arc1644_controller_transfer(from: address, to: address, partition: address, amount: uint64, data: bytes, operator_data: bytes) -> (code: uint64)`
- `arc1644_controller_redeem(from: address, partition: address, amount: uint64, operator_data: bytes) -> (code: uint64)`
- `arc1644_is_controllable() -> (flag: uint64)`
- `arc1644_set_controller(new_controller: address)`
- `arc1644_add_controller(addr: address)` / `arc1644_remove_controller(addr: address)`
- `set_investor_flag(addr: address, flag_key: bytes, value: uint64)` (un-prefixed governance/compliance helper; MAY be optionally namespaced `arc1594_set_investor_flag`)

Notes:

- `partition` is always an ARC-4 `address`; the zero address MAY denote the default/unrestricted partition.
- Implementations MAY expose supplementary view methods for pagination; those should follow the same snake_case + ARC-4 typing convention.

Box / state key naming MUST be canonical ASCII lowercase with hyphen separation where applicable.

## Return Codes

ARC-1594 defines standardized failure codes; see that spec. Implementations MUST log the code (uint64) in the validate call and MAY log a reason (UTF-8 string) for discovery off-chain.

## Security Considerations

- Use logic to prevent double-spend across partitions (sum of partitions == ARC-200 balance).
- Ensure controller authority cannot bypass validation except where explicitly permitted.
- Off-chain document URIs must be integrity-protected by cryptographic hash; consider IPFS CID v1.
- Governance multisig threshold changes must be timelocked.
- Avoid storing PII on-chain; store only hashed or coded references.

## Backwards Compatibility

ARC-1400 implementers MAY simultaneously expose standard ARC-200 metadata fields. Non-partitioned wallets can still transfer unrestricted partition units if exposed as standard ARC-200 units where appropriate.

## Reference Implementation

[Security token](https://github.com/scholtz/arc-1400/blob/main/projects/arc-1400/smart_contracts/security_token/)

## Rationale

Aggregation clarifies compatibility claims for integrators; a single ARC number signals presence of all subordinate capabilities.

## Copyright

Copyright and related rights waived via CC0 1.0 Universal.
Loading