Skip to content

chore(orchestration): create scaffold for swap anything asyncFlow #11304

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

Open
wants to merge 33 commits into
base: master
Choose a base branch
from

Conversation

anilhelvaci
Copy link
Collaborator

@anilhelvaci anilhelvaci commented Apr 21, 2025

closes: #8863 #10127

Description

Crosschain Swaps (XCS) is a CosmWasm contract on Osmosis that lets users swap tokens over incoming IBC transactions and send them to their destination. This destination could be on Osmosis, the incoming chain, or any other chain. What enables XCS contract to handle this complex operation is two other CosmWasm contracts, namely, SwapRouter and Crosschain Registry.

  • SwapRouter: In charge of keeping track of routes between tokens. Especially useful when there's no direct pool between the assets the user wants to swap.
  • Crosschain Registry: Responsible for keeping the information related to figuring out what chain a certain address belongs to, and what is the IBC channel between those chains

swap-anything.contract.js is an orchestration that's very similar to send-anywhere.contract.js. The contract logic just makes sure it sends a valid ICS-20 message with a special focus on;

  • The destination chain is ALWAYS Osmosis.
  • The memo is a special JSON string that can be picked up by IBC hooks on Osmosis so it can be forwarded to the XCS contract.

Here's the helper function we use to build that memo:

