diff --git a/docs/learn/migrate/migrate-erc1155-to-lsp7-lsp8.md b/docs/learn/migrate/migrate-erc1155-to-lsp7-lsp8.md new file mode 100644 index 0000000000..30821adfef --- /dev/null +++ b/docs/learn/migrate/migrate-erc1155-to-lsp7-lsp8.md @@ -0,0 +1,66 @@ +--- +sidebar_label: 'πŸŽ’ ERC1155 to LSP7 + LSP8' +sidebar_position: 4 +description: 'Step-by-step guide to migrating an ERC1155 multi-asset contract to LSP7 and LSP8 on LUKSO by splitting the collection along fungible vs identifiable shape.' +--- + +# πŸŽ’ Migrate ERC1155 to LSP7 + LSP8 + +ERC1155 packs fungible and identifiable token IDs into one contract, with the type signaled by convention in the ID bits. Migrating to LUKSO splits that surface along asset semantics instead: fungible IDs become an [**LSP7**](../../standards/tokens/LSP7-Digital-Asset.md) contract, identifiable IDs become an [**LSP8**](../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) contract. Both dispatch transfer notifications through the same [LSP1](../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver(typeId, data)` hook β€” receivers branch on `typeId`, not on a per-collection ID-bit convention. + +:::info Estimate +Roughly a week for a typical game or edition contract. Effort scales with how much of your existing code assumed the ERC1155 type-bit decoding convention rather than reading semantics from the standard itself. +::: + +## When to migrate + +Migrate when downstream code needs to dispatch on asset shape β€” fungible vs. identifiable β€” by interface, without a per-collection decoding rule. Keep ERC1155 when atomic cross-ID batch transfers are a primitive your protocol depends on, or when per-ID semantics are baked into your contract's state machine in a way that resists splitting. + +## Step 1 β€” classify every token ID + +For each token ID (or ID range) in the existing ERC1155 contract, decide: is it **fungible** (interchangeable units, summable) or **identifiable** (each unit unique)? Fungible β†’ LSP7. Identifiable β†’ LSP8. + +:::warning Each distinct fungible ID normally needs its own LSP7 contract +LSP7 has one `balanceOf(address)` per contract β€” there's no token-ID dimension to a balance. If ID 1 (5 units) and ID 2 (7 units) were two _different_ fungible assets in the ERC1155 contract, minting both into the same LSP7 contract collapses them into one indistinguishable balance of 12. Give each distinct fungible asset its own LSP7 deployment unless merging them into a single balance is actually what you want. A semi-fungible edition (e.g. 100 prints of the same piece, one ID) is the case where a single LSP7 contract with `decimals` set to `0` is correct β€” it was one fungible asset to begin with. +::: + +Some ERC1155 collections signal fungible-vs-unique by convention in the high bits of the token ID β€” that's one pattern some contracts use, not a property ERC1155 itself defines. Don't assume it applies to a given contract without checking its actual minting logic. + +## Step 2 β€” deploy the contracts + +One LSP7 contract per distinct fungible asset in the old collection, one LSP8 contract for the identifiable inventory (a single LSP8 contract can hold many unique token IDs, since LSP8 balances are already per-ID). [LSP4](../../standards/tokens/LSP4-Digital-Asset-Metadata.md) metadata lives independently on each. + +## Step 3 β€” port the holders + +Snapshot ERC1155 balances per token ID, then cut the old contract off from further transfers **before or atomically with** minting the new balances β€” see Step 5 for how, since minting from a snapshot that the old contract can still transfer against leaves both the old and new balances simultaneously spendable. For each fungible ID, mint the equivalent amount into that ID's dedicated LSP7 contract β€” don't combine amounts from different fungible IDs into one contract's balance. For LSP8 IDs, mint `bytes32`-encoded token IDs into the LSP8 contract. This is typically one coordinated mint script per new contract, sometimes merkle-claimed for large holder sets. + +## Step 4 β€” port the integrations + +Every place that implemented `IERC1155Receiver` needs [LSP1](../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver` support instead. The `typeId` on each call lets a receiver branch on "is this an LSP7 transfer?" vs. "is this an LSP8 transfer?" β€” one hook function handles both, rather than separate single/batch receiver interfaces. + +## Step 5 β€” cut over from the old contract + +Minting new balances from a snapshot only works if the old contract can no longer move value after that snapshot β€” otherwise a holder can still transfer, sell, or double-spend the old balance after already receiving the new one. **ERC-1155 defines no standard pause function**, so most existing deployments have no built-in way to freeze transfers; check your specific contract before assuming this step is available. Pick a cutover that matches what your contract actually supports: + +- **Atomic pause + burn** β€” if your contract does have a pause/freeze modifier (common on OpenZeppelin-based deployments that added `Pausable`), pause it in the same transaction or block as the final snapshot, so nothing can transfer after the snapshot but before the new contracts go live. +- **Escrow-and-claim** β€” holders deposit their ERC1155 balance into an escrow or burn contract in exchange for the new LSP7/LSP8 tokens. This works even without a pause function, since it only relies on the standard `safeTransferFrom`, not owner-level control. + +A delay-and-reconcile approach without one of the above isn't a fix β€” catching transfers that happened _during_ a window does nothing to stop the old balances from moving again _after_ the new claims go live, so both representations stay permanently spendable, not just spendable during the window. Whichever method you use, the old contract should end up paused or drained into escrow β€” a persistent, on-chain cutoff β€” not just watched during a delay period. Keep the contract deployed for reference; don't redeploy over it. + +## Gotchas + +- **Snapshot-then-mint without a hard cutoff is a double-spend risk** β€” if the old ERC1155 contract can still be transferred after the balances used for minting were read, a holder can spend both the old and new balance. Confirm your contract actually has a pause function before planning around one, and use escrow-and-claim if it doesn't (see Step 5) β€” a delay period alone isn't replay protection. +- Multiple contracts instead of one β€” one LSP7 deployment per distinct fungible asset, plus one LSP8 deployment for the identifiable inventory. Deployment cost and indexing surface scale with how many distinct fungible IDs the old contract actually held. +- Batch transfers are now per-standard and per-contract, not cross-standard β€” a batch can't mix LSP7 and LSP8 items, or items from two different LSP7 contracts, in one call. +- Holders with mixed ERC1155 IDs need a coordinated mint across every new contract that inherits part of their balance. +- Marketplaces that supported your ERC1155 contract won't automatically pick up the new LSP7 + LSP8 contracts β€” plan for marketplace re-listing. + +## Verify the migration + +- Every ERC1155 token ID is mapped to either an LSP7 amount (in that asset's own contract) or an LSP8 token ID β€” no two distinct fungible assets share one LSP7 balance. +- The old contract is actually unable to honor transfers against re-minted balances β€” paused or escrowed with a persistent on-chain cutoff, not just "planned to be ignored." +- Holder balances are reproduced identically across every new contract. +- Batch transfer behavior is tested per standard. +- LSP1 receivers correctly handle both LSP7 and LSP8 `typeId`s. + +**Related reading:** [ERC1155 vs LSP7 + LSP8](../why-lukso/compare/erc1155-vs-lsp7-lsp8.md) Β· [ERC1155's complexity problem](../why-lukso/problems/erc1155-complexity.md) diff --git a/docs/learn/migrate/migrate-safe-to-universal-profile.md b/docs/learn/migrate/migrate-safe-to-universal-profile.md new file mode 100644 index 0000000000..4da8f96cd0 --- /dev/null +++ b/docs/learn/migrate/migrate-safe-to-universal-profile.md @@ -0,0 +1,63 @@ +--- +sidebar_label: 'πŸ” Safe to Universal Profile' +sidebar_position: 5 +description: 'Step-by-step guide to migrating custody from a Gnosis Safe multisig to a LUKSO Universal Profile: mapping signers to LSP6 controller permissions.' +--- + +# πŸ” Migrate from Safe to Universal Profile + +Moving custody from a Safe (Gnosis Safe) multisig to a [**Universal Profile**](../universal-profile/metadata/read-profile-data.md) maps the multisig-signer model onto [LSP6 Key Manager](../../standards/access-control/lsp6-key-manager.md) controllers. Each Safe owner becomes an LSP6 controller address with its own permission bitfield (`CALL`, `SETDATA`, `TRANSFERVALUE`, `ADDCONTROLLER`, `SIGN`, and more) β€” narrower and more expressive than a flat multisig owner list. + +:::info Estimate +Roughly a day for a small Safe. Longer if the Safe custodies many distinct asset types, since each needs its own transfer step. +::: + +## When to migrate + +Migrate when per-controller permission scoping in a standard vocabulary β€” the LSP6 bitfield plus `AllowedCalls` and `AllowedERC725YDataKeys` β€” profile-native metadata storage, or [LSP25](../../standards/accounts/lsp25-execute-relay-call.md) relay execution through the Key Manager (no bundler or paymaster contract needed) is what you need. Stay on Safe when an m-of-n multisig threshold is the exact primitive your product encodes β€” that pattern doesn't map one-to-one onto LSP6 and needs an explicit recovery-contract layer to express instead. + +## Step 1 β€” model the Safe as permissions + +Safe's "3 of 5" threshold doesn't exist directly in LSP6 β€” controllers are individual, not aggregated by a vote. Two patterns work: + +- **Recovery contract** β€” deploy a contract that enforces the threshold itself, and register that contract as a controller holding both `ADDCONTROLLER` and `EDITPERMISSIONS` on the profile. Both are required: `ADDCONTROLLER` lets it install a brand-new replacement controller (the one that's never held permissions before β€” the whole point of recovery), while `EDITPERMISSIONS` alone only lets it edit or remove a controller that already has some permission entry. Day-to-day controllers handle daily operations; the recovery contract handles ownership-level changes. +- **Single day-to-day controller + cold multisig** β€” flatten daily operations to one controller, and keep the Safe (or a new threshold contract) as the cold recovery layer behind it. + +## Step 2 β€” deploy the Universal Profile + +Deploy with the standard `lsp-factory.js` (or equivalent) deployment script. Set [LSP3](../../standards/metadata/lsp3-profile-metadata.md) profile metadata, then add controllers per the design from Step 1. + +## Step 3 β€” transfer assets + +For each asset class, the Safe is always the one initiating the transfer via `Safe.execTransaction`: + +- **Native LYX** β€” `Safe.execTransaction(profileAddress, value, "0x", ...)`, sending value directly to the profile's address. There's no separate call needed on the profile side to receive a plain LYX transfer. +- **ERC20 tokens moving to LSP7** β€” the Safe calls `token.transfer(profile, balance)` on the old ERC20 contract (unchanged ERC20 syntax) if you're sweeping the legacy token, or the LSP7 contract's own `transfer(from, to, amount, force, data)` if you're moving an already-migrated LSP7 balance β€” LSP7's `transfer` is a 5-argument function, not ERC20's 2-argument one. +- **NFTs** β€” `token.safeTransferFrom(safe, profile, id)` for existing ERC721 assets works as-is between EOAs and contracts implementing `onERC721Received`, but a default Universal Profile does **not** implement `onERC721Received` natively. Before sending an ERC721 NFT with `safeTransferFrom`, register LSP17's [`OnERC721ReceivedExtension`](../../contracts/contracts/LSP17Extensions/OnERC721ReceivedExtension.md) on the profile for that selector, or use `transferFrom` (the non-safe variant) instead. NFTs already migrated to LSP8 move with `transferBatch(...)` or the LSP8 `transfer(from, to, tokenId, force, data)` call, which works against a Universal Profile with no extension needed. + +If the Safe holds many distinct assets, write a sweep contract that batches the outbound transfers instead of sending them one by one. + +## Step 4 β€” update integrations + +Identify every protocol that references the Safe's address directly: vesting contracts, DAO memberships, subscriptions, allowance grants. Addresses don't migrate on their own β€” every external reference needs to be re-pointed to the new profile address. + +## Step 5 β€” sunset the Safe + +Once nothing material remains in the Safe, sweep out any remaining gas dust and treat it as historical. Leave the contract deployed β€” destroying multisig contracts is unsupported and risky. + +## Gotchas + +- Multisig threshold semantics don't map one-to-one to LSP6 β€” model it as a recovery-controller contract that enforces the threshold, then register that contract as an LSP6 controller with both `ADDCONTROLLER` and `EDITPERMISSIONS` (installing a never-before-permissioned replacement controller needs `ADDCONTROLLER`; `EDITPERMISSIONS` alone isn't enough). +- Asset transfer is many separate transactions unless the Safe owns assets through a sweep contract that batches outbound moves. +- Anything connected to the Safe's address β€” vesting schedules, allowance grants, on-chain memberships β€” needs to be re-pointed to the new profile address; addresses don't migrate, only the references you update do. +- Safe modules have their own permission shape; LSP6 controllers plus [LSP17](../../standards/accounts/lsp17-contract-extension.md) extensions are the closest LSP-side equivalents. Map each module to its closest LSP primitive deliberately rather than assuming a 1:1 translation. + +## Verify the migration + +- All assets transferred to the Universal Profile. +- Old Safe emptied of material assets β€” Safe has no built-in pause function, so "sunset" means the Safe holds nothing worth protecting anymore, not that it's disabled on-chain. +- Controllers and permissions configured on the new profile. +- Recovery policy in place, with at least one cold controller holding both `ADDCONTROLLER` and `EDITPERMISSIONS`. +- Off-chain integrations updated to the new address. + +**Related reading:** [EOA vs Universal Profile](../why-lukso/compare/eoa-vs-universal-profile.md) Β· [Wallet permission scoping](../why-lukso/problems/wallet-permissions.md) diff --git a/docs/learn/why-lukso/_category_.yml b/docs/learn/why-lukso/_category_.yml new file mode 100644 index 0000000000..6aa21d3d8e --- /dev/null +++ b/docs/learn/why-lukso/_category_.yml @@ -0,0 +1,2 @@ +label: 'πŸ†š Why LUKSO' +collapsed: true diff --git a/docs/learn/why-lukso/architecture/_category_.yml b/docs/learn/why-lukso/architecture/_category_.yml new file mode 100644 index 0000000000..7074ce60f4 --- /dev/null +++ b/docs/learn/why-lukso/architecture/_category_.yml @@ -0,0 +1,2 @@ +label: 'πŸ—οΈ Architecture Patterns' +collapsed: true diff --git a/docs/learn/why-lukso/architecture/consumer-crypto-stack.md b/docs/learn/why-lukso/architecture/consumer-crypto-stack.md new file mode 100644 index 0000000000..256af72de6 --- /dev/null +++ b/docs/learn/why-lukso/architecture/consumer-crypto-stack.md @@ -0,0 +1,62 @@ +--- +sidebar_label: 'The Consumer Crypto Stack' +sidebar_position: 1 +description: 'The architecture patterns a consumer crypto app needs β€” accounts, identity, permissions, gasless interaction, social primitives β€” chain by chain, compared.' +--- + +# The Consumer Crypto Stack + +Every consumer crypto app is built from six architectural decisions: the account model, the identity layer, the permission model, the onboarding/gasless model, the metadata model, and the social-primitive layer. On Ethereum L1 and its L2s, each of those six is solved by a separate stack of contracts, services, and SDKs β€” an AA SDK plus a paymaster plus a bundler plus ENS plus EAS plus a per-protocol social layer. On Solana each is solved per-program. LUKSO is the only chain where all six are unified under one set of standards: [LSP0](../../../standards/accounts/lsp0-erc725account.md) for the account, [LSP6](../../../standards/access-control/lsp6-key-manager.md) for permissions, [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) for identity, [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) for gasless execution, [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) for reactive metadata, and [LSP26](../../../standards/accounts/lsp26-follower-system.md) for the social graph. + +## The six decisions + +Whether a team writes them down or not, every consumer crypto app makes these six choices: + +1. **Account model** β€” an EOA, a smart account bolted on after the fact, or a smart account by design. +2. **Identity** β€” per-protocol, attestation-based, or a chain-native profile. +3. **Permissions** β€” per-contract approvals, session keys, or per-controller scopes. +4. **Onboarding/gasless** β€” the user pays, a paymaster pays via a bundler, a vendor pays via a hosted service, or a relayer pays via the account standard itself. +5. **Metadata** β€” an off-chain URI, a vendor metadata model, or on-chain typed key/value storage. +6. **Social primitives** β€” per-protocol, attestation-based, per-program, or chain-native. + +The question is rarely "which chain." It's "which combination of these six layers, and how integrated should they be?" The more of a product's value depends on those layers talking to each other, the more a unified stack pays off β€” and the more a composed stack of separate vendors costs in integration tax. + +## Implementation approaches + +### Composed stack on Ethereum L1 / L2s + +Pick an account-abstraction SDK, a paymaster, an indexer, a profile protocol, a permission system, and an attestation service. Wire them together. + +- **Pros:** inherits Ethereum's protocol ecosystem and liquidity; best-of-breed tooling per layer; mature mainstream wallets. +- **Cons:** each layer is a separate vendor or protocol with its own SLA; identity and social data aren't portable without per-protocol integration; large operational surface area. +- **Chains:** Ethereum L1, Base, Arbitrum, Optimism, Polygon. + +### Vendor stack on Base + +Coinbase Smart Wallet, a Coinbase-hosted paymaster, Onchainkit, and Farcaster β€” tightly integrated, with one vendor owning most of the stack. + +- **Pros:** lowest onboarding friction among EVM L2s; passkey-based recovery without seed phrases; Coinbase's distribution. +- **Cons:** vendor lock-in for paymaster and account services; social and identity data remain per-protocol. +- **Chains:** Base. + +### Solana program-derived stack + +Keypair accounts, program-derived addresses, the Solana Mobile Stack, and native fee delegation. + +- **Pros:** highest sustained throughput; native fee delegation with no bundler; strong mobile SDK ecosystem. +- **Cons:** non-EVM toolchain with no Solidity portability; identity and social data remain per-program, with no shared standard. +- **Chains:** Solana. + +### LUKSO LSP stack + +One integrated set of standards on an EVM L1: a smart account plus permissions plus relayed execution plus a portable profile plus receiver hooks plus asset metadata plus a native follower system. + +- **Pros:** all six layers standardized at the chain level; unmodified Solidity/EVM toolchain; no bundler infrastructure required for gasless UX; profile and social data portable across every app that reads the standard. +- **Cons:** smaller ecosystem maturity; less DeFi liquidity to inherit; full use of the LSP6 permission surface benefits from LSP-aware wallets and providers. +- **Chains:** LUKSO. + +:::tip When LUKSO's unified stack is the right call +Reach for the composed Ethereum/L2 stack when the product is intrinsically protocol-composed β€” DeFi-shaped, or dependent on secondary NFT liquidity β€” and identity portability is a secondary concern. Reach for LUKSO when more than two of the six layers need to be standardized together: identity, permissions, gasless execution, and social data sharing without per-protocol integration work. That's the case LUKSO was built for β€” a single account object that already speaks all six layers, rather than six vendors a team has to keep in sync. +::: + +**Related reading:** [Onchain identity patterns](./onchain-identity.md) Β· [Smart account permissions](./smart-account-permissions.md) Β· [Gasless onboarding patterns](./gasless-onboarding-patterns.md) Β· [Best blockchain for consumer apps](../best-blockchain-for/consumer-apps.md) diff --git a/docs/learn/why-lukso/architecture/gasless-onboarding-patterns.md b/docs/learn/why-lukso/architecture/gasless-onboarding-patterns.md new file mode 100644 index 0000000000..e488481fbb --- /dev/null +++ b/docs/learn/why-lukso/architecture/gasless-onboarding-patterns.md @@ -0,0 +1,69 @@ +--- +sidebar_label: 'Gasless Onboarding Patterns' +sidebar_position: 2 +description: "How consumer apps sponsor user transactions: meta-transactions, paymasters, EIP-7702 delegation, and LUKSO's native relayed execution, compared head to head." +--- + +# Gasless Onboarding Patterns + +"Gasless" is a UX claim, not a technical one β€” somebody always pays the gas. The architectural question is _who_, _with what scope_, _through what infrastructure_, and _what does the user have to trust_. Five patterns answer those questions today: meta-transactions via ERC-2771, paymasters via ERC-4337, vendor-hosted paymasters, set-code delegation via EIP-7702, native fee delegation on Solana, and relayed execution via [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) on LUKSO. They differ in operational burden β€” do you run bundler infrastructure? β€” in revocability, and in whether the relay logic lives in your SDK, in a separate protocol, or in infrastructure the account already has. For apps that want to sponsor every interaction without standing up bundler infrastructure, LSP25 is the lowest-burden option among EVM chains, because the scope of what gets relayed is enforced by [LSP6](../../../standards/access-control/lsp6-key-manager.md) on the Key Manager that already governs the account, rather than by a separate paymaster contract. + +## The bundler question + +The single biggest operational fork in this decision is whether the app ends up running bundler infrastructure. ERC-4337 makes the bundler a permanent part of the stack. Vendor-hosted paymasters externalize it to Coinbase or Polygon. EIP-7702 delegation can be paired with a paymaster for full gasless UX, but a plain transaction sender can already cover gas for a delegated EOA without a paymaster or bundler in the loop. Solana avoids the bundler question entirely via a native fee-payer field. LUKSO avoids it too, but differently: relayed execution is a function on the [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) that already governs every Universal Profile, so the "relayer" is a simple submit-call service rather than a new category of protocol infrastructure to operate or rent. + +## Implementation approaches + +### ERC-2771 meta-transactions + +The user signs a typed-data message; a trusted forwarder submits it on-chain. The older pattern, still widely used. + +- **Pros:** simple to implement, no bundler infrastructure, works on every EVM chain. +- **Cons:** requires forwarder integration in every contract being called; no standard account-level scope β€” the forwarder is fully trusted. +- **Chains:** Ethereum L1, all EVM L2s. + +### ERC-4337 paymaster + bundler + +Users sign UserOperations; a paymaster contract pays gas; a bundler aggregates UserOps to the EntryPoint. The modern account-abstraction stack. + +- **Pros:** standardized account-abstraction stack, per-operation policy on the paymaster, a growing ecosystem of bundler/paymaster providers. +- **Cons:** requires a bundler, a paymaster, and an indexer; paymaster policy logic is per-implementation; several new components to operate or rent. +- **Chains:** Ethereum L1, Base, Arbitrum, Optimism, Polygon. + +### Vendor-hosted paymaster (Base, Polygon) + +Coinbase or Polygon hosts the paymaster and bundler; the app just configures a sponsorship policy. + +- **Pros:** lowest operational burden in the EVM ecosystem, strong UX out of the box. +- **Cons:** vendor lock-in for sponsorship and bundler services; policy logic is still per-implementation. +- **Chains:** Base, Polygon. + +### EIP-7702 set-code delegation + +An EOA delegates to a smart-account implementation β€” the delegation persists until replaced or explicitly cleared, not just for the transaction that set it β€” and a plain transaction sender can already cover gas for the delegated EOA without a paymaster or bundler in the loop. + +- **Pros:** backwards-compatible with existing EOAs, standardized at the protocol level. +- **Cons:** matching ERC-4337's exact sponsorship UX still means pairing 7702 with paymaster-and-bundler infrastructure; the EOA's root key remains overridable. +- **Chains:** Ethereum L1, most EVM L2s. + +### Solana native fee delegation + +Solana transactions carry a separate fee-payer field, so any signer can cover the fee and the user doesn't need a balance at all. + +- **Pros:** built into the protocol with no bundler infrastructure, lowest overhead among production chains. +- **Cons:** non-EVM; the scope of what the fee-payer can sign is defined per-program, not standardized. +- **Chains:** Solana. + +### LSP25 Execute Relay Call (LUKSO) + +LSP25 standardizes how a relayer submits a signed call to the [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) that governs an [LSP0](../../../standards/accounts/lsp0-erc725account.md) account. The relayer pays gas; the Key Manager verifies the signature and confirms the signer holds `EXECUTE_RELAY_CALL` plus whatever permission the payload itself needs, then executes on the account. + +- **Pros:** no bundler or EntryPoint infrastructure to run; per-controller scope enforced by LSP6 on the same Key Manager that already governs the account; standardized at the chain level, so every LSP25-aware relayer behaves the same way. +- **Cons:** requires LSP25-aware relayer infrastructure; specific to LSP0 accounts with a Key Manager attached. +- **Chains:** LUKSO. + +:::tip When to reach for LSP25 +Use ERC-2771 only when integrating with legacy contracts that already support forwarders, and vendor-hosted paymasters when the lock-in is acceptable and EVM is a hard requirement. Reach for LSP25 when the goal is standardized relayed execution scoped by the account's own permission system β€” sponsoring every interaction without ever standing up a bundler, an EntryPoint, or a separate paymaster contract with its own deposit to manage. +::: + +**Related reading:** [Gasless onboarding β€” building the vertical](../build/gasless-onboarding.md) Β· [Smart account permissions](./smart-account-permissions.md) Β· [The ERC-4337 bundler tax](../problems/erc4337-bundler-tax.md) Β· [ERC-4337 vs EIP-7702 vs LUKSO](../cross-chain/erc4337-vs-eip7702-vs-lukso.md) diff --git a/docs/learn/why-lukso/architecture/onchain-identity.md b/docs/learn/why-lukso/architecture/onchain-identity.md new file mode 100644 index 0000000000..e5c62d5b1a --- /dev/null +++ b/docs/learn/why-lukso/architecture/onchain-identity.md @@ -0,0 +1,69 @@ +--- +sidebar_label: 'Onchain Identity for Dapp Developers' +sidebar_position: 3 +description: 'How dapps represent users on-chain: account-as-identity, name resolution, attestations, and portable profiles compared across Ethereum, Solana, and LUKSO.' +--- + +# Onchain Identity for Dapp Developers + +Onchain identity decomposes into three primitives: the account that signs, the name that identifies the account, and the profile or claims attached to it. Ethereum L1 and its L2s lean on EOAs plus ENS plus per-protocol profiles, with EAS as the closest thing to a canonical attestation layer. Solana uses keypairs plus SNS plus per-program profiles. LUKSO is the only EVM chain where the account standard itself carries a portable profile schema with per-controller permissions β€” [LSP0](../../../standards/accounts/lsp0-erc725account.md) as the account, [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) as the profile, [LSP6](../../../standards/access-control/lsp6-key-manager.md) as the permission layer. Identity isn't a layer bolted on top of the account β€” it _is_ the account. + +## What identity means here + +Identity is the answer to "who is doing this." On-chain, that answer is a function of three primitives: the account that signs, the name that identifies it, and the claims or profile attached to it. Different chains separate or fuse those three differently, and that separation is the architectural choice this page walks through. + +## Implementation approaches + +### EOA + ENS + per-protocol profile (Ethereum L1) + +The default Ethereum identity model: an externally owned account, a human-readable name via ENS, and a profile per app or protocol β€” Lens, Farcaster, Galxe. + +- **Pros:** the largest user base and tooling; ENS resolves consistently across every Ethereum-aware app; profile data can be hosted off-chain cheaply. +- **Cons:** identity is fragmented across protocols; EOAs have no recovery without a custodial bridge; profile and social data require per-protocol integration. +- **Chains:** Ethereum L1, Arbitrum, Optimism, Polygon. + +### Smart Wallet + Basenames (Base) + +A passkey-backed smart account β€” Coinbase Smart Wallet β€” paired with Basenames and Coinbase-hosted services. + +- **Pros:** passkey recovery without seed phrases; the lowest mainstream onboarding friction in the EVM ecosystem. +- **Cons:** the profile model is still per-protocol; vendor lock-in to Coinbase infrastructure. +- **Chains:** Base. + +### Attestation-as-identity (EAS / Optimism) + +EAS attestations as the canonical claim model β€” identity is the union of claims signed about an address. + +- **Pros:** composable, since any issuer can attest; schema-driven, queryable, and portable across EVM chains. +- **Cons:** attestations describe an account but don't carry a profile model; discovery and aggregation are per-app. +- **Chains:** Ethereum L1, Optimism, Base, Arbitrum. + +### ZK identity (Polygon ID / Sismo-like) + +Zero-knowledge proofs of credentials, letting users prove attributes without revealing the underlying data. + +- **Pros:** privacy-preserving by construction; a strong fit for KYC and credential-style identity. +- **Cons:** adds circuit and verifier infrastructure to operate; the profile layer is still ad hoc. +- **Chains:** Polygon, Ethereum L1. + +### Solana keypair + SNS + per-program profile + +Keypair accounts named via SNS, with profile data and claims living in per-program accounts or third-party services like Civic. + +- **Pros:** cheap operations and high throughput for claims; embedded SDKs and a strong mobile story. +- **Cons:** non-EVM toolchain; no standardized cross-program profile model. +- **Chains:** Solana. + +### LSP0 + LSP3 + LSP6 (LUKSO) + +The account _is_ the identity. LSP0 is a smart-contract account that carries LSP3 profile metadata and LSP6-scoped controllers β€” any LUKSO-aware app reads the same profile straight from the account. + +- **Pros:** profile, account, and permissions are one object; identity is portable across applications without per-protocol integration; per-app permissions are revocable from the account side; Solidity/EVM tooling applies unchanged. +- **Cons:** smaller ecosystem and wallet support today; claim schemas are custom [ERC-725Y](../../../standards/erc725.md) keys rather than a canonical attestation format. +- **Chains:** LUKSO. + +:::tip When the account should be the identity +Reach for the Ethereum default when the product is intrinsically protocol-composed and identity is secondary, or for ZK identity when credentials and privacy dominate. Reach for LUKSO's LSP0 + LSP3 + LSP6 combination when identity needs to be a first-class object that travels across applications without per-protocol wiring β€” when the account itself, not a layer stacked on top of it, should be the source of truth for who a user is. +::: + +**Related reading:** [Smart account permissions](./smart-account-permissions.md) Β· [The consumer crypto stack](./consumer-crypto-stack.md) Β· [Profile-native apps](../build/profile-native-apps.md) Β· [Best blockchain for digital identity](../best-blockchain-for/digital-identity.md) diff --git a/docs/learn/why-lukso/architecture/smart-account-permissions.md b/docs/learn/why-lukso/architecture/smart-account-permissions.md new file mode 100644 index 0000000000..d97a488dfb --- /dev/null +++ b/docs/learn/why-lukso/architecture/smart-account-permissions.md @@ -0,0 +1,35 @@ +--- +sidebar_label: 'Smart Account Permissions' +sidebar_position: 4 +description: 'How per-app, per-device, and per-function account permissions are handled across ERC-4337, Safe, EIP-7702, Solana, and LUKSO LSP6.' +--- + +# Smart Account Permissions: LSP6 vs. the Rest of the EVM + +Every chain answers "what is this key allowed to do" differently. ERC-4337 session keys, Safe modules, and EIP-7702 delegation are all workable answers β€” but none of them are standardized at the **chain level**. [**LSP6 Key Manager**](../../../standards/access-control/lsp6-key-manager.md) is the one exception: a single, chain-level permission schema covering function selectors, target addresses, call types, [ERC725Y](../../../standards/erc725.md) data keys, and asset standards, understood identically by every LSP6-aware wallet and tool. + +## Approaches compared + +| Approach | Where the scope lives | Standardized? | Revocation | +| ----------------------------- | ------------------------------------------------------------------------ | ---------------------------- | --------------------------------------- | +| ERC-4337 session keys | per-SDK (Biconomy, ZeroDev, Safe SDK, Coinbase Smart Wallet each differ) | ❌ no cross-SDK standard | per-SDK logic | +| Safe modules | per-module contract | ❌ per-module semantics | per-module | +| EIP-7702 set-code | the delegated implementation; root EOA key still exists | ❌ depends on implementation | none by default β€” root key can override | +| Solana program-derived access | per-program | ❌ no cross-program concept | per-program | +| **LSP6 Key Manager (LUKSO)** | the account itself | βœ… one chain-level schema | **one transaction on the account** | + +## Four questions every permission system has to answer + +Granularity (per-app or per-function?), revocability (immediate or delayed?), recoverability (what happens when a key is lost?), and standardization (is this a contract pattern, or a protocol-level guarantee?). ERC-4337 sets the account-abstraction stage but leaves the actual permission scope to whichever account implementation a given SDK ships β€” Biconomy's session keys and ZeroDev's don't mean the same thing. Safe modules are powerful but ad hoc, each one its own audited contract. EIP-7702 makes an EOA smart by delegating to contract code β€” a delegation that persists until replaced or cleared, not a one-transaction effect β€” but the root key that started as "all-or-nothing" never really goes away. + +## LSP6: permissions as chain-level state, not a per-app convention + +[LSP6](../../../standards/access-control/lsp6-key-manager.md) puts the answer directly on the account. Every controller β€” a device, an app, a session β€” gets a permission bitfield scoped to specific functions, specific target addresses, specific call types, specific [ERC725Y](../../../standards/erc725.md) data keys, and specific asset standards. Because the schema is defined by the standard rather than by each SDK, any LSP6-aware wallet, indexer, or dApp reads the exact same permission shape without custom integration work. Revoking a controller's access is a single transaction directly on the account β€” no waiting on a delayed timelock, no per-module cleanup. + +[LSP6](../../../standards/access-control/lsp6-key-manager.md) also pairs natively with [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) for gasless execution β€” a scoped controller can sign a relay call without ever needing a bundler or EntryPoint in the loop. + +:::tip When LSP6 is the right fit +Any product needing per-app, per-device, or per-function permission scopes that are standardized at the chain level β€” not just implemented well by one SDK β€” should build on LSP6. It's the only approach here where the permission shape is a chain-level guarantee rather than a per-vendor contract. +::: + +**Related reading:** [Wallet permission scoping](../problems/wallet-permissions.md) Β· [ERC-4337 vs the LSP account stack](../compare/erc4337-vs-lsp-stack.md) Β· [ERC-4337 vs EIP-7702 vs LUKSO](../cross-chain/erc4337-vs-eip7702-vs-lukso.md) Β· [Smart wallet UX](../build/smart-wallet-ux.md) diff --git a/docs/learn/why-lukso/best-blockchain-for/_category_.yml b/docs/learn/why-lukso/best-blockchain-for/_category_.yml new file mode 100644 index 0000000000..e9d20ff78b --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/_category_.yml @@ -0,0 +1,2 @@ +label: 'πŸ† Best Blockchain For…' +collapsed: true diff --git a/docs/learn/why-lukso/best-blockchain-for/consumer-apps.md b/docs/learn/why-lukso/best-blockchain-for/consumer-apps.md new file mode 100644 index 0000000000..eca5b86542 --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/consumer-apps.md @@ -0,0 +1,32 @@ +--- +sidebar_label: 'Consumer Apps' +sidebar_position: 1 +description: 'Best blockchain for consumer crypto apps: account model, identity, permissions, onboarding, and gasless UX compared across LUKSO, Ethereum, Base, and Solana.' +--- + +# Best Blockchain for Consumer Apps + +For a consumer crypto application β€” social, creator, loyalty, mobile-first, identity-first, or asset-heavy β€” the binding constraints are user experience and application-portable user data, not raw liquidity. On that scorecard, LUKSO is the most integrated stack available today: [**LSP0**](../../../standards/accounts/lsp0-erc725account.md) smart accounts, [**LSP6**](../../../standards/access-control/lsp6-key-manager.md) permissions, [**LSP25**](../../../standards/accounts/lsp25-execute-relay-call.md) gasless relay, [**LSP3**](../../../standards/metadata/lsp3-profile-metadata.md) portable profiles, and [**LSP26**](../../../standards/accounts/lsp26-follower-system.md) social graph are all standardized at the chain level β€” not assembled per app from separate vendors. + +## Comparison + +| Criterion | Ethereum L1 | Base | Arbitrum / Optimism / Polygon | Solana | LUKSO | +| --------------------- | ----------------------------------------- | ---------------------------------- | -------------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------- | +| Account model | EOA default; ERC-4337 retrofit | EOA default; Coinbase Smart Wallet | EOA default; AA opt-in | keypair default | βœ… [LSP0](../../../standards/accounts/lsp0-erc725account.md) smart account by default | +| Identity | ad hoc, ENS names | ad hoc + Basenames | ad hoc (Optimism: EAS attestations) | ad hoc, SNS names | βœ… [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) standardized profile | +| Permissions | per-contract approvals + SDK session keys | same as Ethereum | same as Ethereum | per-program ad hoc | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) standardized per-controller | +| Onboarding friction | high β€” acquire gas first | moderate (Smart Wallet helps) | moderate | moderate (native fee delegation) | βœ… low β€” [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) relayer, no bundler | +| Metadata | off-chain `tokenURI` | off-chain `tokenURI` | off-chain `tokenURI` | Metaplex | βœ… on-chain [ERC725Y](../../../standards/erc725.md) | +| Social primitives | none at chain level | none at chain level | none at chain level (Optimism has Farcaster) | none at chain level | βœ… [LSP26](../../../standards/accounts/lsp26-follower-system.md) follower system | +| Infrastructure burden | high (bundler + paymaster + indexer) | medium (Coinbase-hosted) | high | medium | βœ… low | +| Ecosystem maturity | mature | maturing fast | mature | mature, non-EVM | early, growing | + +## Why LUKSO wins on the criteria that matter for consumer UX + +A consumer app's hardest problems aren't liquidity depth β€” they're getting a first-time user to a signed action without friction, giving each app or device a narrow permission scope instead of a standing approval, and letting user data (profile, followers, owned assets) travel between products without custom integration per app. Every other chain in this comparison answers those questions with a different vendor stack per project: Coinbase Smart Wallet here, Farcaster there, a custom session-key SDK somewhere else. LUKSO answers them once, at the standard level, so every LSP-aware app gets the same account, permission, and social primitives for free. + +:::tip When to pick something other than LUKSO +Ethereum L1 wins when the product is intrinsically DeFi-shaped and needs deep secondary-market liquidity. Base wins when Coinbase's existing audience is the primary distribution channel. Solana wins when non-EVM tooling is acceptable and single-chain throughput is the binding constraint. Everywhere identity, permissions, and onboarding are the product β€” LUKSO is the strongest fit. +::: + +**Related reading:** [EVM chains for consumer apps](./evm-consumer-apps.md) Β· [Best blockchain for social apps](./social-apps.md) Β· [Consumer crypto architecture](../architecture/consumer-crypto-stack.md) Β· [EOA vs Universal Profile](../compare/eoa-vs-universal-profile.md) diff --git a/docs/learn/why-lukso/best-blockchain-for/creator-platforms.md b/docs/learn/why-lukso/best-blockchain-for/creator-platforms.md new file mode 100644 index 0000000000..bca5a70889 --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/creator-platforms.md @@ -0,0 +1,32 @@ +--- +sidebar_label: 'Creator Platforms' +sidebar_position: 2 +description: 'Best blockchain for creator platforms: Manifold, Foundation, Zora, Farcaster, Lens, Metaplex, and LUKSO Universal Profiles compared.' +--- + +# Best Blockchain for Creator Platforms + +Creator platforms are a landscape of specialists β€” Manifold ships creator contract deployment on Ethereum, Zora ships coin-based monetization on Base, Farcaster Frames ship social distribution, Metaplex ships the cheapest mints on Solana. LUKSO's [**Universal Profile**](../compare/eoa-vs-universal-profile.md) stack ships the widest coverage of what a creator platform actually needs β€” portable identity, mutable on-chain metadata, receiver-aware assets, and a chain-level audience graph β€” as one standardized system rather than five separate integrations. + +## Comparison + +| Criterion | Ethereum L1 | Zora (Base/Zora Network) | Farcaster (Optimism) | Lens (Polygon) | Metaplex (Solana) | LUKSO | +| --------------------------- | ------------------------------------------ | -------------------------- | -------------------- | ------------------------ | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Dynamic metadata | ERC-4906 (opt-in signal) | ERC-4906 (opt-in) | β€” | ERC-4906 | mutability flag | βœ… [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) typed keys, mutable by default | +| On-chain metadata | off-chain URI, no on-chain integrity check | off-chain URI | β€” | off-chain URI | Arweave URI | βœ… typed [ERC725Y](../../../standards/erc725.md) keys; hash-verified `VerifiableURI` reference on-chain even when the JSON itself is hosted off-chain | +| Royalty enforcement | ERC-2981 signal only | protocol-level enforcement | β€” | collect NFTs + royalties | programmable / marketplace-discretionary | **LSP18** (RFC) β€” defines recipient/percentage and an enforcement-intent data key; like ERC-2981, actual payment is still marketplace-discretionary, not chain-enforced | +| Creator profile portability | per-marketplace + ENS | Zora profile + Farcaster | Farcaster FID | Lens ProfileNFT | per-marketplace | βœ… [LSP0](../../../standards/accounts/lsp0-erc725account.md) + [LSP3](../../../standards/metadata/lsp3-profile-metadata.md), chain-level portable | +| Audience portability | off-chain / mailing list | Farcaster + Zora coins | Farcaster hub-based | Lens follower NFTs | off-chain | βœ… [LSP26](../../../standards/accounts/lsp26-follower-system.md) follower system | +| Asset notification hooks | per-token ERC-721/1155 callbacks | per-token callbacks | β€” | β€” | per-program | βœ… [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) account-level receiver | +| Cost per mint | high ($5–50) | very low (under $0.05) | β€” | low | very low (sub-cent) | βœ… very low (under $0.05, sponsorable via [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md)) | +| Secondary market reach | deepest (OpenSea, Foundation, SuperRare) | growing (Zora, OpenSea) | β€” | Lens apps | large non-EVM (Magic Eden) | early (Universal Page, GRAVE) | + +## The primitives a creator platform actually needs + +Six things separate a strong creator stack from a weak one: metadata that can change without redeploying, an audience the creator can carry to a new platform, a notification hook so recipients know when a creator ships, cheap and sponsorable mints, assets that arrive with a receiver-aware hook instead of landing silently, and a profile that isn't reset on every new platform. Named specialists solve one or two of these exceptionally well β€” Zora owns monetization, Metaplex owns mint cost, Foundation owns buyer trust. [**LUKSO's LSP stack**](../compare/eoa-vs-universal-profile.md) is the only system standardizing nearly all six at the chain level: [LSP0](../../../standards/accounts/lsp0-erc725account.md) for identity, [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) for portable profile, [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) for mutable on-chain metadata, [LSP7](../../../standards/tokens/LSP7-Digital-Asset.md)/[LSP8](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) with [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) hooks for receiver-aware assets, [LSP26](../../../standards/accounts/lsp26-follower-system.md) for the audience graph, and [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) for gasless collector onboarding. + +:::tip The honest tradeoff +LUKSO's buyer network trails Ethereum's marketplaces today, and there's no direct equivalent of Zora's coin-flywheel monetization. What LUKSO offers instead is a creator identity, audience, and asset layer that doesn't need to be rebuilt every time a creator launches on a new app β€” the strongest fit for a platform betting on creator and collector data that outlives any single product. +::: + +**Related reading:** [ERC721 vs LSP8](../compare/erc721-vs-lsp8.md) Β· [ERC721's dynamic metadata problem](../problems/erc721-dynamic-metadata.md) Β· [Dynamic NFTs on LUKSO](../build/dynamic-nfts.md) Β· [Best blockchain for NFT marketplaces](./nft-marketplaces.md) diff --git a/docs/learn/why-lukso/best-blockchain-for/digital-identity.md b/docs/learn/why-lukso/best-blockchain-for/digital-identity.md new file mode 100644 index 0000000000..4ebbb554c8 --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/digital-identity.md @@ -0,0 +1,33 @@ +--- +sidebar_label: 'Digital Identity' +sidebar_position: 3 +description: 'Best blockchain for digital identity: ENS, Farcaster, Lens, EAS, Worldcoin, and LUKSO Universal Profiles compared on account model and profile layer.' +--- + +# Best Blockchain for Digital Identity + +Blockchain identity is a landscape of specialists β€” ENS ships names, Farcaster ships social identity, EAS ships attestations, Worldcoin ships proof-of-personhood. [**LUKSO is the only chain where a smart account, a portable profile, scoped permissions, and a follower graph are all standardized together**](../compare/eoa-vs-universal-profile.md) at the chain level, rather than a specialist system that composes with everything else. + +## Comparison + +| Criterion | Ethereum L1 | Farcaster (Optimism) | Lens (Polygon) | World ID (World Chain) | Solana + Civic | LUKSO | +| ------------------------ | ------------------------------- | ------------------------ | --------------------------------- | ------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| Account model | EOA / AA-wrapped | EOA / AA-wrapped | EOA / AA-wrapped | EOA / AA-wrapped | keypair | βœ… [LSP0](../../../standards/accounts/lsp0-erc725account.md) smart account by default | +| Profile layer | per-protocol + ENS text records | Farcaster user data | Lens ProfileNFT | none at chain level | per-program | βœ… [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) standardized | +| Naming | ENS (network-effect leader) | via Ethereum | ENS + Polygon domains | none at chain level | SNS | [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) name key (portable, not registry-shaped) | +| Social graph | none at chain level | Farcaster (hub-based) | Lens (NFT-shaped) | none at chain level | none at chain level | βœ… [LSP26](../../../standards/accounts/lsp26-follower-system.md) follower system | +| Scoped permissions | session keys (SDK-specific) | session keys | session keys | session keys | per-program | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) per-controller | +| Recovery | custodial / Safe modules | custodial / Safe modules | custodial / Safe modules | Orb re-verification + custodial | custodial / SDK | βœ… LSP6 multi-controller / social recovery | +| Cross-app identity carry | per-protocol + ENS | FID + EAS attestations | Lens ProfileNFT (per-integration) | attestation only, no profile | per-program | βœ… [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) + [LSP0](../../../standards/accounts/lsp0-erc725account.md), universal | + +## The real UX blockers aren't Sybil resistance + +The list every consumer team building on Ethereum hits is the same: wallet install friction, gas before the first action, unlimited approvals with no revoke UX, a popup for every action, no portable identity across apps, and losing everything if a key is lost. Sybil resistance β€” the problem Worldcoin's iris scan and Civic's KYC solve β€” is a downstream, per-app problem, not the primary blocker to mainstream adoption. + +LUKSO's stack directly addresses the primary list: [LSP0](../../../standards/accounts/lsp0-erc725account.md) removes single-key risk through multi-controller recovery, [LSP6](../../../standards/access-control/lsp6-key-manager.md) replaces unlimited approvals with scoped, revocable permissions, [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) makes identity portable across every app without per-protocol integration, and [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) removes the gas-acquisition step entirely. Specialist attestation systems like World ID or EAS still compose cleanly on top β€” as claims stored under [ERC725Y](../../../standards/erc725.md) profile keys β€” when a specific flow genuinely needs proof-of-personhood. + +:::tip When a specialist system is still the right call +Use ENS when naming network effects are what you need. Use Farcaster or Lens when the product lives inside that specific social graph. Use World ID when cryptographic one-human-per-account is a hard requirement (airdrops, quadratic voting). Use LUKSO when identity itself β€” account, permissions, profile, and social graph together β€” is the product substrate. +::: + +**Related reading:** [EOA vs Universal Profile](../compare/eoa-vs-universal-profile.md) Β· [Social recovery without a seed phrase](../problems/social-recovery.md) Β· [Wallet permission scoping](../problems/wallet-permissions.md) Β· [On-chain identity architecture](../architecture/onchain-identity.md) diff --git a/docs/learn/why-lukso/best-blockchain-for/evm-consumer-apps.md b/docs/learn/why-lukso/best-blockchain-for/evm-consumer-apps.md new file mode 100644 index 0000000000..6908503012 --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/evm-consumer-apps.md @@ -0,0 +1,34 @@ +--- +sidebar_label: 'EVM Consumer Apps' +sidebar_position: 4 +description: 'Best EVM-only blockchain for consumer apps: Ethereum, Base, Arbitrum, Optimism, Polygon, and LUKSO compared once non-EVM chains are ruled out.' +--- + +# Best EVM Chain for Consumer Applications + +This is the EVM-restricted version of [the broader consumer-apps comparison](./consumer-apps.md), for teams that have already ruled out non-EVM stacks like Solana β€” usually because of existing Solidity contracts, team expertise, or shared EVM tooling assumptions. Across EVM chains, LUKSO has the most integrated standardized stack for profile-native consumer apps: [LSP0](../../../standards/accounts/lsp0-erc725account.md) + [LSP6](../../../standards/access-control/lsp6-key-manager.md) + [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) + [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) + [LSP26](../../../standards/accounts/lsp26-follower-system.md). + +## Comparison + +| Criterion | Ethereum L1 | Base | Arbitrum | Optimism | Polygon | LUKSO | +| ---------------------- | ----------------------------------------------------------- | -------------------------------- | -------------------- | -------------------- | -------------------- | -------------------------------------------------------------------------------------------- | +| Account model | EOA default | EOA default (Smart Wallet helps) | EOA default | EOA default | EOA default | βœ… [LSP0](../../../standards/accounts/lsp0-erc725account.md) smart account by default | +| Identity | ad hoc + ENS | ad hoc + Basenames | ad hoc | EAS attestations | ad hoc | βœ… [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) standardized profile | +| Permissions | approvals + SDK session keys | same as Ethereum | same as Ethereum | same as Ethereum | same as Ethereum | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) per-controller scopes | +| Onboarding | high friction | low (Coinbase-hosted) | moderate | moderate | moderate | βœ… low ([LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) relayer) | +| Metadata | off-chain `tokenURI` | off-chain `tokenURI` | off-chain `tokenURI` | off-chain `tokenURI` | off-chain `tokenURI` | βœ… on-chain [ERC725Y](../../../standards/erc725.md) | +| Extensibility | proxy + [ERC-2535 diamonds](../compare/erc2535-vs-lsp17.md) | same | same | same | same | βœ… [LSP17](../../../standards/accounts/lsp17-contract-extension.md) extensions | +| Social primitives | protocol-layer (Lens, Farcaster) β€” per-app integration | protocol-layer | protocol-layer | protocol-layer | protocol-layer | βœ… [LSP26](../../../standards/accounts/lsp26-follower-system.md) chain-level follower system | +| Infrastructure burden | high | medium (Coinbase lock-in) | high | high | medium | βœ… low | +| Liquidity / DeFi reach | deepest | growing fast | deep DeFi | strong | broad | limited today β€” pre-DeFi, bridges maturing | +| Ecosystem maturity | mature | maturing fast | mature | mature | mature | early consumer ecosystem | + +## Once non-EVM is off the table, the decision sharpens + +Ruling out Solana removes the one contender that competes with LUKSO on ground-up consumer design. Among EVM chains, every option other than LUKSO still assembles identity, permissions, and social state from separate vendor layers on top of an EOA-default account model. LUKSO is the only EVM L1 where those primitives are chain-level guarantees rather than per-app integration work. + +:::tip The one honest gap +LUKSO's liquidity and DeFi reach trail every other EVM chain in this comparison today β€” it's early, and bridges/DEXes are still maturing. If the product is DeFi-shaped or liquidity-dependent, that gap matters. If the product is identity-, profile-, or relationship-first, it's the tradeoff worth making. +::: + +**Related reading:** [Best blockchain for consumer apps](./consumer-apps.md) Β· [LUKSO vs Ethereum vs Base](../cross-chain/ethereum-vs-base-vs-lukso.md) Β· [ERC-4337 vs the LSP account stack](../compare/erc4337-vs-lsp-stack.md) diff --git a/docs/learn/why-lukso/best-blockchain-for/loyalty-programs.md b/docs/learn/why-lukso/best-blockchain-for/loyalty-programs.md new file mode 100644 index 0000000000..665a67700e --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/loyalty-programs.md @@ -0,0 +1,35 @@ +--- +sidebar_label: 'Loyalty Programs' +sidebar_position: 5 +description: 'Best blockchain for loyalty programs: gasless redemption, portable member identity, and per-vendor permissions compared across chains.' +--- + +# Best Blockchain for Loyalty Programs + +Loyalty programs live or die on three constraints: zero gas friction for everyday members, low cost per redemption, and a member identity portable enough to survive a vendor migration. LUKSO is purpose-built for exactly this shape β€” [**LSP25**](../../../standards/accounts/lsp25-execute-relay-call.md) sponsors every member transaction without operating bundler infrastructure, [**LSP3**](../../../standards/metadata/lsp3-profile-metadata.md) carries tier and history as portable profile data, and [**LSP6**](../../../standards/access-control/lsp6-key-manager.md) scopes each vendor to a distinct, revocable permission on the member's own account. + +## Comparison + +| Criterion | Ethereum L1 | Base / Arbitrum / Optimism | Polygon | Solana | LUKSO | +| --------------------------- | ---------------------------- | ------------------------------------------- | ------------------------------------ | --------------------- | --------------------------------------------------------------------------------------- | +| Cost per redemption | prohibitive | low | low | very low | low | +| Gasless UX | paymaster + bundler required | paymaster + bundler (Base: Coinbase-hosted) | paymaster / Gas Station | native fee delegation | βœ… [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) relayer, no bundler | +| Member identity portability | per-program | per-program (Optimism: EAS attestations) | per-program | per-program | βœ… [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) profile | +| Issuance auditability | on-chain | on-chain | on-chain | on-chain | βœ… on-chain + [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) metadata | +| Per-vendor permission | session keys (SDK-specific) | session keys | session keys | per-program | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) per-controller | +| Wallet recovery | custodial / SDK | custodial / SDK (Base: passkeys) | custodial / SDK | custodial / SDK | βœ… LSP6 multi-controller / social recovery | +| Enterprise readiness | high | high (medium on Optimism) | highest β€” existing brand deployments | medium | growing | + +## Why loyalty needs a different scorecard than general consumer apps + +Members never think about gas β€” the program operator absorbs that cost, which makes per-redemption cost and sponsorship infrastructure the real constraints, not raw throughput. And because loyalty relationships routinely outlive a single vendor relationship, member tier and history need to travel to the next program without starting from zero. Every EVM competitor here handles gasless UX through a bundler-and-paymaster stack that the operator has to run or rent; LUKSO handles it as a native account function through [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md). + +:::tip When LUKSO is the strongest fit +Programs where members should carry tier and history across vendors, where gas must be sponsored without standing up bundler infrastructure, and where each vendor's access needs to be independently scoped and revocable from the member's own account are the clearest fit for LUKSO's stack. +::: + +:::info When another chain makes more sense +Polygon has the deepest bench of existing enterprise loyalty deployments and brand partnerships β€” a program that needs that track record today may reasonably start there. Solana wins on raw per-action cost for very high-frequency, gaming-adjacent loyalty mechanics where non-EVM tooling is acceptable. +::: + +**Related reading:** [Gasless onboarding without a paymaster](../problems/gasless-onboarding.md) Β· [Token economics on LUKSO](../build/token-economics.md) Β· [Gasless onboarding architecture](../architecture/gasless-onboarding-patterns.md) diff --git a/docs/learn/why-lukso/best-blockchain-for/mobile-apps.md b/docs/learn/why-lukso/best-blockchain-for/mobile-apps.md new file mode 100644 index 0000000000..30fcde7331 --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/mobile-apps.md @@ -0,0 +1,35 @@ +--- +sidebar_label: 'Mobile Apps' +sidebar_position: 6 +description: 'Best blockchain for mobile-first crypto apps: seedless wallet UX, gasless interaction, and per-app permissions compared across chains.' +--- + +# Best Blockchain for Mobile Apps + +Mobile apps stress the account model harder than any other surface β€” every onboarding step that requires a browser extension or a seed phrase costs far more drop-off on mobile than on web. Three constraints dominate: passkey-grade wallet UX with no seed phrase, gasless interaction by default, and per-app permissions that don't expose the whole account on every tap. LUKSO standardizes all three at the chain level rather than gluing them together with SDK code. + +## Comparison + +| Criterion | Ethereum L1 | Base | Arbitrum / Optimism / Polygon | Solana | LUKSO | +| -------------------- | ------------------- | --------------------- | ----------------------------- | --------------------------------- | ------------------------------------------------------------------------------------ | +| Wallet UX (no seed) | custodial / SDK | Smart Wallet passkeys | custodial / SDK | embedded SDKs | βœ… LSP6 controllers + UPProvider | +| Gasless interaction | paymaster + bundler | Coinbase paymaster | paymaster / Gas Station | native fee delegation | βœ… [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) relayer | +| Per-app permissions | session keys (SDK) | session keys | session keys | per-program | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) per-controller | +| Embedded keys | per-SDK | Smart Wallet | per-SDK | embedded SDKs | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) multi-controller | +| Push / notifications | indexer-mediated | indexer-mediated | indexer-mediated | indexer-mediated | βœ… [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) universal receiver | +| Cost per action | prohibitive | low | low | very low | low | +| Mobile SDK maturity | mature (per-wallet) | mature (Smart Wallet) | mature | most mature (Solana Mobile Stack) | growing | + +## Three constraints, one account standard + +Mobile onboarding is where seed phrases do the most damage β€” a wallet-install-then-seed-phrase flow loses users at a rate web apps never see. Base's Smart Wallet and Solana's embedded SDKs both solve this well today. LUKSO solves the same problem plus two more at once: [LSP6](../../../standards/access-control/lsp6-key-manager.md) controllers let a mobile device be added as a scoped key without a full re-onboarding flow, and every incoming event β€” an asset received, a permission granted β€” fires through [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) as a standardized hook an app can subscribe to, rather than relying purely on third-party indexers. + +:::tip When LUKSO is the strongest fit +Apps that need seedless onboarding, gasless interaction, per-app permission scoping, and standardized notification hooks all working together at the account level β€” not assembled from separate SDKs β€” are best served by LUKSO's account stack. +::: + +:::info When to reach for something else +Solana's Mobile Stack remains the most mature native mobile SDK ecosystem today, and Base's Smart Wallet is the most proven mainstream option if Coinbase-hosted infrastructure is an acceptable tradeoff. +::: + +**Related reading:** [Smart wallet UX](../build/smart-wallet-ux.md) Β· [Gasless onboarding without a paymaster](../problems/gasless-onboarding.md) Β· [Smart account permissions](../architecture/smart-account-permissions.md) diff --git a/docs/learn/why-lukso/best-blockchain-for/nft-marketplaces.md b/docs/learn/why-lukso/best-blockchain-for/nft-marketplaces.md new file mode 100644 index 0000000000..a4b5749680 --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/nft-marketplaces.md @@ -0,0 +1,33 @@ +--- +sidebar_label: 'NFT Marketplaces' +sidebar_position: 8 +description: 'Best blockchain for NFT marketplaces: secondary liquidity, royalty enforcement, and receiver-aware assets compared across Ethereum, Base, Solana, and LUKSO.' +--- + +# Best Blockchain for NFT Marketplaces + +NFT marketplaces are decided by two things: where the liquidity already is, and what happens to an asset once it lands in a buyer's account. Ethereum L1, Base, and Solana lead on secondary liquidity today. [**LUKSO wins decisively on the second axis**](../compare/erc721-vs-lsp8.md) β€” receiver-aware assets, mutable on-chain metadata, and a portable creator graph are chain-level defaults, not marketplace-specific integrations. + +## Comparison + +| Criterion | Ethereum L1 | Base | Polygon | Solana | LUKSO | +| ------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------ | ------------------------ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Secondary liquidity | deepest | fastest-growing EVM | strong enterprise mints | deepest non-EVM | early | +| Royalty enforcement | [ERC-2981](https://eips.ethereum.org/EIPS/eip-2981) signal | signal | signal | enforced on compressed NFTs | **LSP18** (RFC) β€” defines recipient/percentage and an enforcement-intent data key; like ERC-2981, actual payment is still marketplace-discretionary, not chain-enforced | +| Asset metadata | off-chain URI, untyped, no integrity check + [ERC-4906](https://eips.ethereum.org/EIPS/eip-4906) signal | off-chain URI + ERC-4906 | off-chain URI + ERC-4906 | Metaplex | βœ… typed [ERC725Y](../../../standards/erc725.md) keys via [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md); the JSON reference and its hash are on-chain and swap-detectable, even when the JSON itself is hosted off-chain | +| Receiver awareness | per-token `onERC721Received` (opt-in) | same | same | per-program | βœ… [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) universal receiver, on every transfer to an LSP1-supporting contract | +| Approval risk | high (`setApprovalForAll`) | high | high | per-program | βœ… low ([LSP6](../../../standards/access-control/lsp6-key-manager.md) scopes) | +| Creator graph | per-marketplace | per-marketplace | per-marketplace | per-marketplace | βœ… [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) + [LSP12](../../../standards/metadata/lsp12-issued-assets.md) | +| Marketplace tooling | mature (Seaport, Reservoir) | mature | mature | mature (Metaplex) | growing (Universal Page, GRAVE) | + +## Why two rows decide most marketplace decisions + +Secondary liquidity β€” where the buyers already are β€” is currently a strong vote for Ethereum L1, Base, Polygon, and Solana. Receiver awareness β€” what happens to an asset the moment it's bought β€” is a strong vote for LUKSO. Every asset built on [LSP8](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) fires [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver` on the buyer's account for every transfer into a contract that implements LSP1 β€” a Universal Profile always qualifies β€” not just on an opt-in `safeTransferFrom` variant, so a marketplace or the buyer's own account can react automatically: register the new asset, unlock holder-only content, or reject a suspicious transfer outright. And because [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) stores a typed, hash-verified reference to the metadata on-chain (a `VerifiableURI`, not just a bare pointer), a client can detect if the underlying JSON was swapped β€” a real integrity guarantee `tokenURI` alone doesn't give you, even where the JSON itself still lives off-chain. + +Most marketplace decisions come down to whether the team is happy to inherit existing liquidity, or willing to build a materially better post-purchase experience and bootstrap volume around it. + +:::tip When LUKSO is the strongest fit +Marketplaces built around receiver-aware assets, mutable on-chain metadata, and a portable creator profile β€” and willing to grow secondary liquidity rather than inherit it β€” are the clearest fit for LUKSO. +::: + +**Related reading:** [ERC721 vs LSP8](../compare/erc721-vs-lsp8.md) Β· [Dynamic NFTs on LUKSO](../build/dynamic-nfts.md) Β· [Best blockchain for creator platforms](./creator-platforms.md) Β· [ERC721's dynamic metadata problem](../problems/erc721-dynamic-metadata.md) diff --git a/docs/learn/why-lukso/best-blockchain-for/social-apps.md b/docs/learn/why-lukso/best-blockchain-for/social-apps.md new file mode 100644 index 0000000000..d0ff2af1f3 --- /dev/null +++ b/docs/learn/why-lukso/best-blockchain-for/social-apps.md @@ -0,0 +1,35 @@ +--- +sidebar_label: 'Social Apps' +sidebar_position: 7 +description: 'Best blockchain for social apps: portable profiles and follower graphs compared across Ethereum, Farcaster, Lens, Solana, and LUKSO.' +--- + +# Best Blockchain for Social Apps + +Social apps are decided by two architectural requirements above all others: a portable user profile, and a portable social graph. [**LUKSO is the only EVM chain that standardizes both at the chain level**](../compare/eoa-vs-universal-profile.md) β€” [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) profile metadata plus [LSP26](../../../standards/accounts/lsp26-follower-system.md) follower system β€” rather than relying on a protocol layer above the chain that only works inside apps that specifically integrate it. + +## Comparison + +| Criterion | Ethereum L1 | Base (Farcaster) | Polygon (Lens) | Optimism (EAS) | Solana | LUKSO | +| ------------------------- | ----------------------- | --------------------------------- | ---------------------------- | ----------------- | -------------------- | ------------------------------------------------------------------------------------ | +| Profile portability | per-protocol | per-protocol (Farcaster dominant) | per-protocol (Lens dominant) | EAS attestations | per-protocol | βœ… chain-native ([LSP3](../../../standards/metadata/lsp3-profile-metadata.md)) | +| Follower graph | per-protocol | Farcaster | Lens | per-protocol | per-protocol | βœ… [LSP26](../../../standards/accounts/lsp26-follower-system.md) | +| Permissions per app | session keys via AA SDK | same | same | same | per-program ad hoc | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) per-controller | +| Onboarding | high friction | low (Smart Wallet + Warpcast) | moderate | moderate | moderate | βœ… low ([LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) relayer) | +| Notification hooks | none standardized | none standardized | none standardized | none standardized | none standardized | βœ… [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) universal receiver | +| Throughput | low | medium | medium | medium | high | medium | +| Existing social user base | largest crypto-native | largest active social (Farcaster) | Lens active users | smaller | active consumer base | growing | + +## Chain-level vs. protocol-level is the whole story here + +Lens and Farcaster both solve portable identity and social graphs β€” but at the protocol layer, one tier above the chain. That works well, but only inside apps that specifically integrate that protocol; a Farcaster-native profile means nothing to a Lens app and vice versa. [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) and [LSP26](../../../standards/accounts/lsp26-follower-system.md) solve the identical problem one tier down, at the **account standard** itself β€” so any app on LUKSO that reads a [Universal Profile](../compare/eoa-vs-universal-profile.md) gets the same profile and follower data automatically, with no external protocol integration required. + +:::tip When LUKSO is the strongest fit +Products that need profile and follower data shared across multiple applications, per-app permissions revocable directly from the user's account, and gasless interaction without operating bundler infrastructure β€” not just within one app's walled garden β€” are the clearest fit for LUKSO. +::: + +:::info When a protocol-layer network is the right call +If the product's entire value is distribution inside an existing crypto-native social graph, building directly on Farcaster (via Base) or Lens (via Polygon) gets access to an established, active audience that LUKSO's younger ecosystem doesn't yet have. +::: + +**Related reading:** [EOA vs Universal Profile](../compare/eoa-vs-universal-profile.md) Β· [Best blockchain for creator platforms](./creator-platforms.md) Β· [On-chain identity architecture](../architecture/onchain-identity.md) Β· [Profile-native apps](../build/profile-native-apps.md) diff --git a/docs/learn/why-lukso/build/_category_.yml b/docs/learn/why-lukso/build/_category_.yml new file mode 100644 index 0000000000..00c9edf9e6 --- /dev/null +++ b/docs/learn/why-lukso/build/_category_.yml @@ -0,0 +1,2 @@ +label: 'πŸš€ Build With LUKSO' +collapsed: true diff --git a/docs/learn/why-lukso/build/dynamic-nfts.md b/docs/learn/why-lukso/build/dynamic-nfts.md new file mode 100644 index 0000000000..73f07ff0a2 --- /dev/null +++ b/docs/learn/why-lukso/build/dynamic-nfts.md @@ -0,0 +1,29 @@ +--- +sidebar_label: 'Dynamic NFTs' +sidebar_position: 1 +description: 'Build stateful, evolving NFTs on LUKSO with LSP8, LSP4, and ERC725Y: per-token on-chain data, no metadata server or marketplace refresh button needed.' +--- + +# Build Dynamic NFTs on LUKSO + +If an NFT's traits evolve, levels accrue, or its art changes on a schedule, metadata has to be data β€” not just a URL. [**LSP8**](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) makes that distinction structural rather than bolted on: every token's attributes, image reference, and provenance live under typed [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) keys on the token contract, and updates go through `setDataForTokenId(tokenId, key, value)` β€” gated by [LSP6](../../../standards/access-control/lsp6-key-manager.md) permissions. No metadata refresh button on a marketplace, no off-chain server required for state to change, no [ERC-4906](https://eips.ethereum.org/EIPS/eip-4906) update event bolted on after the fact. + +## The stack + +| Standard | Role | +| -------------------------------------------------------------------- | ----------------------------------------------------------- | +| [LSP8](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) | Ownership + per-token `bytes32` IDs | +| [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) | Collection and per-token metadata under typed keys | +| [ERC725Y](../../../standards/erc725.md) | The typed key-value storage substrate underneath LSP4 | +| VerifiableURI (LSP4 option) | Integrity guarantees when off-chain media is still involved | +| [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) | Receiver hook so profiles can react when the NFT lands | + +## When this vertical fits + +The asset has per-token state that mutates over time, and that state needs to be readable by _any_ app β€” not just yours. The token ID itself might carry meaning (a content hash, a serial). And you don't want to rely on users clicking "refresh metadata" every time a marketplace's cache goes stale. + +:::info When ERC721 is still the right call +If marketplace compatibility on day one is the entire product β€” the collection has to list on every major NFT venue immediately β€” ERC721 is the pragmatic path, tokenURI tax included. LSP8 is the right call once dynamic, per-token, on-chain state is a real requirement rather than a nice-to-have. +::: + +**Related reading:** [ERC721 vs LSP8](../compare/erc721-vs-lsp8.md) Β· [ERC721's dynamic metadata problem](../problems/erc721-dynamic-metadata.md) Β· [ERC721 token ID limits](../problems/erc721-tokenid-limits.md) Β· [Migrate ERC721 to LSP8](../../migrate/migrate-erc721-to-lsp8.md) diff --git a/docs/learn/why-lukso/build/extending-deployed-contracts.md b/docs/learn/why-lukso/build/extending-deployed-contracts.md new file mode 100644 index 0000000000..dca06f4c5a --- /dev/null +++ b/docs/learn/why-lukso/build/extending-deployed-contracts.md @@ -0,0 +1,30 @@ +--- +sidebar_label: 'Extending Deployed Contracts' +sidebar_position: 2 +description: 'Add new function selectors to a deployed LUKSO contract with LSP17 Contract Extension β€” no admin upgrade key, no shared storage, no diamond-cut ceremony.' +--- + +# Extending Deployed Contracts with LSP17 + +Adding new capability to a contract after deployment usually means an upgrade key someone has to hold forever, or a diamond-storage layout everyone has to get right. [**LSP17 Contract Extension**](../../../standards/accounts/lsp17-contract-extension.md) takes a third path: the base contract's fallback resolves an incoming function selector against an [ERC725Y](../../../standards/erc725.md)-keyed registry of extension addresses and forwards the call. New capability ships as a new extension contract plus a permission-gated `setData` call β€” no admin upgrade key, no shared storage, no diamond-cut ceremony. + +## The stack + +| Standard | Role | +| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | +| [LSP17](../../../standards/accounts/lsp17-contract-extension.md) | The fallback router on the base contract | +| Extension contracts | One per new function selector you want to add | +| [LSP14](../../../standards/access-control/lsp14-ownable-2-step.md) | Safer ownership rotation for whoever can register extensions | +| [LSP6](../../../standards/access-control/lsp6-key-manager.md) | Governs the permission to write the registration key, when the registrant is a Universal Profile controller | + +## When this vertical fits + +You're shipping a contract that will need to grow over time β€” new asset receivers, new signature verifiers, new app-specific entry points β€” without ever surrendering a permanent upgrade key over the base bytecode. Or you're extending a [Universal Profile](../compare/eoa-vs-universal-profile.md) with custom behavior that should be registered per-profile rather than coded directly into the account contract. + +A [Universal Profile](../../../standards/accounts/lsp0-erc725account.md) is the canonical use case, but LSP17 is a general primitive β€” any contract can adopt the same fallback-and-registry pattern. + +:::tip The structural guarantee +Known functions always execute as immutable base bytecode. Only unknown selectors ever hit the fallback and route to a registered extension β€” so there's no path back to silently rewriting behavior the base contract already has, unlike a diamond's permanent `diamondCut` authority. +::: + +**Related reading:** [ERC2535 Diamonds vs LSP17](../compare/erc2535-vs-lsp17.md) Β· [The contract-extension problem](../problems/contract-extension.md) diff --git a/docs/learn/why-lukso/build/gasless-onboarding.md b/docs/learn/why-lukso/build/gasless-onboarding.md new file mode 100644 index 0000000000..3b35c53d2a --- /dev/null +++ b/docs/learn/why-lukso/build/gasless-onboarding.md @@ -0,0 +1,29 @@ +--- +sidebar_label: 'Gasless Onboarding' +sidebar_position: 3 +description: 'Build sponsored, gasless transaction flows on LUKSO with LSP25 executeRelayCall β€” no bundler, no EntryPoint, no paymaster contract.' +--- + +# Build Gasless Onboarding on LUKSO + +Gasless UX is fundamentally a separation between the signer and the payer. [**LSP25**](../../../standards/accounts/lsp25-execute-relay-call.md) puts that separation on the [Key Manager](../../../standards/access-control/lsp6-key-manager.md) every profile already has: a controller signs a payload via `executeRelayCall(signature, nonce, validityTimestamps, payload)`, a relayer submits it to the Key Manager and pays the gas, and the Key Manager verifies the signature and the controller's [LSP6](../../../standards/access-control/lsp6-key-manager.md) permissions β€” including `EXECUTE_RELAY_CALL` β€” before executing on the account. No parallel `UserOperation` mempool, no `EntryPoint` singleton, no separate paymaster contract holding its own deposit β€” sponsored execution is a native function call on infrastructure the profile already has. + +## The stack + +| Standard | Role | +| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) | `executeRelayCall` on the account's Key Manager | +| [LSP6](../../../standards/access-control/lsp6-key-manager.md) | Constrains what the signing controller may authorize (requires `EXECUTE_RELAY_CALL` for relayed calls) | +| [LSP20](../../../standards/accounts/lsp20-call-verification.md) | Lets controllers call the account directly inline when relay isn't needed | + +Relayers can be self-hosted against LSP15's standardized relayer API, or mocked for testing via `github.com/lukso-network/tools-mock-relayer`. + +## When this vertical fits + +You're onboarding users who don't already hold LYX, and you want to sponsor the first N actions, an entire app session, or just the expensive operations (recovery, large `setData` calls) β€” without standing up and operating a bundler. + +:::tip Nonce channels +Use higher nonce channels for parallel session streams instead of serializing every relayed call through channel 0 β€” this lets multiple concurrent flows (e.g. one per device or app) submit relay calls without blocking on each other's ordering. +::: + +**Related reading:** [Gasless onboarding without a paymaster](../problems/gasless-onboarding.md) Β· [ERC-4337's bundler tax](../problems/erc4337-bundler-tax.md) Β· [ERC-4337 vs the LSP account stack](../compare/erc4337-vs-lsp-stack.md) Β· [Gasless onboarding architecture patterns](../architecture/gasless-onboarding-patterns.md) diff --git a/docs/learn/why-lukso/build/profile-native-apps.md b/docs/learn/why-lukso/build/profile-native-apps.md new file mode 100644 index 0000000000..5b7cd33127 --- /dev/null +++ b/docs/learn/why-lukso/build/profile-native-apps.md @@ -0,0 +1,30 @@ +--- +sidebar_label: 'Profile-Native Apps' +sidebar_position: 4 +description: 'Build apps on LUKSO where identity is the account, inventory is on-chain, and the social graph is portable β€” LSP3, LSP5, LSP12, and LSP26.' +--- + +# Build Profile-Native Apps on LUKSO + +A profile-native app reads the account, not its own database. Name, avatar, owned assets, and created assets live directly on the [Universal Profile](../compare/eoa-vs-universal-profile.md) under standardized [**LSP3**](../../../standards/metadata/lsp3-profile-metadata.md), [**LSP5**](../../../standards/metadata/lsp5-received-assets.md), and [**LSP12**](../../../standards/metadata/lsp12-issued-assets.md) keys; followers are indexed by [**LSP26**](../../../standards/accounts/lsp26-follower-system.md), a separate chain-level registry contract keyed by address rather than data stored on the profile itself. Either way, your app composes existing primitives instead of mirroring them into a separate login system. + +## The stack + +| Standard | Role | +| -------------------------------------------------------------- | ------------------------------------------------------------------------- | +| [LSP0](../../../standards/accounts/lsp0-erc725account.md) | The account itself | +| [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) | Profile-level metadata β€” name, avatar, bio, links | +| [LSP5](../../../standards/metadata/lsp5-received-assets.md) | Received-assets inventory registry | +| [LSP12](../../../standards/metadata/lsp12-issued-assets.md) | Issued-assets registry, for creators | +| [LSP26](../../../standards/accounts/lsp26-follower-system.md) | The follower graph | +| [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) | Receiver hook β€” LSP5 entries get written automatically when assets arrive | + +## When this vertical fits + +You're building anything social-shaped on LUKSO: a feed reader, a creator marketplace, a collector index, a follow-based discovery surface. The product _is_ a way to consume what the profile graph already publishes β€” no per-app login flow, no separate identity database to keep in sync. + +:::tip Why this matters for distribution +Because [LSP3](../../../standards/metadata/lsp3-profile-metadata.md), [LSP5](../../../standards/metadata/lsp5-received-assets.md), [LSP12](../../../standards/metadata/lsp12-issued-assets.md), and [LSP26](../../../standards/accounts/lsp26-follower-system.md) are chain-level standards rather than protocol-specific conventions, any new app that speaks the LSP surface reads a user's existing identity, inventory, and social graph on day one β€” no cold-start problem, no asking users to rebuild a profile from scratch. +::: + +**Related reading:** [EOA vs Universal Profile](../compare/eoa-vs-universal-profile.md) Β· [The EOA key-risk problem](../problems/eoa-key-risk.md) Β· [Best blockchain for social apps](../best-blockchain-for/social-apps.md) Β· [On-chain identity architecture](../architecture/onchain-identity.md) diff --git a/docs/learn/why-lukso/build/smart-wallet-ux.md b/docs/learn/why-lukso/build/smart-wallet-ux.md new file mode 100644 index 0000000000..e9dd00dff9 --- /dev/null +++ b/docs/learn/why-lukso/build/smart-wallet-ux.md @@ -0,0 +1,29 @@ +--- +sidebar_label: 'Smart Wallet UX' +sidebar_position: 5 +description: 'Build session keys, scoped delegation, guardian-based recovery, and signature verification on LUKSO with LSP6, LSP11 social recovery, LSP20, ERC-1271.' +--- + +# Build Smart Wallet UX on LUKSO + +A smart wallet UX is a permission graph, not a connect button. [**LSP6 Key Manager**](../../../standards/access-control/lsp6-key-manager.md) is the standardized vocabulary for that graph β€” controllers, permissions, allowed calls, allowed data keys β€” and [**LSP0**](../../../standards/accounts/lsp0-erc725account.md) is the account those controllers act on. Because every account speaks the same permission vocabulary, cross-wallet session portability is possible in principle, unlike ERC-4337's per-vendor validator SDKs that don't share a common permission shape. + +## The stack + +| Standard | Role | +| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| [LSP0](../../../standards/accounts/lsp0-erc725account.md) | The account contract | +| [LSP6](../../../standards/access-control/lsp6-key-manager.md) | Per-controller permissions: target, selector, data-key scoping | +| LSP11 Basic Social Recovery | Guardian-voting recovery ([contract reference](../../../contracts/contracts/LSP11BasicSocialRecovery/LSP11BasicSocialRecovery.md)) | +| [LSP20](../../../standards/accounts/lsp20-call-verification.md) | Inline call verification, when controllers call the account directly rather than through the Key Manager wrapper | +| ERC-1271 | Contract signature validation β€” Sign-In-with-Ethereum, off-chain order signing | + +## When this vertical fits + +Anything where "the wallet has full account control" is the wrong default: session keys scoped to one app, controllers scoped to a single marketplace integration, recovery controllers a user never touches day-to-day, or multi-device flows where each device gets its own permission set instead of sharing one master key. + +:::tip One permission vocabulary, every account +Because LSP6 permissions are a chain-level standard rather than a per-SDK convention, a session key granted by one LSP6-aware wallet means the same thing to every other LSP6-aware wallet or tool β€” no per-vendor integration required to understand what a given controller can do. +::: + +**Related reading:** [Wallet permission scoping](../problems/wallet-permissions.md) Β· [Social recovery without a seed phrase](../problems/social-recovery.md) Β· [The EOA key-risk problem](../problems/eoa-key-risk.md) Β· [EOA vs Universal Profile](../compare/eoa-vs-universal-profile.md) Β· [Smart account permissions](../architecture/smart-account-permissions.md) diff --git a/docs/learn/why-lukso/build/token-economics.md b/docs/learn/why-lukso/build/token-economics.md new file mode 100644 index 0000000000..762182de45 --- /dev/null +++ b/docs/learn/why-lukso/build/token-economics.md @@ -0,0 +1,28 @@ +--- +sidebar_label: 'Token Economics' +sidebar_position: 6 +description: 'Build fungible token economics on LUKSO with LSP7 and LSP4 β€” tokens that integrate with profile identity instead of sitting on a detached balance sheet.' +--- + +# Build Token Economics on LUKSO + +Token economics on LSP isn't a rebuild of ERC20 β€” it's the same balance-and-transfer model with everything you used to bolt on afterward baked in from the start. [**LSP7 Digital Asset**](../../../standards/tokens/LSP7-Digital-Asset.md) ships transfer with [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) receiver hooks, operator authorization, and transfer context, paired with [**LSP4**](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) for typed metadata under [ERC725Y](../../../standards/erc725.md). Because token holders are [Universal Profiles](../compare/eoa-vs-universal-profile.md) by default, distribution, airdrops, and holder-gated features integrate naturally with [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) identity, [LSP5](../../../standards/metadata/lsp5-received-assets.md) received-assets tracking, and the [LSP26](../../../standards/accounts/lsp26-follower-system.md) follower graph β€” instead of a balance sheet detached from who actually holds it. + +## The stack + +| Standard | Role | +| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| [LSP7](../../../standards/tokens/LSP7-Digital-Asset.md) | Balances, transfers, operator authorization | +| [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) | Asset metadata under [ERC725Y](../../../standards/erc725.md) typed keys | +| [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) | Fires automatically on recipients you control (Universal Profile delegates handle this by default) | +| [LSP6](../../../standards/access-control/lsp6-key-manager.md) | Controller-side permission scoping, when account-level authorization policy matters | + +## When this vertical fits + +You're shipping a fungible asset that participates in app or profile UX: a creator coin, a governance token with proposal links in its metadata, an in-game currency that needs to notify vaults on deposit, or a membership asset where every transfer should fire a hook on the recipient's profile. + +:::info When ERC20 is still the right call +When the minimum-interface property is the actual design goal. ERC20 ships exactly balances, a transfer, an allowance pattern, and three metadata fields β€” no recipient call, no reentrancy surface on transfer, no `force` flag to decide on per integration. If a token's entire value proposition is that downstream code can integrate it with zero opinions about behavior beyond "moves balance, fires an event," ERC20 is the right interface, and the absence of an LSP1 hook is a feature rather than a gap. +::: + +**Related reading:** [ERC20 vs LSP7](../compare/erc20-vs-lsp7.md) Β· [The ERC20 approval problem](../problems/erc20-approval-risks.md) Β· [ERC20's missing transfer hooks](../problems/erc20-transfer-hooks.md) Β· [Migrate ERC20 to LSP7](../../migrate/migrate-erc20-to-lsp7.md) diff --git a/docs/learn/why-lukso/compare/_category_.yml b/docs/learn/why-lukso/compare/_category_.yml new file mode 100644 index 0000000000..d07bfe9195 --- /dev/null +++ b/docs/learn/why-lukso/compare/_category_.yml @@ -0,0 +1,2 @@ +label: 'βš–οΈ Compare Standards' +collapsed: true diff --git a/docs/learn/why-lukso/compare/eoa-vs-universal-profile.md b/docs/learn/why-lukso/compare/eoa-vs-universal-profile.md new file mode 100644 index 0000000000..e40cb225d5 --- /dev/null +++ b/docs/learn/why-lukso/compare/eoa-vs-universal-profile.md @@ -0,0 +1,38 @@ +--- +sidebar_label: 'EOA vs. Universal Profile' +sidebar_position: 4 +description: 'Externally owned accounts compared with LUKSO Universal Profiles across identity, permissions, recovery, signing, and gasless execution, feature by feature.' +--- + +# EOA vs. Universal Profile + +An externally owned account (EOA) is one private key and one address β€” portable across every EVM chain, but with no metadata, no per-app permissions, no recovery path, and no receiver hooks. A [**Universal Profile**](../../universal-profile/metadata/read-profile-data.md) is a smart contract account implementing [LSP0 ERC725Account](../../../standards/accounts/lsp0-erc725account.md): [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) for scoped controller permissions, [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) for asset-receipt awareness, [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) for on-chain profile metadata, and [LSP20](../../../standards/accounts/lsp20-call-verification.md) for inline call verification. Where an EOA is a bare signer, a Universal Profile is an account-shaped product out of the box. + +## Structural comparison + +| Feature | EOA | Universal Profile | +| -------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| Account type | key pair | smart contract ([LSP0](../../../standards/accounts/lsp0-erc725account.md)) | +| Identity | address only, off-chain profile elsewhere | [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) profile metadata + asset inventories, on-chain | +| Controllers | exactly one private key | many, each with independently scoped [LSP6](../../../standards/access-control/lsp6-key-manager.md) permissions | +| Recovery | seed phrase β€” a single point of failure | policy expressed through controllers, devices, and trusted contracts | +| Signing | `ecrecover` | `isValidSignature` (ERC-1271, contract-native) | +| Gasless transactions | needs an external forwarder / paymaster | native relay execution via [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) | +| Receiver hooks | none | [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver` + delegate | +| `msg.sender` at target contracts | the EOA's address | the profile contract's own address | + +## An account, not just a signer + +A raw EOA is the minimum a chain can express: `address = keccak(pubkey)[12:]`, one key, one signer. Everything a user-facing product needs on top β€” profile, permissions, recovery, notifications β€” has to be bolted on by the app, off-chain, per project. + +A Universal Profile ships all of it as the account itself. [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) gives every profile a name, image, and links that any dApp can read the same way. [LSP6](../../../standards/access-control/lsp6-key-manager.md) lets a user grant one controller "transfer this token to this pool" and another controller "update this playlist" β€” narrow, revocable, auditable, instead of one key that can do everything. [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) means the profile knows the moment it receives an asset and can react β€” register it, forward it, or reject it β€” without a separate indexing service. + +## Gasless by default, not by integration + +New users don't need to understand "gas" before they can use an app. A Universal Profile executes relayed transactions natively through [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) β€” no custom forwarder contract, no per-app paymaster integration, no forcing users to acquire native currency before their first interaction. That removes the entire onboarding funnel of downloading a wallet, buying crypto, and funding an address just to try a dApp. + +:::tip When a Universal Profile is the right call +Any product where the account carries identity, permissions, or recovery as part of the experience should build on Universal Profiles. An EOA remains the right choice only when "one private key controls one address" is itself the feature you're building around β€” pure cross-chain signer plumbing, hardware-wallet-anchored flows. +::: + +**Related reading:** [The EOA key-risk problem](../problems/eoa-key-risk.md) Β· [Social recovery without a seed phrase](../problems/social-recovery.md) Β· [Wallet permission scoping](../problems/wallet-permissions.md) Β· [Benefits of the LUKSO standards](../../benefits-lukso-standards.md) diff --git a/docs/learn/why-lukso/compare/erc1155-vs-lsp7-lsp8.md b/docs/learn/why-lukso/compare/erc1155-vs-lsp7-lsp8.md new file mode 100644 index 0000000000..fdae893081 --- /dev/null +++ b/docs/learn/why-lukso/compare/erc1155-vs-lsp7-lsp8.md @@ -0,0 +1,36 @@ +--- +sidebar_label: 'ERC1155 vs. LSP7 + LSP8' +sidebar_position: 3 +description: "ERC1155 multi-token contracts compared with LUKSO's LSP7 + LSP8 split-by-shape model: transfer hooks, metadata, batch transfers, and indexing cost." +--- + +# ERC1155 vs. LSP7 + LSP8 + +ERC1155 packs fungible, semi-fungible, and non-fungible token types into a single contract, with type semantics conventionally encoded in the token ID bits β€” a convention every integrator has to reverse-engineer per collection. LUKSO splits the same surface at the standard level instead of the ID level: [**LSP7**](../../../standards/tokens/LSP7-Digital-Asset.md) handles divisible balances, [**LSP8**](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) handles unique items, and downstream code can tell which is which just by checking the interface β€” no per-collection convention required. + +## Comparison + +| Feature | ERC1155 | LSP7 + LSP8 | +| --------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| Contracts per product | one | one LSP7 contract per distinct fungible asset, plus one LSP8 collection for all identifiable items β€” not a fixed number | +| Fungibility signal | convention encoded in token-id bits | declared by the standard itself (LSP7 vs LSP8) | +| Transfer hook | `IERC1155Receiver` (single + batch variants) | one [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver` shape for both | +| Metadata | `uri(id) β†’ string` | [ERC725Y](../../../standards/erc725.md) + [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) keys, per-token via LSP8 | +| Batch transfer | `safeBatchTransferFrom` | `transferBatch(...)` on each standard | +| Indexing cost | high β€” every collection needs its own decoding logic | low β€” asset shape is declared, not inferred | + +## The semantic boundary moves from convention to standard + +ERC1155 makes fungible-vs-unique a contract-internal decision. The high bits of a token ID might mean "this is fungible" in one collection and something else entirely in the next β€” wallets and indexers have to learn each project's convention individually, and get it wrong often enough that this is a recurring source of marketplace bugs. + +LSP7 and LSP8 put that boundary where it belongs: in the standard. If it's fungible, it's an LSP7 contract. If it's identifiable, it's an LSP8 contract. A marketplace or wallet branches on the interface ID once β€” not on a token-ID bit pattern that changes per project. + +## One receiver hook shape, not four + +`IERC1155Receiver` requires implementing both a single-transfer and a batch-transfer callback, on top of whatever `IERC721Receiver` shape a mixed integration also needs to support. LSP7 and LSP8 both dispatch through the same [LSP1 `universalReceiver`](../../../standards/accounts/lsp1-universal-receiver.md) hook, branching on a `typeId` β€” one interface to implement, one code path to audit, regardless of which LUKSO asset is arriving. + +:::info The honest tradeoff +Where ERC1155 ships one contract for a multi-asset product, LSP7 + LSP8 ships one contract per distinct fungible asset plus one LSP8 collection β€” two contracts for a product with a single fungible currency and a single NFT collection, more if there are several distinct fungible assets to keep separate. That's a real, scaling deployment cost, not a flat one-time fee. For a game with a handful of stable item categories, the integration savings from clean type separation pay that back immediately. For a single collection of thousands of loosely related items, ERC1155's one-contract model can still be the pragmatic choice. +::: + +**Related reading:** [ERC1155's complexity problem](../problems/erc1155-complexity.md) Β· [ERC20 vs LSP7](./erc20-vs-lsp7.md) Β· [ERC721 vs LSP8](./erc721-vs-lsp8.md) Β· [Migrate ERC1155 to LSP7 + LSP8](../../migrate/migrate-erc1155-to-lsp7-lsp8.md) diff --git a/docs/learn/why-lukso/compare/erc20-vs-lsp7.md b/docs/learn/why-lukso/compare/erc20-vs-lsp7.md new file mode 100644 index 0000000000..477236a131 --- /dev/null +++ b/docs/learn/why-lukso/compare/erc20-vs-lsp7.md @@ -0,0 +1,42 @@ +--- +sidebar_label: 'ERC20 vs. LSP7' +sidebar_position: 1 +description: 'ERC20 vs LSP7 Digital Asset compared function by function: transfer signatures, receiver hooks, operator authorization, and on-chain metadata storage.' +--- + +# ERC20 vs. LSP7 Digital Asset + +ERC20 is Ethereum's minimum fungible-token interface: six functions, two events, no receiver hook, no structured metadata, no per-transfer context. [**LSP7 Digital Asset**](../../../standards/tokens/LSP7-Digital-Asset.md) keeps the exact same mental model β€” balances, transfers, allowances β€” and then fixes everything that ERC20 leaves for every integrator to rebuild: a `bytes data` payload on every transfer, [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) notifications fired on sender **and** recipient, operator authorization that plugs into the [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) permission system, and structured [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) metadata instead of three bare getter functions. + +## Function-by-function comparison + +| Feature | ERC20 | LSP7 | +| ------------------------------ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Balance model | `balanceOf(address) β†’ uint256` | `balanceOf(address) β†’ uint256` | +| Transfer signature | `transfer(to, amount)` | `transfer(from, to, amount, force, data)` | +| Recipient notification | ❌ none | βœ… [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver` on both sender and recipient, when they're LSP1-supporting contracts | +| Transfer context payload | ❌ none β€” bolted on via wrapper contracts | βœ… native `bytes data` on every transfer | +| Authorization | `approve` / `allowance` / `transferFrom` | `authorizeOperator` β€” scoped, revocable, and notifies the operator | +| Metadata | `name()` / `symbol()` / `decimals()` only | βœ… unlimited [ERC725Y](../../../standards/erc725.md) key-value storage under [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) | +| Accidental-transfer protection | ❌ none | βœ… required `force` flag β€” `force=false` rejects both EOAs and non-LSP1 contracts; `force=true` permits either | +| Batch operations | ❌ none natively | βœ… `transferBatch(...)` | + +## Why the LSP1 hook matters more than it looks + +ERC20's biggest structural gap isn't the missing metadata β€” it's that a token contract has no way to tell the recipient "you just received tokens." That silence is why the `approve` β†’ `transferFrom` two-step exists at all: contracts can't react to incoming value, so they have to be asked for permission in advance instead. + +LSP7 closes that gap directly. Every transfer fires [`universalReceiver`](../../../standards/accounts/lsp1-universal-receiver.md) on both sides through LSP1, for any sender or recipient that's a contract implementing it (EOAs, having no code to call, are unaffected either way). A [Universal Profile](../../universal-profile/metadata/read-profile-data.md) can register received tokens automatically, forward a share to a savings vault, or reject a transfer outright by reverting inside the hook β€” logic that on ERC20 requires a custom wrapper contract deployed and audited per project. On LUKSO it's the default behavior of every LSP7 asset moving between LSP1-aware accounts. + +## Authorization is scoped at the account, not the token + +`authorizeOperator` is still amount-scoped, same as ERC20's `approve` β€” LSP7 doesn't pretend otherwise. What actually changes the security story is where that call originates. On a [Universal Profile](../../universal-profile/key-manager/grant-permissions.md), the controller invoking `authorizeOperator` is itself bound by [LSP6](../../../standards/access-control/lsp6-key-manager.md) permissions β€” allowed calls, allowed data keys, value limits, and one-transaction revocation. Instead of trusting every token contract you've ever approved forever, the account decides what each app controller may do, and can cut it off instantly. + +:::tip When to reach for LSP7 +Any product that wants recipient-aware transfers, structured on-chain metadata, or account-scoped authorization should build on LSP7 from day one β€” it costs nothing over ERC20 and removes an entire category of integration work later. +::: + +## Migrating an existing ERC20 token + +See the hands-on guide: [Migrate ERC20 to LSP7](../../migrate/migrate-erc20-to-lsp7.md). + +**Related reading:** [The ERC20 approval problem](../problems/erc20-approval-risks.md) Β· [ERC20's missing transfer hooks](../problems/erc20-transfer-hooks.md) Β· [Choosing between LSP7 and LSP8](../../digital-assets/choose-lsp7-vs-lsp8.md) diff --git a/docs/learn/why-lukso/compare/erc2535-vs-lsp17.md b/docs/learn/why-lukso/compare/erc2535-vs-lsp17.md new file mode 100644 index 0000000000..e7a3bc591b --- /dev/null +++ b/docs/learn/why-lukso/compare/erc2535-vs-lsp17.md @@ -0,0 +1,36 @@ +--- +sidebar_label: 'ERC2535 Diamonds vs. LSP17' +sidebar_position: 7 +description: 'ERC-2535 Diamond proxies compared with LSP17 Contract Extension: storage model, upgrade authority, and permissioning for adding functions post-deploy.' +--- + +# ERC2535 Diamonds vs. LSP17 Contract Extension + +Both standards let a deployed contract grow new functions after launch. ERC-2535 Diamonds route calls via `delegatecall` to facet contracts sharing one storage layout, controlled by a permanent `diamondCut` admin authority. [**LSP17 Contract Extension**](../../../standards/accounts/lsp17-contract-extension.md) routes unknown function selectors through a fallback to per-selector extension contracts using `CALL` β€” never `delegatecall` β€” registered under [ERC725Y](../../../standards/erc725.md) and gated by [LSP6](../../../standards/access-control/lsp6-key-manager.md), with the base contract's own bytecode immutable either way. + +## Comparison + +| Feature | ERC-2535 Diamonds | LSP17 Contract Extension | +| ------------------------------------ | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Extension mechanism | `delegatecall` to a facet, dispatched by selector | `CALL` to an extension, dispatched by selector β€” `delegatecall` is explicitly not used | +| Storage | shared with facets β€” strict layout discipline required | extension keeps its own separate storage; the base contract's storage can't be corrupted by an extension | +| Registration | `diamondCut`, called by the diamond owner | `setData` on [ERC725Y](../../../standards/erc725.md), permissioned via [LSP6](../../../standards/access-control/lsp6-key-manager.md) | +| Upgrade authority over base bytecode | diamond owner can replace any facet, permanently | none β€” the base contract's own bytecode is immutable either way | +| Which extension handles a selector | diamond owner can repoint any facet, permanently | whoever holds `ADDEXTENSIONS`/`CHANGEEXTENSIONS` can add or change an extension mapping β€” scoped by LSP6, not a single admin lever over the whole contract | +| Permission granularity | one `diamondCut` function gates everything | per-selector `setData` grants β€” independently scoped per extension | + +## CALL, not DELEGATECALL β€” a narrower blast radius by construction + +Diamonds make the base contract itself a router: every call goes through `delegatecall` into a facet sharing the diamond's storage, so a malicious or buggy facet can corrupt the diamond's own storage or `selfdestruct` it outright. Both adding and upgrading behavior go through the same `diamondCut` function β€” powerful, but a standing risk: whoever holds `diamondCut` authority can rewrite what the contract does, permanently, at any time. + +LSP17 is stricter by construction. Extensions are invoked with `CALL`, never `delegatecall` β€” an extension runs in its own storage context and can't overwrite the base contract's storage or `selfdestruct` it, whatever it does. Known functions run as immutable base bytecode that no extension can touch; only _unknown_ selectors hit the fallback. Which extension handles a given selector can still be changed by whoever holds `ADDEXTENSIONS`/`CHANGEEXTENSIONS` β€” that's a real, scoped LSP6 permission, not a claim that extensions can never change β€” but the base contract's own bytecode has no `diamondCut`-style lever at all. + +## The right fit for an account, not just a token + +For a protocol contract that genuinely needs to patch bugs after launch, Diamonds' upgrade lever is the point β€” and users accept that authority as the cost. For an **account** contract like a Universal Profile, that tradeoff inverts: users should never have to trust that a single admin key can rewrite an already-defined function on their account. [LSP0](../../../standards/accounts/lsp0-erc725account.md) uses LSP17 for exactly this reason β€” a Universal Profile can register a new signature verifier or a new asset receiver as an extension for a selector that didn't exist before, but no key, however permissioned, can touch the base contract's own bytecode or the behavior of a function it already defines. + +:::tip When to reach for LSP17 +Extending an account, a wallet, or any contract where the base bytecode needs to stay immutable no matter who holds permissions later β€” LSP17 can only add handling for previously-undefined selectors, never rewrite one the contract already implements. Reach for Diamonds only when shared storage across facets is a genuine requirement and the permanent `diamondCut` authority β€” including the ability to replace already-defined behavior β€” is an accepted cost. +::: + +**Related reading:** [The contract-extension problem](../problems/contract-extension.md) Β· [ERC721 vs LSP8](./erc721-vs-lsp8.md) diff --git a/docs/learn/why-lukso/compare/erc4337-vs-eip7702.md b/docs/learn/why-lukso/compare/erc4337-vs-eip7702.md new file mode 100644 index 0000000000..320457b641 --- /dev/null +++ b/docs/learn/why-lukso/compare/erc4337-vs-eip7702.md @@ -0,0 +1,32 @@ +--- +sidebar_label: 'ERC4337 vs. EIP-7702' +sidebar_position: 6 +description: 'ERC-4337 alt-mempool smart accounts vs EIP-7702 EOA delegation: infrastructure, address migration, gas sponsorship, and residual key risk compared.' +--- + +# ERC4337 vs. EIP-7702 + +ERC-4337 and EIP-7702 are two complementary attempts to bring account-abstraction UX to a chain built around EOAs. ERC-4337 (live since 2023) defines the `UserOperation`, `EntryPoint`, bundler, and paymaster architecture β€” smart accounts get first-class transactor behavior through a parallel mempool. EIP-7702 (Pectra, 2025) lets an _existing_ EOA delegate execution to smart-account code via a signed authorization, without migrating to a new address. Both are real progress on Ethereum. Neither is a substitute for an account that was account-shaped from the start. + +## Comparison + +| Feature | ERC-4337 | EIP-7702 | +| ----------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| Primary object | `UserOperation` from a smart account | set-code transaction from an EOA | +| Infrastructure required | bundler + `EntryPoint` + optional paymaster | normal transaction path plus delegated code | +| Address migration | not by itself β€” a new smart-account address is typical | none needed β€” the existing EOA keeps its address | +| Gas sponsorship | paymaster via `EntryPoint` | any transaction sender can already cover gas for a delegated EOA; matching ERC-4337's exact paymaster UX means pairing 7702 with 4337 infrastructure | +| Validation | `validateUserOp` on the account | EOA authorizes delegation; delegated code handles execution | +| Residual EOA key risk | not present for native contract accounts | present, unless the wallet design adds explicit revocation | + +## Complementary, and both still EOA-shaped underneath + +EIP-7702 solves the migration problem ERC-4337 alone couldn't: users already have EOAs holding assets, ENS names, transaction history, and airdrop eligibility, and 7702 lets them upgrade in place instead of starting over on a new smart-account address. ERC-4337 solves the ecosystem problem β€” bundlers, paymasters, and wallet infrastructure that already exists. Used together, a wallet can upgrade an address via 7702 and route sponsored or bundled operations through 4337 infrastructure. + +What neither one solves is the underlying question of what the account _is_. EIP-7702 delegates execution to code, but the original ECDSA key remains authoritative unless the wallet design carefully handles the delegation lifecycle β€” the single-key risk that started the whole account-abstraction effort doesn't fully go away, it moves. + +## Where LUKSO starts from a different place + +A [Universal Profile](./eoa-vs-universal-profile.md) isn't an EOA wearing delegated code β€” it's [LSP0](../../../standards/accounts/lsp0-erc725account.md), an account contract from creation, with [LSP6](../../../standards/access-control/lsp6-key-manager.md) as the permission layer and no residual single-key authority to reason about. There's no delegation lifecycle to manage because there was never a bare key holding the account in the first place. + +**Related reading:** [ERC-4337 vs the LSP account stack](./erc4337-vs-lsp-stack.md) Β· [EOA vs Universal Profile](./eoa-vs-universal-profile.md) Β· [The EOA key-risk problem](../problems/eoa-key-risk.md) diff --git a/docs/learn/why-lukso/compare/erc4337-vs-lsp-stack.md b/docs/learn/why-lukso/compare/erc4337-vs-lsp-stack.md new file mode 100644 index 0000000000..d087894267 --- /dev/null +++ b/docs/learn/why-lukso/compare/erc4337-vs-lsp-stack.md @@ -0,0 +1,38 @@ +--- +sidebar_label: 'ERC4337 vs. LSP Account Stack' +sidebar_position: 5 +description: "ERC-4337 account abstraction compared with LUKSO's native LSP0 + LSP6 + LSP20 + LSP25 account stack: transaction flow, permissions, gas sponsorship." +--- + +# ERC4337 vs. the LSP Account Stack + +ERC-4337 bolts account abstraction onto Ethereum without protocol changes: a parallel `UserOperation` mempool, an `EntryPoint` singleton, bundlers, and paymaster contracts. LUKSO doesn't need the workaround β€” the [**LSP account stack**](../../../standards/accounts/lsp0-erc725account.md) puts the account contract directly on the normal call path, with permissions and sponsored execution built in as composed standards from day one. + +## Structural comparison + +| Feature | ERC-4337 | LSP account stack | +| --------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| Transaction flow | user β†’ UserOp pool β†’ bundler β†’ EntryPoint β†’ account | user (controller) β†’ account contract, directly | +| Permission layer | validator module β€” different per account implementation | [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) β€” one standardized vocabulary everywhere | +| Gas sponsorship | separate paymaster contract | [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) `executeRelayCall`, on the Key Manager the account already has | +| Signature verification | `validateUserOp` on the account | [LSP20](../../../standards/accounts/lsp20-call-verification.md) `lsp20VerifyCall`, inline | +| Explorer / trace experience | extra hops: `handleOps` β†’ `EntryPoint` β†’ account | a direct call into the account contract | +| Permission vocabulary | non-standard β€” every validator module defines its own | uniform LSP6 permission bitfield + allowed calls + allowed data keys | + +## No bundler, no EntryPoint, no extra hop + +ERC-4337 exists because Ethereum's transaction model is EOA-anchored, and smart-account behavior has to be layered on top without a protocol change. That's a reasonable constraint for Ethereum mainnet β€” but LUKSO isn't constrained by it. A [Universal Profile](./eoa-vs-universal-profile.md) **is** [LSP0](../../../standards/accounts/lsp0-erc725account.md), the account contract, sitting directly on the normal call path. There's no `UserOperation` envelope to construct, no bundler to trust, no `EntryPoint` singleton to route through. `msg.sender` at every downstream contract is the account itself. + +## One permission vocabulary instead of one per validator + +ERC-4337's permission model lives in whatever validator module a given smart-account implementation chooses to ship β€” pluggable, but non-standard: a permission set built for one 4337 wallet doesn't necessarily translate to another. [LSP6](../../../standards/access-control/lsp6-key-manager.md) is a single, standardized permission bitfield with allowed calls, allowed standards, and allowed [ERC725Y](../../../standards/erc725.md) data keys, understood the same way by every Universal Profile, every LUKSO wallet, and every tool built against the standard. + +## Sponsored execution without a paymaster contract + +ERC-4337 sponsors gas through a separate paymaster contract that the EntryPoint calls out to. On LUKSO, sponsored execution is a function on the [Key Manager](../../../standards/access-control/lsp6-key-manager.md) that every Universal Profile already has β€” [`executeRelayCall`](../../../standards/accounts/lsp25-execute-relay-call.md) via LSP25 β€” with nonce channels that support parallel signed-payload streams, no separate sponsorship contract to deploy or trust. + +:::tip When the LSP stack wins +Any product where the account contract itself should be the call entry point β€” clean traces, one standardized permission vocabulary, sponsored execution without extra infrastructure β€” is better served by the native LSP stack than by layering ERC-4337 on top of an EOA-anchored chain. +::: + +**Related reading:** [ERC-4337's bundler tax](../problems/erc4337-bundler-tax.md) Β· [Gasless onboarding without a paymaster](../problems/gasless-onboarding.md) Β· [Wallet permission scoping](../problems/wallet-permissions.md) Β· [ERC-4337 vs EIP-7702](./erc4337-vs-eip7702.md) diff --git a/docs/learn/why-lukso/compare/erc721-vs-lsp8.md b/docs/learn/why-lukso/compare/erc721-vs-lsp8.md new file mode 100644 index 0000000000..6e94ccef4b --- /dev/null +++ b/docs/learn/why-lukso/compare/erc721-vs-lsp8.md @@ -0,0 +1,45 @@ +--- +sidebar_label: 'ERC721 vs. LSP8' +sidebar_position: 2 +description: 'ERC721 vs LSP8 Identifiable Digital Asset compared function by function: token ID types, on-chain metadata, transfer hooks, and operator authorization.' +--- + +# ERC721 vs. LSP8 Identifiable Digital Asset + +ERC721 standardized NFT ownership around a `uint256 tokenId`, one owner, and a single `tokenURI` string. [**LSP8 Identifiable Digital Asset**](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) keeps that ownership model and upgrades every primitive around it: `bytes32` token IDs paired with a declared [`LSP8TokenIdFormat`](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) so a hash, an address, or a name reads as what it is instead of an undifferentiated number, typed per-token data instead of one URI string, and an [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) receiver hook on transfers to any LSP1-supporting contract instead of an opt-in `safeTransferFrom` variant. + +## Function-by-function comparison + +| Feature | ERC721 | LSP8 | +| ------------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Token ID type | `uint256` β€” no standard way to declare what it represents | `bytes32` with a declared [`LSP8TokenIdFormat`](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) β€” number, string, address, or hash, stated explicitly | +| Metadata pointer | `tokenURI(id) β†’ string` | `getDataForTokenId(id, key) β†’ bytes` β€” typed, per-token | +| Dynamic metadata | `MetadataUpdate` event (ERC-4906) β€” signal only, no on-chain write path | βœ… `setDataForTokenId(...)` β€” an actual typed write | +| Transfer hook | `onERC721Received` β€” only on `safeTransferFrom` | βœ… `universalReceiver` ([LSP1](../../../standards/accounts/lsp1-universal-receiver.md)) on every transfer to an LSP1-supporting contract | +| Transfer data payload | `bytes _data` β€” only on `safeTransferFrom` | βœ… `bytes data` on every transfer, plus a required `force` flag β€” `force=false` rejects both EOAs and non-LSP1 contracts, `force=true` permits either | +| Off-chain data integrity | trust the host | βœ… optional `VerifiableURI` in [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) | +| Batch transfers | ❌ none natively | βœ… `transferBatch(...)` | + +## Your NFT is not just an image anymore + +The single biggest limitation of `tokenURI` is that it's a pointer, not a data store. Anything the NFT needs to say about itself has to live off-chain, and updating it only ever produces a _signal_ (ERC-4906's `MetadataUpdate` event) that a metadata server changed β€” never an on-chain guarantee of what changed. + +LSP8 replaces that pointer with [ERC725Y](../../../standards/erc725.md) typed storage per token, under [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) keys. Traits, attributes, and state can evolve on-chain, permissioned and auditable β€” exactly what dynamic NFTs, evolving game items, and reveal mechanics need, without a centralized metadata server holding the real state. + +## A declared format removes a whole category of workaround + +A `uint256` ID is fine for a sequential counter β€” and `uint256` and `bytes32` are the same 256 bits, so capacity was never the issue. What ERC721 lacks is a standard way to say what an ID _means_: a content hash, a serial number, a reference to another contract all look identical to a bare integer, so every integrator either assumes "it's a counter" or has to go learn that project's specific convention. LSP8's [`LSP8TokenIdFormat`](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) data key declares it once, on-chain β€” no per-project convention to reverse-engineer. + +## Receiver awareness by default, not by convention + +ERC721's `onERC721Received` hook only fires when the sender calls `safeTransferFrom` β€” plain `transferFrom` skips it entirely, which is exactly how NFTs get stuck in contracts that can't handle them. LSP8 fires [`universalReceiver`](../../../standards/accounts/lsp1-universal-receiver.md) on every transfer to a contract that implements LSP1, gated by a required `force` flag: `force=false` rejects the transfer to both EOAs and non-LSP1 contracts, while `force=true` permits either β€” though only an LSP1-implementing contract actually gets the callback, since an EOA has no code to call. Marketplaces, vaults, and Universal Profiles can register, forward, or reject incoming NFTs automatically β€” no wrapper contract required. + +:::tip When to reach for LSP8 +Any collection where token IDs need to carry meaning, metadata needs to evolve after mint, or recipients need guaranteed transfer awareness should be built on LSP8 β€” the ERC721 mental model transfers directly, with none of the workarounds. +::: + +## Migrating an existing ERC721 collection + +See the hands-on guide: [Migrate ERC721 to LSP8](../../migrate/migrate-erc721-to-lsp8.md). + +**Related reading:** [ERC721's dynamic metadata problem](../problems/erc721-dynamic-metadata.md) Β· [ERC721 token ID limits](../problems/erc721-tokenid-limits.md) Β· [ERC721's opt-in safe transfer](../problems/erc721-safe-transfer.md) Β· [Choosing between LSP7 and LSP8](../../digital-assets/choose-lsp7-vs-lsp8.md) diff --git a/docs/learn/why-lukso/cross-chain/_category_.yml b/docs/learn/why-lukso/cross-chain/_category_.yml new file mode 100644 index 0000000000..f86f88dc7b --- /dev/null +++ b/docs/learn/why-lukso/cross-chain/_category_.yml @@ -0,0 +1,2 @@ +label: '⛓️ Cross-Chain Comparisons' +collapsed: true diff --git a/docs/learn/why-lukso/cross-chain/erc4337-vs-eip7702-vs-lukso.md b/docs/learn/why-lukso/cross-chain/erc4337-vs-eip7702-vs-lukso.md new file mode 100644 index 0000000000..c76d896c7a --- /dev/null +++ b/docs/learn/why-lukso/cross-chain/erc4337-vs-eip7702-vs-lukso.md @@ -0,0 +1,50 @@ +--- +sidebar_label: 'ERC-4337 vs EIP-7702 vs LUKSO' +sidebar_position: 1 +description: 'ERC-4337, EIP-7702, and LUKSO Universal Profiles compared as three answers to what a smart account should be β€” infrastructure, security, and permissions.' +--- + +# ERC-4337 vs EIP-7702 vs LUKSO Universal Profiles + +Three different answers to "what should a smart account be," and only one of them ships as the ecosystem's own default account. ERC-4337 layers account abstraction on top of Ethereum's EOA-shaped transaction system β€” a parallel `UserOperation` mempool, an `EntryPoint` singleton, bundlers, paymasters. EIP-7702 lets an EOA delegate to smart-contract code while keeping its address and its root key β€” the delegation persists until the EOA replaces or clears it, not just for one transaction. LUKSO skips the retrofit: on an unmodified EVM chain, [Universal Profiles](../../../standards/accounts/lsp0-erc725account.md) _are_ the account, with permissions ([LSP6](../../../standards/access-control/lsp6-key-manager.md)) and relayed execution ([LSP25](../../../standards/accounts/lsp25-execute-relay-call.md)) standardized alongside it since mainnet launch in 2023 β€” not bolted on later as an opt-in SDK choice. + +That head start comes with a real tradeoff. ERC-4337 runs on every EVM chain today, and EIP-7702 upgrades an existing EOA in place without asking a user to move anywhere. LUKSO's integrated stack only ships natively on LUKSO. Pick the right one for the reach you actually need. + +## No parallel infrastructure to trust + +ERC-4337 exists because Ethereum's transaction model can't change at the protocol level, so smart-account behavior gets bolted on above it: a `UserOperation` envelope, a bundler that has to include it, an `EntryPoint` singleton every call routes through, and β€” if the user shouldn't pay gas β€” a separate paymaster contract. Each of those is infrastructure a team has to run, rent, or trust. A Universal Profile has none of that between the user and the call: the account contract sits directly on the normal call path, and [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) sponsors execution as a function on the [Key Manager](../../../standards/access-control/lsp6-key-manager.md) every profile already has β€” no separate paymaster contract to deploy. + +## No root key sitting underneath + +EIP-7702 is the more conservative move β€” an EOA delegates to smart-contract code but keeps its private key as the ultimate override. That's a genuine backwards-compatibility win, and also the model's central limitation: the account is only as safe as that one key, permission scoping on top of it is still whatever the delegated implementation invents, and there's no chain-standardized way to revoke or scope a controller. LUKSO's [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) makes per-controller scopes β€” function, address, call type, data key β€” a first-class part of the account standard, not an implementation detail of whichever contract an EOA happens to delegate to this week. + +## Structural comparison + +| Criterion | ERC-4337 | EIP-7702 | LUKSO Universal Profile | +| ----------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Account type | Smart contract account, EOA still exists | EOA with delegated code | Smart contract account ([LSP0](../../../standards/accounts/lsp0-erc725account.md)) | +| Default on which chains | None β€” opt-in stack on every EVM chain | Per-EOA opt-in on EVM chains supporting EIP-7702 | Ecosystem default on LUKSO β€” the account type the ecosystem is standardized around, on an unmodified EVM chain | +| Infrastructure required | Bundler + EntryPoint + paymaster + indexer | Delegated code alone covers execution; a paymaster + bundler is only needed for the same UX as ERC-4337 | LSP25 relayer submitting to the Key Manager β€” a single submit-call service, no bundler or EntryPoint | +| Permission model | Per-SDK session keys (Biconomy, ZeroDev, Coinbase, Safe) | Inherits delegated implementation; EOA root key overrides | LSP6 per-controller scopes (function, address, call type, data key) | +| Revocation | Per-SDK; per-session expiry common | Per delegated implementation; root key can override | Single transaction on the account, immediate | +| Recovery | Per-SDK; passkeys in Smart Wallet, modules in Safe | Inherits delegated implementation | Multi-controller / social recovery via LSP6 + [LSP11 Basic Social Recovery](../../../contracts/contracts/LSP11BasicSocialRecovery/LSP11BasicSocialRecovery.md) | +| Gasless UX | Paymaster + bundler | Delegated code plus any gas-paying sender; paymaster + bundler only if matching ERC-4337's exact flow | LSP25 relayer submitting to the Key Manager, no bundler | +| Cross-app portability | Address portable; permission semantics per-SDK | Address portable; behavior depends on current delegation | Account, profile, permissions, and social graph portable as one object | +| EVM compatibility | Works on every EVM chain | Works on EVM chains that adopt the EIP | Native on LUKSO; deployable as an EIP-4337-style account on any other EVM chain | +| Maturity | Mature; production across L1 and L2s | Recent; rolling out as part of Pectra | Production on LUKSO since mainnet (2023) | + +## When each chain wins + +- **ERC-4337** wins when EVM-wide compatibility is the binding constraint and the team already operates, or is willing to rent, bundler and paymaster infrastructure. +- **EIP-7702** wins when backwards compatibility with an existing EOA user base is the priority β€” users keep their address and gain smart-account behavior on top of it. +- **LUKSO Universal Profile** wins whenever the account standard itself should carry permissions and relayed execution instead of assembling them from separate infrastructure β€” the common case for identity-, permission-, or gasless-first products, and the reason the LUKSO ecosystem standardizes on it as the default rather than treating it as an add-on. + +:::tip When Universal Profiles are the right call +If the product needs the account contract to be the call entry point β€” clean traces, one standardized permission vocabulary, sponsored execution without extra infrastructure β€” Universal Profiles deliver that natively. ERC-4337 and EIP-7702 both exist to approximate the same outcome on chains that can't change the account model at the protocol level. +::: + +:::info Being honest about the tradeoff +LUKSO's smart-account stack is the most integrated of the three, but it's also the youngest in wall-clock terms and the only one that doesn't run natively across the rest of the EVM. A team that needs day-one reach across every EVM chain still has a real reason to reach for ERC-4337. +::: + +**Related reading:** [ERC-4337 vs the LSP account stack](../compare/erc4337-vs-lsp-stack.md) Β· [ERC-4337 vs EIP-7702](../compare/erc4337-vs-eip7702.md) Β· [The 4337 extension for Universal Profiles](../../universal-profile/advanced-guides/4337-extension.md) Β· [Best blockchain for consumer apps](../best-blockchain-for/consumer-apps.md) diff --git a/docs/learn/why-lukso/cross-chain/ethereum-vs-base-vs-lukso.md b/docs/learn/why-lukso/cross-chain/ethereum-vs-base-vs-lukso.md new file mode 100644 index 0000000000..ec5041e89f --- /dev/null +++ b/docs/learn/why-lukso/cross-chain/ethereum-vs-base-vs-lukso.md @@ -0,0 +1,41 @@ +--- +sidebar_label: 'Ethereum vs Base vs LUKSO' +sidebar_position: 2 +description: 'Ethereum L1, Base, and LUKSO compared head-to-head for consumer apps β€” account model, identity, onboarding, permissions, and social primitives.' +--- + +# Ethereum vs Base vs LUKSO for Consumer Apps + +Three different bets on what a consumer crypto app needs. Ethereum L1 is the largest market and the deepest liquidity, but the consumer stack β€” account, identity, permissions, social β€” is composed from separately maintained protocols and infrastructure on top of a chain that wasn't designed around any of them. Base is the lowest-friction onboarding surface in EVM today, but the integration is vendor-hosted: Coinbase owns the wallet, the paymaster, and most of the distribution. LUKSO is the only one of the three where account, permissions, relay, profile, and social are unified under one set of standards at the chain level β€” the trade is a smaller ecosystem today in exchange for not having to assemble that stack yourself. + +## Structural comparison + +| Criterion | Ethereum L1 | Base | LUKSO | +| ---------------------- | -------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------- | +| Account model | EOA + ERC-4337 (per-SDK) | EOA + Coinbase Smart Wallet (passkeys) | [LSP0](../../../standards/accounts/lsp0-erc725account.md) smart account by default | +| Identity | ENS + per-protocol profile | Basenames + Smart Wallet | [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) portable profile | +| Permissions | Session keys via AA SDK | Session keys via Smart Wallet | [LSP6](../../../standards/access-control/lsp6-key-manager.md) per-controller scopes | +| Onboarding | High friction (EOA + AA stack) | Low friction (Coinbase-hosted) | Low friction ([LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) relayer) | +| Social primitives | Protocol-layer (Lens, Farcaster) | Farcaster dominant | [LSP26](../../../standards/accounts/lsp26-follower-system.md) follower system | +| Gasless UX | Paymaster + bundler | Coinbase paymaster | LSP25 relayer (no bundler) | +| Liquidity / DeFi reach | Deepest | Growing fast | Limited (early) | +| Ecosystem maturity | Mature | Maturing fast | Early | +| Vendor lock-in | None | Coinbase-hosted services | LSP-aware wallets / providers | + +## Where LUKSO pulls ahead, and where it doesn't yet + +Ethereum L1's consumer stack is assembled, not designed: ENS resolves a name, an ERC-4337 SDK adds a smart account, a paymaster sponsors gas, and Lens or Farcaster supplies a social graph β€” four separate integrations, none of which know about each other. Base collapses most of that into one vendor: Coinbase Smart Wallet handles the account, the passkey, and the paymaster in a single onboarding flow, which is exactly why it's the fastest EVM onboarding path today. The cost is that the flow is Coinbase's to change. + +LUKSO collapses the same stack differently β€” not into a vendor, but into the account standard itself. A [Universal Profile](../compare/eoa-vs-universal-profile.md) is simultaneously the smart account (LSP0), the permission surface (LSP6), the gasless relay target (LSP25), the portable identity (LSP3), and a node in the follower graph (LSP26). No app needs to integrate Coinbase's infrastructure or a third-party social protocol to get all five; every LUKSO wallet and every profile-aware app reads the same standardized state. The honest gap is liquidity and ecosystem depth β€” LUKSO is not where Ethereum L1's secondary markets or Base's Coinbase-scale distribution already are, and a team choosing LUKSO is choosing integration over inherited reach. + +## When each chain wins + +- **Ethereum L1** wins when liquidity, protocol composability, or institutional credibility is the binding constraint β€” the product is intrinsically protocol-shaped and needs to sit where the deepest markets already are. +- **Base** wins when mainstream consumer reach via Coinbase distribution and passkey UX dominates, and the lock-in to Coinbase-hosted infrastructure is an acceptable trade for that reach. +- **LUKSO** wins when the product is identity-first or social-first, needs gasless interaction without operating bundler infrastructure, benefits from per-controller permissions, or expects to share profile data across multiple applications without per-protocol integration β€” which is most consumer products that aren't fundamentally about trading. + +:::tip Picking between the three +Ask which primitive the product can least afford to assemble itself. If it's liquidity, go to Ethereum L1. If it's mainstream onboarding today, go to Base. If it's a standardized account, identity, and social layer that every future app on the chain already speaks, LUKSO is the only one of the three built around that from the start. +::: + +**Related reading:** [Best blockchain for consumer apps](../best-blockchain-for/consumer-apps.md) Β· [Best EVM chain for consumer apps](../best-blockchain-for/evm-consumer-apps.md) Β· [ERC-4337 vs EIP-7702 vs LUKSO](./erc4337-vs-eip7702-vs-lukso.md) Β· [EOA vs Universal Profile](../compare/eoa-vs-universal-profile.md) diff --git a/docs/learn/why-lukso/cross-chain/lukso-vs-major-evms.md b/docs/learn/why-lukso/cross-chain/lukso-vs-major-evms.md new file mode 100644 index 0000000000..1df0b5b540 --- /dev/null +++ b/docs/learn/why-lukso/cross-chain/lukso-vs-major-evms.md @@ -0,0 +1,33 @@ +--- +sidebar_label: 'LUKSO vs. Major EVMs' +sidebar_position: 3 +description: 'LUKSO compared with Ethereum L1, Base, Arbitrum, Optimism, and Polygon on account model, permissions, and app-layer standardization.' +--- + +# LUKSO vs. Major EVMs + +Ethereum L1, Base, Arbitrum, Optimism, and Polygon all still start from the same account reality: EOAs by default, smart accounts opt-in, session keys defined per-SDK, and profile or social state assembled from separate vendor layers (Farcaster here, Lens there, EAS attestations somewhere else). LUKSO starts from a different baseline: [**LSP0**](../../../standards/accounts/lsp0-erc725account.md) accounts, [**LSP6**](../../../standards/access-control/lsp6-key-manager.md) permissions, [**LSP25**](../../../standards/accounts/lsp25-execute-relay-call.md) relays, and [**LSP3/LSP4**](../../../standards/metadata/lsp3-profile-metadata.md) metadata are standardized at the chain level, not assembled per app. + +## Comparison + +| Criterion | Ethereum L1 | Base | Arbitrum | Optimism | Polygon | LUKSO | +| -------------------------- | ----------------------------------------- | ------------------------------------- | --------------------------------------- | --------------------------------------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| Account model | EOA default; ERC-4337/EIP-7702 retrofit | EOA default; Coinbase Smart Wallet | EOA default; ERC-4337 opt-in | EOA default; ERC-4337 opt-in | EOA default; AA via tooling | βœ… [LSP0](../../../standards/accounts/lsp0-erc725account.md) smart contract account by default | +| Permission model | per-wallet / per-account implementation | vendor wallet / session-key model | per-wallet / per-account implementation | per-wallet / per-account implementation | per-wallet / per-account implementation | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) per-controller scopes on the account | +| Gasless UX infrastructure | bundler + EntryPoint + paymaster | Coinbase-hosted wallet/paymaster path | bundler + paymaster stack | bundler + paymaster stack | AA tooling / gas station paths | βœ… [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) relay call β€” no bundler/EntryPoint split | +| Metadata and profile state | ENS, tokenURI, per-protocol profiles | Basenames, Farcaster, app-level data | ENS-compatible, app-level data | EAS attestations, app-level data | Lens, app-level data | βœ… [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) + [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) via ERC-725Y | +| Receiver-aware assets | ERC-721/1155 hooks; ERC-20 receiver-blind | same ERC split as Ethereum | same ERC split as Ethereum | same ERC split as Ethereum | same ERC split as Ethereum | βœ… [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) notifications for every LSP7/LSP8 transfer | +| Extensibility | proxy patterns, ERC-2535 diamonds | proxy patterns, vendor modules | proxy patterns, account modules | proxy patterns, account modules | proxy patterns, account modules | βœ… [LSP17](../../../standards/accounts/lsp17-contract-extension.md) contract extensions | +| Main tradeoff today | deepest liquidity, mainnet cost/UX | strongest onboarding, vendor-hosted | mature DeFi, weaker identity defaults | strong ecosystem alignment, weaker profile defaults | broad integrations, assembled primitives | smallest ecosystem today, but the most standardized app-layer substrate | + +## Why "inherits Ethereum" isn't the same as "app-layer ready" + +Base, Arbitrum, Optimism, and Polygon all benefit from Ethereum's settlement, tooling, and research β€” that's real, and it matters. But each one still composes a consumer app's actual UX (accounts, sessions, permissions, profile data, sponsored gas) from a different stack of vendors and conventions. A Base app reaches for Coinbase Smart Wallet and Farcaster; an Optimism app reaches for EAS; a Polygon app reaches for Lens. Each is a strong local answer. None of them is a shared substrate the chain itself guarantees. + +LUKSO's bet is to standardize exactly those primitives β€” account, permission, relay, metadata, receiver, and social state β€” at the chain level, so every app on LUKSO starts from the same foundation instead of re-assembling it from scratch. + +:::tip When LUKSO is the strongest EVM choice +Identity-first, creator-first, profile-first, asset-first, or social-first products that need granular per-application permissions, gas-sponsored UX without a bundler stack, and account/metadata/receiver/asset/social standards designed as one coherent system β€” not five vendor integrations β€” are best served by LUKSO among major EVMs today. +::: + +**Related reading:** [EVM chains for consumer apps](../best-blockchain-for/evm-consumer-apps.md) Β· [Best blockchain for consumer apps](../best-blockchain-for/consumer-apps.md) Β· [Ethereum vs Base vs LUKSO](./ethereum-vs-base-vs-lukso.md) Β· [Consumer crypto architecture](../architecture/consumer-crypto-stack.md) diff --git a/docs/learn/why-lukso/cross-chain/solana-vs-lukso.md b/docs/learn/why-lukso/cross-chain/solana-vs-lukso.md new file mode 100644 index 0000000000..3df3fc088d --- /dev/null +++ b/docs/learn/why-lukso/cross-chain/solana-vs-lukso.md @@ -0,0 +1,37 @@ +--- +sidebar_label: 'Solana vs. LUKSO' +sidebar_position: 4 +description: 'Solana vs LUKSO for consumer apps: non-EVM throughput and mobile SDKs versus a standardized EVM account, permission, and social stack.' +--- + +# Solana vs. LUKSO for Consumer Apps + +Solana and LUKSO are the two chains built around the consumer use case first, and they take opposite architectural bets to get there. Solana bets on non-EVM throughput, native fee delegation, and the most mature mobile SDK ecosystem in the industry. LUKSO bets on standardizing accounts, permissions, profiles, asset notifications, and social primitives at the chain level β€” while staying fully EVM-compatible. + +## Comparison + +| Criterion | Solana | LUKSO | +| --------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Account model | keypair + program-derived addresses | βœ… [LSP0](../../../standards/accounts/lsp0-erc725account.md) smart-contract account by default | +| Identity / profile | per-program; SNS for names, Civic for KYC | βœ… [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) portable profile β€” one chain-level standard | +| Permissions | per-program access checks; no shared standard | βœ… [LSP6](../../../standards/access-control/lsp6-key-manager.md) per-controller scopes (function, address, call type, data key) | +| Gasless UX | native fee delegation | βœ… [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) relayer β€” no bundler required | +| Asset notifications | per-program | βœ… [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) universal receiver | +| Social primitives | per-program / per-app | βœ… [LSP26](../../../standards/accounts/lsp26-follower-system.md) follower system | +| Metadata model | Metaplex (mutable on/off-chain) | [ERC725Y](../../../standards/erc725.md) key/value via [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) + [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) | +| Throughput | highest among consumer-ready chains | medium | +| EVM compatibility | none | βœ… native β€” Solidity, Hardhat, Foundry work unchanged | +| Mobile SDK maturity | most mature (Solana Mobile Stack) | growing | +| Consumer ecosystem maturity | mature, non-EVM | early, growing | + +## The honest tradeoff + +Solana wins on raw throughput and mobile-SDK maturity today, at the cost of leaving the entire EVM tooling ecosystem behind β€” no Solidity, no Hardhat, no Foundry, no reusing existing contracts. LUKSO keeps full EVM compatibility while standardizing the exact primitives consumer apps rebuild on every other EVM chain: account, permission, relay, identity, social, and metadata, all defined at the standard level instead of assembled per program. + +The decision reduces to one question: is non-EVM tooling acceptable for this product? If yes, Solana's throughput and embedded-wallet ecosystem are the more mature path today. If EVM compatibility matters β€” existing Solidity contracts, existing tooling, future portability across EVM chains β€” LUKSO is the only chain in this comparison that ships standardized accounts, permissions, gasless UX, profiles, and a follower system together, on infrastructure your team already knows. + +:::tip When LUKSO is the better fit +Products that need EVM tooling for existing contracts or team expertise, or that need chain-level standardized accounts, permissions, profiles, and social primitives rather than assembling them from separate per-program components, are better served by LUKSO than by Solana. +::: + +**Related reading:** [Best blockchain for consumer apps](../best-blockchain-for/consumer-apps.md) Β· [Best blockchain for mobile apps](../best-blockchain-for/mobile-apps.md) Β· [Consumer crypto architecture](../architecture/consumer-crypto-stack.md) diff --git a/docs/learn/why-lukso/erc-explainers/_category_.yml b/docs/learn/why-lukso/erc-explainers/_category_.yml new file mode 100644 index 0000000000..dab2963de3 --- /dev/null +++ b/docs/learn/why-lukso/erc-explainers/_category_.yml @@ -0,0 +1,2 @@ +label: 'πŸ“– ERC Standards Explained' +collapsed: true diff --git a/docs/learn/why-lukso/erc-explainers/erc-20.md b/docs/learn/why-lukso/erc-explainers/erc-20.md new file mode 100644 index 0000000000..51105ae3e0 --- /dev/null +++ b/docs/learn/why-lukso/erc-explainers/erc-20.md @@ -0,0 +1,93 @@ +--- +sidebar_label: 'What is ERC20?' +sidebar_position: 1 +description: 'ERC20 origin, the six-function ABI, its well-known approval and receiver-hook limits, and the LSP7 standard its own author built to fix them.' +--- + +# What is ERC20? + +ERC20 is Ethereum's fungible token standard, proposed as [GitHub issue #20](https://github.com/ethereum/EIPs/issues/20) on 19 November 2015 by **Fabian Vogelsteller** and co-authored with **Vitalik Buterin**, later formalized as [EIP-20](https://eips.ethereum.org/EIPS/eip-20). Six functions, two events, no opinion about receivers, metadata, accounts, or permissions β€” that minimalism is exactly what let the DeFi economy build on top of it, and exactly what's left every integrator since 2015 paying an integration tax for the gaps. Vogelsteller went on to co-found **LUKSO** and design **LSP7 Digital Asset** as the standard he wished he'd been able to write the first time. + +## Origin + +Vogelsteller filed the proposal as a minimum interface any token contract could implement β€” deliberately underspecified. That underspecification is _why_ it worked: an exchange, a wallet, a DEX, and a lending market written by five different teams could all transact the same token without coordinating. By 2017 the standard had absorbed the ICO boom. By 2020, Compound and Uniswap were entire economies built on the assumption that `balanceOf` and `transferFrom` mean exactly what the spec says. A decade later, trillions of dollars in supply move through that six-function interface every week. + +## The interface + +```solidity +// EIP-20 β€” required +function totalSupply() external view returns (uint256); +function balanceOf(address owner) external view returns (uint256); +function transfer(address to, uint256 value) external returns (bool); +function transferFrom(address from, address to, uint256 value) external returns (bool); +function approve(address spender, uint256 value) external returns (bool); +function allowance(address owner, address spender) external view returns (uint256); + +event Transfer(address indexed from, address indexed to, uint256 value); +event Approval(address indexed owner, address indexed spender, uint256 value); + +// EIP-20 β€” optional +function name() external view returns (string memory); +function symbol() external view returns (string memory); +function decimals() external view returns (uint8); +``` + +## Where it breaks down + +### The approval problem + +`approve(spender, amount)` and the spender's later `transferFrom` split user intent across two transactions the wallet can't connect. Apps request max-uint approvals to skip the second prompt, turning every authorized spender into a standing risk β€” if the spender contract is later upgraded, drained, or misconfigured, the approval is already sitting there. Revoke flows are reactive cleanup, not prevention. Workarounds layered on top: `revoke.cash`, EIP-2612 `permit`, Uniswap's Permit2, ERC-1363 approve-and-call β€” none fix intent context at the primitive layer. + +### The receiver problem + +`transfer` never calls the recipient. If the receiver is a contract that doesn't know what to do with incoming tokens, the balance is simply stranded β€” no on-transfer hook exists to credit a deposit or revert. Millions of dollars in tokens sent directly to token contracts themselves is a direct, well-documented consequence. + +### The metadata problem + +Three optional methods β€” `name`, `symbol`, `decimals` β€” and nothing else. No icon, no description, no link to documentation or an audit, no way to attach structured data without forking the contract or pinning a JSON file to a server that can go offline. + +### The accounts problem + +ERC20 assumes its owners are addresses β€” either an EOA (one key, no recovery, no scoping) or a contract that has to implement everything itself. The standard pushes account abstraction entirely onto the application layer, which is why a 2015 token spec spawned years of follow-on EIP work (ERC-4337, EIP-7702) trying to retrofit what an account should be. + +### The integration tax + +Permit (EIP-2612), Permit2, ERC-1363, ERC-777, ERC-3009, ERC-4626 β€” each adds a layer instead of fixing the substrate, and each ships its own adoption problem because half the ecosystem keeps using the original six functions underneath. + +## The LUKSO successor + +[**LSP7 Digital Asset**](../../../standards/tokens/LSP7-Digital-Asset.md) keeps ERC20's balance model exactly, and builds the surrounding system directly into the standard instead of leaving it as a patch layer. + +| | ERC20 | LSP7 | +| ---------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| Transfer signature | `transfer(to, amount)` | `transfer(from, to, amount, force, data)` | +| Recipient notification | none | [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver` on either side that's a contract implementing it | +| Authorization | `approve` / `allowance` / `transferFrom` | `authorizeOperator` + [LSP6](../../../standards/access-control/lsp6-key-manager.md) controller scope | +| Metadata | `name` / `symbol` / `decimals` only | [ERC725Y](../../../standards/erc725.md) under [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) keys | +| Transfer data payload | none β€” workaround via wrappers | native `bytes data` | + +Behind LSP7 sits the rest of the LUKSO account stack: [LSP0](../../../standards/accounts/lsp0-erc725account.md) makes the account a programmable contract instead of a bare address, [LSP3](../../../standards/metadata/lsp3-profile-metadata.md) gives it a profile, [LSP6](../../../standards/access-control/lsp6-key-manager.md) makes permissions an account property instead of a token allowance, and [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) makes gasless onboarding a native function rather than a bolted-on mempool. Where ERC20 pushed every one of these concerns to the application layer, LUKSO designed them into the account and token system from the start. + +:::info Is ERC20 still the right choice? +When compatibility is the entire product β€” a wrapped reserve asset, a stablecoin whose universe of integrators expects the exact ERC20 ABI, or a protocol whose audit assumes zero recipient-side code execution on transfer β€” ERC20 remains the right interface. LSP7's advantages matter once receiver awareness, structured metadata, or account-level permission scoping become real requirements. +::: + +## FAQ + +### What does ERC20 stand for? + +ERC stands for Ethereum Request for Comments β€” ERC20 is the twentieth proposal in that series, later formalized as EIP-20 at [eips.ethereum.org/EIPS/eip-20](https://eips.ethereum.org/EIPS/eip-20). + +### What is wrong with ERC20? + +Five well-known limits: split-intent approvals that enable phishing, no receiver hook (stranding tokens sent to contracts), thin metadata (three strings, no schema), no built-in account abstraction, and a growing stack of patch standards (Permit, Permit2, ERC-1363, ERC-777, ERC-4337) instead of a fixed substrate. + +### What is LSP7? + +LSP7 is LUKSO's Digital Asset standard, designed by Fabian Vogelsteller β€” ERC20's original author β€” as the fungible token standard for Universal Profiles. It keeps the balance model and adds a required `force` flag, a `data` payload on every transfer, [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) notifications on whichever side is a contract implementing it, and account-scoped operator authorization via [LSP6](../../../standards/access-control/lsp6-key-manager.md). + +### Can I migrate an ERC20 token to LSP7? + +Yes β€” the balance model is identical, so migration is contract-level, not user-level. See the [full migration guide](../../migrate/migrate-erc20-to-lsp7.md). + +**Related reading:** [ERC20 vs LSP7](../compare/erc20-vs-lsp7.md) Β· [The ERC20 approval problem](../problems/erc20-approval-risks.md) Β· [ERC20's missing transfer hooks](../problems/erc20-transfer-hooks.md) diff --git a/docs/learn/why-lukso/erc-explainers/erc-4337.md b/docs/learn/why-lukso/erc-explainers/erc-4337.md new file mode 100644 index 0000000000..e8a72fd283 --- /dev/null +++ b/docs/learn/why-lukso/erc-explainers/erc-4337.md @@ -0,0 +1,92 @@ +--- +sidebar_label: 'What is ERC4337?' +sidebar_position: 3 +description: 'ERC4337 account abstraction explained: UserOperations, bundlers, EntryPoint, paymasters, and the native LSP0 + LSP6 + LSP25 alternative.' +--- + +# What is ERC4337? + +ERC4337 is Ethereum's account abstraction standard: activated on mainnet 1 March 2023, originally proposed 29 September 2021 by **Vitalik Buterin** with **Yoav Weiss**, **Dror Tirosh**, **Shahaf Nacson**, **Alex Forshtat**, **Kristof Gazso**, and **Tjaden Hess**. Rather than modify the protocol, it introduces a parallel `UserOperation` mempool, a singleton `EntryPoint` contract, bundlers that batch UserOps into transactions, and paymasters that can sponsor gas. Contract accounts get first-class transactor behavior β€” at the cost of an entirely separate infrastructure layer sitting beside the normal transaction flow. LUKSO's [**LSP0 + LSP6 + LSP20 + LSP25**](../compare/erc4337-vs-lsp-stack.md) stack ships the same account-abstraction outcomes natively, with no parallel layer required. + +## Origin + +Protocol-level account abstraction had been attempted for years before ERC4337 β€” [EIP-86](https://github.com/ethereum/EIPs/issues/86) (2017), [EIP-2938](https://eips.ethereum.org/EIPS/eip-2938) (2020), [EIP-3074](https://eips.ethereum.org/EIPS/eip-3074) (2021) β€” none shipped, each requiring consensus-layer changes the network wasn't ready to ratify. In parallel, a meta-transaction lineage ran from [ERC-1613](https://eips.ethereum.org/EIPS/eip-1613) (Tabookey, 2018) through [ERC-2771](https://eips.ethereum.org/EIPS/eip-2771) (2020, co-authored by the same Tabookey team). ERC4337 is that meta-transaction lineage absorbing the account-abstraction ambition: the bundler is GSN's relay server, generalized; the EntryPoint is GSN's RelayHub, generalized. + +Vitalik's own introduction post was titled, in plain text, ["ERC-4337: Account Abstraction _Without_ Ethereum Protocol Changes."](https://medium.com/infinitism/erc-4337-account-abstraction-without-ethereum-protocol-changes-d75c9d94dc4a) That title is the clearest acknowledgment available of the design constraint being worked around β€” a hard fork wasn't politically available, so the whole architecture was built to sit above the protocol instead of inside it. + +## The interface + +```solidity +// EntryPoint (singleton) +function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary) external; + +// IAccount (each smart account implements) +function validateUserOp( + PackedUserOperation calldata userOp, + bytes32 userOpHash, + uint256 missingAccountFunds +) external returns (uint256 validationData); + +// IPaymaster (optional, sponsors gas) +function validatePaymasterUserOp( + PackedUserOperation calldata userOp, + bytes32 userOpHash, + uint256 maxCost +) external returns (bytes memory context, uint256 validationData); + +function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost, uint256 actualUserOpFeePerGas) external; +``` + +## Where the layered approach costs + +### The parallel mempool + +UserOperations aren't transactions β€” they live in a separate alt-mempool, get packed by bundlers, and route through the singleton EntryPoint before ever reaching the account. Every UserOp pays a bundler-submission cost on top of the gas it consumes, and the bundler position carries censorship and reordering risk the broader L1 mempool doesn't share. + +### A worse trace and explorer surface + +Block explorers show `handleOps`, not the user's actual function call. Debugging happens through an extra indirection: `handleOps` β†’ `EntryPoint` β†’ account β†’ target, instead of a direct call. + +### Per-account validator modules, no shared permission vocabulary + +`validateUserOp` is one function each account implements however it wants β€” deliberately. The cost is that Safe, Kernel, Biconomy, Alchemy LightAccount, and Soul Wallet each invent their own validator shape. Permission audits are per-account, not per-standard, and migrating between implementations is non-trivial. + +### Paymaster trust as a separate failure surface + +Gas sponsorship needs a paymaster contract with its own ETH deposit, its own validation function, and its own post-execution accounting β€” a second failure point on every sponsored operation, and wallets end up shipping per-paymaster integrations. + +### Account abstraction sits above the EVM, not in it + +[EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) (Pectra, May 2025) β€” co-authored by Vitalik himself β€” is the eventual protocol-level patch. Its own Motivation section cites that existing EOAs can't become ERC4337 smart accounts without losing address history, ENS, reputation, and airdrop eligibility. Four years after framing account abstraction as something to ship _without_ protocol changes, its lead author was co-signing the protocol change that closes the gap the layered design left open. + +## The LUKSO successor + +| | ERC4337 | LSP account stack | +| ---------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| Transaction flow | user β†’ UserOp pool β†’ bundler β†’ EntryPoint β†’ account | user (controller) β†’ account contract, directly | +| Permission layer | per-account validator module | [LSP6](../../../standards/access-control/lsp6-key-manager.md) β€” one standardized vocabulary | +| Gas sponsorship | separate paymaster contract | [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) `executeRelayCall`, on the Key Manager the account already has | +| Signature verification | `validateUserOp` on the account | [LSP20](../../../standards/accounts/lsp20-call-verification.md) inline verification | +| Trace / explorer | `handleOps` + EntryPoint hop | direct call into the account contract | + +[LSP0 ERC725 Account](../../../standards/accounts/lsp0-erc725account.md) _is_ the call entry point β€” there's no EntryPoint singleton, no UserOperation envelope, no bundler. `msg.sender` at every downstream contract is the account itself. Even accounting for Pectra's real progress, the LSP stack still covers ground 7702 + 4337 don't: a Universal Profile has no residual EOA root key that can override a recovered configuration, whereas a 7702-delegated EOA's original key remains authoritative unless the wallet design explicitly handles revocation (a gap [EIP-7851](https://eips.ethereum.org/EIPS/eip-7851) still only proposes to close, as a draft). Permissions, receiving hooks, ownership transitions, and extensions are one shared vocabulary across every LUKSO account, not a per-wallet-vendor module. + +:::info Is ERC4337 still the right choice? +When account abstraction needs to layer onto a chain whose native transaction model is EOA-anchored, with pluggable per-account validators and a sponsorship plane decoupled from the account contract itself, ERC4337's decoupling is a genuine strength β€” validators are swappable and mixable per UserOp. The LSP stack wins when the account contract itself should be the entry point, with no EntryPoint hop and one standardized permission vocabulary. +::: + +## FAQ + +### What is a bundler? + +An off-chain actor that watches the UserOperation mempool, validates UserOps, packs valid ones into a single `handleOps` transaction, and submits it on-chain β€” earning fees per UserOp, with real censorship and reordering risk. + +### What's the difference between ERC4337 and EIP-7702? + +ERC4337 deploys a separate smart-contract account processed through bundlers and an EntryPoint. EIP-7702 lets an _existing_ EOA delegate execution to contract code without redeploying β€” but the original ECDSA key remains authoritative unless the wallet design handles revocation carefully. See [ERC4337 vs EIP-7702](../compare/erc4337-vs-eip7702.md). + +### Is a Universal Profile a smart wallet that uses ERC4337? + +No. A Universal Profile is a smart wallet built on [LSP0](../../../standards/accounts/lsp0-erc725account.md), [LSP6](../../../standards/access-control/lsp6-key-manager.md), [LSP20](../../../standards/accounts/lsp20-call-verification.md), and [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) that doesn't use the 4337 stack at all β€” no EntryPoint, no UserOperation, no bundler. + +**Related reading:** [ERC4337 vs the LSP account stack](../compare/erc4337-vs-lsp-stack.md) Β· [ERC4337 vs EIP-7702](../compare/erc4337-vs-eip7702.md) Β· [ERC4337's bundler tax](../problems/erc4337-bundler-tax.md) Β· [Gasless onboarding without a paymaster](../problems/gasless-onboarding.md) diff --git a/docs/learn/why-lukso/erc-explainers/erc-721.md b/docs/learn/why-lukso/erc-explainers/erc-721.md new file mode 100644 index 0000000000..a3a0e00241 --- /dev/null +++ b/docs/learn/why-lukso/erc-explainers/erc-721.md @@ -0,0 +1,105 @@ +--- +sidebar_label: 'What is ERC721?' +sidebar_position: 2 +description: 'ERC721 origin, the NFT ABI, tokenURI rot and other well-known limits, and the LSP8 Identifiable Digital Asset standard built to address them.' +--- + +# What is ERC721? + +ERC721 is Ethereum's non-fungible token standard, drafted by **Dieter Shirley** (then CTO of Dapper Labs) as [GitHub issue #721](https://github.com/ethereum/EIPs/issues/721) on 22 September 2017, and finalized on 24 January 2018 as [EIP-721](https://eips.ethereum.org/EIPS/eip-721) with **William Entriken** as lead author alongside Shirley, Jacob Evans, and Nastassia Sachs. Every token has a unique `uint256 tokenId`, one owner, and one metadata URI. It became the substrate for the entire NFT economy β€” CryptoKitties, generative art, PFP collections β€” while exposing exactly the limits a minimum interface leaves behind. LUKSO's **LSP8** addresses each one at the standard level. + +## Origin + +Dapper Labs needed a way for each CryptoKitty to be a uniquely-owned on-chain object with its own provenance, and Shirley's draft was the substrate. CryptoKitties [launched on 28 November 2017](https://en.wikipedia.org/wiki/CryptoKitties), between the draft and the final spec, and immediately demonstrated both the thesis (people would pay real money for unique on-chain assets) and the limit (one-transfer-per-token congested Ethereum at launch). The EIP-721 Rationale is candid about its central design tradeoff: _"Different functions are used for `transfer` and `safeTransferFrom` to give the caller control over their risk."_ The safety hook was opt-in by design. + +## The interface + +```solidity +// EIP-721 β€” required +function balanceOf(address owner) external view returns (uint256); +function ownerOf(uint256 tokenId) external view returns (address); +function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) external payable; +function safeTransferFrom(address from, address to, uint256 tokenId) external payable; +function transferFrom(address from, address to, uint256 tokenId) external payable; +function approve(address to, uint256 tokenId) external payable; +function setApprovalForAll(address operator, bool approved) external; +function getApproved(uint256 tokenId) external view returns (address); +function isApprovedForAll(address owner, address operator) external view returns (bool); + +event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); +event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); +event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + +// EIP-721 metadata extension β€” optional +function tokenURI(uint256 tokenId) external view returns (string memory); +``` + +## Where it breaks down + +### The tokenURI trap + +`tokenURI(tokenId)` returns one string per token β€” an HTTPS URL or an IPFS CID pointing to off-chain JSON. Nothing about the resolution chain is typed or verified on-chain: a marketplace fetches the URI, downloads JSON from a server or gateway, parses it against the de-facto OpenSea metadata schema, then fetches the referenced image from yet another host. When a pinning subscription lapses or a backend goes dark, the NFT renders blank everywhere at once β€” a failure that has hit collections worth hundreds of millions of dollars. [ERC-4906](https://eips.ethereum.org/EIPS/eip-4906) added a `MetadataUpdate` signal event in 2022, but it doesn't carry the new state or guarantee anyone re-reads the URI. + +### setApprovalForAll is collection-wide + +A single signature grants blanket authority over every token in the collection β€” past, present, and future mints. The named-collection phishing drains that recur again and again ride exactly this primitive: one bad signature, every token gone. + +### safeTransferFrom is only half a hook + +`onERC721Received` is checked only on the `safeTransferFrom` variant. The plain `transferFrom` still ships alongside it, and NFTs routinely land in contracts with no idea what to do with them β€” the receiver problem was a deliberate scope-out, not an oversight. + +### uint256 tokenIds carry no declared meaning + +Cheap for sequential mints, but a bare `uint256` gives no signal about what it represents β€” a content hash and a plain counter look identical to any tool reading the contract. There's no standard way to declare "these IDs are hashes" or "these IDs are addresses," so teams write their own documentation and every integrator has to go find it. + +### One token, one transaction + +Transferring 50 NFTs is 50 transactions. CryptoKitties exposed the cost at launch; every airdrop, sweep, and migration since has paid the same tax. + +## What the standard's own author says + +Two years after EIP-721 shipped, **Dieter Shirley** β€” co-author and Dapper Labs CTO β€” published a retrospective naming three specific shortcomings of the standard he helped design ([Medium, 23 March 2020](https://medium.com/dapperlabs/resource-oriented-programming-bee4d69c8f8e)): + +> "ERC-721 defines an ownership model for NFTs that assumes that only Ethereum addresses can own an NFT. However, the idea of an asset itself owning other assets... is very interesting in some use cases, and required a new specification (ERC-998) to be created." + +On LUKSO, accounts _are_ contracts by default β€” a [Universal Profile](../compare/eoa-vs-universal-profile.md) can own LSP8 NFTs, other profiles, or LSP7 tokens, and any of those can own further assets recursively, without a separate composability standard. + +> "Implementing it [ERC-998] properly is very difficult, and retroactively applying its features to existing ERC-721 assets is effectively impossible due to the immutable nature of Ethereum smart contracts." + +[LSP17 Contract Extension](../../../standards/accounts/lsp17-contract-extension.md) is a first-class standard for adding behavior to a deployed account after the fact β€” the "we deployed it, now it's frozen" problem becomes a designed-for upgrade path. + +> "With the ledger model, it's hard to know who should pay this rent. For example, the CryptoKitties contract represents tens of thousands of players with almost two million Kitties and over 111MB of on-chain data." + +[ERC725Y](../../../standards/erc725.md) with [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) puts per-asset and per-token data on typed keys rather than one central mapping, so storage sits with whichever entity actually reads and pays for it. + +## The LUKSO successor + +[**LSP8 Identifiable Digital Asset**](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) keeps ERC721's ownership model and upgrades every primitive around it. + +| | ERC721 | LSP8 | +| ------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| Token ID type | `uint256` β€” no standard way to declare what it means | `bytes32` with a declared [`LSP8TokenIdFormat`](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) β€” number, string, address, or hash | +| Metadata | `tokenURI(id) β†’ string` (off-chain, untyped) | `getDataForTokenId(id, key) β†’ bytes` (typed, on-chain reference) | +| Off-chain media integrity | trust the host | `VerifiableURI` β€” on-chain hash, swaps detectable | +| Transfer hook | `onERC721Received`, opt-in only | [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver`, on every transfer to an LSP1-supporting contract | +| Operator authorization | collection-wide `setApprovalForAll` | per-token, gated through [LSP6](../../../standards/access-control/lsp6-key-manager.md) | + +:::info Is ERC721 still the right choice? +When token IDs are sequential counters, metadata is immutable or already redundantly pinned, and the contract is built to be consumed by code that hardcodes the ERC721 ABI, the smaller surface is genuinely the feature. LSP8 wins once the token ID needs a declared, standardized meaning, per-token state mutates after mint, or transfers into LSP1-supporting contracts β€” not just the opt-in safe variant β€” need to notify the recipient. +::: + +## FAQ + +### What's the difference between ERC20 and ERC721? + +ERC20 is fungible β€” balances are interchangeable. ERC721 is non-fungible β€” each `tokenId` is unique and individually owned via `ownerOf(tokenId)`. See [What is ERC20?](./erc-20.md) for the fungible counterpart. + +### What is ERC-721A, and does it fix anything? + +ERC-721A is Azuki's gas-optimized _implementation_ of ERC721 β€” same external ABI, same selectors, internal batch-mint optimizations. It reduces mint gas but doesn't change `tokenURI`, doesn't add transfer hooks, and doesn't fix the approval model. + +### Can I migrate an ERC721 collection to LSP8? + +Yes β€” the ownership model is the same, so migration is contract-level. See the [full migration guide](../../migrate/migrate-erc721-to-lsp8.md). + +**Related reading:** [ERC721 vs LSP8](../compare/erc721-vs-lsp8.md) Β· [ERC721's dynamic metadata problem](../problems/erc721-dynamic-metadata.md) Β· [ERC721's opt-in safe transfer](../problems/erc721-safe-transfer.md) Β· [ERC721 token ID limits](../problems/erc721-tokenid-limits.md) diff --git a/docs/learn/why-lukso/problems/_category_.yml b/docs/learn/why-lukso/problems/_category_.yml new file mode 100644 index 0000000000..ef6321b986 --- /dev/null +++ b/docs/learn/why-lukso/problems/_category_.yml @@ -0,0 +1,2 @@ +label: '🧩 Problems LUKSO Solves' +collapsed: true diff --git a/docs/learn/why-lukso/problems/contract-extension.md b/docs/learn/why-lukso/problems/contract-extension.md new file mode 100644 index 0000000000..fdbba51794 --- /dev/null +++ b/docs/learn/why-lukso/problems/contract-extension.md @@ -0,0 +1,38 @@ +--- +sidebar_label: 'Contract Extension' +sidebar_position: 1 +description: 'Add new functions to a deployed contract without a permanent upgrade key: LSP17 compared with proxies, diamonds, and Safe modules.' +--- + +# Extend a Deployed Contract Without an Upgrade Key + +Adding a new function to a contract you've already deployed usually forces a trade-off: an upgradeable proxy leaves a permanent admin key able to swap your logic overnight, or an [ERC-2535 Diamond](https://eips.ethereum.org/EIPS/eip-2535) trades that key for a shared storage layout you now have to manage by hand. [**LSP17 Contract Extension**](../../../standards/accounts/lsp17-contract-extension.md) skips both trade-offs. It routes unrecognized function calls to extension contracts registered in the base contract's own [ERC725Y](../../../standards/erc725.md) storage, so new behavior ships by registering an extension β€” not by upgrading, and not by touching a shared storage-slot map. Every Universal Profile grows this way, and any contract can adopt the same primitive. + +## Comparing the extension models + +| Approach | Mechanism | Upgrade authority | Storage risk | +| ------------------------ | -------------------------------------------------- | ---------------------------------- | ---------------------------------------- | +| Transparent / UUPS proxy | swap the implementation address | permanent admin key, forever | none β€” single implementation | +| ERC-2535 Diamond | facet registry routes selectors via `delegatecall` | none once ownership is renounced | shared storage layout, developer-managed | +| Safe modules | sandboxed module calls scoped to one Safe | module owner controls installation | isolated per module, Safe-specific | +| LSP17 | fallback router looks up an extension per selector | none β€” base bytecode is immutable | isolated per extension, no shared layout | + +## Why a permanent upgrade key is a standing liability + +A UUPS or transparent proxy is proven infrastructure, but the trade-off never goes away: whoever holds `upgradeTo` can replace the logic behind a stable address at any time. Every user of that contract is permanently exposed to that key, whether or not it's ever misused. Diamonds remove the key but introduce a different tax β€” facets share one storage layout, and getting that layout wrong across upgrades is a well-documented way to corrupt state. Account-specific patterns like Safe modules and ERC-4337 validators solve extension for one account type; they aren't a general-purpose primitive a token or registry contract can adopt. + +## What builders reach for today + +Most teams pick between the same handful of options. **OpenZeppelin's transparent and UUPS proxies** are battle-tested, but the upgrade key is a permanent trust assumption baked into the deployment. **ERC-2535 Diamonds** are genuinely modular and let you add facets after launch, at the cost of specialized tooling and storage discipline that has to be maintained correctly forever. **Safe modules and guards** work well for the one account they're scoped to, but don't generalize to arbitrary contracts. **Bespoke plugin patterns** baked into an app contract solve the immediate need but ship with no shared tooling and no audit trail beyond the one project that wrote them. + +## How LSP17 solves it + +LSP17 defines a fallback router: when a contract receives a call for a function selector it doesn't recognize, it looks up an extension contract registered under an [ERC725Y](../../../standards/erc725.md) data key for that selector, and forwards the call. The base contract's bytecode never changes and there is no admin key that can swap it. Extensions each keep their own storage, so there's no shared layout to coordinate across facets. + +Universal Profiles use LSP17 to grow new capabilities without proxy upgrades β€” a profile deployed years ago can gain new selector-level behavior just by registering an extension. The same primitive is available to any contract that wants to add functions after deployment without asking its users to trust a permanent upgrade key. + +:::tip LSP17 in one line +No upgrade authority over the base contract, no diamond-style storage discipline to maintain. If a deployed contract needs a new capability, register an extension instead of scheduling an upgrade. +::: + +**Related reading:** [Wallet permission scoping](./wallet-permissions.md) Β· [ERC-4337's bundler tax](./erc4337-bundler-tax.md) Β· [ERC-2535 vs. LSP17](../compare/erc2535-vs-lsp17.md) diff --git a/docs/learn/why-lukso/problems/eoa-key-risk.md b/docs/learn/why-lukso/problems/eoa-key-risk.md new file mode 100644 index 0000000000..6ab96c7088 --- /dev/null +++ b/docs/learn/why-lukso/problems/eoa-key-risk.md @@ -0,0 +1,37 @@ +--- +sidebar_label: 'EOA Key Risk' +sidebar_position: 2 +description: 'A single private key controls identity, funds, and authority in an EOA. Universal Profiles separate the account from its controllers.' +--- + +# Stop Binding Identity and Funds to a Single Private Key + +An externally owned account binds identity, funds, and authority to one ECDSA private key: lose it and everything is unrecoverable, leak it and everything is drained. LUKSO fixes this at the account level, not with better key custody. A [**Universal Profile**](../../universal-profile/metadata/read-profile-data.md) is a smart contract account β€” [LSP0 ERC725Account](../../../standards/accounts/lsp0-erc725account.md) holds the identity and the assets, while [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) controllers sign for it. No controller _is_ the account; controllers can be added, rotated, or removed while the address, the profile, and the on-chain history stay exactly the same. + +## Structural comparison + +| Risk | EOA | Universal Profile | +| ---------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| Key lost | total, irreversible loss | a recovery controller can install a new one β€” see [social recovery](./social-recovery.md) | +| Key stolen | full drain, instant, irreversible | scoped controller permissions limit the blast radius, and can be revoked in one transaction | +| Key rotation | requires migrating assets to a new address | swap the controller, keep the same account address | +| Multiple devices | share one key, or fragment across several addresses | multiple controllers under one profile, each independently scoped | +| Custody upgrade | migrate every asset to a new wallet | add a hardware-backed controller without moving anything | + +## Why one key is a structural single point of failure + +`address = keccak(pubkey)[12:]` is the entire account model for an EOA. The private key signs, owns, and identifies the account all at once β€” there's no social fallback, no time-locked recovery, no rotating authority built in. Every one of those has to be solved as a product problem, outside the account, by a wallet vendor. dApps also can't ask for limited authority in a standard, account-level way: connecting a dApp to an EOA means handing over the one key that does everything. + +## What people try today + +**Hardware wallets** improve key custody but keep the same fundamental model β€” one key still signs everything, it's just harder to extract. **Custodial backends** like Privy, Magic, and Web3Auth trade self-custody for a reset-password experience, which solves recoverability by reintroducing a trusted third party. **Wallet SDKs** such as RainbowKit or wagmi connectors smooth the UX over the same EOA primitive without changing what's underneath it. **ENS plus app databases** separate identity from custody, but profile state still lives app-by-app instead of on the account. + +## How Universal Profiles remove the single point of failure + +A Universal Profile is an account _contract_, not a key. It stores profile metadata through [ERC725Y](../../../standards/erc725.md) and [LSP3](../../../standards/metadata/lsp3-profile-metadata.md), delegates control through [LSP6](../../../standards/access-control/lsp6-key-manager.md) controllers, reacts to incoming assets through [LSP1](../../../standards/accounts/lsp1-universal-receiver.md), and supports sponsored execution through [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md). The account becomes a user-owned object apps can read and interact with, rather than an address that lives or dies with one key. Add a controller, remove a controller, rotate a controller β€” the address is unchanged, the profile is unchanged, the asset history is unchanged. + +:::tip The account outlives the key +On a Universal Profile, losing a controller key is a recoverable event, not a terminal one. The account was never the key in the first place. +::: + +**Related reading:** [Wallet permission scoping](./wallet-permissions.md) Β· [Social recovery without a seed phrase](./social-recovery.md) Β· [EOA vs. Universal Profile](../compare/eoa-vs-universal-profile.md) diff --git a/docs/learn/why-lukso/problems/erc1155-complexity.md b/docs/learn/why-lukso/problems/erc1155-complexity.md new file mode 100644 index 0000000000..a71d34c6de --- /dev/null +++ b/docs/learn/why-lukso/problems/erc1155-complexity.md @@ -0,0 +1,38 @@ +--- +sidebar_label: 'ERC1155 Complexity' +sidebar_position: 3 +description: 'ERC-1155 packs fungible and non-fungible items into one contract via ID bit conventions. LSP7 and LSP8 split the semantics at the standard level.' +--- + +# Split Fungible and Unique Assets at the Standard Level, Not the Token ID + +ERC-1155 packs fungible tokens, semi-fungible editions, and one-of-one items into a single contract, using the high bits of `tokenId` to signal which is which. That convention is cheap on-chain and expensive everywhere else β€” every indexer, marketplace, and wallet has to learn and decode it per collection, and there's no way to tell what an ID means just by reading it. LUKSO splits the semantics at the standard layer instead: [**LSP7**](../../../standards/tokens/LSP7-Digital-Asset.md) for fungible and semi-fungible assets, [**LSP8**](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) for identifiable ones. Both share the same transfer shape, the same [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) receiver hook, and the same [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) metadata pattern, so downstream code dispatches on the contract's interface, not on bits buried in an integer. + +## ERC-1155 vs. the LSP7 / LSP8 split + +| Aspect | ERC-1155 | LSP7 + LSP8 | +| -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | +| Where semantics live | encoded in `tokenId` bits, per-collection convention | in the contract's standard interface itself | +| Balance query | `balanceOf(account, id)` | LSP7 `balanceOf(account)`; LSP8 `tokenOwnerOf(tokenId)` | +| Receiver hook | `IERC1155Receiver` (one interface, single + batch callbacks) | one LSP1 `universalReceiver`, `typeId`-discriminated | +| Metadata | `uri(id)` with `{id}` substitution clients resolve manually | [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md) typed [ERC725Y](../../../standards/erc725.md) keys, per-asset or per-token | +| Batch operations | native batch transfer | `transferBatch` on LSP7/LSP8, same hook path | +| Integrator cost | decode the type-bit convention per contract | read the standard interface β€” no decoding required | + +## Why packing everything into one contract breaks down + +One ERC-1155 contract can hold currency-like tokens, edition NFTs, and unique items simultaneously. Wallets and indexers have to reconstruct the intent of each ID from whatever per-collection rules that project chose to use β€” there's no standard way to know which IDs are fungible and which are unique without reading that project's specific convention. Marketplaces end up writing custom logic per contract. Batch transfer is genuinely useful for gas savings, but it composes awkwardly with receiver hooks: every receiver must implement `IERC1155Receiver` for both single and batch cases, so the savings on gas land as integration costs downstream. + +## What people try today + +**High-bit token ID conventions** encode "this range is fungible, this range is unique" into the top bits of the ID. It works inside one contract, but any external code that wants the same semantics has to learn that specific convention from scratch. **One-contract-per-game patterns** restore the type boundary ERC-1155 erased, which negates the gas savings that motivated using ERC-1155 in the first place. **Off-chain ID registries** move the typing problem into an off-chain index, leaving the on-chain state with no opinion at all. **Custom decoder logic per contract** works, but the cost compounds with every new integration that has to write its own version. + +## How the LSP7 / LSP8 split fixes it + +LSP7 covers fungible and semi-fungible assets. LSP8 covers identifiable assets. They share transfer naming conventions β€” the same `force` flag and `bytes data` parameter β€” the same LSP1 receiver hook, and the same LSP4 metadata pattern, so a contract that handles one knows most of what it needs to handle the other. When a project genuinely needs multi-asset packing, it deploys multiple LSP7/LSP8 contracts side by side. The semantic boundary between "this is money" and "this is unique" lives in the standard itself, not in a token-ID bit range that every integrator has to reverse-engineer. + +:::tip When the split pays off +Any product mixing currency-like tokens with unique items should reach for LSP7 and LSP8 together rather than one ERC-1155 contract β€” the type boundary ships for free instead of becoming decoder logic every integrator has to write. +::: + +**Related reading:** [ERC-721 token ID limits](./erc721-tokenid-limits.md) Β· [ERC-20's missing transfer hooks](./erc20-transfer-hooks.md) Β· [ERC-1155 vs. LSP7/LSP8](../compare/erc1155-vs-lsp7-lsp8.md) Β· [Choosing between LSP7 and LSP8](../../digital-assets/choose-lsp7-vs-lsp8.md) diff --git a/docs/learn/why-lukso/problems/erc20-approval-risks.md b/docs/learn/why-lukso/problems/erc20-approval-risks.md new file mode 100644 index 0000000000..b3541d12f8 --- /dev/null +++ b/docs/learn/why-lukso/problems/erc20-approval-risks.md @@ -0,0 +1,37 @@ +--- +sidebar_label: 'ERC20 Approval Risk' +sidebar_position: 4 +description: 'ERC-20 approve grants standing, often unlimited, allowances. LSP7 moves scope enforcement to LSP6 account permissions instead.' +--- + +# Stop Granting Standing Allowances to Token Contracts + +ERC-20's `approve(spender, amount)` asks a token contract to trust a spender indefinitely β€” most dApps request max-uint to avoid a second prompt, and that allowance sits live on-chain until someone manually revokes it. LSP7's `authorizeOperator` is still amount-scoped, exactly like `approve`, and revoking an operator allowance once it's been granted still takes the same `revokeOperator` call ERC-20 requires β€” LUKSO isn't pretending otherwise. What changes is _who gets to create that allowance in the first place_: on a [Universal Profile](../../universal-profile/metadata/read-profile-data.md), the controller calling `authorizeOperator` is itself bound by [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) permissions, so a compromised or over-trusted app controller can be cut off from granting _new_ allowances in one transaction β€” a layer of defense ERC-20 has no equivalent for, even though it doesn't reach back and undo an allowance that controller already created. + +## Before / after + +| Mechanism | ERC-20 | LSP7 + LSP6 | +| ---------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Grant call | `approve(spender, amount)` | `authorizeOperator(operator, amount, data)` | +| Common default | dApps request max-uint to skip re-prompting | still amount-scoped by convention β€” not fixed by LSP7 alone | +| Who can grant new approvals | anyone holding the private key, forever | only a controller whose LSP6 permissions allow calling `authorizeOperator` β€” revocable at the account level | +| Revoking an existing allowance | separate `approve(spender, 0)` transaction, per token | separate `revokeOperator(operator, tokenOwner, false, "0x")` transaction, per token β€” same shape as ERC-20, LSP7 doesn't change this | +| Stopping a controller from granting more | not applicable β€” ERC-20 has no controller layer | revoke the controller's LSP6 permission β€” one transaction, account-wide, but any operator allowance it already granted stays live until separately revoked | + +## Why approve/transferFrom splits intent from execution + +`approve` sets an allowance; the spender calls `transferFrom` later, whenever it wants, for any amount up to that allowance. The user signs intent once and never sees the individual executions, so a wallet can't meaningfully explain what it's authorizing beyond a number. Because most dApps request max-uint to avoid asking twice, every approved spender becomes a standing risk β€” if that spender contract is later upgraded, exploited, or misconfigured, the allowance is already sitting there waiting to be used. + +## What people try today + +**Revoke flows** like revoke.cash and wallet allowance dashboards are reactive β€” they require the user to notice and act, after the allowance has already lived on-chain, potentially for months. **EIP-2612 Permit** moves the approval from a transaction into a signature, which is cheaper to give but is still blanket authority that still trusts the spender contract not to misuse it. **Permit2** scopes allowances per transaction and per contract, which helps materially, but only when both the token and the integrating app support it. **Approve-and-call wrapper contracts** add a router in front of the token, which fragments the UX across two contracts without touching the underlying ERC-20 model. + +## How LSP6 moves the policy to the account + +LSP7's `authorizeOperator` doesn't magically fix amount-scoped allowances β€” that part of the risk is unchanged. What's different is that on a Universal Profile, the controller invoking `authorizeOperator` is itself governed by LSP6: allowed calls, allowed standards, allowed [ERC725Y](../../../standards/erc725.md) data keys, value limits, all revocable per controller. Instead of asking the token contract to police every future `transferFrom` forever, the account decides what each app controller may invoke, and can cut that controller off in a single transaction β€” without ever touching a token-level allowance. + +:::tip Honest framing, real change +LSP7 operators are still amount-scoped, and an allowance already granted still needs its own `revokeOperator` call β€” that isn't a magic fix, and LUKSO doesn't claim otherwise. The real shift is one layer up: a session controller's _ability to grant new allowances_ can be switched off in one LSP6 transaction, account-wide β€” a kill switch ERC-20 simply has no equivalent for, on top of (not instead of) revoking the allowance itself. +::: + +**Related reading:** [Wallet permission scoping](./wallet-permissions.md) Β· [ERC-20's missing transfer hooks](./erc20-transfer-hooks.md) Β· [ERC-20 explained](../erc-explainers/erc-20.md) Β· [ERC20 vs. LSP7](../compare/erc20-vs-lsp7.md) diff --git a/docs/learn/why-lukso/problems/erc20-transfer-hooks.md b/docs/learn/why-lukso/problems/erc20-transfer-hooks.md new file mode 100644 index 0000000000..93f928d850 --- /dev/null +++ b/docs/learn/why-lukso/problems/erc20-transfer-hooks.md @@ -0,0 +1,37 @@ +--- +sidebar_label: 'ERC20 Transfer Hooks' +sidebar_position: 5 +description: 'ERC-20 transfer() never calls the recipient, stranding tokens sent to contracts. LSP7 fires an LSP1 universalReceiver hook on every transfer, both sides.' +--- + +# Give Every Token Transfer a Receiver Hook + +ERC-20's `transfer` is two storage writes and an event β€” the recipient contract is never called, so it has no way to react when tokens arrive. That silence is the root cause of tokens getting stranded in contracts that can't credit a deposit without a second function call or an off-chain indexer watching for `Transfer` events. LSP7 closes the gap natively: every transfer carries a `bytes data` payload and fires [LSP1](../../../standards/accounts/lsp1-universal-receiver.md) `universalReceiver` on the sender and recipient, for whichever side is a contract implementing it. And because LSP1 is the same hook used by LSP8 and by native value transfers, a receiving contract implements it once instead of maintaining four separate receiver interfaces. + +## Before / after + +| Behavior | ERC-20 | LSP7 | +| -------------------------------------------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| Transfer signature | `transfer(to, amount)` | `transfer(from, to, amount, force, data)` | +| Recipient notified | no | yes, if the recipient is a contract implementing LSP1 β€” via `universalReceiver` | +| Context payload | none | native `bytes data` on every transfer | +| Send to a non-receiving contract | tokens strand silently | required `force` flag β€” `force=false` rejects both EOAs and non-LSP1 contracts; `force=true` permits either | +| Receiver interfaces to implement for "accept everything" | one per asset standard (ERC-721, ERC-1155, ERC-777) | one β€” LSP1, shared across LSP7, LSP8, and value transfers | + +## Why silence at the recipient is the root problem + +A vault that needs to credit a user on deposit has exactly two options with ERC-20: trust `msg.sender` to call a second function after the transfer, or poll `Transfer` events from an indexer and reconcile state after the fact. Neither is a hook β€” both are workarounds for the contract having no way to react to incoming value. This is also the direct cause of the "tokens stuck in contract" pattern: a user sends ERC-20 directly to a contract address, the contract has no mechanism to notice, and the tokens sit there until someone writes a rescue function. + +## What people try today + +**ERC-777's `tokensReceived`** added receiver awareness back in 2017, but introduced reentrancy issues that led most teams to avoid it β€” it's largely abandoned in new deployments. **ERC-1363's `transferAndCall` / `approveAndCall`** is a clean opt-in design, but adoption is thin: any token that doesn't implement it still strands on arrival. **Wrapper contracts** that require a deposit call after the transfer move the burden onto the user and add an extra transaction, and don't compose with contracts that weren't built to expect the pattern. **Off-chain `Transfer` event indexers** are necessary for reading app state, but a subgraph can't trigger an on-chain reaction β€” it can only tell you what already happened. + +## How LSP1 and LSP7 fix it + +LSP7's `transfer` takes a required `force` flag and a `bytes data` payload. With `force=false`, the transfer reverts unless the recipient is a contract implementing LSP1 β€” that rejects both EOAs and non-LSP1 contracts. With `force=true`, the transfer goes through regardless of what the recipient is, but only a contract that actually implements LSP1 gets the `universalReceiver` callback; an EOA has no code to call, so it just receives the tokens silently, same as ERC-20. A recipient contract that implements LSP1 receives a `typeId` describing what just happened along with the transfer data, and can run accept, reject, or routing logic inside one standard hook β€” no polling, no second transaction. The structural win is that LSP1 is one interface across LSP7, LSP8, and native value transfers: a Universal Profile, or any LSP1-aware contract, handles every asset type through the same entry point, and the four-receiver-interfaces problem simply doesn't exist. + +:::tip One hook, every asset +If a contract needs to react when it receives value, LSP1 is the only interface to implement β€” it covers fungible tokens, identifiable tokens, and native transfers alike. +::: + +**Related reading:** [ERC-721's safe transfer gap](./erc721-safe-transfer.md) Β· [The ERC-20 approval problem](./erc20-approval-risks.md) Β· [ERC-20 explained](../erc-explainers/erc-20.md) Β· [ERC20 vs. LSP7](../compare/erc20-vs-lsp7.md) diff --git a/docs/learn/why-lukso/problems/erc4337-bundler-tax.md b/docs/learn/why-lukso/problems/erc4337-bundler-tax.md new file mode 100644 index 0000000000..eae114bf89 --- /dev/null +++ b/docs/learn/why-lukso/problems/erc4337-bundler-tax.md @@ -0,0 +1,40 @@ +--- +sidebar_label: 'ERC-4337 Bundler Tax' +sidebar_position: 6 +description: 'ERC-4337 account abstraction runs on a parallel mempool of bundlers and paymasters. LSP0 + LSP6 + LSP25 put relay execution on infrastructure the account already has.' +--- + +# Skip the Bundler Mempool for Account Abstraction + +ERC-4337 delivers account abstraction by building an entirely parallel transaction system on top of the one that already exists β€” UserOperations, EntryPoint contracts, bundlers, paymasters, signature aggregators. Every team that adopts it either runs that infrastructure or rents it from a third party, and debugging happens on a separate plane from ordinary transactions: block explorers show `handleOps`, not your function call. A [**Universal Profile**](../../universal-profile/metadata/read-profile-data.md) gets the same account-abstraction outcomes β€” sponsored execution, scoped permissions, contract-native signing β€” by composing [LSP0](../../../standards/accounts/lsp0-erc725account.md) (the account), [LSP6](../../../standards/access-control/lsp6-key-manager.md) (its Key Manager), [LSP20](../../../standards/accounts/lsp20-call-verification.md), and [LSP25](../../../standards/accounts/lsp25-execute-relay-call.md) β€” every profile ships paired with its Key Manager, so this is standard infrastructure the account already has, not an extra hop, a bundler, or a UserOperation envelope. + +## Before / after + +| Layer | ERC-4337 | LSP stack (Universal Profile) | +| ---------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------ | +| Entry point | separate `EntryPoint` contract | the account contract itself | +| Mempool | alt-mempool for UserOperations | ordinary chain mempool | +| Sponsorship | paymaster contract + EntryPoint deposit | LSP25 `executeRelayCall`, relayer submits to the Key Manager | +| Validation | pluggable validator module per account | LSP6 permission check + LSP20 inline call verification | +| `msg.sender` at downstream targets | the account, after EntryPoint routing | the account, directly | +| Tooling required | bundler infrastructure β€” Pimlico, Alchemy, Biconomy, etc. | standard RPC plus a relayer service | + +## Why the parallel mempool is a real cost + +ERC-4337 ships account abstraction _over_ a transaction system that was designed for EOAs, and the price is a second infrastructure stack most product teams don't want to run or rent: `UserOperation`s, `EntryPoint` contracts, bundlers, paymasters, signature aggregators. The value model still routes through the EntryPoint even though the account itself is a smart contract, so debugging, tracing, and gas accounting all sit on a separate plane from regular transaction flow. + +## What people try today + +**Safe plus a relayer** is a solid model β€” Safe is a smart account contract, and a relayer simply submits the multisig's `exec` call, with no `UserOperation` and no bundler involved. It's just less standardized than ERC-4337. **EIP-7702** lets an EOA delegate to a contract's code β€” the delegation persists until replaced or cleared, not just for one transaction β€” getting smart-account semantics while keeping its original address; a plain transaction sender can already cover gas for it, though matching ERC-4337's exact sponsorship UX still means pairing it with a paymaster. **Custodial backends** sign user actions server-side for the simplest possible UX, at the cost of the weakest custody story. **App-specific forwarders** built on EIP-2771 are straightforward but narrow to a single product and are hard to share across protocols. + +## How the LSP stack avoids the extra hop + +A Universal Profile is a contract account composed from LSP0 (the ERC-725 account contract itself), LSP6 (its Key Manager, holding permissions for every controller), LSP25 (`executeRelayCall` on that Key Manager, for sponsored execution), and LSP20 (inline call verification). There is no bundler and no EntryPoint: a controller signs, a relayer submits to the Key Manager, the Key Manager validates via LSP6 (including the `EXECUTE_RELAY_CALL` permission), and executes on the account. Because the relayed call goes through the same permission check as any other call, the controller signing the relay payload can only authorize what its permissions allow. + +The trade-off is worth stating plainly. ERC-4337 decouples the account from its validator, so you can swap validators independently of the account or mix them per UserOperation β€” that decoupling is the model's real strength, and its cost shows up as extra hops and a non-standard, per-wallet permission shape. The LSP stack fuses the account and its policy instead: LSP6 is the one permission vocabulary, LSP20 verifies calls inline, and LSP25 handles sponsored execution as a function on the Key Manager every profile already has. Extending behavior later means adding [LSP17 extensions](./contract-extension.md) or composing new LSPs, not hot-swapping a validator module. + +:::tip Fewer hops, one vocabulary +If a product wants sponsored transactions and scoped permissions without operating bundler infrastructure, LSP0 + LSP6 + LSP20 + LSP25 deliver it on infrastructure the account already has β€” the trade is a fused account/policy model instead of ERC-4337's swappable validators. +::: + +**Related reading:** [Gasless onboarding without a paymaster](./gasless-onboarding.md) Β· [Wallet permission scoping](./wallet-permissions.md) Β· [ERC-4337 vs. the LSP stack](../compare/erc4337-vs-lsp-stack.md) Β· [ERC-4337 vs. EIP-7702](../compare/erc4337-vs-eip7702.md) Β· [The 4337 extension for Universal Profiles](../../universal-profile/advanced-guides/4337-extension.md) diff --git a/docs/learn/why-lukso/problems/erc721-dynamic-metadata.md b/docs/learn/why-lukso/problems/erc721-dynamic-metadata.md new file mode 100644 index 0000000000..c84952a4d6 --- /dev/null +++ b/docs/learn/why-lukso/problems/erc721-dynamic-metadata.md @@ -0,0 +1,37 @@ +--- +sidebar_label: 'Dynamic NFT Metadata' +sidebar_position: 7 +description: 'ERC-721 tokenURI returns one string with no integrity guarantee. LSP8 stores per-token metadata as typed, verifiable ERC725Y keys.' +--- + +# Make NFT Metadata Verifiable, Not Just Hosted + +ERC-721's `tokenURI(tokenId)` returns a single string, and the contract has no opinion about what that string points to, who can change it, or whether a marketplace's cached copy is still accurate. LSP8 replaces the single URI with typed [ERC725Y](../../../standards/erc725.md) key-value storage per token: `getDataForTokenId(tokenId, key)` returns the raw `bytes` stored under that key, which a client decodes against the key's declared [LSP2](../../../standards/metadata/lsp2-json-schema.md) schema, and [LSP4](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md)'s `VerifiableURI` type pairs an off-chain pointer with an on-chain hash, so a client can detect a silent swap of the underlying file instead of trusting it blindly. + +## Before / after + +| Property | ERC-721 `tokenURI` | LSP8 + LSP4 | +| -------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Storage shape | one string per token | typed [ERC725Y](../../../standards/erc725.md) key-value pairs per token | +| Integrity | none β€” trust whoever hosts the JSON | `VerifiableURI` carries an on-chain hash of the payload, checked client-side β€” the chain itself never fetches or validates the remote file | +| Update signal | ERC-4906 `MetadataUpdate` event (says something changed, not what) | a `setDataForTokenId` call, readable directly on-chain | +| Update authorization | contract-specific, often owner-only | ownership-based by default (the token contract's `owner()`) β€” LSP6-controller scoping only applies if that owner is itself an LSP0 account with a Key Manager attached | +| Read pattern | fetch the URI, fetch the JSON, trust the cache | `getDataForTokenId(tokenId, key)` returns raw `bytes`, decoded client-side, hash-checkable via `VerifiableURI` | + +## Why one cached string can't carry dynamic state + +`tokenURI(tokenId)` returns one string, and the contract has no opinion on what changes, when, or by whom. If the metadata is meant to be dynamic, the actual truth lives wherever the JSON is hosted β€” and every marketplace, wallet, and explorer caches its own copy on its own schedule. "Refresh metadata" buttons exist precisely because there's no standard signal for what changed; ERC-4906 added a `MetadataUpdate` event, but consumers still have to go ask the server what the new state actually is. + +## What people try today + +**Mutable IPFS pointers** are convenient, but the JSON is still off-chain and trust-anchored to whoever holds the ability to repin the CID. **Server-rendered metadata APIs** work at scale but couple the NFT permanently to the issuer's infrastructure β€” the collection falls over when the server does. **ERC-4906's `MetadataUpdate` event** signals that something changed without saying what changed or carrying the new state. **Fully on-chain SVG generators** give the strongest integrity guarantee, but are expensive and awkward for rich media β€” a good answer for a narrow set of collections, not a general one. + +## How LSP8 and LSP4 make metadata a verifiable graph + +LSP8 assets store per-token metadata through [ERC725Y](../../../standards/erc725.md) data keys instead of a single `tokenURI` string. LSP4 defines the metadata conventions β€” name, symbol, JSON schema β€” and its `VerifiableURI` type lets a key point to an off-chain payload _with a hash_, so a client can verify that payload against the on-chain hash rather than trusting it outright β€” the chain stores and exposes the hash, but never fetches or enforces the remote file itself. Apps read `getDataForTokenId(tokenId, key)` and get the raw `bytes` back, decoded against the key's LSP2 schema. Dynamic updates go through `setDataForTokenId`, gated by the token contract's own ownership model β€” LSP6 scoping applies only when that owner is deliberately set up as an LSP0 account with a Key Manager, not automatically. The metadata stops being a URL to trust and becomes a typed key-value graph you can verify. + +:::tip Verifiable beats cached +For any collection where metadata legitimately changes after mint, LSP8's `VerifiableURI` gives consumers a hash to check against client-side β€” instead of asking them to trust that the cache is current. +::: + +**Related reading:** [ERC-721's safe transfer gap](./erc721-safe-transfer.md) Β· [ERC-721 token ID limits](./erc721-tokenid-limits.md) Β· [Building dynamic NFTs](../build/dynamic-nfts.md) Β· [ERC721 vs. LSP8](../compare/erc721-vs-lsp8.md) diff --git a/docs/learn/why-lukso/problems/erc721-safe-transfer.md b/docs/learn/why-lukso/problems/erc721-safe-transfer.md new file mode 100644 index 0000000000..d488458ad1 --- /dev/null +++ b/docs/learn/why-lukso/problems/erc721-safe-transfer.md @@ -0,0 +1,44 @@ +--- +sidebar_label: 'Safe Transfer Hooks' +sidebar_position: 8 +description: 'ERC-721 safeTransferFrom only checks one receiver interface, and the unsafe path still ships. LSP1 gives every asset type one universal hook.' +--- + +# One Receiver Hook for Every Asset Type + +`safeTransferFrom` only checks `onERC721Received` β€” and only when the caller chooses the safe variant, since plain `transferFrom` still ships and bypasses the check entirely. Every other asset standard defines its own receiver shape on top: ERC-1155's `IERC1155Receiver` (one interface, with both a single-transfer and a batch-transfer callback), ERC-777's now-abandoned `tokensReceived`. A multi-asset contract ends up implementing several interfaces, and a legacy one silently strands whatever it wasn't built to check for. LSP1 replaces all of it with one hook β€” [`universalReceiver(bytes32 typeId, bytes data)`](../../../standards/accounts/lsp1-universal-receiver.md) β€” that LSP7, LSP8, and native value transfers all call, with `typeId` telling the receiver exactly what just happened. + +## Before / after + +| Asset type | ERC ecosystem receiver | LUKSO receiver | +| ---------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| ERC-721 / LSP8 | `onERC721Received`, opt-in via `safeTransferFrom` only | `universalReceiver`, `typeId`-discriminated | +| ERC-1155 | `IERC1155Receiver` (`onERC1155Received` + `onERC1155BatchReceived`, one interface) | same LSP1 hook | +| ERC-20 / LSP7 | none natively | `universalReceiver` on every transfer to an LSP1-supporting contract | +| Native value | none | `universalReceiver` via the account's receive path | +| Interfaces to implement to "accept everything" | up to three | one | + +## Why a narrow, opt-in check still strands assets + +`safeTransferFrom` is real safety, but it's narrow: it only checks `IERC721Receiver`, only on that one call, only when the caller chose the safe variant. The plain `transferFrom` still ships, and most ERC-20 transfers go straight to a balance update with no recipient interaction of any kind. In practice, every asset standard invented its own receiver shape β€” `IERC721Receiver`, `IERC1155Receiver`, ERC-777's `tokensReceived` β€” so a multi-asset contract has to implement each one separately, and there's still no general "I received value of some kind" hook to fall back on. + +## What people try today + +**Implementing every receiver interface** is required for compliance with each standard, but it's boilerplate that grows with every new asset type and doesn't compose into a shared policy. **OpenZeppelin's Holder mixins** (`ERC721Holder`, `ERC1155Holder`) accept everything unconditionally, which just defers the policy question to somewhere else. **Defensive transfer-then-call patterns** are ad hoc per integration and don't interoperate across projects. **Sweeper or rescue contracts** recover stranded assets after the fact β€” they treat the symptom, not the missing hook. + +## How LSP1 generalizes the hook + +LSP1 defines one hook signature: + +```solidity +function universalReceiver(bytes32 typeId, bytes calldata data) + external payable returns (bytes memory); +``` + +LSP7 and LSP8 transfers call it on both sender and recipient that implement LSP1, with a `typeId` that tells the receiver what kind of interaction just occurred β€” token sent, token received, asset registered, and so on. A Universal Profile uses a **Universal Receiver Delegate** to dispatch on that signal: register received assets in [LSP5](../../../standards/metadata/lsp5-received-assets.md), reject spam by `typeId`, or run app-specific logic β€” one hook, every asset type, a declared policy instead of several bespoke interfaces. + +:::tip One interface to implement +If a contract or account needs to react to receiving any kind of asset β€” fungible, identifiable, or native value β€” LSP1 is the only receiver interface it needs. +::: + +**Related reading:** [ERC-20's missing transfer hooks](./erc20-transfer-hooks.md) Β· [Dynamic NFT metadata](./erc721-dynamic-metadata.md) Β· [ERC721 vs. LSP8](../compare/erc721-vs-lsp8.md) Β· [Accept or reject incoming assets](../../universal-profile/universal-receiver/accept-reject-assets.md) diff --git a/docs/learn/why-lukso/problems/erc721-tokenid-limits.md b/docs/learn/why-lukso/problems/erc721-tokenid-limits.md new file mode 100644 index 0000000000..bab1833715 --- /dev/null +++ b/docs/learn/why-lukso/problems/erc721-tokenid-limits.md @@ -0,0 +1,38 @@ +--- +sidebar_label: 'Token ID Design' +sidebar_position: 9 +description: 'ERC-721 token IDs are a bare, undeclared uint256 β€” a hash and a counter look identical on-chain. LSP8 lets a collection declare what its ID means.' +--- + +# Let Token IDs Declare What They Mean, Not Just Hold a Number + +`uint256` and [`bytes32`](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) are both exactly 256 bits β€” a content hash, a packed address, or a structured reference fits in either one without truncation. That's not where ERC-721 actually falls short. The real gap is that a bare `uint256 tokenId` carries no signal about what it _is_: a sequential counter, a `keccak256` hash, an encoded address, all look identical β€” just a number β€” to any wallet, marketplace, or indexer reading the contract. LSP8 fixes the signal, not the size: the [`LSP8TokenIdFormat`](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) data key lets a collection **declare** how its token IDs should be interpreted, so external tooling can render them correctly without a per-project convention to reverse-engineer. + +## Before / after + +| Use case | ERC-721 `uint256` | LSP8 `bytes32` + `LSP8TokenIdFormat` | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | +| Sequential mint counter | native fit | native fit β€” format `0` (`Number`) | +| Content hash (e.g. `keccak256`) | fits the bits, but looks identical to a counter β€” no signal it's a hash | format `4` (`bytes32` hash digest) β€” declared, so tooling knows to treat it as a hash | +| Off-chain serial / name | side convention required to know it isn't a counter | format `1` (`string`, up to 32 UTF-8 bytes) β€” declared directly | +| Token ID as its own contract address | possible to pack, but no standard way to say "this ID is an address" | format `2` (`address`) β€” the ID **is** a contract address other code can call or read ERC725Y from | +| Mixed collection (mostly numbers, some contracts) | no standard way to express this per-token | formats `100`–`104` (`Mixed`) β€” default interpretation declared, per-token override queryable | +| Marketplace readability | a 78-digit number; correct interpretation depends on knowing that project's off-chain convention | the same numeric case still works, or a self-describing, declared format when you choose one | + +## Why an undeclared integer forces a side convention + +`uint256 tokenId` is a deliberate, minimal primitive β€” cheap and predictable, and for sequential mints it's genuinely the right answer. The cost shows up the moment the ID needs to carry meaning beyond a count: nothing in ERC-721 lets a contract declare "these IDs are hashes" or "these IDs are addresses." Every integrator either assumes a plain counter or has to go find that project's own documentation to learn its convention β€” the token ID itself, and the interface, give no hint. That's a discoverability problem, not a storage one. + +## What people try today + +**Off-chain registries** map `uint256` IDs to richer identifiers in a database. It works, but it couples the asset permanently to that project's infrastructure and gives generic tooling nothing to go on. **Documented per-project conventions** (a README, a subgraph schema) tell integrators how to interpret an ID, but only the integrators who found and read that documentation. **Hash-as-tokenId patterns** are common and technically fine β€” a `keccak256` hash fits a `uint256` exactly β€” but a wallet or marketplace has no on-chain way to know it should render the ID as a hash instead of a serial number. + +## How LSP8TokenIdFormat makes the declaration standard + +[LSP8](../../../standards/tokens/LSP8-Identifiable-Digital-Asset.md) keeps the same 256-bit ID β€” `bytes32` instead of `uint256`, a more neutral fixed-length container for something that isn't always semantically a number β€” and adds `LSP8TokenIdFormat`, a data key every LSP8 contract sets once. Any LSP8-aware wallet or marketplace reads that key and knows immediately whether a given ID is a plain number, a UTF-8 string, a hash digest, or a contract address, without guessing or consulting off-chain docs. A collection that wants every NFT to itself be an [ERC725Y](../../../standards/erc725.md)-compatible contract β€” carrying its own evolving state β€” can do that natively with the `address` format, something a bare `uint256` has no standard way to express at all. + +:::tip The win is declared interpretation, not extra room +LSP8's `bytes32` isn't bigger than `uint256` β€” both are 256 bits. What LSP8 adds is `LSP8TokenIdFormat`: a standard way to say what a token ID means, so integrators read that declaration instead of reverse-engineering a project-specific convention. +::: + +**Related reading:** [Dynamic NFT metadata](./erc721-dynamic-metadata.md) Β· [ERC-721's safe transfer gap](./erc721-safe-transfer.md) Β· [ERC-1155 complexity](./erc1155-complexity.md) Β· [ERC721 vs. LSP8](../compare/erc721-vs-lsp8.md) diff --git a/docs/learn/why-lukso/problems/gasless-onboarding.md b/docs/learn/why-lukso/problems/gasless-onboarding.md new file mode 100644 index 0000000000..23071716f3 --- /dev/null +++ b/docs/learn/why-lukso/problems/gasless-onboarding.md @@ -0,0 +1,36 @@ +--- +sidebar_label: 'Gasless Onboarding' +sidebar_position: 10 +description: 'New users need native gas before they can do anything. LSP25 lets a relayer submit signed calls with no bundler or paymaster contract.' +--- + +# Onboard Users Without Making Them Buy Gas First + +A brand-new EOA can't do anything until it holds native currency, because an EOA signs and pays in the same step. Every popular fix for this β€” trusted forwarders, ERC-4337 paymasters, third-party relayer SDKs β€” adds a layer of infrastructure the builder has to run, rent, or trust. [**LSP25**](../../../standards/accounts/lsp25-execute-relay-call.md) puts relay execution on the [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) that already governs every [Universal Profile](../../universal-profile/metadata/read-profile-data.md): a controller signs a payload, a relayer submits it to the Key Manager and pays gas, and `executeRelayCall` verifies the signature and the controller's permissions before executing on the account β€” no bundler, no EntryPoint, no separate mempool. + +## Before / after + +| Architecture | What it requires | Sponsorship model | +| ------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------- | +| ERC-2771 trusted forwarder | every recipient contract inherits `ERC2771Context` | forwarder pays; contracts trust the forwarder | +| ERC-4337 + paymaster | EntryPoint deposit, bundler, `validatePaymasterUserOp` | a paymaster contract sponsors per `UserOperation` | +| Third-party relayers (Gelato, Biconomy, Defender) | vendor SDK integration | vendor-hosted infrastructure | +| LSP25 `executeRelayCall` | a signed payload plus a nonce channel | a relayer submits to the Key Manager β€” no bundler | + +## Why sponsorship needs a separate signer and payer + +A new user needs native currency before they can do anything with an EOA, because signing and paying are the same act. Any sponsorship model has to add a layer that separates who signs from who pays β€” and every popular pattern does that by adding infrastructure: a trusted forwarder you have to trust, an ERC-4337 EntryPoint and bundler you have to run or rent, or a third-party relayer with its own SDK and its own vendor lock-in. The cost lands on the builder, not on the spec. + +## What people try today + +**EIP-2771 trusted forwarders** let contracts extract the real `msg.sender` from calldata. It's cheap, but every protected contract has to be built forwarder-aware and has to trust that specific forwarder. **ERC-4337 plus a paymaster** is powerful but means shipping or renting the full `UserOperation` pool, bundler, and `EntryPoint` stack. **Turnkey relayers** like Gelato, Biconomy, and Defender wrap one of the above into a convenient SDK, at the cost of vendor coupling. **EIP-7702** lets an EOA delegate to smart-account code β€” a delegation that persists until replaced or cleared, not just for one transaction β€” and a plain transaction sender can already cover gas for it, though matching ERC-4337's exact sponsorship UX still means pairing it with a paymaster. + +## How LSP25 keeps it native, with no new infrastructure + +LSP25 defines `executeRelayCall` on the [LSP6 Key Manager](../../../standards/access-control/lsp6-key-manager.md) β€” the same contract that already checks permissions for every direct call on the profile. A controller signs a payload β€” with a nonce channel for ordering and replay protection β€” and a relayer submits it to the Key Manager, which verifies the signature, confirms the signer holds the `EXECUTE_RELAY_CALL` permission alongside whatever permission the payload itself needs, and then executes it on the account. There's still no bundler, no `EntryPoint`, no separate mempool: gasless execution is one function call on infrastructure every Universal Profile already has, not a new protocol layer bolted on top. Because the Key Manager checks the same [LSP6](../../../standards/access-control/lsp6-key-manager.md) permissions it always would, the controller signing the relay payload can only authorize what its permissions allow, so the sponsoring relayer can never escalate the controller's authority just by paying for gas. + +:::tip Sponsorship without extra trust +A relayer that pays gas through `executeRelayCall` never gains more authority than the signing controller already had β€” the Key Manager checks LSP6 permissions (including `EXECUTE_RELAY_CALL`) on every relayed call, not bypassed by it. +::: + +**Related reading:** [ERC-4337's bundler tax](./erc4337-bundler-tax.md) Β· [Wallet permission scoping](./wallet-permissions.md) Β· [Building gasless onboarding](../build/gasless-onboarding.md) Β· [Gasless onboarding patterns](../architecture/gasless-onboarding-patterns.md) Β· [Execute relay transactions](../../universal-profile/key-manager/execute-relay-transactions.md) diff --git a/docs/learn/why-lukso/problems/social-recovery.md b/docs/learn/why-lukso/problems/social-recovery.md new file mode 100644 index 0000000000..ed1ac2fdc7 --- /dev/null +++ b/docs/learn/why-lukso/problems/social-recovery.md @@ -0,0 +1,37 @@ +--- +sidebar_label: 'Social Recovery' +sidebar_position: 11 +description: 'Recovery should be a permission graph, not a seed phrase. LSP11 lets guardians install a new controller with no residual key above the system.' +--- + +# Recover an Account Without a Seed Phrase + +Recovery in the EOA world means one seed phrase β€” lose it and there is no fallback, no guardian, no delay, nothing to appeal to. [**LSP11 Basic Social Recovery**](../../../contracts/contracts/LSP11BasicSocialRecovery/LSP11BasicSocialRecovery.md) turns recovery into a permission the [Universal Profile](../../universal-profile/metadata/read-profile-data.md) itself enforces: a set of guardian addresses vote to install a new controller once a configurable threshold is met, and that recovery contract only ever holds the specific [LSP6](../../../standards/access-control/lsp6-key-manager.md) permission it needs β€” typically the ability to add and remove controllers, nothing more. + +## Before / after + +| Recovery model | Mechanism | Residual key risk | +| ------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| EOA seed phrase | one phrase, memorized or stored | total loss if lost, total compromise if leaked | +| Argent guardians | bespoke contract, guardian set plus time lock | reference implementation, hard to port to other wallets | +| Safe owner rotation | multisig vote to add or remove owners | the multisig itself is the recovery primitive | +| ERC-4337 recovery modules | validator module attached per wallet SDK | module-by-module, no shared vocabulary | +| LSP11 + LSP6 | guardian threshold installs a new controller via the `ADDCONTROLLER` permission | the recovery contract holds only the permission it needs; daily controllers stay narrow | + +## Why a single phrase is the wrong shape for recovery + +An EOA has exactly one private key. Lose it, lose everything β€” there's no social fallback, no time-locked recovery, no rotating authority built into the account model. Those are all product problems that wallet vendors have had to solve outside the account, one bespoke implementation at a time. Smart-account recovery is possible today, but every model encodes its own rules and most are non-interoperable β€” migrating recovery from one wallet to another usually means migrating the whole account, not just the recovery policy. + +## What people try today + +**Argent's guardian model** has a guardian set sign a time-locked recovery request through custom contract logic. It's the reference implementation for social recovery, but it's hard to port outside Argent's own wallet. **Safe's owner rotation** uses the multisig itself as the recovery primitive β€” owners vote to add or remove owners, which works well but ties recovery to holding a Safe specifically. **ERC-4337 recovery modules** attach validator logic to a smart account, but each wallet SDK defines its own module shape, so there's no shared recovery vocabulary across products. **Custodial recovery** from providers like Privy, Magic, and Web3Auth gives a reset-password experience, trading away some self-custody for predictability. + +## How LSP11 and LSP6 express recovery as permissions + +A Universal Profile is one account with many controllers. LSP6 lets a project register a recovery controller β€” or a full recovery contract like LSP11 that enforces a guardian threshold, a delay, or a voting policy β€” with exactly the permissions it needs to do its job, typically just the ability to add and remove other controllers. Recovery becomes a design choice expressed as permissions, not a hardcoded wallet feature. Day-to-day controllers stay narrow in scope; the recovery controller stays cold and rarely used. If a daily key is lost, the recovery controller adds a replacement and removes the old one β€” the account address never changes, and neither does the profile or its history. + +:::tip Recovery is only real without a residual key +A recovery model only holds up when there's no leftover EOA key sitting above the permission system β€” otherwise a stolen key can override any recovery configuration you've set up. LSP11 guardians authorize a new controller through LSP6; there's no master key left to bypass them. +::: + +**Related reading:** [Wallet permission scoping](./wallet-permissions.md) Β· [The EOA key-risk problem](./eoa-key-risk.md) Β· [EOA vs. Universal Profile](../compare/eoa-vs-universal-profile.md) Β· [LSP14 Ownable 2-Step](../../../standards/access-control/lsp14-ownable-2-step.md) diff --git a/docs/learn/why-lukso/problems/wallet-permissions.md b/docs/learn/why-lukso/problems/wallet-permissions.md new file mode 100644 index 0000000000..a326909c67 --- /dev/null +++ b/docs/learn/why-lukso/problems/wallet-permissions.md @@ -0,0 +1,46 @@ +--- +sidebar_label: 'Scoped Wallet Permissions' +sidebar_position: 12 +description: 'Connecting a dApp usually hands over the whole account. LSP6 Key Manager grants per-controller, per-call, revocable permissions on-chain.' +--- + +# Grant a dApp Less Than the Whole Account + +Connect most wallets to a dApp and the dApp effectively gets the whole account β€” there's no standard way to ask for less. Session-key systems exist, but each ships as a different vendor SDK with its own validator shape, so nothing is portable across wallets. [**LSP6 Key Manager**](../../../standards/access-control/lsp6-key-manager.md) makes scoped access a standard, on-chain vocabulary every [Universal Profile](../../universal-profile/metadata/read-profile-data.md) speaks: a permission bitfield per controller, plus optional allowed calls (target, standard, selector) and allowed [ERC725Y](../../../standards/erc725.md) data keys. Granting a narrow session controller is one transaction. Revoking it is one transaction. And because the profile checks permissions on every call, the scope is enforced on-chain β€” not just hidden behind a wallet UI that a compromised frontend could ignore. + +## Before / after + +| Model | Scope granularity | Enforced where | Portable across wallets | +| --------------------------- | ------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------ | +| Default wallet connect | full account | nowhere β€” the dApp gets whatever it asks the user to sign | n/a | +| Argent session keys | per-contract allowlist | wallet-specific validator | no | +| ERC-4337 validation modules | pluggable per wallet | validator contract | mechanism is standard, policy is not | +| Zodiac scope guards (Safe) | module-level | Safe module | Safe-only | +| LSP6 Key Manager | permission bitfield + allowed calls + allowed data keys | the account contract itself | every Universal Profile | + +## Why "connect wallet" defaults to full access + +EOAs sign every action with the same key, so there's no narrower unit of authority to hand out in the first place. Even most smart wallets expose the full account on connect, because there's no standard way for a dApp to ask for something less than everything. When fine-grained authority does exist today, it usually lives in custom validator code written per wallet β€” interoperability is poor, and revocation tends to be all-or-nothing rather than scoped to the one controller that should lose access. + +## What people try today + +**Argent session keys** issue temporary signing keys scoped to an allowed-contract list, but the mechanism is vendor-specific to Argent's wallet. **ERC-4337 validation modules** make pluggable validators possible, which standardizes the _mechanism_ for defining what a key may do, without standardizing the _policy vocabulary_ itself β€” every wallet SDK still defines its own shape. **Zodiac scope guards** on Safe gate access at the module level, which is powerful but complex to author and scoped entirely to Safe accounts. **Off-chain delegation** via signed capability objects is flexible, but enforcement depends entirely on the relayer honoring the capability β€” there's no on-chain check backing it up. + +## How LSP6 makes scope a standard vocabulary + +LSP6 Key Manager assigns each controller a permission bitfield β€” `CALL`, `SETDATA`, `TRANSFERVALUE`, `ADDCONTROLLER`, `SIGN`, and more β€” plus optional allowed calls (target contract, standard interface, function selector) and allowed ERC725Y data keys: + +``` +Controller: app session key 0xabc... +Permission: CALL +AllowedCalls: + - target: 0x, standard: LSP7, selector: transfer +``` + +Granting an app a session controller is one transaction. Revoking it is one transaction. The Universal Profile checks permissions on every call through [LSP20](../../../standards/accounts/lsp20-call-verification.md), so the scope is enforced on-chain, not at a wallet UI layer that a malicious or compromised frontend could bypass. Core LSP6 has no controller-expiry field β€” a controller's permissions stay live until someone explicitly revokes them in a transaction. An app can layer its own off-chain "session" convention on top (e.g. stop using a key after N hours), but that's an app-level policy, not something the account itself enforces; a direct call with that controller's key still works until the on-chain permission is actually revoked. + +:::tip Ask for less, by default +A Universal Profile lets a dApp request exactly the permission it needs β€” one token, one function, one target contract β€” instead of defaulting to full account access just because there was no standard way to ask for less. +::: + +**Related reading:** [The ERC-20 approval problem](./erc20-approval-risks.md) Β· [Social recovery without a seed phrase](./social-recovery.md) Β· [The EOA key-risk problem](./eoa-key-risk.md) Β· [Grant controller permissions](../../universal-profile/key-manager/grant-permissions.md) Β· [Get controller permissions](../../universal-profile/key-manager/get-controller-permissions.md) diff --git a/sidebars.js b/sidebars.js index 27337a51ea..cda555aba7 100644 --- a/sidebars.js +++ b/sidebars.js @@ -47,6 +47,11 @@ module.exports = { 'learn/overview', 'learn/benefits-lukso-standards', 'learn/getting-started', + { + type: 'category', + label: 'πŸ†š Why LUKSO', + items: [{ type: 'autogenerated', dirName: 'learn/why-lukso' }], + }, { type: 'category', label: 'πŸ”€ Migrate to LUKSO',