-
Notifications
You must be signed in to change notification settings - Fork 829
Add ERC: Lot-based Token #579
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mrosendin
wants to merge
35
commits into
ethereum:master
Choose a base branch
from
capsign:eip-equity-token
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+401
−0
Open
Changes from 32 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
80a10a5
Create eip-equity_token.md
mrosendin 17b8380
Fix reference and rebase to v1
mrosendin 06614d5
Update ERCS/eip-equity_token.md
mrosendin 1e3d53b
Update ERCS/eip-equity_token.md
mrosendin 46f43e8
Rename eip-equity_token.md to eip-7752.md
mrosendin 68f8318
Rename eip-7752.md to erc-7752.md
mrosendin df902f0
Update erc-7752.md
mrosendin b0bbca0
Update erc-7752.md
mrosendin 87d5cc1
Remove external link
mrosendin fc50883
Merge branch 'master' into eip-equity-token
mrosendin 191f3a8
Update erc-7752.md
mrosendin 2b21514
Update erc-7752.md
mrosendin 59bdc35
Update erc-7752.md
mrosendin 8449d95
mint -> issue, burn -> cancel
mrosendin e7078b2
Info about vesting and governing docs
mrosendin 276d9fb
update rationale of separate vesting and documentation contracts
mrosendin 8e59d6d
fix: eip validation error
mrosendin 30f498e
Merge branch 'master' into eip-equity-token
mrosendin de0586a
erc-1155 update
mrosendin 3112713
add links
mrosendin 6112ad2
update links
mrosendin 649e5dd
fix markdown-link-first
mrosendin 0cd1e89
fix markdown-first-link
mrosendin 69a9d8d
Update erc-7752.md
mrosendin 832b472
eip walidator fixes
mrosendin d206258
Update erc-7752.md
mrosendin 1b35045
fix walidator issues
mrosendin 608ef19
Set table for international use
mrosendin 20507ae
Update title
mrosendin 633242b
Merge branch 'master' into eip-equity-token
mrosendin c219014
Update erc-7752.md
mrosendin 0cf11ae
Merge branch 'master' into eip-equity-token
mrosendin beaabad
Update erc-7752.md
mrosendin 5577912
Update erc-7752.md
mrosendin 0051018
Update erc-7752.md
mrosendin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,249 @@ | ||
| --- | ||
| eip: 7752 | ||
| title: Lot Token | ||
| description: A token treating each acquisition (lot) as an on-chain record with cost-basis, lineage, and regulatory controls. | ||
| author: Matt Rosendin (@mrosendin) <[email protected]> | ||
| discussions-to: https://ethereum-magicians.org/t/erc-7752-equity-token/20735 | ||
| status: Draft | ||
| type: Standards Track | ||
| category: ERC | ||
| created: 2024-08-06 | ||
| requires: 173 | ||
| --- | ||
|
|
||
| ## Abstract | ||
|
|
||
| [ERC-7752](./eip-7752.md) defines a **lot-based** token model. Each issuance or acquisition event mints a unique **lot** (`lotId`) that carries its own quantity, cost basis, acquisition date, and lineage. The core interface also standardizes pause, freeze, forced-transfer, and other **administrative hooks** mandated for real-world securities and off-chain assets. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Traditional token standards excel at _fungibility_ ([ERC-20](./eip-20.md)) or _single, indivisible items_ ([ERC-721](./eip-721.md)). | ||
| Real-world assets—equity certificates, debt notes, real-estate fractions—sit between those extremes: | ||
|
|
||
| - One issuer may create **thousands of discrete lots** over time. | ||
| - Each lot needs cost-basis, vesting, or lock-up data. | ||
| - Regulators demand pause, freeze, and forced-transfer powers. | ||
|
|
||
| ERC-7752 bakes those requirements into a single, deterministic ABI so wallets, custodians, tax engines, and regulators can interoperate without bespoke dialects. | ||
|
|
||
| ## Specification | ||
|
|
||
| Key words **MUST**, **SHOULD**, etc. have the meanings of RFC 2119 / 8174. | ||
|
|
||
| ### Core Interface | ||
|
|
||
| ```solidity | ||
| // SPDX-License-Identifier: BUSL-1.1 | ||
mrosendin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| pragma solidity ^0.8.20; | ||
|
|
||
| /** | ||
| * @title ERC-7752 Lot Token (Core) | ||
| */ | ||
| interface LotToken { | ||
| /*────────────────────────────────── | ||
| Data structures | ||
| ──────────────────────────────────*/ | ||
| enum TransferType { INTERNAL, SALE, GIFT, INHERITANCE, REWARD } | ||
|
|
||
| struct Lot { | ||
| bytes32 parentLotId; | ||
| uint256 quantity; | ||
| address paymentCurrency; | ||
| uint256 costBasis; | ||
| uint256 acquisitionDate; | ||
| uint256 lastUpdate; | ||
| bool isValid; | ||
| address owner; | ||
| TransferType tType; | ||
| string uri; | ||
| bytes data; | ||
| } | ||
|
|
||
| /*────────────────────────────────── | ||
| Events (deterministic for audit) | ||
| ──────────────────────────────────*/ | ||
|
|
||
| event LotCreated( | ||
| address indexed owner, | ||
| bytes32 indexed lotId, | ||
| bytes32 indexed parentLotId, | ||
| uint256 quantity, | ||
| address paymentCurrency, | ||
| uint256 costBasis, | ||
| uint256 acquisitionDate, | ||
| uint256 lastUpdate, | ||
| string uri, | ||
| bytes data, | ||
| TransferType tType | ||
| ); | ||
|
|
||
| event LotTransferred( | ||
| bytes32 indexed lotId, | ||
| address indexed from, | ||
| address indexed to, | ||
| uint256 quantity, | ||
| string uri, | ||
| bytes data, | ||
| TransferType tType, | ||
| uint256 newCostBasis | ||
| ); | ||
|
|
||
| event LotAdjusted( | ||
| bytes32 indexed oldLotId, | ||
| bytes32 indexed newLotId, | ||
| address operator, | ||
| uint256 newQuantity, | ||
| uint256 newCostBasis, | ||
| address paymentCurrency, | ||
| uint256 newAcquisitionDate, | ||
| string newUri, | ||
| bytes newData, | ||
| string reason, | ||
| TransferType tType | ||
| ); | ||
|
|
||
| event LotInvalidated(bytes32 indexed lotId); | ||
|
|
||
| event PausedSet(bool indexed paused); | ||
| event AccountFrozen(address indexed account, bool frozen); | ||
| event LotFrozen(bytes32 indexed lotId, bool frozen); | ||
|
|
||
| event ForcedTransfer( | ||
| bytes32 indexed lotId, | ||
| address indexed from, | ||
| address indexed to, | ||
| uint256 quantity, | ||
| string reason | ||
| ); | ||
|
|
||
| event TransferControllerAdded(address indexed controller); | ||
|
|
||
| /*────────────────────────────────── | ||
| Read functions | ||
| ──────────────────────────────────*/ | ||
| function name() external view returns (string memory); | ||
| function symbol() external view returns (string memory); | ||
| function getLot(bytes32 id) external view returns (Lot memory); | ||
|
|
||
| /*────────────────────────────────── | ||
| Lot CRUD | ||
| ──────────────────────────────────*/ | ||
| function createLot( | ||
| address owner, | ||
| uint256 quantity, | ||
| address paymentCurrency, | ||
| uint256 costBasis, | ||
| uint256 acquisitionDate, | ||
| string calldata uri, | ||
| bytes calldata data, | ||
| uint256 customId /* 0 = auto */ | ||
| ) external returns (bytes32 lotId, uint256 assignedCustomId); | ||
|
|
||
| function transfer( | ||
| bytes32 lotId, | ||
| address to, | ||
| uint256 quantity, | ||
| TransferType tType, | ||
| uint256 newCostBasis, | ||
| string calldata uri, | ||
| bytes calldata data | ||
| ) external returns (bytes32 newLotId); | ||
|
|
||
| function transferFrom( | ||
| bytes32 lotId, | ||
| address from, | ||
| address to, | ||
| uint256 quantity, | ||
| TransferType tType, | ||
| uint256 newCostBasis, | ||
| string calldata uri, | ||
| bytes calldata data | ||
| ) external returns (bytes32 newLotId); | ||
|
|
||
| function adjustLot( | ||
| bytes32 oldLotId, | ||
| uint256 newQuantity, | ||
| uint256 newCostBasis, | ||
| address newPaymentCurrency, | ||
| uint256 newAcquisitionDate, | ||
| string calldata newUri, | ||
| bytes calldata newData, | ||
| string calldata reason, | ||
| uint256 customId | ||
| ) external returns (bytes32 newLotId); | ||
|
|
||
| /*────────────────────────────────── | ||
| Administrative hooks (normative) | ||
| ──────────────────────────────────*/ | ||
| function pause() external; | ||
| function unpause() external; | ||
|
|
||
| function freezeAccount(address account, bool frozen) external; | ||
| function freezeLot(bytes32 lotId, bool frozen) external; | ||
|
|
||
| function forcedTransfer( | ||
| bytes32 lotId, | ||
| address from, | ||
| address to, | ||
| uint256 quantity, | ||
| string calldata reason | ||
| ) external returns (bytes32 newLotId); | ||
|
|
||
| /* Transfer-policy plug-in */ | ||
| function setTransferController(address controller) external; | ||
| function transferController() external view returns (address); | ||
|
|
||
| /* Optional merge / split helpers */ | ||
| function mergeLots(bytes32[] calldata sourceLots, string calldata uri, bytes calldata data) | ||
| external returns (bytes32 newLotId); | ||
| function splitLot(bytes32 lotId, uint256[] calldata quantities, string[] calldata uris) | ||
| external returns (bytes32[] memory newLotIds); | ||
| } | ||
| ``` | ||
|
|
||
| > Any function marked "external" **MUST** revert if the contract is paused, the caller/lot is frozen, or a transferController vetoes the action. | ||
|
|
||
| ### Tax Considerations (informative) | ||
|
|
||
| The `TransferType` enum enables implementations to capture transfer context for jurisdiction-specific tax compliance: | ||
|
|
||
| | TransferType | Purpose | Tax implications vary by jurisdiction | | ||
| | ------------- | ---------------------------------------- | ------------------------------------- | | ||
| | `INTERNAL` | Administrative transfers, splits, merges | Typically no taxable event | | ||
| | `SALE` | Market transactions for consideration | Usually triggers capital gains | | ||
| | `GIFT` | Gratuitous transfers | Donor/recipient tax treatment varies | | ||
| | `INHERITANCE` | Estate/probate transfers | Step-up rules vary by jurisdiction | | ||
| | `REWARD` | Compensation, airdrops, staking rewards | Often treated as ordinary income | | ||
|
|
||
| **Example U.S. Treatment**: `GIFT` uses carry-over basis (IRC § 1015), `INHERITANCE` gets stepped-up basis (IRC § 1014). | ||
mrosendin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| **Example UK Treatment**: Different rules under TCGA 1992. | ||
| **Example Singapore**: No capital gains tax. | ||
|
|
||
| Implementations **SHOULD** consult local tax authorities and **MAY** implement jurisdiction-specific tax calculation modules. | ||
|
|
||
| ## Rationale | ||
|
|
||
| **Administrative hooks (pause, freeze, forcedTransfer, adjustLot)** are embedded in ERC-7752 Core because issued assets **cannot satisfy real-world statutory and fiduciary duties without them.** | ||
|
|
||
| - Securities law obliges issuers and registrars to rectify errors, comply with court orders, and enforce sanctions. | ||
| - Transfer-control modules (ERC-7752-Policy) rely on a deterministic ABI to implement jurisdiction-specific rules. | ||
| - Standardizing these events enables third-party auditors, tax engines, and regulators to ingest compliance actions uniformly. | ||
|
|
||
| While purely decentralized assets may deem these functions unnecessary, they may be voluntarily disabled by renouncing the `ADMIN_ROLE` or setting `transferController = address(0)`. Omission from the ABI, however, would break composability for the majority of regulated implementations. | ||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| ERC-7752 is not backward-compatible with ERC-20/721/[ERC-1155](./eip-1155.md) because each lotId represents a semi-fungible slice with its own lineage and admin controls. | ||
mrosendin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## Security Considerations | ||
|
|
||
| - Implementers MUST secure admin roles (e.g., OpenZeppelin `AccessControl` or `AccessManager`). | ||
| - A compromised `transferController` could veto or force-transfer lots; multi-sig ownership is **RECOMMENDED**. | ||
|
|
||
| ### Conclusion | ||
|
|
||
| ERC‑7752 brings granular accounting, compliance, and administrative certainty to on‑chain representations of traditional assets—unlocking the next wave of securitised and regulated token markets. | ||
mrosendin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## Copyright | ||
|
|
||
| Copyright and related rights waived via [CC0](../LICENSE.md). | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.