docs(tip-1028): rewrite for clarity#3782
Conversation
cc7e14b to
58eccc3
Compare
Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019ddf7e-fb21-76a8-b851-e98e593519cd
58eccc3 to
5e77486
Compare
| This TIP introduces receiver-controlled inbound policies for TIP-20 tokens. Today, only token issuers (via TIP-403 / TIP-1015) decide who may move a token; receivers cannot independently filter what arrives in their balance. This TIP adds: | ||
|
|
||
| The escrow precompile stores only one keyed amount for each open blocked receipt. The rest of the receipt identity — receipt version, requested recipient, block reason, transfer-vs-mint kind, memo, timestamp, and nonce — is authenticated by the receipt witness and emitted in the blocked event. Claims may unwind to the receiver or reroute elsewhere; claim-to-receiver is an unwind, while reroutes are new spends. This TIP applies only to TIP-20 precompile flows; ordinary ERC-20 contracts remain out of scope. | ||
| 1. **Token sets** — a new TIP-403 primitive that lets any address declare which TIP-20 token addresses may credit it. | ||
| 2. **Address-level receive policies** — a per-address configuration on the TIP-403 registry that filters inbound TIP-20 credits by originator and by token, with an optional dedicated recovery contract. | ||
| 3. **An escrow precompile** — a new precompile that records a fine-grained receipt for each blocked inbound TIP-20 transfer, and lets the receiver (or its designated recovery contract) claim or reroute the funds later. | ||
|
|
||
| A blocked inbound never reverts: the funds are credited to a reserved `ESCROW_ADDRESS` inside the TIP-20 token, one receipt bucket is recorded by the escrow precompile, and a blocked-inbound event is emitted that authenticates the receipt witness. Pre-existing token-level (issuer-side) failures continue to revert exactly as today. | ||
|
|
||
| This TIP applies only to TIP-20 precompile flows; ordinary ERC-20 contracts deployed as userland code remain out of scope. |
There was a problem hiding this comment.
I dont think we need to mention token sets, other types or other types in the abstract. A high level summary should do for this section IMO. Something like:
| This TIP introduces receiver-controlled inbound policies for TIP-20 tokens. Today, only token issuers (via TIP-403 / TIP-1015) decide who may move a token; receivers cannot independently filter what arrives in their balance. This TIP adds: | |
| The escrow precompile stores only one keyed amount for each open blocked receipt. The rest of the receipt identity — receipt version, requested recipient, block reason, transfer-vs-mint kind, memo, timestamp, and nonce — is authenticated by the receipt witness and emitted in the blocked event. Claims may unwind to the receiver or reroute elsewhere; claim-to-receiver is an unwind, while reroutes are new spends. This TIP applies only to TIP-20 precompile flows; ordinary ERC-20 contracts remain out of scope. | |
| 1. **Token sets** — a new TIP-403 primitive that lets any address declare which TIP-20 token addresses may credit it. | |
| 2. **Address-level receive policies** — a per-address configuration on the TIP-403 registry that filters inbound TIP-20 credits by originator and by token, with an optional dedicated recovery contract. | |
| 3. **An escrow precompile** — a new precompile that records a fine-grained receipt for each blocked inbound TIP-20 transfer, and lets the receiver (or its designated recovery contract) claim or reroute the funds later. | |
| A blocked inbound never reverts: the funds are credited to a reserved `ESCROW_ADDRESS` inside the TIP-20 token, one receipt bucket is recorded by the escrow precompile, and a blocked-inbound event is emitted that authenticates the receipt witness. Pre-existing token-level (issuer-side) failures continue to revert exactly as today. | |
| This TIP applies only to TIP-20 precompile flows; ordinary ERC-20 contracts deployed as userland code remain out of scope. | |
| TIP-1028 extends TIP-403 with address-level receive policies and token sets, allowing a receiver to control which originators and which TIP-20 tokens may credit it. When a receive policy blocks an inbound transfer or mint, the operation still succeeds at the protocol level, but the funds are credited to `ESCROW_ADDRESS` instead of the receiver and a corresponding escrow receipt is recorded. | |
| The receiver or a designated recovery contract may later claim these escrowed funds. Claims back to the receiver act as an unwind of the original inbound, while claims to other addresses are treated as new transfers. | |
| Token-level authorization remains unchanged and continues to revert on failure. This behavior applies only to TIP-20 precompile flows; ordinary contracts and other precompiles are unaffected. |
| TIP-403 is an issuer-side authorization layer: the token issuer decides who may send, who may receive, and who may mint to whom. It cannot answer the receiver's question, *"do I want to be exposed to this counterparty or this token at all?"* | ||
|
|
||
| Plain receiver-side reverts are not a viable substitute. Once an issuer–receiver–counterparty relationship exists, a receiver that later changes a revert-based policy can break in-flight flows and recurring inbounds across the entire system. Reverting receivers also create an asymmetric DoS surface against issuers, integrations, and protocol-owned distribution paths (DEX payouts, fee distributions, AMM outputs). | ||
|
|
||
| The design choice in TIP-1028 is therefore: | ||
|
|
||
| - **Token-level / issuer-side failures keep reverting.** Issuers retain full control of the token's authorization model; nothing in their semantics changes. | ||
| - **Receiver-side failures escrow instead of reverting.** Senders, mints, DEX payouts, and fee distributions still succeed at the protocol layer; the asset is held in `ESCROW_ADDRESS` until the receiver (or its recovery contract) claims it. | ||
|
|
||
| This TIP keeps issuer-side semantics unchanged and changes only receiver-side failure handling. Token-level failures still revert; receiver-side failures are escrowed instead. The escrow representation must preserve more than an aggregate amount because TIP-1022 virtual addresses carry attribution in the literal `to` address, memo-bearing transfers carry memo data, recovery contracts may want time-based or memo-based rules, and offchain recovery flows need the original sender identity. | ||
| Escrowed funds must carry enough metadata to be programmatically recoverable. An aggregate "amount blocked per (token, receiver)" bucket cannot express TIP-1022 attribution, memo routing, originator-based recovery rules, or transfer-vs-mint provenance. Therefore, the escrow precompile stores one keyed amount per blocked inbound, with ALL the receipt identity (version, requested recipient, block reason, kind, memo, timestamp, nonce) authenticated by the receipt witness and emitted in the blocked event — keeping persistent state to a single slot per receipt while preserving full attribution offchain. |
There was a problem hiding this comment.
nit: Consider simplifying a bit. Feel free to accept or ignore the following suggestion:
| TIP-403 is an issuer-side authorization layer: the token issuer decides who may send, who may receive, and who may mint to whom. It cannot answer the receiver's question, *"do I want to be exposed to this counterparty or this token at all?"* | |
| Plain receiver-side reverts are not a viable substitute. Once an issuer–receiver–counterparty relationship exists, a receiver that later changes a revert-based policy can break in-flight flows and recurring inbounds across the entire system. Reverting receivers also create an asymmetric DoS surface against issuers, integrations, and protocol-owned distribution paths (DEX payouts, fee distributions, AMM outputs). | |
| The design choice in TIP-1028 is therefore: | |
| - **Token-level / issuer-side failures keep reverting.** Issuers retain full control of the token's authorization model; nothing in their semantics changes. | |
| - **Receiver-side failures escrow instead of reverting.** Senders, mints, DEX payouts, and fee distributions still succeed at the protocol layer; the asset is held in `ESCROW_ADDRESS` until the receiver (or its recovery contract) claims it. | |
| This TIP keeps issuer-side semantics unchanged and changes only receiver-side failure handling. Token-level failures still revert; receiver-side failures are escrowed instead. The escrow representation must preserve more than an aggregate amount because TIP-1022 virtual addresses carry attribution in the literal `to` address, memo-bearing transfers carry memo data, recovery contracts may want time-based or memo-based rules, and offchain recovery flows need the original sender identity. | |
| Escrowed funds must carry enough metadata to be programmatically recoverable. An aggregate "amount blocked per (token, receiver)" bucket cannot express TIP-1022 attribution, memo routing, originator-based recovery rules, or transfer-vs-mint provenance. Therefore, the escrow precompile stores one keyed amount per blocked inbound, with ALL the receipt identity (version, requested recipient, block reason, kind, memo, timestamp, nonce) authenticated by the receipt witness and emitted in the blocked event — keeping persistent state to a single slot per receipt while preserving full attribution offchain. | |
| TIP-403 allows token issuers to control who may use a token, but it does not give receivers control over which inbounds they accept. Some applications require receivers to restrict incoming transfers or mints based on the originator or token. | |
| A revert-based receiver policy introduces a liveness problem: once a sender relationship exists, a receiver can later change its policy and cause future transfers or mints to revert, breaking integrations and making delivery unreliable. | |
| TIP-1028 introduces receiver-side controls while preserving liveness. Instead of reverting when a receiver blocks an inbound, the transfer or mint succeeds at the protocol level and the funds are escrowed. | |
| Blocked inbounds remain recoverable and attributable, allowing receivers (or their recovery contracts) to claim funds while preserving context needed for offchain handling. |
| ## Assumptions | ||
|
|
||
| - **TIP-403 registry exists and is extensible.** Token sets and address-level receive policies are added as new state and new entrypoints on the existing TIP-403 precompile registry, alongside its current address-policy surface. This TIP does not modify the existing TIP-403 address-policy ABI. | ||
| - **TIP-1015 compound policies are unchanged.** Receiver-side address-level controls evaluate exactly one axis (originator → receiver), and never cross-validate against the issuer's `transfer_recipient` / `mint_recipient` roles. | ||
| - **TIP-1022 resolution is available at TIP-20 inbound time.** Virtual-address resolution runs before TIP-1028 receiver-side checks; failed resolution still reverts. | ||
| - **TIP-1016 gas model.** Cost analysis uses the storage-cost model from TIP-1016 (fresh slot ≈ 250k gas, hot slot update ≈ 2.9k gas, baseline TIP-20 transfer ≈ 50k gas). | ||
| - **`ESCROW_ADDRESS` is reserved at protocol level.** No TIP-20 implementation, system contract, or user may treat `ESCROW_ADDRESS` as an ordinary holder. Userland transfers and mints to it MUST revert. | ||
| - **Reward state never lives at `ESCROW_ADDRESS`.** Implementations rely on treating the escrow sink as a reward-exempt always-opted-out address; violating this assumption silently corrupts the opted-in supply for reward distribution. | ||
| - **Backwards compatibility.** Addresses with no configured receive controls behave exactly as today. The only new ambient cost on existing flows is one cold storage read of the receiver's packed receive config slot. |
There was a problem hiding this comment.
This section is a bit confusing, several of these read like core design constraints or requirements (e.g. TIP-403 extension, ESCROW_ADDRESS semantics, reward handling), rather than assumptions. It’s not clear what is truly assumed vs what this TIP enforces. Might be worth tightening this or removing.
|
|
||
| The escrow precompile and the TIP-403 registry are independent: the escrow precompile authenticates claims using the receipt witness and the receipt's snapshotted recovery contract, and does **not** read the receiver's current TIP-403 receive config when claiming. The TIP-403 registry, conversely, has no knowledge of escrowed amounts: it only answers `verifyAddressInbound(...)` and exposes the current `recoveryContract` of an address. | ||
|
|
||
| ### 1.2 End-to-End Flow Overview |
| Escrow-->>Claimer: success<br/>(emit BlockedReceiptClaimed) | ||
| ``` | ||
|
|
||
| ### 1.3 Scope |
There was a problem hiding this comment.
Would update to TIP-20 Affected Paths or similar.
| DEX internal balances are not TIP-20 wallet balances and are not subject to address-level receive checks until withdrawn back onto the TIP-20 ledger. | ||
| - `approve` / `permit` / `burn` | ||
| - fee refunds via `transfer_fee_post_tx` | ||
| - non-TIP-20 tokens deployed as ordinary contracts |
There was a problem hiding this comment.
I dont think its necessary to mention this again. We should mention that "TIP-1028 only applies to TIP-20 precompile flows; ordinary contracts and other precompiles are unaffected" once in the overview, rather than restate this throughout the doc.
| - `approve` / `permit` / `burn` | ||
| - fee refunds via `transfer_fee_post_tx` | ||
| - non-TIP-20 tokens deployed as ordinary contracts | ||
| - future recipient-bearing system-credit paths that do not identify a concrete originator |
There was a problem hiding this comment.
What is an example of this? When would there not be an originator?
There was a problem hiding this comment.
i don't have any concrete example, let's delete for the sake of simplicity
| - fee refunds via `transfer_fee_post_tx` | ||
| - non-TIP-20 tokens deployed as ordinary contracts | ||
| - future recipient-bearing system-credit paths that do not identify a concrete originator | ||
| - DEX *internal* balances. Gating only kicks in when funds are withdrawn back onto the TIP-20 ledger |
There was a problem hiding this comment.
Similar to the comment above, I think we can remove this if already specifying that TIP-1028 only applies to TIP20. This implies DEX internal balance is not included in scope.
Co-authored-by: 0xKitsune <77890308+0xKitsune@users.noreply.github.com>
Co-authored-by: 0xKitsune <77890308+0xKitsune@users.noreply.github.com>
Co-authored-by: 0xKitsune <77890308+0xKitsune@users.noreply.github.com>
Co-authored-by: 0xKitsune <77890308+0xKitsune@users.noreply.github.com>
Co-authored-by: 0xKitsune <77890308+0xKitsune@users.noreply.github.com>
Co-authored-by: 0xKitsune <77890308+0xKitsune@users.noreply.github.com>
- Simplify Abstract per suggestion (high-level summary, no component list) - Simplify Motivation per suggestion (remove design-choice bullets, streamline) - Remove Assumptions section (items were design constraints, not assumptions) - Remove redundant intro sentence in System Architecture - Rename Section 1.3 to 'TIP-20 Affected Paths' - Remove redundant scope bullets (non-TIP-20, no-originator, DEX internals) already covered by Abstract's 'only TIP-20 precompile flows' statement Amp-Thread-ID: https://ampcode.com/threads/T-019de20c-08f4-7330-8f51-c6f45b8b89fb Co-authored-by: Amp <amp@ampcode.com>
…016) Amp-Thread-ID: https://ampcode.com/threads/T-019de20c-08f4-7330-8f51-c6f45b8b89fb Co-authored-by: Amp <amp@ampcode.com>
|
closing in favor of #3791 |
rewrite for clarity