From ced1cac35b6b980a73e26ccbac270b260a5ef0a9 Mon Sep 17 00:00:00 2001 From: Mudassir Shabbir Date: Tue, 25 Feb 2025 20:51:12 +0500 Subject: [PATCH] chore: updates --- main/.vitepress/config.mjs | 4 + main/.vitepress/themeConfig/nav.js | 4 + main/guides/orchestration/txvsportfolio.md | 168 +++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 main/guides/orchestration/txvsportfolio.md diff --git a/main/.vitepress/config.mjs b/main/.vitepress/config.mjs index 02289f3ad..89fd8414d 100644 --- a/main/.vitepress/config.mjs +++ b/main/.vitepress/config.mjs @@ -128,6 +128,10 @@ export default defineConfig({ text: 'What is Agoric Orchestration?', link: '/guides/orchestration/', }, + { + text: 'Transactional vs Portfolio', + link: '/guides/orchestration/txvsportfolio', + }, { text: 'Key Concepts and APIs', link: '/guides/orchestration/key-concepts', diff --git a/main/.vitepress/themeConfig/nav.js b/main/.vitepress/themeConfig/nav.js index 0bbd2f63b..35a52886a 100644 --- a/main/.vitepress/themeConfig/nav.js +++ b/main/.vitepress/themeConfig/nav.js @@ -13,6 +13,10 @@ export const nav = [ text: 'What is Agoric Orchestration?', link: '/guides/orchestration/', }, + { + text: 'Transactional vs Portfolio', + link: '/guides/orchestration/txvsportfolio', + }, { text: 'Key Concepts and APIs', link: '/guides/orchestration/key-concepts', diff --git a/main/guides/orchestration/txvsportfolio.md b/main/guides/orchestration/txvsportfolio.md new file mode 100644 index 000000000..e978dbf7f --- /dev/null +++ b/main/guides/orchestration/txvsportfolio.md @@ -0,0 +1,168 @@ +# Transactional vs. Portfolio Contracts + +In Agoric Orchestration, **transactional contracts** and **portfolio contracts** serve distinct use +cases. This section explains their differences and provides examples from real contracts. + +| **Characteristic** | **Transactional Contracts** | **Portfolio Contracts** | +| :------------------------ | :---------------------------- | :------------------------------- | +| **Interaction Type** | Single-shot | Multi-step, ongoing | +| **Persistent User State** | No | Yes | +| **User Account** | No dedicated on-chain account | Separate sub-account or position | +| **Example Use Case** | Token swap, cross-chain send | Staking, vaults | + +Table: Comparison of Transactional and Portfolio Contracts in Agoric Orchestration + +## Transactional Contracts + +**Transactional contracts** perform one-shot actions, e.g., swapping tokens or sending them from one +chain to another. Each use is typically triggered by a single transaction, after which there is no +long-lived state managed by the contract for that user. + +**Characteristics**: + +- **Initiation**: Usually initiated by a single transaction from the user or external source. +- **No per-user account**: The contract does not maintain persistent user-specific state. +- **No smart wallet requirement**: Users often do not require a dedicated on-chain account. +- **One-shot**: The operation typically starts and finishes in a single interaction. + +### Example: "Send Anywhere" Contract + +The `send-anywhere` contract illustrates how a user can send tokens from Agoric to another chain in +one action. Under the hood, it exposes an `invitation` that, when exercised, moves tokens once and +does not maintain any further user-specific state. + +**Key Points**: + +- The contract exposes a single `makeSendInvitation` method that returns an Invitation. +- Users deposit tokens into the contract just for this one transaction. +- After the tokens are sent, the seat concludes and there is no ongoing position to manage. + +Below is a simplified snippet adapted from the flows (`send-anywhere.flows.js`) showing the essence +of the transactional flow: + +```js +/** + * Example flow for a transactional contract: + * + * @param {ZCFSeat} seat - The user's seat, including the tokens to send. + * @param {string} chainName - Name of the destination chain. + * @param {string} destAddr - Destination address on the chain. + */ +export const sendIt = async ( + orch, + { sharedLocalAccountP, log, zoeTools }, + seat, + { chainName, destAddr } +) => { + // 1. Extract the single token amount from the proposal. + const { give } = seat.getProposal(); + const [[_, amount]] = Object.entries(give); + + // 2. Transfer the tokens into a local account that can initiate the IBC transfer. + await zoeTools.localTransfer(seat, sharedLocalAccountP, give); + + // 3. Execute the cross-chain transfer. + await sharedLocalAccountP.transfer( + { value: destAddr, chainId: (await orch.getChain(chainName)).chainId }, + { denom, value: amount.value } + ); + + // 4. Finish. No persistent state is stored for this user. + seat.exit(); +}; +``` + +In this snippet, we perform the following steps: + +1. **Extract token Detailss**: The user's seat provides the tokens details (_amount_, _brand_) in a single `give`. +2. **Send via localAccount**: A local account is used only as a helper to transfer funds from `seat` to an ICA account. +3. **IBC transfer**: Tokens are transferred cross-chain in a single step via [`transfer` API](/guides/orchestration/key-concepts#funds-transfer). +4. **No ongoing portfolio**: Once done, the seat concludes—there is no stored user state. + +This is a typical example of a transactional contract. It performs a single +action (sending tokens) and does not maintain any ongoing user state. The user interacts once, and +the contract concludes the operation. This contract is discussed in more detail in the +[`send-anywhere` contract walkthrough](/guides/orchestration/contract-walkthroughs/send-anywhere). + +## Portfolio Contracts + +**Portfolio contracts** manage long-lived user state and support multiple interactions over time. +They resemble "vaults": you open a portfolio (or vault) position, then manipulate or monitor it as +desired. + +**Characteristics**: + +- **Persistent per-user state**: Each user typically has their own account. +- **Multiple user actions**: The state can be updated with each new user action. +- **Orchestration Flows**: Users often interact through orchestration flows. +- **Onboarding**: Users first deposit tokens into a personal portfolio for management. + +### Example: "Auto Stake It" Contract + +The `auto-stake-it` contract demonstrates how to continuously manage a user's positions. A user +"onboards" into the contract by creating accounts on multiple chains (via orchestration) and then +depositing tokens to be staked automatically. + +Key points in code: + +1. **Prepare a PortfolioHolder** that stores each user's accounts in a single structure. +2. **Add accounts** to the portfolio. Each user might have multiple chain accounts. +3. **Perform multi-step operations** (e.g. receive tokens over IBC, transfer to staking). + +Below is a simplified snippet (`auto-stake-it.flows.js`) showing how a portfolio is set up when a +user calls the "makeAccounts" flow: + +```js +export const makeAccounts = async (orch, { makeStakingTap, makePortfolioHolder, chainHub }, + seat, { chainName, validator }) => { + seat.exit(); // no funds are exchanged at the moment + + // 1. Get chain references (e.g., 'agoric' chain and the remote chain). + const [agoric, remoteChain] = await Promise.all([ + orch.getChain('agoric'), + orch.getChain(chainName), + ]); + + // 2. Create orchestration accounts for each chain. + const [localAccount, stakingAccount] = await Promise.all([ + agoric.makeAccount(), + remoteChain.makeAccount(), + ]); + + // 3. Combine them into a single PortfolioHolder for the user. + const accountEntries = harden([ + ['agoric', localAccount], + [chainName, stakingAccount], + ]); + const publicTopicEntries = /* gather each account's public topics */; + const portfolioHolder = makePortfolioHolder(accountEntries, publicTopicEntries); + + // 4. Return a continuing offer result with invitationMakers, etc. + return portfolioHolder.asContinuingOffer(); +}; +``` + +In this snippet, we create multiple accounts (one for Agoric, one for the remote chain). +Then, the contract uses `makePortfolioHolder` to maintain these in a persistent store. +Users can perform multiple future actions without losing state. Learn more about the +`makePortfolioHolder` function in the [`makePortfolioHolder` Orchestration API reference](https://agoric-sdk.pages.dev/funcs/_agoric_orchestration.preparePortfolioHolder). + +## Choosing Between Transactional and Portfolio + +Use **transactional contracts** (like "send-anywhere") when: + +- You want a **single-shot interaction** (e.g. a swap, a cross-chain send). +- No ongoing user state is needed. +- The user wants minimal overhead (no dedicated on-chain structure). + +Use **portfolio contracts** (like "auto-stake-it") when: + +- You need **long-lived, multi-step interactions** (e.g. staking, compounding). +- Each user needs a separate sub-account or position. +- User state must persist (e.g., "my tokens remain staked until I withdraw"). + +Agoric Orchestration supports both **transactional contracts** for single interactions and +**portfolio contracts** for rich, persistent user positions. Use the pattern that best fits +your user flows. A purely transactional workflow is great for quick, one-time trades, while +a portfolio-based approach is ideal for scenarios like vaults or staking, where users +maintain ongoing positions.