diff --git a/src/pages/docs/guide/bridge-bungee.mdx b/src/pages/docs/guide/bridge-bungee.mdx index fe3bae07..287a097d 100644 --- a/src/pages/docs/guide/bridge-bungee.mdx +++ b/src/pages/docs/guide/bridge-bungee.mdx @@ -7,6 +7,8 @@ description: Bridge tokens to and from Tempo using Bungee. Covers Bungee Link, d [Bungee](https://www.bungee.exchange/) is a cross-chain routing protocol built by the [SOCKET](https://docs.socket.tech/) team. For Tempo, the recommended integration path is Bungee Deposit: request a quote, execute the returned source-chain transaction, and track the request until Bungee delivers funds on the destination chain. +If you are bringing an existing ERC-20 to Tempo, first decide whether to preserve it as the canonical asset behind a bridge adapter or migrate issuance to native TIP-20. See [Migrate an ERC-20 to TIP-20](/docs/guide/issuance/migrate-erc20-to-tip20). + Tempo's Bungee chain ID is **`4217`**. ## How Bungee Deposit works diff --git a/src/pages/docs/guide/bridge-layerzero.mdx b/src/pages/docs/guide/bridge-layerzero.mdx index 2d4a1ad1..931f25f9 100644 --- a/src/pages/docs/guide/bridge-layerzero.mdx +++ b/src/pages/docs/guide/bridge-layerzero.mdx @@ -14,6 +14,8 @@ There are two flavors of OFT on Tempo: Both use the same underlying LayerZero endpoint on Tempo. +If you are bringing an existing ERC-20 to Tempo, first decide whether to preserve it as the canonical asset behind a bridge adapter or migrate issuance to native TIP-20. See [Migrate an ERC-20 to TIP-20](/docs/guide/issuance/migrate-erc20-to-tip20). + ## USDC.e and native USDC USDC.e is the bridged representation of USDC on Tempo. It is backed 1:1 by native USDC in Stargate liquidity infrastructure. diff --git a/src/pages/docs/guide/bridge-relay.mdx b/src/pages/docs/guide/bridge-relay.mdx index 5437ef2b..fd45e788 100644 --- a/src/pages/docs/guide/bridge-relay.mdx +++ b/src/pages/docs/guide/bridge-relay.mdx @@ -7,6 +7,8 @@ description: Bridge tokens to and from Tempo using Relay. Includes supported tok [Relay](https://relay.link/) is a cross-chain payments network powered by a solver that fills bridge requests instantly. Users deposit on the source chain and receive funds on the destination chain within seconds - no lock-and-mint or messaging protocol required. +If you are bringing an existing ERC-20 to Tempo, first decide whether to preserve it as the canonical asset behind a bridge adapter or migrate issuance to native TIP-20. See [Migrate an ERC-20 to TIP-20](/docs/guide/issuance/migrate-erc20-to-tip20). + Tempo's Relay chain ID is **`4217`**. ## Contracts on Tempo diff --git a/src/pages/docs/guide/issuance/index.mdx b/src/pages/docs/guide/issuance/index.mdx index 81fdd681..00268237 100644 --- a/src/pages/docs/guide/issuance/index.mdx +++ b/src/pages/docs/guide/issuance/index.mdx @@ -19,6 +19,13 @@ Create and manage your own stablecoin on Tempo. Launch the token first, then con title="Create a Stablecoin" /> + + >Registry: Create or select policy + end + + Note over Issuer,Token: One-time token deployment + Issuer->>Factory: createToken(name, symbol, currency, quoteToken, admin, salt, logoURI) + Factory-->>Token: Create native TIP-20 token + Factory->>Token: Grant DEFAULT_ADMIN_ROLE to Admin + + opt Compliance required + Note over Admin,Token: Attach the prepared policy before token operations + Admin->>Token: changeTransferPolicyId(policyId) + end + + Note over Admin,Operator: One-time or occasional role setup + Admin->>Token: grantRole(ISSUER_ROLE, Operator) + Admin->>Token: grantRole(PAUSE_ROLE, pause wallet) + Admin->>Token: grantRole(UNPAUSE_ROLE, unpause wallet) + + opt Compliance required + Note over PolicyAdmin,Registry: Ongoing compliance maintenance + PolicyAdmin->>Registry: Add, remove, allow, or block wallets + end + + Note over Operator,Vault: Ongoing issuance operations + Operator->>Token: mint(Vault, amount) + Token->>Token: Check ISSUER_ROLE, pause state, and supply cap + Token->>Registry: Authorize Vault as mint recipient + Token-->>Vault: TIP-20 balance increases`} /> + +`TIP20Factory.createToken` does not accept a policy ID. If compliance is required, prepare the policy first, deploy the token, and attach the policy before minting or transferring. A new TIP-20 otherwise starts on the always-allow policy. + +## 3. Complete the implementation checklist + +Before production deployment, confirm: + +- TIP-20 token is deployed through `TIP20Factory`. +- Token name, symbol, currency, quote token, and logo are correct. +- Admin address is controlled by the issuer or approved operator. +- `ISSUER_ROLE` is granted only to the intended issuer wallet, manager, or bridge adapter. +- Native role queries use `hasRole(account, role)`, and each role's administrator is confirmed with `getRoleAdmin(role)`. +- `PAUSE_ROLE` and `UNPAUSE_ROLE` are assigned. +- Supply cap is set deliberately. Every TIP-20 starts at `type(uint128).max`; prefer the lowest practical cap and increase it as issuance grows. +- Transfer policy is configured if compliance is required. +- If using a compound policy, sender, recipient, and mint-recipient checks are tested independently. +- Existing ERC-20 supply is burned, locked, or reconciled if this is a migration or bridge. +- Decimal conversion is tested. +- Mint and burn flows are tested. +- Mint tests cover the supply cap, paused state, and TIP-403 mint-recipient authorization. +- Pause tests confirm that transfers, minting, normal burns, and `burnBlocked` are blocked while role and configuration administration remain available. +- Transfer and transferFrom are tested. +- Custody and treasury wallets are funded and labeled. +- Tokenlist and explorer metadata are ready. +- The issuer has an operational runbook for role changes, pausing, minting, burning, and reconciliation. + +### Review mutable and immutable settings + +| Setting | Initial value | Can it change? | +|---|---|---| +| Token address | Deterministically derived from factory caller and salt | No | +| Name and symbol | Set at creation | No | +| Decimals | `6` | No | +| Currency | Set at creation | No | +| Supply cap | `type(uint128).max` | Yes, but not below current total supply | +| Quote token | Set at creation | Yes, through the staged quote-token update flow | +| Transfer policy | Always-allow policy (`1`) | Yes, by the TIP-20 admin | +| Logo URI | Set or left empty at creation | Yes, by the TIP-20 admin | +| Roles and role administrators | Admin receives `DEFAULT_ADMIN_ROLE` | Yes | +| Pause state | Unpaused | Yes, through `PAUSE_ROLE` and `UNPAUSE_ROLE` | + +### Choose the currency by unit-price behavior + +Set `currency` to the asset or unit of account that **one token unit is designed to remain approximately 1:1 with**. The deciding question is how the price of one token unit behaves, not whether the token generates yield. + +| Token behavior | Example | `currency` | +|---|---|---| +| USD stablecoin that remains near $1 | USDC | `USD` | +| Rebasing USD yield token whose balance grows while each unit remains near $1 | Rebasing USD yield token | `USD` | +| Wrapped token that remains 1:1 with BTC | WBTC or cbBTC | `BTC` | +| Accumulating token whose balance stays fixed while its redemption value rises | PRIME or cbETH | Its own asset identifier, such as `PRIME` or `cbETH` | +| Non-rebasing wrapper around a rebasing asset | Wrapped yield token | Its own asset identifier, unless one wrapper unit remains near 1:1 with the underlying denomination | + +Do not set `currency = USD` merely because an accumulating token is USD-backed or reports its value in USD. Classifying a TIP-20 as USD makes it eligible for infrastructure designed for near-par USD assets: + +- The [Fee AMM](/docs/protocol/fees/spec-fee-amm) converts between a user's fee token and a validator's preferred fee token at fixed near-par rates. If one token unit is worth more than $1, that conversion would misprice it. +- The [Stablecoin DEX](/docs/protocol/exchange) is market-priced, but it is designed for USD-classified pairs and currently limits orders to a ±2% range around parity. An accumulating token can move outside that range as its redemption value grows. + +Because `currency` is immutable, confirm the token's unit-price behavior before deployment. See [Currency Declaration](/docs/protocol/tip20/overview#currency-declaration) for the full selection rules. + +## 4. Inspect the current ERC-20 + +Before deploying on Tempo, identify the current token architecture. + +| Item | What to check | +|---|---| +| Token address | Current ERC-20 or proxy address | +| Implementation | Proxy implementation, if upgradeable | +| Metadata | `name`, `symbol`, `decimals` | +| Supply | `totalSupply`, current holder or treasury vault | +| Roles | Admin, minter, burner, pauser, upgrader | +| Mint flow | Who can create supply and through what contract | +| Burn flow | Who can destroy supply and whether `burnFrom` is used | +| Pause flow | Who can pause and unpause | +| Compliance | Allowlist, blocklist, sanctions, KYC, transfer hooks | +| Bridge | Source adapter, destination adapter, replay protection | +| Oracle | NAV, FX, redemption price, or yield calculation | + +## 5. Map OpenZeppelin patterns to TIP-20 + +Map by capability, not by contract name. + +| OpenZeppelin pattern | Common behavior | Tempo mapping | +|---|---|---| +| `ERC20` / `ERC20Upgradeable` | Core transfer and allowance surface | Native TIP-20 transfer and allowance surface | +| `IERC20Metadata` | `name`, `symbol`, `decimals` | Set name, symbol, and currency at TIP-20 creation | +| `AccessControl` | Role-based permissions | TIP-20 native roles | +| `Pausable` | Pause state and pause checks | `PAUSE_ROLE` and `UNPAUSE_ROLE` | +| `ERC20Burnable` | `burn` and often `burnFrom` | An `ISSUER_ROLE` holder can burn only its own TIP-20 balance; TIP-20 does not expose `burnFrom` | +| `ERC20Wrapper` | Deposit underlying ERC-20, mint wrapped ERC-20 | Use an external adapter that mints TIP-20 | +| Proxy upgrade pattern | Upgradeable implementation and storage | Not portable to TIP-20 | + +## 6. Map roles + +| Current role or control | Tempo role | +|---|---| +| `DEFAULT_ADMIN_ROLE` or owner | TIP-20 `DEFAULT_ADMIN_ROLE` for role and configuration administration; grant `ISSUER_ROLE` separately for minting | +| `MINTER_ROLE` | `ISSUER_ROLE` | +| `BURNER_ROLE` | `ISSUER_ROLE` for normal burns | +| `PAUSER_ROLE` | `PAUSE_ROLE` plus `UNPAUSE_ROLE` | +| Compliance admin | TIP-403 simple-policy admin | +| Bridge operator | Bridge adapter with `ISSUER_ROLE`; enforce limits in the adapter | +| Lifecycle manager | Manager contract with `ISSUER_ROLE`; enforce limits in the manager | + +Important: `Pausable` does not define who can pause. That authority usually comes from `AccessControl`, `Ownable`, or a custom manager. On TIP-20, pause and unpause are separate permissions. + +`ISSUER_ROLE` is not natively scoped by amount, destination, or operation. If an issuer needs those limits, its wallet, manager, or adapter must enforce them before calling TIP-20. + +## 7. Handle decimals + +OpenZeppelin ERC-20 tokens commonly use 18 decimals unless overridden. TIP-20 uses 6 decimals. + +Before migration or integration: + +- Define the conversion rule. +- Decide how to handle dust. +- Update bridge and migration scripts. +- Update reporting and accounting. +- Confirm wallet and explorer display. +- Test mint, burn, transfer, and reconciliation in smallest units. + +Example: + +```text +1.000000 TIP-20 unit = 1 token +1 TIP-20 token = 1,000,000 base units +``` + +## 8. Move custom logic outside the token + +TIP-20 is native. Do not port custom ERC-20 methods into the token. + +| Current custom logic | Tempo location | +|---|---| +| Transfer hooks | TIP-403 policy, maintained by an issuer compliance sync service | +| Allowlist or blocklist | TIP-403 simple or compound policy | +| Sanctions checks | TIP-403 policy, maintained by an issuer compliance sync service | +| Subscription flow | Lifecycle manager | +| Redemption flow | Lifecycle manager | +| Fee logic | Lifecycle manager | +| Rate limits | Lifecycle manager | +| Oracle pricing | External oracle or manager-facing oracle | +| Bridge accounting | Bridge adapter | + +## 9. Map compliance hooks to TIP-403 + +Many issuer ERC-20s enforce compliance inside token transfer hooks such as `_beforeTokenTransfer` or `_update`. + +Common checks include: + +- allowlisted sender +- allowlisted recipient +- blocked sender or recipient +- sanctions screening +- KYC or identity registry membership +- transfer-agent or operator eligibility + +On Tempo, keep the token native and move transfer eligibility into TIP-403 policy. + +A TIP-20 token points to one `transferPolicyId`. Do not model compliance as attaching both an allowlist policy and a blocklist policy directly to the token. Use a simple whitelist or blacklist when the same rule applies to transfer senders, transfer recipients, and mint recipients. Use a compound policy when those three rules need to differ. + +### Simple vs compound policies + +| Policy type | Use when | +|---|---| +| Simple whitelist | The same allowlist applies to transfer-sender, transfer-recipient, and mint-recipient checks | +| Simple blacklist | The same blocklist applies to transfer-sender, transfer-recipient, and mint-recipient checks | +| Compound policy | Sender, recipient, and mint-recipient rules need to differ | + +For simple whitelist or blacklist policies, the flow is shorter: create the simple policy, update its wallet entries, and set the token's `transferPolicyId` to that simple policy ID. Use a compound policy only when sender, recipient, and mint-recipient rules need to differ. + +A compound policy references three simple policies: + +| Compound policy component | Used for | +|---|---| +| `senderPolicyId` | Transfer senders and blocked-account burn eligibility | +| `recipientPolicyId` | Transfer recipients | +| `mintRecipientPolicyId` | Mint recipients | + +For compound policies, the TIP-20 admin attaches the compound policy initially: + +```text +TIP20.changeTransferPolicyId(compoundPolicyId) +``` + +The TIP-20 admin can later change `transferPolicyId` to another policy. Day to day, the issuer updates the underlying simple policies as wallets become allowed, blocked, or ineligible. + +### Separate policy and token responsibilities + +Each TIP-403 simple policy has one admin address. A compliance sync service must operate through that address; the service is not a parallel native authority. + +| Responsibility | Owner | +|---|---| +| Select which policy the token uses | TIP-20 admin | +| Maintain allowlist, blocklist, KYC, or sanctions entries | Simple-policy admin address, which may be operated by a compliance sync service | +| Mint and burn normal supply | Issuer wallet, lifecycle manager, or bridge adapter with `ISSUER_ROLE` | +| Pause or unpause token operations | `PAUSE_ROLE` / `UNPAUSE_ROLE` holders | + +### Follow the compliance flow + +>Registry: Create sender policy + Updater->>Registry: Create recipient policy + Updater->>Registry: Create mint recipient policy + Updater->>Registry: Create compound policy(senderPolicyId, recipientPolicyId, mintRecipientPolicyId) + + Note over Admin,Token: One-time policy attachment + Admin->>Token: changeTransferPolicyId(compoundPolicyId) + + Note over Compliance,Registry: Ongoing compliance maintenance + Compliance->>Updater: Wallet eligibility changes + Updater->>Registry: Update simple policy entries + + Note over Sender,Recipient: Ongoing transfer checks + Sender->>Token: transfer(Recipient, amount) + Token->>Registry: Check senderPolicyId for Sender + Token->>Registry: Check recipientPolicyId for Recipient + Registry-->>Token: allow or reject + Token-->>Recipient: Balance increases if allowed + + Note over Issuer,Recipient: Ongoing mint checks + Issuer->>Token: mint(Recipient, amount) + Token->>Token: Check ISSUER_ROLE, not paused, and totalSupply + amount <= supplyCap + Token->>Registry: Check mintRecipientPolicyId for Recipient + Registry-->>Token: allow or reject`} /> + +Minting succeeds only when the caller holds `ISSUER_ROLE`, the token is not paused, the resulting supply does not exceed the supply cap, and TIP-403 authorizes the mint recipient. + +The recipient can also configure an account-level receive policy, which the issuer does not control. If that policy blocks a transfer or mint, the call succeeds but delivery is redirected to `ReceivePolicyGuard` as a claimable receipt. Issuers should monitor this outcome when reconciling mints and transfers. See [Receive policies](/docs/protocol/tip403/receive-policies). + +### Map ERC-20 compliance patterns + +| Current ERC-20 compliance pattern | Tempo mapping | +|---|---| +| `_beforeTokenTransfer` allowlist check | Simple whitelist or compound policy component | +| `_update` allowlist check | Simple whitelist or compound policy component | +| Blocklist check | Simple blacklist or compound policy component | +| Sanctions-list contract | Simple blacklist maintained by issuer compliance sync service | +| Identity or KYC registry | Simple whitelist maintained by issuer compliance sync service | +| Different sender and recipient rules | Compound policy | +| Mint recipient eligibility | Compound policy `mintRecipientPolicyId` | +| Operator or spender compliance | Lifecycle manager or bridge adapter check | +| Compliance admin | TIP-403 simple-policy admin | + +## 10. Avoid incompatible ERC-20 assumptions + +- Do not deploy a custom ERC-20 when the target is native TIP-20. +- Do not assume proxy storage or upgrade logic carries over. +- Do not grant issuer permissions to broad operational wallets without a reason. +- Use TIP-20's native role-query methods; do not assume OpenZeppelin argument ordering. +- Do not ignore 18-to-6 decimal conversion. +- Do not put compliance or subscription logic inside the token. + +## 11. Consult the operational reference + +### Query and administer native roles + +TIP-20 exposes native methods for querying and administering roles: + +```solidity +hasRole(address account, bytes32 role) +getRoleAdmin(bytes32 role) +setRoleAdmin(bytes32 role, bytes32 adminRole) +``` + +Unlike OpenZeppelin's `hasRole(role, account)`, TIP-20's `hasRole` takes the account first: `hasRole(account, role)`. + +`DEFAULT_ADMIN_ROLE` is `bytes32(0)`. Multiple accounts may hold it. It governs role and token administration, but it does not itself permit minting; an account or contract must hold `ISSUER_ROLE` to mint. + +Initially, every role is governed by `DEFAULT_ADMIN_ROLE`. `setRoleAdmin(role, adminRole)` can later change which role is authorized to grant and revoke that role. Use `getRoleAdmin(role)` to inspect the current relationship before changing role assignments. + +### Apply pause semantics + +Since the T3 upgrade, a paused TIP-20 blocks: + +- transfers +- minting +- normal burns +- `burnBlocked` + +Administration remains available while paused. + +`transferFeePreTx` and `transferFeePostTx` are system-only entrypoints used by Tempo protocol precompiles, not issuer-callable operations. As a protocol invariant, `transferFeePreTx` respects pause while `transferFeePostTx` remains available so fee settlement can complete. + +## Related references + +- [OpenZeppelin ERC-20 contracts](https://docs.openzeppelin.com/contracts/5.x/api/token/erc20) +- [OpenZeppelin access control](https://docs.openzeppelin.com/contracts/5.x/access-control) +- [TIP-20 tokens specification](/docs/protocol/tip20/spec) \ No newline at end of file diff --git a/src/pages/docs/quickstart/integrate-tempo.mdx b/src/pages/docs/quickstart/integrate-tempo.mdx index 4a1f6c2a..c387d6e9 100644 --- a/src/pages/docs/quickstart/integrate-tempo.mdx +++ b/src/pages/docs/quickstart/integrate-tempo.mdx @@ -67,6 +67,12 @@ Tempo is EVM-compatible and targets the **Osaka** EVM hard fork. Most Ethereum t ## Bridges and Ecosystem +