Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Orch/Unbond Walkthrough #1221

Merged
merged 2 commits into from
Oct 7, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,163 +1,118 @@
# Cross-Chain Unbond Contract

## Overview Diagram
<!-- XXX the diagram below is a useful part of the page but it needs to be updated before it is uncommented. -->
<!-- ## Overview Diagram

<br/>
<img src="/reference/assets/sequence-diagrams/orchestration-unbond-example.svg" width="100%" />
<br/>

## Imports

```js
import { M } from '@endo/patterns';
import { withOrchestration } from '../utils/start-helper.js';
```
<br/> -->

- `M`: Imported from @endo/patterns, provides pattern-matching utilities.
- `withOrchestration`: Imported from a utility module, used to set up and provide access to orchestration tools.
This walkthrough outlines the functionality of the Unbond Contract that enables unbonding of assets from a
Cosmos-based chain and transferring them to another chain using IBC (Inter-Blockchain Communication).

## JSDoc Annotations for Type Information
## Overview

```js
/**
* @import {Orchestrator, IcaAccount, CosmosValidatorAddress} from '../types.js'
* @import {TimerService} from '@agoric/time';
* @import {Baggage} from '@agoric/vat-data';
* @import {LocalChain} from '@agoric/vats/src/localchain.js';
* @import {NameHub} from '@agoric/vats';
* @import {Remote} from '@agoric/internal';
* @import {Zone} from '@agoric/zone';
* @import {CosmosInterchainService} from '../exos/cosmos-interchain-service.js';
* @import {OrchestrationTools} from '../utils/start-helper.js';
*/
```
The Unbond Contract leverages the Agoric Orchestration API to interact with external chains, like Osmosis and Stride, to facilitate unbonding and transferring assets from one chain to another.

This includes type information annotations to help with TypeScript or JSDoc, making it easier to understand the types used throughout the contract.
The contract consists of two main parts:

## `unbondAndLiquidStakeFn` Function
- **Contract File (`unbond.contract.js`)**: Defines the contract structure, and public-facing APIs.
- **Flows File (`unbond.flows.js`)**: Implements the logic for the unbonding and transfer operations.

```js
/**
* @param {Orchestrator} orch
* @param {object} ctx
* @param {ZCF} ctx.zcf
* @param {ZCFSeat} _seat
* @param {undefined} _offerArgs
*/
const unbondAndLiquidStakeFn = async (orch, { zcf }, _seat, _offerArgs) => {
// ...
```
## Contract: `unbond.contract.js`

### Function Parameters
This file contains the main Orchestration contract, which is wrapped using the `withOrchestration` helper for Zoe. It exposes a public facet that allows users to initiate the unbonding process and transfer assets to another chain.

- `orch`: The orchestrator object to manage interactions with chains/accounts.
- `ctx`: Context object containing zcf.
- `_seat`: The seat representing the user’s position in the contract (not used in this function, hence `_` prefix).
- `_offerArgs`: Arguments provided with the offer (not used in this function, hence `_` prefix).
### Imports

## Interacting with Chains
The key imports include the `withOrchestration` helper, pattern matching utility `M`, and the flows from `unbond.flows.js` files.

```js
const omni = await orch.getChain('omniflixhub');
const omniAccount = await omni.makeAccount();
import { M } from '@endo/patterns';
import { withOrchestration } from '../utils/start-helper.js';
import * as flows from './unbond.flows.js';
```

### Get Chain

Retrieves the omniflixhub chain object using the orchestrator.
### `contract` Function

