1 of 75

Writing

Asynchronous

Cross-Chain

Smart Contracts

Nebular - July 2024

1

@Jovonni

Developer Relations Engineer

2 of 75

Agenda

  • Foundations
  • Basic Concepts
  • Writing Contracts
  • Upgrading Contracts
  • Orchestration Contracts

Nebular Workshop - July 2024

3 of 75

The Destination of Today’s Journey

const unbondAndLiquidStakeFn = async (orch, {

zcf,

celestiaAccount

}, _seat, _offerArgs) => {

const delegations = await celestiaAccount.getDelegations();

await celestiaAccount.undelegate(delegations);

const stride = await orch.getChain('stride');

const strideAccount = await stride.makeAccount();

const tiaAmt = await celestiaAccount.getBalance('TIA');

await celestiaAccount.transfer(tiaAmt*0.90, strideAccount.getAddress());

await strideAccount.liquidStake(tiaAmt);

};

4 of 75

Foundations

Section 1

Writing Asynchronous Cross-Chain Smart Contracts

4

5 of 75

What is Agoric?

  • Smart contract platform
  • Write smart contracts in JavaScript.
  • Object Capability (OCap) Security Model
  • Hardened JavaScript.

6 of 75

What is a Smart Contract

We define a “smart contract” as a contract-like arrangement expressed in code where the behavior of the program enforces the terms of the contract.

7 of 75

What are Object Capabilities?

an object reference familiar from object programming is a permission.

If object Bob has no reference to object Carol, then Bob cannot invoke Carol; Bob can't provoke whatever behavior Carol would have.

If Alice has a reference to Bob and invokes Bob, passing Carol as an argument, then Alice has both used her permission to invoke Bob and given Bob permission to invoke Carol.

8 of 75

Why Hardened JavaScript?

  • Traditional JavaScript is malleable, making it susceptible to security vulnerabilities.
  • Locks down JavaScript's built-in objects.
  • Freezes these objects to prevent modification.
  • Ensures a secure and predictable execution environment.
  • Useful for making objects whose internal state can only change in accordance to the API the programmer defines.

Array.prototype.push =

function(element) {

doSomethingFishy()

this[this.length] = element;

};

9 of 75

Hardening Objects

Without Hardened JavaScript, anyone can clobber the properties of our objects.

const makePoint = (x, y) => {

return {

getX: () => x,

getY: () => y,

};

};

const p11 = makePoint(1, 1);

p11.getX = () => 'I am not a number!';

const makePoint = (x, y) => {

return harden({

getX: () => x,

getY: () => y,

});

};

const p11 = makePoint(1, 1);

p11.getX = () => 1; // throws

To prevent tampering, use the harden() function, which is a deep form of Object.freeze.

10 of 75

State Management in Contracts

Contracts can use ordinary variables and data structures for state.

let balance = 1000;

let owner = 'Alice';

let transactions = [];

const rooms = new Map();

11 of 75

Hello World Smart Contract

import { Far } from '@endo/far';

const greet = who => `Hello, ${who}!`;

export const start = () => {

return {

publicFacet: Far('Hello', { greet }),

};

};

Start function: A contract is defined by a JavaScript module that exports a start function

This is similar to the constructor in Solidity, or the instantiate function in Cosmwasm/Rust

12 of 75

Access Control with Objects

Access control is based on separation of powers, and is based on the Object-Capability Model

import { Far } from '@endo/far';

export const start = () => {

let value = 'Hello, World!';

const get = () => value;

const set = v => (value = v);

// Far creates a remotable object

return {

publicFacet: Far('ValueView', { get }),

creatorFacet: Far('ValueCell', { get, set }),

};

};

This pattern allows for:

  • get to be invoked publicly
  • set can only be invoked by the creator

13 of 75

vats

  • Combination of an event loop with a message queue, a stack, and a heap.
  • Unit of Isolation.
  • All synchronous communication is only within a vat.
  • All communications between vats is async
  • Contracts run inside of their own vat
  • You can also async communication within a vat

