#+Title: Composability Extreminists: Callback or No-Callbacks? #+Subtitle: ETHPrague 2022-06-10 #+Author: Miao, ZhiCheng (hellwolf) / Superfluid #+Email: miao@superfluid.finance #+OPTIONS: num:nil toc:nil timestamp:nil #+REVEAL_THEME: night #+REVEAL_TRANS: Concave #+REVEAL_EXTRA_CSS: ../css/sf-slide-dark2022.css #+REVEAL_TITLE_SLIDE_BACKGROUND: ../images/sf-slide-dark2022-bg1.png #+REVEAL_DEFAULT_SLIDE_BACKGROUND: ../images/sf-slide-dark2022-bg1.png #+begin_notes :revisions | Date | Notes | | 2022-06-05 | Draft for [[https://cfp.paralelnipolis.cz/ethprague-2022/talk/NUW9M3/][ETHPrague Speech]] | | 2022-06-08 | Added Super Agreement Forwarder Control Flow per Protocol Workshop | | 2022-06-19 | Added Compatibility with Standard section | #+end_notes * What We Have Got Today? ** ERC-777 Transaction Event Hooks *** Good Idea: single tx of ~send+tokensReceived~ #+begin_src solidity function tokensReceived( address operator, address from, address to, uint256 amount, bytes calldata data, bytes calldata operatorData ) external #+end_src *** Bad Idea: Change the Semantics of ~ERC20.transfer~ - First Uniswap V1 (~300k $ drained) - Then lendf.me (defunct) (~24m $ drained) *** Reality: We Are Stuck With ~approve/transferFrom~ #+begin_src solidity // Two ransactions from the EOA token1.approve(exchange, amount); exchange.doSomeExchangeOp(); // in Exchange contract: function doSomeExchangeOp(uint amount) external { token1.transferFrom(msg.sender, amount); swapSomeToken1ForToken2(amount); token2.transfer(msg.sender, amount); } #+end_src *** Super Token "Dialect" - Supports ERC-777 interface. - But only invoking ~tokensReceived~ callback in ~ERC777.send~. - Supports more callbacks... ** History of ERC-777 #+ATTR_ORG: :width 480 #+ATTR_HTML: :width 30% :height 30% [[file:erc777-history.png]] More details at https://github.com/superfluid-finance/protocol-monorepo/wiki/About-ERC-777 §Reference (Kudos to Didi/@d10r) ** Super Token Agreement Life Cycle Events Hooks *** Superfluid Agreements - Representing *ongoing* relationships between accounts. - Providing *real time balances* to related accounts. #+begin_src haskell balanceOfAccountAt holderAccount t = foldr -- (js) reduce ((+) . (`providedBalanceOfAnyAgreement` t)) def -- initial balance: def(ault) value/zero (agreementsOfAccount holderAccount) #+end_src - Implemented agreements: - Constant Flow Agreement (CFA) - Streaming money every block. - Instant Distribution Agreement (IDA) - Money Fan-out on steroids. - To be introduced: - Transferable Balance Agreement (TBA) - ERC20 "baptized". - Decaying Flow Agreement. - General Distribution Agreement (GDA). *** CFA Life Cycle Events #+begin_src solidity function beforeAgreementCreated; function afterAgreementCreated; function beforeAgreementUpdated; function afterAgreementUpdated; function beforeAgreementTerminated; function afterAgreementTerminated; #+end_src *** Become a Composability Extremist - Super App, the contract that implemented the agreement life cycle hooks. - It enables atomic/transactional composable agreements. - Let's see some examples... *** Super App Example 1: DCA #+begin_quote Many want to invest every day some USDC for BTC. #+end_quote #+begin_src plantuml :file DCA.png title Dollar Cost Averaging skinparam backgroundColor #EEEBDC skinparam handwritten true skinparam rectangle { BackgroundColor<> PaleGreen backgroundColor<> DarkKhaki BackgroundColor<> DeepSkyBlue } rectangle Alice <> rectangle Bob <> rectangle Agent circle Index rectangle "DCA Super App" as DCA <> rectangle Exchange <> DCA -up-> Index: Distribute WBTC DCA -right-> Exchange: Swap USDC for WBTC Periodically Agent -up-> DCA: Poll & Trigger Swaps Alice -down-> DCA: Stream USDC Alice <-- Index: Receives WBTC Bob -down-> DCA: Stream USDC Bob <-- Index: Receives WBTC #+end_src #+RESULTS: [[file:DCA.png]] *** Super App Example 2: Referral Link App #+begin_quote Marketer gets commission paid the moment referral link is used. #+end_quote #+begin_src plantuml :file referral-link-app.png title Referral Link App skinparam backgroundColor #EEEBDC skinparam handwritten true skinparam rectangle { BackgroundColor<> PaleGreen backgroundColor<> DarkKhaki BackgroundColor<> DeepSkyBlue } rectangle Buyer <> rectangle Merchant <> rectangle Marketer <> rectangle "Referral Link App" as App <> Marketer ~right~> Buyer: Share referral link to Merchant Buyer ~left~> Merchant: Subscribe to a service Buyer -down-> App: Use referral link and stream payment App -up-> Marketer: 10% referral fee in streams App -up-> Merchant: 90% money #+end_src #+RESULTS: [[file:referral-link-app.png]] * What Are the Problems? ** Callbacks Are Scary: - Callbacks /can (but not necessarily)/ introduce re-entrance issue, - in additional to the bad design of breaking change of ~ERC-777.transfer~, - the list of EVM related exploits are wrought with re-entrance issues. ** Solutions to Callback Problems: - Avoidance (not really a solution). - Idiomatic programming patterns: - Checks-effects-interactions. - Mutex/Re-entrance lock. - Use different account model (e.g. EUTXO), which curbs the bad habit of introducing side effects in callbacks without easy way of reasoning the effects. ** Agreement Exit Guarantee #+begin_quote Stream sender must always be able to close the stream. #+end_quote *Problem*: /agreement deleted/ event hook can fail or enter a dead-loop. *App Jail System* - Triggers in a /agreement deleted/ event hook: - reverts, - out of /callback gas budget/, - etc. - Rationale: If an app can fail once, it's like can fail again * Slow Down. What is Composability? ** What Do Some Web3 People Have to Say - https://multicoin.capital/2022/02/16/composable-web3-compute/ - https://future.com/how-composability-unlocks-crypto-and-everything-else/ - https://blog.aragon.org/what-is-composality/ - https://thenewstack.io/ceramics-web3-composability-resurrects-web-2-0-mashups/ ** Conceptual Interoperability Model [[file:The-Levels-of-Conceptual-Interoperability-Model.png]] ** Integratability (L1 Technical, L2 Syntactic) - EVM opcodes for interacting with contract codes: - ~staticcall~ - readonly/view calls, getter, query, etc. - ~delegatecall~ (deprecating ~callcode~) - let other code manage caller's storage, proxy contract - ~call~ - internal transaction. - EVM transaction supports roll-backs. ** Interoperability (L3 Semantic, L4 Pragmatic) - Solidity ABI Contract Specification. - ⚠ ATTN! Non-Solidity Contract ABI interaction with contract code is always possible. - Exploit of Solidty Contract ABI decoder bug. - Under the radar transactions. ** Composability (L5 Dynamic, L6 Conceptual) - Modular: - Distribute your concepts through open-sourced solidity interface files. - Autonomous: - Permission-less. - Discoverable: - Provide example codes helping others to integrate. - Self-documented contracts? (Example in other domains: WSDL) ** Composability Patterns & Examples: *** Compatibility with Standards - ERC-20 and its ecosystem (wallet, defi, block scanner, etc.) - ERC-777 and its lack of adoption. - EIP-2771: Secure Protocol for Native Meta Transactions. /The Trusted Forwarder is responsible for calling the Recipient contract and MUST append the address of the Transaction Signer (20 bytes of data) to the end of the call data./ *** "Plugins" - rDAI strategy (using Compound Finance) - Yearn Vaults. - Superfluid Agreements. *** Transactional Systems - Flash Loans - Borrow -> Attempt arbitrage -> Revert if not profitable - Any Super Apps - Revert *** Conceptual Systems, Beyond Single Transaction or Single Agent: - Automated Market Makers (AMMs) - Certain Super App: DCA, Stream Swap, Transferable Cash Flow etc. * Different Control Flows ** Actual Control Flow #+begin_src plantuml :file actual-control-flow.png title Actual Superfluid Control Flow - createFlow to a Super App skinparam backgroundColor #EEEBDC skinparam handwritten true skinparam dpi 80 autoactivate on actor Alice entity "Superfluid Host" as Host entity CFA entity Token entity "Super App" as App Alice -> Host: callAgreement Host -> CFA: createFlow note left: ducktype-polymorphic\nevm external call to:\nabi.encodeCall(createFlow,...) CFA -> Token #red: token storages updates return CFA -> App #green : flow life-cycle callback opt Level 1 Super App wants to compose App -> App: Do Some App Stuff return App -> Host #gold : Nested callAgreement Host -> Host: Perform re-entrance protection note left: some nested operations... Host -> Token #red: update token storages return return end return return return #+end_src #+RESULTS: [[file:actual-control-flow.png]] ** Agreement Forwarder *** What it Is - ~userData~ in overloaded functions, or single function with it default to "0x" - overloaded function may give confusing error messages, unless we utilize user defined types for generic types "bytes[]". - version management - immutable forwarder. - release new version of forwarders for upgrades. - functions: - :CFA - ~creatFlow~ - ~updateFlow~ - ~deleteFlow~ - ~setFlow~ / ~flow~ (user.flow) - :IDA - ~createIndex~ - ~updateSubscriptionUnit~ - ~approveSubscription~ - ~revokeSubscripion~ - ~distribute~ - ~claim~ - :FDA - ~updateIndexFlow~ *** How it looks like #+begin_src plantuml :file super-agreement-forwarder.png title Super Agreement Forwarder skinparam backgroundColor #EEEBDC skinparam handwritten true skinparam defaultFontName Impact skinparam dpi 100 autoactivate on actor Mario entity "Super Agreement\nForwarder" as SAF entity "Superfluid Host" as Host entity "CFA" as CFA entity Token Mario -> SAF: createFlow SAF -> Host: forwardBatchCall [createFlow] Host -> CFA: createFlow CFA -> Token #red: update storages return return return return #+end_src #+RESULTS: [[file:super-agreement-forwarder.png]] ** Functional Agreement and Token Centric Control Flow *** Advantages - Token becomes what Alice interacts first: - More natural API: - ~token.transfer~ - ~token.createFlow/updateFlow/deleteFlow~ - ~token.createIndex/distributeToIndex/updateIndexFlow/subcribeToIndex~ - Functional composition, restraining side effects *** How It Looks Like #+begin_src plantuml :file independent-composer.png title Token Centric Control Flow skinparam backgroundColor #EEEBDC skinparam handwritten true skinparam defaultFontName Impact skinparam dpi 100 autoactivate on actor Alice entity Token entity "Superfluid Host" as Host entity "Functional CFA" as CFA entity "Super App" as App Alice -> Token: createFlow Token -> Host: safeCallAgreement Host -> CFA: createFlow (pure) note left: type-safe runtime polymorphic\n+ pure function return token update instructions opt Super App wants to compose Host -> App #green: flow life-cycle callback App -> Host #gold: Nested callAgreement note left: save additional\ntoken update instructions return return end Host -> Token #red: commit token update instructions return return return #+end_src #+RESULTS: [[file:independent-composer.png]] * Conclusion - Composability is powerful, and worth the efforts. - Composability is not a new problem, we should draw insights and lessons from the past. - There is more than one way to achieve composability. * Thank You - Join Superfluid: http://jobs.superfluid.finance/ - Check our bug bounty: https://bugs.immunefi.com/ - Start hacking Superlfuid money: https://github.com/superfluid-finance/protocol-monorepo/ - Presentation source code (org-mode + org-reveal): [[https://github.com/superfluid-finance/protocol-monorepo/wiki/Presentation:-Composability-Extremists]]