### Make Account
The `contract` function when wrapped inside `withOrchestration` defines the [`start` function](#start-function) which is the entry point of the contract. The contract exports a `start` function [below](#start-function). It is merely a convention/convenience that we define a more abstract `contract` function here and pass it to `withOrchestration`. The `contract` function parameters include:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There needs to be a separate h3 for the start function below for the link to work. It doesn't, right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Will add back.


Creates an account on the omniflixhub chain.

## Interaction with Stride Chain
- `zcf`: Zoe Contract Facet.
- `privateArgs`: Object containing remote references to various services.
- `zone`: A `Zone` object with access to storage for persistent data.
- `OrchestrationTools`: A set of Orchestration related tools needed by the contract.

```js
const stride = await orch.getChain('stride');
const strideAccount = await stride.makeAccount();
const contract = async (
zcf,
privateArgs,
zone,
{ orchestrateAll, zcfTools }
) => {
const { unbondAndTransfer } = orchestrateAll(flows, { zcfTools });

const publicFacet = zone.exo('publicFacet', undefined, {
makeUnbondAndTransferInvitation() {
return zcf.makeInvitation(
unbondAndTransfer,
'Unbond and transfer',
undefined,
harden({
give: {},
want: {},
exit: M.any()
})
Comment on lines +60 to +64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a line or two about why this contract doesn't have or expect any give/want terms?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel the need for it - this would raise more questions that rather not be answer here.

);
}
});

return harden({ publicFacet });
};
```

### Get Chain

Retrieves the stride chain object using the orchestrator.

### Make Account
The `orchestrateAll` function links the flows from the flows file to the contract logic. In this case, it links the `unbondAndTransfer` flow. The `publicFacet` exposes the `makeUnbondAndTransferInvitation` method, which creates a Zoe invitation to allow users to make an offer for the unbonding and transferring process.

Creates an account on the stride chain.
### `start` function

## `contract` Function

The `contract` function when wrapped inside `withOrchestration` defines the [`start` function](#start-function) which is the entry point of the contract. The contract exports a `start` function [below](#start-function). It is merely a convention/convenience that we define a more abstract `contract` function here and pass it to `withOrchestration`. The arguments of this function are `zcf`, `privateAge`, `zone`, and `tools` for orchestration.
The following code defines the `start` function of the contract that is returned by a call to `withOrchestration` with [`contract` function](#contract-function) as a parameter. In essence `contract` function is the entry point or `start` function of this contract with some Orchestration setup.

```js
/**
* Orchestration contract to be wrapped by withOrchestration for Zoe
*
* @param {ZCF} zcf
* @param {{
* agoricNames: Remote<NameHub>;
* localchain: Remote<LocalChain>;
* orchestrationService: Remote<CosmosInterchainService>;
* storageNode: Remote<StorageNode>;
* marshaller: Marshaller;
* timerService: Remote<TimerService>;
* }} privateArgs
* @param {Zone} zone
* @param {OrchestrationTools} tools
*/
const contract = async (zcf, privateArgs, zone, { orchestrate }) => {
export const start = withOrchestration(contract);
```

### `contract` Function Parameters:
## Flows: `unbond.flows.js`

- `zcf`: Zoe Contract Facet.
- `privateArgs`: Object containing remote references to various services.
- `zone`: A `Zone` object with access to storage for persistent data.
- `OrchestrationTools`: A set of orchestration related tools needed by the contract.
This file contains the Orchestration flow that performs the unbonding and transferring of assets across chains.

### Flow Function: `unbondAndTransfer`

## Offer Handler for Unbond and Liquid Stake
The `unbondAndTransfer` flow orchestrates the process of unbonding assets from a source chain (e.g., Osmosis) and transferring them to a destination chain (e.g., Stride).

```js
/** @type {OfferHandler} */
const unbondAndLiquidStake = orchestrate(
'LSTTia',
{ zcf },
unbondAndLiquidStakeFn
);
```
export const unbondAndTransfer = async (orch, { zcfTools }) => {
const osmosis = await orch.getChain('osmosis');
const osmoDenom = (await osmosis.getChainInfo()).stakingTokens[0].denom;

### Offer Handler
const osmoAccount = await osmosis.makeAccount();
const delegations = await osmoAccount.getDelegations();
const osmoDelegations = delegations.filter(d => d.amount.denom === osmoDenom);

Defines the offer handler for the unbond and liquid stake operation using [`unbondAndLiquidStakeFn`](#unbondandliquidstakefn-function).
await osmoAccount.undelegate(osmoDelegations);

## Make Invitation and Create `publicFacet`
const stride = await orch.getChain('stride');
const strideAccount = await stride.makeAccount();

```js
const publicFacet = zone.exo('publicFacet', undefined, {
makeUnbondAndLiquidStakeInvitation() {
return zcf.makeInvitation(
unbondAndLiquidStake,
'Unbond and liquid stake',
undefined,
harden({
// Nothing to give; the funds come from undelegating
give: {},
want: {}, // XXX ChainAccount Ownable?
exit: M.any()
})
);
}
});

return harden({ publicFacet });
const balance = await osmoAccount.getBalance(osmoDenom);
await osmoAccount.transfer(strideAccount.getAddress(), balance);
};
```

Defines the `publicFacet` for the contract, which includes the method to make an `invitation`, and returns the hardened public facet. Defining `publicFacet` with `zone.exo` makes it [remotely accessible](/glossary/#exo) and persistent through contract upgrades with a [durable `zone`](/glossary/#zone).
The above code achieve several things including:

## `start` Function

```js
export const start = withOrchestration(contract);
```
- Retrieval of `osmosis` chain object, and `osmo` denom from the chain info.
- Create an `osmoAccount` on `osmosis`. _Note that in real-life scenario, this step would not be needed as we would be using an account that has already delegated some `osmo` assets_.
- Perform `undelegate` on `osmo` delegations of the `osmoAccount`.
- Create an account on `stride` chain.
- Transfer all `osmo` balance from `osmoAccount` to `strideAccount`.

Defines the `start` function of the contract that is returned by a call to `withOrchestration` with [`contract` function](#contract-function) as a parameter. In essence `contract` function is the entry point or `start` function of this contract with some orchestration setup.
Upon successful transfer, the assets are moved from the Osmosis chain to the Stride chain, ready for the user to claim.
Loading