14 of 75

Basic Concepts

Section 2

Writing Asynchronous Cross-Chain Smart Contracts

14

15 of 75

Basic Concepts

Brand

Amount

Purse

Payment

Asset Kind

Facet

Mint

Issuer

16 of 75

Basic Concepts: Brand

A Brand represents the unique identity of a type of digital asset. It is associated with a specific Mint and Issuer.

Identifies the type of issuer, such as "ATOM", "BLD", “USDC” etc. Brands are one of the two elements that make up an Amount

const brand = await E(tokenIssuer).getBrand();

17 of 75

Basic Concepts: Amount

An Amount is a description of digital assets, answering the questions "how much?" (its Amount Value) and "of what kind?” (its Brand).

Represent specific quantities of digital assets. Amounts can describe either fungible, non-fungible, or semi-fungible assets.

import { AmountMath } from '@agoric/ertp';

const amount = AmountMath.make(someBrand, 1000n);

18 of 75

Agoric Concepts: Issuer

Responsible for validating and managing a specific currency.

An issuer is a trusted authority that validates payments.

const { brand, issuer, mint } = makeIssuerKit('ATOM');

19 of 75

Agoric Concepts: Mint

Mints create new instances of digital assets, such as a new currency, tickets or NTFs.

Only a Mint can issue new digital assets.

A Mint has a one-to-one relationship with both an Issuer and a Brand. It can only mint new assets of that Brand and is the only Mint that can mint new assets of that Brand.

import { makeIssuerKit, AmountMath } from '@agoric/ertp';

const { brand, issuer, mint } = makeIssuerKit('MyToken');

const amount = AmountMath.make(brand, 1000n);

const payment = mint.mintPayment(amount);

20 of 75

Agoric Concepts: Payment

Payments are spendable representations of digital assets.

A unit of digital assets created by a Mint that can be transferred between Purses or used in smart contract transactions

const { mint: atomMint, brand: atomBrand } = makeIssuerKit('ATOM');

const atom123 = AmountMath.make(atomBrand, 123n);

const atomPayment = atomMint.mintPayment(atom123);

21 of 75

Agoric Concepts: Purse

Purses are containers for holding digital assets of a specific brand.

An object that holds a balance of digital assets created by a specific Mint

Purses can receive deposits and allow withdrawals

const { issuer: atomIssuer, mint: atomMint, brand: atomBrand } = makeIssuerKit('ATOM');

const atomPurse = atomIssuer.makeEmptyPurse();

const atom123 = AmountMath.make(atomBrand, 123n);

const atomPayment = atomMint.mintPayment(atom123);

atomPurse.deposit(atomPayment)

...

atomPurse.withdraw(atomPayment)

22 of 75

Agoric Concepts: Asset Kind (Fungible/Non-Fungible)

There are several kinds of Assets.��AssetKind.NAT: Used with fungible assets.��AssetKind.COPY_BAG: Used with semi-fungible assets where there can be duplicates.�

import { AssetKind, makeIssuerKit } from '@agoric/ertp';

// Defaults to AssetKind.NAT

makeIssuerKit('ATOM');

makeIssuerKit('concertTickets', AssetKind.COPY_BAG);

23 of 75

Agoric Concepts: Facet

A facet is an object that exposes an API or particular view of some larger entity, which may be an object itself

import { Far } from '@endo/far';

export const start = () => {

let value = 'Hello, World!';

const get = () => value;

const set = v => (value = v);

// Far creates a remotable object

return {

publicFacet: Far('ValueView', { get }), // read-only

creatorFacet: Far('ValueCell', { get, set }), // read/write

};

};

24 of 75

Writing Contracts

Section 3

Writing Asynchronous Cross-Chain Smart Contracts

24

25 of 75

Offer Safety

Offer Safety means that the user is guaranteed to either get what they said they wanted or receive a full refund of what they offered.

