-
Notifications
You must be signed in to change notification settings - Fork 830
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.
Open
Changes from 34 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,391 @@ | ||
| --- | ||
| eip: 7752 | ||
| title: Lot-based Token | ||
| description: A token treating each acquisition (lot) as an on-chain record with cost-basis and lineage. | ||
| 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 | ||
| --- | ||
|
|
||
| ## Abstract | ||
|
|
||
| [ERC-7752](./eip-7752.md) defines a **lot-based** token model where each issuance or acquisition event creates a unique **lot** (`lotId`) with its own quantity, basis, acquisition date, and lineage. This standard provides core lot management functions while enabling modular extensions for transfer controls, compliance, and administrative features. | ||
|
|
||
| ## 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—require granular tracking: | ||
|
|
||
| - **Distinct lots** with individual basis and acquisition dates | ||
| - **Lineage tracking** for audit trails and compliance | ||
| - **Modular architecture** supporting jurisdiction-specific extensions | ||
| - **Deterministic events** for tax engines and regulatory reporting | ||
|
|
||
| ERC-7752 provides the foundational interface for lot-based tokens while enabling specialized implementations through modular extensions. | ||
|
|
||
| ## Specification | ||
|
|
||
| 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. | ||
|
|
||
| ### Core Interface | ||
|
|
||
| ```solidity | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.20; | ||
|
|
||
| /** | ||
| * @title ERC-7752 Lot Token (Core Interface) | ||
| * @notice Core lot management | ||
| */ | ||
| interface IERC7752 { | ||
mrosendin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /*────────────────────────────────── | ||
| Data Structures | ||
| ──────────────────────────────────*/ | ||
|
|
||
| enum TransferType { | ||
| INTERNAL, // Administrative transfers, splits, merges | ||
| SALE, // Market transactions for consideration | ||
| GIFT, // Gratuitous transfers | ||
| INHERITANCE, // Estate/probate transfers | ||
| INCOME // Compensation, dividends, staking rewards, airdrops | ||
| } | ||
|
|
||
| struct Lot { | ||
| bytes32 parentLotId; // Parent lot for lineage tracking | ||
| uint256 quantity; // Amount in this lot | ||
| address currency; // Currency used for acquisition | ||
| uint256 basis; // Total cost basis (in currency) | ||
| uint256 acquisitionDate; // Unix timestamp of original acquisition | ||
| uint256 lastUpdate; // Unix timestamp of last modification | ||
| address owner; // Current owner address | ||
| TransferType transferType; // How this lot was acquired | ||
| string uri; // Metadata URI | ||
| bytes data; // Additional lot-specific data | ||
| } | ||
|
|
||
| /*────────────────────────────────── | ||
| Events | ||
| ──────────────────────────────────*/ | ||
|
|
||
| /** | ||
| * @notice Emitted when a new lot is created | ||
| */ | ||
| event LotCreated( | ||
| address indexed owner, | ||
| bytes32 indexed lotId, | ||
| bytes32 indexed parentLotId, | ||
| uint256 quantity, | ||
| address currency, | ||
| uint256 basis, | ||
| uint256 acquisitionDate, | ||
| TransferType transferType, | ||
| string uri, | ||
| bytes data | ||
| ); | ||
|
|
||
| /** | ||
| * @notice Emitted when a lot is transferred (full or partial) | ||
| */ | ||
| event LotTransferred( | ||
| bytes32 indexed lotId, | ||
| bytes32 indexed newLotId, | ||
| address indexed from, | ||
| address indexed to, | ||
| uint256 quantity, | ||
| uint256 newBasis, | ||
| TransferType transferType, | ||
| string uri, | ||
| bytes data | ||
| ); | ||
|
|
||
| /** | ||
| * @notice Emitted when lots are merged into a new lot | ||
| */ | ||
| event LotsMerged( | ||
| bytes32[] sourceLotIds, | ||
| bytes32 indexed newLotId, | ||
| address indexed owner, | ||
| uint256 totalQuantity, | ||
| string uri, | ||
| bytes data | ||
| ); | ||
|
|
||
| /*────────────────────────────────── | ||
| Core Read Functions | ||
| ──────────────────────────────────*/ | ||
|
|
||
| /** | ||
| * @notice Returns the token name | ||
| */ | ||
| function name() external view returns (string memory); | ||
|
|
||
| /** | ||
| * @notice Returns the token symbol | ||
| */ | ||
| function symbol() external view returns (string memory); | ||
|
|
||
| /** | ||
| * @notice Returns lot details for a given lot ID | ||
| * @param lotId The lot identifier | ||
| * @return lot The lot data structure | ||
| */ | ||
| function getLot(bytes32 lotId) external view returns (Lot memory lot); | ||
|
|
||
| /** | ||
| * @notice Returns all lot IDs owned by an address | ||
| * @param owner The owner address | ||
| * @return lotIds Array of lot identifiers | ||
| */ | ||
| function getLotsOf(address owner) external view returns (bytes32[] memory lotIds); | ||
|
|
||
| /** | ||
| * @notice Returns total quantity across all lots for an owner | ||
| * @param owner The owner address | ||
| * @return totalQuantity Sum of quantities across all lots | ||
| */ | ||
| function balanceOf(address owner) external view returns (uint256 totalQuantity); | ||
|
|
||
| /** | ||
| * @notice Checks if a lot ID exists and is valid | ||
| * @param lotId The lot identifier | ||
| * @return exists True if lot exists | ||
| */ | ||
| function lotExists(bytes32 lotId) external view returns (bool exists); | ||
|
|
||
| /*────────────────────────────────── | ||
| Core Lot Operations | ||
| ──────────────────────────────────*/ | ||
|
|
||
| /** | ||
| * @notice Creates a new lot | ||
| * @param owner Initial owner of the lot | ||
| * @param quantity Amount in the lot | ||
| * @param currency Currency used for acquisition (address(0) for native) | ||
| * @param basis Total cost basis in currency | ||
| * @param acquisitionDate Unix timestamp of acquisition | ||
| * @param transferType How this lot was acquired | ||
| * @param uri Metadata URI for the lot | ||
| * @param data Additional lot-specific data | ||
| * @return lotId The created lot identifier | ||
| */ | ||
| function createLot( | ||
| address owner, | ||
| uint256 quantity, | ||
| address currency, | ||
| uint256 basis, | ||
| uint256 acquisitionDate, | ||
| TransferType transferType, | ||
| string calldata uri, | ||
| bytes calldata data | ||
| ) external returns (bytes32 lotId); | ||
|
|
||
| /** | ||
| * @notice Transfers full or partial lot to another address | ||
| * @param lotId Source lot identifier | ||
| * @param to Recipient address | ||
| * @param quantity Amount to transfer (must be ≤ lot quantity) | ||
| * @param transferType Type of transfer for the new lot | ||
| * @param newBasis Cost basis for the transferred portion | ||
| * @param uri Metadata URI for the new lot | ||
| * @param data Additional data for the new lot | ||
| * @return newLotId Identifier of the newly created lot for recipient | ||
| */ | ||
| function transfer( | ||
| bytes32 lotId, | ||
| address to, | ||
| uint256 quantity, | ||
| TransferType transferType, | ||
| uint256 newBasis, | ||
| string calldata uri, | ||
| bytes calldata data | ||
| ) external returns (bytes32 newLotId); | ||
|
|
||
| /** | ||
| * @notice Transfers lot on behalf of owner (requires approval) | ||
| * @param lotId Source lot identifier | ||
| * @param from Owner address | ||
| * @param to Recipient address | ||
| * @param quantity Amount to transfer | ||
| * @param transferType Type of transfer | ||
| * @param newBasis Cost basis for transferred portion | ||
| * @param uri Metadata URI for new lot | ||
| * @param data Additional data for new lot | ||
| * @return newLotId Identifier of newly created lot | ||
| */ | ||
| function transferFrom( | ||
| bytes32 lotId, | ||
| address from, | ||
| address to, | ||
| uint256 quantity, | ||
| TransferType transferType, | ||
| uint256 newBasis, | ||
| string calldata uri, | ||
| bytes calldata data | ||
| ) external returns (bytes32 newLotId); | ||
|
|
||
| /** | ||
| * @notice Merges multiple lots into a single new lot | ||
| * @param sourceLotIds Array of lot identifiers to merge (must have same owner) | ||
| * @param uri Metadata URI for merged lot | ||
| * @param data Additional data for merged lot | ||
| * @return newLotId Identifier of the merged lot | ||
| */ | ||
| function mergeLots( | ||
| bytes32[] calldata sourceLotIds, | ||
| string calldata uri, | ||
| bytes calldata data | ||
| ) external returns (bytes32 newLotId); | ||
|
|
||
| /*────────────────────────────────── | ||
| Approval System | ||
| ──────────────────────────────────*/ | ||
|
|
||
| /** | ||
| * @notice Approve an operator for a specific lot | ||
| * @param lotId The lot identifier | ||
| * @param operator Address to approve | ||
| * @param approved True to approve, false to revoke | ||
| */ | ||
| function approveLot(bytes32 lotId, address operator, bool approved) external; | ||
|
|
||
| /** | ||
| * @notice Approve an operator for all lots owned by caller | ||
| * @param operator Address to approve | ||
| * @param approved True to approve, false to revoke | ||
| */ | ||
| function setApprovalForAll(address operator, bool approved) external; | ||
|
|
||
| /** | ||
| * @notice Check if operator is approved for a specific lot | ||
| * @param lotId The lot identifier | ||
| * @param operator Address to check | ||
| * @return approved True if approved | ||
| */ | ||
| function isApprovedForLot(bytes32 lotId, address operator) external view returns (bool approved); | ||
|
|
||
| /** | ||
| * @notice Check if operator is approved for all lots of an owner | ||
| * @param owner The owner address | ||
| * @param operator Address to check | ||
| * @return approved True if approved for all | ||
| */ | ||
| function isApprovedForAll(address owner, address operator) external view returns (bool approved); | ||
| } | ||
| ``` | ||
|
|
||
| ### Lot ID Generation | ||
|
|
||
| Implementations MUST generate lot IDs deterministically to ensure uniqueness and prevent collisions: | ||
|
|
||
| ```solidity | ||
| function _generateLotId( | ||
| address owner, | ||
| uint256 quantity, | ||
| uint256 blockNumber, | ||
| uint256 nonce | ||
| ) internal pure returns (bytes32) { | ||
| return keccak256(abi.encodePacked(owner, quantity, blockNumber, nonce)); | ||
| } | ||
| ``` | ||
mrosendin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Transfer Type Usage | ||
|
|
||
| The `TransferType` enum enables implementations to capture transfer context for compliance and tax purposes: | ||
|
|
||
| - `INTERNAL`: Administrative transfers, splits, merges (typically no taxable event) | ||
| - `SALE`: Market transactions for consideration (usually triggers capital gains) | ||
| - `GIFT`: Gratuitous transfers (carry-over basis treatment varies by jurisdiction) | ||
| - `INHERITANCE`: Estate transfers (step-up basis rules vary by jurisdiction) | ||
| - `INCOME`: Compensation, dividends, staking rewards, airdrops (usually ordinary income) | ||
|
|
||
| Implementations MAY use this information for automated compliance reporting but SHOULD consult applicable regulations. | ||
|
|
||
| ## Rationale | ||
|
|
||
| ### Why Lot-Based Architecture? | ||
|
|
||
| Traditional fungible tokens aggregate all holdings into a single balance, losing crucial information needed for: | ||
|
|
||
| - **Tax compliance**: Basis tracking for capital gains calculations | ||
| - **Regulatory reporting**: Audit trails for compliance officers | ||
| - **Corporate actions**: Proper handling of stock splits, dividends, vesting | ||
| - **Estate planning**: Clear lineage for inheritance and probate | ||
|
|
||
| ### Core vs Extensions | ||
|
|
||
| ERC-7752 Core focuses on essential lot operations while enabling modular extensions: | ||
|
|
||
| **Core Standard (ERC-7752):** | ||
|
|
||
| - Lot creation, transfer, merge operations | ||
| - Basic approval system | ||
| - Essential events for tracking | ||
| - No administrative controls or transfer restrictions | ||
|
|
||
| **Potential Extensions:** | ||
|
|
||
| - Lot splitting functionality | ||
| - Transfer controls and compliance checks | ||
| - Pause/freeze functionality | ||
| - Forced transfers and administrative overrides | ||
| - Multi-signature requirements | ||
| - Time-based restrictions | ||
| - Custom lot ID functionality (for legacy systems) | ||
|
|
||
| This separation allows: | ||
|
|
||
| - Simple implementations for basic use cases | ||
| - Complex compliance layers when needed | ||
| - Clear upgrade paths | ||
| - Regulatory flexibility | ||
|
|
||
| ### Event Design | ||
|
|
||
| Events are designed for deterministic compliance reporting: | ||
|
|
||
| - All lot state changes emit structured events | ||
| - Transfer types enable automated tax categorization | ||
| - Lineage tracking supports audit requirements | ||
| - Consistent event signatures across implementations | ||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| ERC-7752 is not backward-compatible with [ERC-20](./eip-20.md), [ERC-721](./eip-721.md), or [ERC-1155](./eip-1155.md) because: | ||
|
|
||
| - Each `lotId` represents a semi-fungible slice with unique properties | ||
| - Transfer operations create new lots rather than moving balances | ||
| - Approval system operates on lot-level rather than token-level | ||
|
|
||
| Implementations MAY provide compatibility layers but this is not required by the standard. | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| ### Lot ID Uniqueness | ||
|
|
||
| Implementations MUST ensure lot IDs are unique across the entire token contract to prevent: | ||
|
|
||
| - Accidental overwrites of existing lots | ||
| - Unauthorized access to lot data | ||
| - Inconsistent state between lot records | ||
|
|
||
| ### Approval Management | ||
|
|
||
| The two-tier approval system (lot-specific and owner-wide) requires careful implementation: | ||
|
|
||
| - Lot-specific approvals SHOULD take precedence over global approvals | ||
| - Approval checks MUST occur before any transfer operation | ||
| - Approvals SHOULD be revoked when lots are transferred or modified | ||
|
|
||
| ### Arithmetic Safety | ||
|
|
||
| Lot operations involving quantities and basis calculations MUST: | ||
|
|
||
| - Check for integer overflow/underflow | ||
| - Validate that split quantities sum to original lot quantity | ||
| - Ensure basis calculations maintain precision | ||
|
|
||
| ## 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.