Skip to content

Commit

Permalink
chore: add tx vs portfolio page - update navigation files
Browse files Browse the repository at this point in the history
chore: updates
  • Loading branch information
amessbee committed Feb 25, 2025
1 parent 6fa4caf commit da3349e
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
4 changes: 4 additions & 0 deletions main/.vitepress/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ export default defineConfig({
text: 'What is Agoric Orchestration?',
link: '/guides/orchestration/',
},
{
text: 'Transactional vs Portfolio Orchestration',
link: '/guides/orchestration/txvsportfolio',
},
{
text: 'Key Concepts and APIs',
link: '/guides/orchestration/key-concepts',
Expand Down
4 changes: 4 additions & 0 deletions main/.vitepress/themeConfig/nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export const nav = [
text: 'What is Agoric Orchestration?',
link: '/guides/orchestration/',
},
{
text: 'Transactional vs Portfolio Orchestration',
link: '/guides/orchestration/txvsportfolio',
},
{
text: 'Key Concepts and APIs',
link: '/guides/orchestration/key-concepts',
Expand Down
166 changes: 166 additions & 0 deletions main/guides/orchestration/txvsportfolio.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# 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.

## 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.

| **Characteristic** | Transactional Contracts | Portfolio Contracts |
| ---------------------------- | ----------------------------- | -------------------------------- |
| **Interaction Type** | Single-shot | Multi-step, ongoing |
| **Persistent User State** | No | Yes |
| **User Account Requirement** | No dedicated on-chain account | Separate sub-account or position |
| **Example Use Case** | Token swap, cross-chain send | Staking, vaults |

0 comments on commit da3349e

Please sign in to comment.