26 of 75

Zoe Concepts

The Zoe framework provides a way to write smart contracts without having to worry about offer safety.�

Invitation

Offer

Seat

Zoe

Proposal

27 of 75

Zoe Concepts: Zoe

Zoe is a service and smart contract API designed for trading assets with reduced risk.

For Users:

  • Increased confidence in transactions.
  • Protection against buggy or malicious contracts.

For Developers:

  • Simplifies error handling in smart contract development.
  • Enhances the overall reliability and safety of the platform.

28 of 75

Zoe Concepts: Zoe Contract Facet(ZCF)

A Zoe Contract Facet (ZCF) is an API object for a running contract instance to access the Zoe state for that instance.

A ZCF is accessed synchronously from within the contract, and usually is referred to in code as zcf.

The contract instance is launched by E(zoe).startInstance(...) and is given access to the zcf object during that launch

const start = async zcf => {

// Contract Logic

};

29 of 75

Zoe Concepts: Proposal

Proposals are records with give, want, and/or exit properties

  • Give: What the offer-making party is willing to give.�
  • Want: What they want in exchange.�
  • Exit: An exit rule defining how/when the offer can be canceled. Must conform to one of three shapes:
    • onDemand: (Default) The offering party can cancel on demand.
    • waived: The offering party can't cancel and relies entirely on the smart contract to complete (finish or fail) their offer.
    • afterDeadline: The offer is automatically cancelled after a deadline, as determined by its timer and deadline properties.

const proposal = harden({

give: { Price: AmountMath.make(brandA, 5n); },

want: { Items: AmountMath.make(brandB, 4n); },

exit: { onDemand: null },

});

30 of 75

Zoe Concepts: Invitation

A payment whose amount represents participation in a contract instance.

  • Necessary for joining contract instances.�
  • Created within the contract by zoe, as needed for new participants.

  • Participation initiated through an offer that includes the invitation.

31 of 75

Zoe Concepts: Invitation

const publicFacet = E(zoe).getPublicFacet(instance);

const terms = await E(zoe).getTerms(instance);

const { issuers, brands, tradePrice } = terms;

const choices = makeCopyBag([['map', 1n], ['scroll', 1n]]);

const proposal = {

give: { Price: tradePrice },

want: { Items: AmountMath.make(brands.Item, choices) },

};

const pmt = await E(purse).withdraw(tradePrice);

const toTrade = E(publicFacet).makeTradeInvitation();

const seat = E(zoe).offer(toTrade, proposal, { Price: pmt });

const items = await E(seat).getPayout('Items');

Create Invitation to trade, and submit offer with proposal using the invitation

32 of 75

Zoe Concepts: Offer

Users interact with contract instances by making offers.��In Zoe, an offer consists of:

  • Invitation to participate in this contract instance.
  • A proposal.
  • Payments corresponding to the amount specified in the proposal.
  • offerArgs expressing additional arguments.

33 of 75

Zoe Concepts: Offer

const publicFacet = E(zoe).getPublicFacet(instance);

const terms = await E(zoe).getTerms(instance);

const { issuers, brands, tradePrice } = terms;

const choices = makeCopyBag([['map', 1n], ['scroll', 1n]]);

const proposal = {

give: { Price: tradePrice },

want: { Items: AmountMath.make(brands.Item, choices) },

};

const pmt = await E(purse).withdraw(tradePrice);

const toTrade = E(publicFacet).makeTradeInvitation();

const seat = E(zoe).offer(toTrade, proposal, { Price: pmt });

const items = await E(seat).getPayout('Items');

Create Proposal

Make Invitation, and submit offer using the proposal, and invitation

34 of 75

Zoe Concepts: Seats

Zoe uses a seat to represent an offer in progress, and has two seat facets representing two views of the same seat:

  • ZCFSeat: Used within smart contracts, it's handed to contract code via an offerHandler function. It manages offers using specific zcf methods.�
  • UserSeat: Given to the offer-making object, it allows checking payout status and receiving agreed items or money.