const buildXCSMemo = swapInfo => {
const memo = {
wasm: {
contract: swapInfo.destAddr,
msg: {
osmosis_swap: {
output_denom: swapInfo.outDenom,
slippage: {
twap: {
window_seconds: swapInfo.slippage.windowSeconds,
slippage_percentage: swapInfo.slippage.slippagePercentage,
},
},
receiver: swapInfo.receiverAddr,
on_failed_delivery: swapInfo.onFailedDelivery,
nextMemo: swapInfo.nextMemo,
},
},
},
};

Security Considerations

Currently we use one localchainAccount to handle incoming offers. I'm not sure this could be limiting factor or not. Just find it beneficial to point out.

Documentation Considerations

If we decide to use this contract in a product, it could be a good idea to document the XCS contract's behavior over certain conditions in order to help developers working on swap-anything.contract.js.

Testing Considerations

Here are the test cases covered at the new job included in the multichain-e2e CI workflow;

  • Swap BLD against OSMO, receiver on Agoric
  • Swap BLD against OSMO, receiver on CosmosHub
  • Swap OSMO against BLD, receiver on Agoric
  • Swap output sent to an unknown address, swap should fail, user purse recovered
  • Verify osmosis xcs state

A word on the test environment
We create a new pool between BLD <-> OSMO on Osmosis and configure crosschain_swaps, crosschain_registry, swaprouter contracts according to the IBC connections established by Starship and the pool we have created. In the next phase, we consider adding new pools and exploring test cases for PFM.

Reasoning for a dedicated starship config.yaml (config.xcs-swap-anything.yaml)
We will need to see the logs of the XCS (and other CosmWasm) contracts, if anything goes wrong. XCS code prints logs to debug level only. In order to avoid unnecessary log output for other tests, we've used our own config.

Upgrade Considerations

None

Copy link

cloudflare-workers-and-pages bot commented Apr 21, 2025

Deploying agoric-sdk with  Cloudflare Pages  Cloudflare Pages

Latest commit: e962549
Status: ✅  Deploy successful!
Preview URL: https://2cc6507c.agoric-sdk.pages.dev
Branch Preview URL: https://jorge-8863-refactor-test-too.agoric-sdk.pages.dev

View logs

@Jorge-Lopes Jorge-Lopes force-pushed the swap-it-async-flow branch from 60b3e9e to fb8e9ae Compare May 6, 2025 14:22
@anilhelvaci anilhelvaci added the force:integration Force integration tests to run on PR label May 9, 2025
@anilhelvaci anilhelvaci marked this pull request as ready for review May 9, 2025 12:49
@anilhelvaci anilhelvaci requested a review from a team as a code owner May 9, 2025 12:49
@anilhelvaci anilhelvaci requested a review from AgoricTriage May 9, 2025 12:49
@anilhelvaci anilhelvaci force-pushed the swap-it-async-flow branch from 86ba674 to 7b5170e Compare May 14, 2025 11:56
@dckc dckc self-requested a review May 14, 2025 17:23
mergify bot added a commit that referenced this pull request May 15, 2025
closes: #11374 

## Description

This PR implements the design described at #11374. The need for such a design is surfaced during when we tried to handle **any** denom incoming to a contract's address hook. We are trying to unblock that functionality with this solution.

### Security Considerations

None.

### Scaling Considerations

No concerns about this.

### Documentation Considerations

None.

### Testing Considerations

We some unit tests that showcases this method's functionality. Ideally there should also be multichain test where a contract uses this method to make sense out of some incoming IBC transfer in its address hook. We do have an on-going effort for that. See #11304 

### Upgrade Considerations

None.
@dckc
Copy link
Member

dckc commented May 19, 2025

34 commits... some include "WIP"... @anilhelvaci it looks like you plan to squash; that is: review by commit is not the way to go. Can you confirm?

@dckc
Copy link
Member

dckc commented May 19, 2025

other CosmWasm contracts [on Osmosis]: Crosschain Registry ...

Interesting. How are changes to the registry governed?

We were going to put chainInfo in agoricNames, but since the design details, especially around maintenance, weren't entirely clear, we rolled it back before shipping Fast USDC


p.s. I see Upload CrossChain Registry Contract V1 Sep 14 2023... looks like there's an owner who has edit rights:

    // Only contract governor can call denom alias CRUD operations
    let is_owner = is_owner(deps.as_ref(), &sender);
    let is_global_admin = is_global_admin(deps.as_ref(), &sender);

    if !is_owner && !is_global_admin {
        return Err(ContractError::Unauthorized {});
    }

-- https://github.com/osmosis-labs/osmosis/blob/01357893442a4d52d20de1401988a865ace498d1/cosmwasm/contracts/crosschain-registry/src/execute.rs#L258C1-L264C6

@anilhelvaci
Copy link
Collaborator Author

34 commits... some include "WIP"... @anilhelvaci it looks like you plan to squash; that is: review by commit is not the way to go. Can you confirm?

Correct.

Interesting. How are changes to the registry governed?

Oh, the contract needs an owner address in its constructor.

Jorge-Lopes and others added 13 commits May 20, 2025 17:06
chore(multichain): set pool config json via script

feat(multichain): add osmosis pool to CI E2E Workflow

fix(multichain): fix lint issues

fix(multichain): fix multichain workflow Invalid input

chore(multichain): change osmosis_pool input type on CI workflow

fix(multichain): fix Unable to use a TTY issue

fix(multichain): fix timing issue when creating pool

fix(multichain): remove kubectl tty flag

refactor(multichain): reduce duplicated code
squash multiple commits
@Jorge-Lopes Jorge-Lopes force-pushed the swap-it-async-flow branch from 18f06c0 to 1e3d148 Compare May 20, 2025 16:09
Copy link
Member

@dckc dckc left a comment

Choose a reason for hiding this comment

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

misc notes so far...

github.event_name == 'workflow_call' ||
github.event_name == 'pull_request' ||
(github.event_name == 'workflow_dispatch' && inputs.test_type == 'swap-on-osmosis')
uses: ./.github/workflows/multichain-e2e-template.yml
Copy link
Member

Choose a reason for hiding this comment

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

so this starts another set of nodes? How long does that take? It appears that landing this would slow down ci for all PRs going forward. Can you estimate how much?

Comment on lines +97 to +99
###############################################################################
### Osmosis Setup ###
###############################################################################
Copy link
Member

Choose a reason for hiding this comment

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

I'm not a fan of ascii art comments, but I suppose that predates this PR.

Suggested change
###############################################################################
### Osmosis Setup ###
###############################################################################
###############################################################################
### Osmosis Setup ###
###############################################################################

balance_1=${balance_1:-0}
balance_2=${balance_2:-0}

if [[ "$balance_1" -ge "$TOKEN_IN_AMOUNT" && "$balance_2" -ge "$TOKEN_OUT_AMOUNT" ]]; then
Copy link
Member

Choose a reason for hiding this comment

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

The tedious best practice of quoting shell variable reference is followed here but not for $OSMOSIS_ADDRESS above. Run shellcheck, please.

What's the cost of adding shell check linting in ci here? I think we have it in some packages / repositories.

I sure don't like adding hundreds of lines of shell code. I'd rather see .js, perhaps using execa.

@@ -0,0 +1,26 @@
#!/bin/bash

# Usage: ./download_specific_files.sh <owner> <repo> <branch> <folder> <destination> <file1> <file2> ...
Copy link
Member

Choose a reason for hiding this comment

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

For all shell scripts, add set -euo pipefail or at least set -e.

cf. a3p doctor
https://github.com/Agoric/agoric-3-proposals/blob/main/packages/synthetic-chain/src/cli/doctor.ts#L15C77-L15C94

@@ -136,6 +136,13 @@ make provision-smart-wallet ADDR=$ADDR
kubectl exec -i noblelocal-genesis-0 -c validator -- nobled query interchain-accounts host params | jq
```

## Optional Features
Copy link
Member

Choose a reason for hiding this comment

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

Don't make folks read the details to know whether the section is relevant:

Suggested change
## Optional Features
## Osmosis Pool (optional)

Comment on lines +11 to +12
TX_FLAGS=--from $(OSMOSIS_GENESIS_ADDRESS) --keyring-backend=test --gas=auto --gas-prices 0.1uosmo --gas-adjustment 1.3 --yes --chain-id osmosislocal
AGD_TX_FLAGS=--from $(AGORIC_GENESIS_ADDRESS) --keyring-backend=test --gas=auto --gas-prices 0.1ubld --gas-adjustment 1.3 --yes --chain-id agoriclocal
Copy link
Member

Choose a reason for hiding this comment

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

suggest: make it clearer which parts are the same by moving them to the beginning and making the variable names the same length:

Suggested change
TX_FLAGS=--from $(OSMOSIS_GENESIS_ADDRESS) --keyring-backend=test --gas=auto --gas-prices 0.1uosmo --gas-adjustment 1.3 --yes --chain-id osmosislocal
AGD_TX_FLAGS=--from $(AGORIC_GENESIS_ADDRESS) --keyring-backend=test --gas=auto --gas-prices 0.1ubld --gas-adjustment 1.3 --yes --chain-id agoriclocal
OSM_TX_FLAGS=--keyring-backend=test --gas=auto --gas-adjustment 1.3 --yes --from $(OSMOSIS_GENESIS_ADDRESS) --gas-prices 0.1uosmo --chain-id osmosislocal
AGD_TX_FLAGS=--keyring-backend=test --gas=auto --gas-adjustment 1.3 --yes --from $(AGORIC_GENESIS_ADDRESS) --gas-prices 0.1ubld --chain-id agoriclocal

If this were in .js, it would be straightforward to reify the authority wielded by these accounts as js objects. I haven't landed it yet, but some work in progress...

export const makeAgdAcct = (

Copy link
Member

@dckc dckc left a comment

Choose a reason for hiding this comment

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

PLEASE EXCUSE THE NOISE

I'm trying out some different tools; I don't really know how they work. But I don't want to throw my work away, so I'm posting this review as is...

@@ -0,0 +1,125 @@
name: agoric-multichain-testing
Copy link
Member

Choose a reason for hiding this comment

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

Something sped up multichain-testing a bunch recently... I wonder whether this config consistent with it... ah; yes, it's completely orthogonal:

Comment on lines +11 to +13
AGORIC_WALLET="test1"
AGORIC_ADDRESS=$(agoric-exec keys show ${AGORIC_WALLET} -a)
OSMOSIS_WALLET="test1"
Copy link
Member

Choose a reason for hiding this comment

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

suggest: only use SHOUTING for variables exported to other processes via env; i.e. to say "hey! this variable is dynamically scoped! take extra care!". For ordinary variables that stay within the script, no need for shouting.

make create-osmosis-pool

## Configure the osmosis registries
cd cd agoric-sdk/multichain-testing/test/xcs-swap-anything
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
cd cd agoric-sdk/multichain-testing/test/xcs-swap-anything
cd agoric-sdk/multichain-testing/test/xcs-swap-anything

useChain,
} = t.context;

const { client, address } = await createFundedWalletAndClient(
Copy link
Member

Choose a reason for hiding this comment

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

This seems to use ambient access to a source of randomness. I suggest injecting it explicitly.

Copy link
Member

Choose a reason for hiding this comment

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

client seems to be based on ambient access to the network too: createFundedWalletAndClient uses SigningStargateClient.connectWithSigner

t.log(`Provisioned Agoric smart wallet for ${agoricAddr}`);

const apiUrl = await useChain('agoric').getRestEndpoint();
const queryClient = makeQueryClient(apiUrl);
Copy link
Member

Choose a reason for hiding this comment

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

makeQueryClient uses ambient access to fetch.

'ubld',
);

const { swapAddress } = await getXcsContractsAddress();
Copy link
Member

Choose a reason for hiding this comment

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

a function of 0 arguments can only return a constant, unless it uses ambient authority.

This one is using $.

const { swapAddress } = await getXcsContractsAddress();

// Send swap offer
const makeAccountOfferId = `swap-ubld-uosmo-${Date.now()}`;
Copy link
Member

Choose a reason for hiding this comment

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

ambient access to the clock

Copy link
Member

Choose a reason for hiding this comment

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

PLEASE EXCUSE THE NOISE pt 2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
force:integration Force integration tests to run on PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

orchestration: example dapp contract that can swap on osmosis
4 participants