35 of 75

Zoe Concepts: ZCF Seat

const tradeHandler = buyerSeat => {

const { want } = buyerSeat.getProposal();

...

sum(bagCounts(want.Items.value)) <= maxItems ||

Fail`max ${q(maxItems)} items allowed: ${q(want.Items)}`;

const newItems = itemMint.mintGains(want); // returns a zcfSeat

atomicRearrange(

zcf,

harden([

[buyerSeat, proceeds, { Price: tradePrice }],

[newItems, buyerSeat, want],

]),

);

buyerSeat.exit(true); // exits a zcfSeat

newItems.exit(); // exits a zcfSeat

return 'trade complete';

};

A ZCF seat (buyerSeat) is passed as an argument to the offer handler

36 of 75

Zoe Concepts: User Seat

const publicFacet = E(zoe).getPublicFacet(instance);

const terms = await E(zoe).getTerms(instance);

const { issuers, brands, tradePrice } = terms;

const choices = makeCopyBag([['map', 1n], ['scroll', 1n]]);

const proposal = {

give: { Price: tradePrice },

want: { Items: AmountMath.make(brands.Item, choices) },

};�

const pmt = await E(purse).withdraw(tradePrice);

const toTrade = E(publicFacet).makeTradeInvitation();

const seat = E(zoe).offer(toTrade, proposal, { Price: pmt });

const items = await E(seat).getPayout('Items');

User receives a seat upon submitting an offer, and uses it to get payouts from the seat

37 of 75

Contract Concepts: Offer Handlers

  • Handlers are responsible for handling any offers made to the contract
  • It accepts a zcfSeat as an argument and returns arbitrary offer results

const tradeHandler = buyerSeat => {

const { want } = buyerSeat.getProposal();

sum(bagCounts(want.Items.value)) <= maxItems ||

Fail`max ${q(maxItems)} items allowed`;

const newItems = itemMint.mintGains(want); // returns a zcfSeat

atomicRearrange(...);

buyerSeat.exit(true);

newItems.exit();

return 'trade complete';

};

38 of 75

Contract Concepts: Private args

  • Private Args is an optional argument that can be passed when starting a contract.
  • It is an object record with values that need to be made available to the contract code, but which should not be in the public terms.
  • For example, to share minting authority among multiple contracts, we can pass an external mint object

39 of 75

Contract Concepts: Public Facet

  • A Public Facet exposes a set of methods and properties for a contract that a developer chooses to be publicly visible and usable.
  • Public facets are expected to be “widely-held”, or shared with as many as needed

const publicFacet = Far('Items Public Facet', {

makeTradeInvitation() {

return zcf.makeInvitation(tradeHandler, 'buy items', undefined, proposalShape);

},

});

return harden({ publicFacet });

40 of 75

Contract Concepts: Creator Facet

  • A Creator Facet is only available to the contract creator, in the result of the startInstance call.
  • The contract designer should use it to encapsulate things that the contract runner might not want to share.
  • Creator facets are expected to be “closely-held”, or shared with as few as needed

const limitedCreatorFacet = Far('Creator', {

makePriviledgedInvitation() {

return makePriviledgedInvitation(zcf, feeSeat, feeBrand, 'Fee');

},

});

41 of 75

Terms

  • Each Contract Instance may have its own Terms associated with it
  • It include the instance's associated issuers, brands, and any optional custom terms

const { issuers, brands } = await E(zoe).getTerms(instance);

42 of 75

How do two VATs communicate?

43 of 75

Eventual Send - E(...)

  • Browsers use fetch(...) for remote communication.�
  • In Agoric, we achieve this using E(...) wrapper. �
  • E Asynchronously invokes methods in another vat, machine, or blockchain.

44 of 75

Example: Install Contract

const installationHandle = zoe.install(bundle)

import { E } from '@endo/eventual-send';

E(zoe).install(bundle)

.then(installationHandle => { ... })

.catch(err => { ... });

45 of 75

Presence

  • Local representation of a remote object.�
  • Acts as a proxy for the remote object.�
  • Facilitating communication across different machines or VATs.

46 of 75

VStorage

  • VStorage, short for "Virtual Storage" is a specialized key-value store in Agoric.
  • Hierarchical Data Storage: Manages data with paths composed of dot-separated segments.
  • Clients can read key-value pairs in VStorage to access data published by contracts.
  • Within the JavaScript VM, VStorage is accessed through an interface called the chainStorage API.

47 of 75

VStorage: Reading and Writing

Write-Only For Contracts

From within the JavaScript VM, nodes in the chainStorage API are write-only.

This means that contracts can write or publish data to these nodes, but they cannot read from them within the VM.

Read-Only For Clients

Clients can only read data from VStorage. They cannot modify it.

The client is generally an off-chain UI, from where users submit transactions, and the UI then reads results the contract writes to vstorage

48 of 75

VStorage: A Visual

vstorage

contract

RPC query

E(storageNode(key)).setValue(value);

value

49 of 75

VStorage: Write Data Example

Using E(privateArgs.storageNode).makeChildNode('metrics') gives the contract access to write to the published.myContract.metrics key and all keys under it.

export const start = (zcf, privateArgs) => {

const storageNode = E(privateArgs.storageNode).makeChildNode(

'metrics',

);

const setValue = async (key, value) => {

await E(storageNode(key)).setValue(value);

};

}

50 of 75

VStorage: Read Data

  1. Using the CLI command agd query to get the data at the storage path

agd query vstorage data 'published.agoricNames'

  • Querying for children of the node at that path

agd query vstorage children 'published.agoricNames'

children:

- brand

- installation

- instance

  • Can also be read via RPC with curl or a javascript fetch(...)

51 of 75

Upgrading

Contracts

Section 4

Writing Asynchronous Cross-Chain Smart Contracts

51

52 of 75

What is upgrading a contract?

State-based Upgrade

  • Keeps the contract’s state intact during the upgrade, ensuring smooth and consistent operations without data loss

Replay-based Upgrade

  • Re-executes the async function using logged interactions in order to reconstruct its last state, ensuring continuity and consistency during contract upgrades.

53 of 75

Why upgrade a contract?

  • Bug Fixes
    • Bugs can exist in initial contract deployments.
  • Feature Enhancements
    • Over time, new features or improvements may be needed.
  • Performance Improvements
    • Optimizing contract code can lead to better performance.
  • Compliance and Standards
    • Regulatory requirements and industry standards can evolve.

54 of 75

Stores

Specialized data structures used to manage and persist key-value pairs or sets of unique keys

  • MapStores
    • A wrapper around JavaScript Map.
    • Distinction between initializing a key-value pair and resetting a key to a new value.
    • These stores hold key-value pairs.
    • Unique keys like in SetStores, and the values can be any durable data.
  • SetStores
    • These stores only hold unique keys.
    • Each key is unique, meaning it isn't the same as any other key in the set.
  • … other store types

55 of 75

Durability: baggage

  • The 3rd argument, baggage, of the start function is a MapStore that provides a way to preserve state and behavior of objects between incarnations in a way that preserves identity of objects as seen from other vats
  • The zone API is a convenient way to manage durability:

import { makeDurableZone } from '@agoric/zone/durable.js';

const zone = makeDurableZone(baggage);

const rooms = zone.mapStore('rooms');

56 of 75

Secure Upgrade with baggage

This approach to contract upgrade preserves access to objects to ensure that the contract doesn’t violate Object Capability security properties

  • No more access after upgrade
  • No less access after upgrade

57 of 75

Exo Object (Exposed Remotable Object)

An Exo object is a special kind of object in the Endo framework Exo objects are usually defined with an InterfaceGuard.

  • Methods
    • Exos can have methods that are accessible remotely

  • InterfaceGuards
    • An InterfaceGuard acts as a protective layer around an Exo object.
    • It validates that the methods called on the object conform to the specified interface, ensuring that only allowed operations are performed and that the arguments are valid.

58 of 75

Making Exos in Zones

  • A Zone can be used to allocate Exos & stores:

const zone = makeDurableZone(baggage);

const publicFacet = zone.exo(

'StakeAtom',

M.interface('StakeAtomI', {

makeAccount: M.call().returns(M.remotable('ChainAccount')),

}),

{

async makeAccount() {

return await makeAccountKit();

},

},

);

59 of 75

Exo Object (Exposed Remotable Object)

60 of 75

Exos - How to Create an Exo

Here we use zone.exo to create an exo

const publicFacet = zone.exo(

'Send PF',

M.interface('Send PF', {

makeSendInvitation: M.call().returns(InvitationShape),

}),

{

makeSendInvitation() {

return zcf.makeInvitation(

sendIt,

'send',

undefined,

M.splitRecord({ give: SingleAmountRecord }),

);

},

},

);

Interface

Guard

Method(s)

61 of 75

Orchestration

Contracts

Section 5

Writing Asynchronous Cross-Chain Smart Contracts

61

62 of 75

This UX is cumbersome with multichain protocols alone

Use Case: I want to liquid-stake my bonded TIA

to receive an airdrop from Stride

Approve undelegate�Keplr Wallet

Liquid stake TIA

Stride application

Approve transfer�Keplr Wallet

Approve LST�Keplr Wallet

… wait 21 days …

Transfer TIA�Stride application

Undelegate TIA�Keplr Dashboard

63 of 75

Orchestration Concepts

  • Orchestrator
  • Chain Object
  • Orchestration Account
    • Interchain Accounts (ICA)
    • LocalChainAccount

64 of 75

Interchain Accounts (ICA)

  • Also known as Interchain Standard 27 (ICS-27)
  • An IBC application designed to enable one blockchain to control an account on another IBC-enabled blockchain.
  • Enables smart contracts to control an account on another chain.
  • Creating contract has sole access and control of ICA.
  • Can delegate certain forms of access to its clients.

65 of 75

Chain Interface

Provides a unified interface for the orchestration logic to manage cross-chain operations effectively.

Remote Implementation

  • makeAccount()
    • Creates an ICA that the contract has exclusive control over
  • getChainInfo()

Local Implementation

  • makeAccount()
    • Allocates a new address on Agoric that the contract has exclusive control over
  • getChainInfo()

66 of 75

OrchestrationAccount Interface

Has an API to

  • Manage Addresses

const address = await orchestrationAccount.getAddress();

  • Manage Balances

const balances = await orchestrationAccount.getBalances();

const balance = await orchestrationAccount.getBalance('uatom');

  • Transfer Funds

await orchestrationAccount.send(receiverAddress, amount);

await orchestrationAccount.transfer(amount, destinationAddress);

await orchestrationAccount.transferSteps(amount, transferMsg);

67 of 75

Orchestrator Interface

Orchestrator Interface exposes the following important API

    • getChain: get Chain object for a given chain name.
    • makeLocalAccount: make a LocalChainAccount
    • getBrandInfo: get information about equivalent local Brand, the Chain on which the denom is held, and the Chain that issues the corresponding asset.
    • asAmount: Convert an amount described in native data to a local, structured Amount.

68 of 75

This UX is cumbersome with multichain protocols alone

Use Case: I want to liquid-stake my bonded TIA

to receive an airdrop from Stride

Approve undelegate�Keplr Wallet

Liquid stake TIA

Stride application

Approve transfer�Keplr Wallet

Approve LST�Keplr Wallet

… wait 21 days …

Transfer TIA�Stride application

Undelegate TIA�Keplr Dashboard

69 of 75

Orchestration Contracts - Cross Chain Unbond

// ICA was previously created, funded, and made a delegation

const unbondAndLiquidStakeFn = async (orch, {

zcf,

celestiaAccount

}, _seat, _offerArgs) => {

const delegations = await celestiaAccount.getDelegations();

// wait for undelegate to be complete (celestia unbonding period is 21 days)

await celestiaAccount.undelegate(delegations);

const stride = await orch.getChain('stride');

const strideAccount = await stride.makeAccount();

const tiaAmt = await celestiaAccount.getBalance('TIA');

await celestiaAccount.transfer(multiply(tiaAmt,0.90), strideAccount.getAddress());

await strideAccount.liquidStake(multiply(tiaAmt,0.90));

};

Get TIA Delegations Amount

Undelegate TIA

Make ICA

Transfer

Liquid Stake transferred TIA

70 of 75

Orchestration Contracts - Cross Chain Unbond

unbondAndLiquidStakeFn Is shown on the next slide

const contract = async (zcf, privateArgs, zone, { orchestrate }) => {

const unbondAndLiquidStake = orchestrate(

'LSTTia',

{ zcf },

unbondAndLiquidStakeFn,

);

const publicFacet = zone.exo('publicFacet', undefined, {

makeUnbondAndLiquidStakeInvitation() {

return zcf.makeInvitation(

unbondAndLiquidStake,

'Unbond and liquid stake',

undefined,

harden({

give: {},

want: {},

exit: M.any(),

}),

);

},

});

return harden({ publicFacet });

};

export const start = withOrchestration(contract);

71 of 75

Orchestration Contracts - Cross Chain Swap

const stakeAndSwapFn = async (orch, { zcf }, seat, offerArgs) => {

const { give } = seat.getProposal();

const omni = await orch.getChain('omniflixhub');

const agoric = await orch.getChain('agoric');

const [omniAccount, localAccount] = await Promise.all([

omni.makeAccount(),

agoric.makeAccount(),

]);

const omniAddress = omniAccount.getAddress();

// deposit funds from user seat to LocalChainAccount

const payments = await withdrawFromSeat(zcf, seat, give);

await deeplyFulfilled(

objectMap(payments, payment =>

localAccount.deposit(payment),

),

);

seat.exit();

...

// build swap instructions with orcUtils library

const transferMsg = orcUtils.makeOsmosisSwap({

destChain: 'omniflixhub',

destAddress: omniAddress,

amountIn: give.Stable,

brandOut: '...',

slippage: 0.03,

});

await localAccount

.transferSteps(give.Stable, transferMsg)

.then(_txResult =>

omniAccount.delegate(

offerArgs.validator,

offerArgs.staked),

)

.catch(e => console.error(e));

}; // end of stakeAndSwapFn

Make

Accounts

Deposit into Account

Execute Transfer then delegate

72 of 75

Orchestration Contracts - Cross Chain Swap

stakeAndSwapFn Is shown on the next slide

const contract = async (zcf, privateArgs, zone, { orchestrate }) => {

const { brands } = zcf.getTerms();

const swapAndStakeHandler = orchestrate('LSTTia', { zcf }, stakeAndSwapFn);

const publicFacet = zone.exo('publicFacet', undefined, {

makeSwapAndStakeInvitation() {

return zcf.makeInvitation(

swapAndStakeHandler,

'Swap for TIA and stake',

undefined,

harden({

give: { Stable: makeNatAmountShape(brands.Stable, 1n) },

want: {},

exit: M.any(),

}),

);

},

});

return harden({ publicFacet });

};

export const start = withOrchestration(contract);

73 of 75

Recap of Today’s Journey

  • Core Foundations
  • Basic Concepts
  • Writing Basic Contracts
  • How Upgrading Contracts works
  • Writing Orchestration Contracts

74 of 75

74

To learn more see the orchestration docs:

docs.agoric.com

75 of 75

75