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

feat: cosmos plugin - IBC swap action #16

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a9fecd3
feat: ibc transfer action
KacperKoza343 Jan 8, 2025
6a929a1
refactor: remove redundant services and use skipClient
KacperKoza343 Jan 8, 2025
b3d8b05
update: eliza package import
KacperKoza343 Jan 9, 2025
2c4b037
feat: add bridge data fetcher and update tests
KacperKoza343 Jan 9, 2025
8e467ff
update: add IBC transfer action to README
KacperKoza343 Jan 9, 2025
63acdd4
refactor: make bridge data fetcher reusable across the project
KacperKoza343 Jan 10, 2025
907ed03
fix: update similes for ibc transfer action
KacperKoza343 Jan 10, 2025
2e3e05e
ELIZAAI-18 IBC swap action initialisation added
stanislawkurzypBD Jan 10, 2025
f0abcf5
Merge branch 'refs/heads/ELIZAAI-17-cross-chain-bridging-ibc-or-exter…
stanislawkurzypBD Jan 10, 2025
131e783
ELIZAAI-18 Added error handling, improved prompt data extracting, fix…
stanislawkurzypBD Jan 10, 2025
291fe6f
ELIZAAI-18 Unit tests added, data extracting template adjusted and ch…
stanislawkurzypBD Jan 10, 2025
7cac6e8
ELIZAAI-18 Switched to chain-registry function for fetching token exp…
stanislawkurzypBD Jan 13, 2025
64e6cb2
fix: update toAddress in ibc transfer
KacperKoza343 Jan 13, 2025
3504675
Merge branch 'develop' into ELIZAAI-17-cross-chain-bridging-ibc-or-ex…
KacperKoza343 Jan 13, 2025
b14ba88
ELIZAAI-18 Updated Readme
stanislawkurzypBD Jan 13, 2025
480e4f6
Merge pull request #14 from blockydevs/ELIZAAI-17-cross-chain-bridgin…
KacperKoza343 Jan 14, 2025
364d382
Merge remote-tracking branch 'refs/remotes/origin/develop' into ELIZA…
stanislawkurzypBD Jan 14, 2025
f8a778f
ELIZAAI-18 removed unused character settings, refactored names in ibc…
stanislawkurzypBD Jan 14, 2025
8f82669
ELIZAAI-18 removed unused character settings, refactored names in ibc…
stanislawkurzypBD Jan 14, 2025
313564e
Merge remote-tracking branch 'origin/ELIZAAI-18-cross-chain-swaps' in…
stanislawkurzypBD Jan 14, 2025
79091c1
Merge remote-tracking branch 'upstream/develop' into ELIZAAI-18-cross…
stanislawkurzypBD Jan 20, 2025
be0bfce
Fix unit tests, adjust custom character
stanislawkurzypBD Jan 20, 2025
74eecbc
Merge branch 'develop' into ELIZAAI-18-cross-chain-swaps
stanislawkurzypBD Jan 21, 2025
466b545
Code review fixes
stanislawkurzypBD Jan 21, 2025
45c3913
Merge branch 'develop' into ELIZAAI-18-cross-chain-swaps
stanislawkurzypBD Jan 21, 2025
5ee70b4
Merge remote-tracking branch 'upstream/develop' into ELIZAAI-18-cross…
stanislawkurzypBD Jan 21, 2025
00d9589
Merge remote-tracking branch 'origin/ELIZAAI-18-cross-chain-swaps' in…
stanislawkurzypBD Jan 21, 2025
267048c
Fix pnpm-lock
stanislawkurzypBD Jan 21, 2025
a3af265
Merge branch 'develop' into ELIZAAI-18-cross-chain-swaps
stanislawkurzypBD Jan 22, 2025
cb815b0
Merge branch 'develop' into ELIZAAI-18-cross-chain-swaps
stanislawkurzypBD Jan 22, 2025
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
28 changes: 18 additions & 10 deletions characters/cosmosHelper.character.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
"settings": {
"voice": {
"model": "en_US-male-medium"
},
"chains": {
"cosmos": ["axelar", "carbon", "mantrachaintestnet2"]
}
},
"plugins": [],
Expand All @@ -26,14 +23,17 @@
"Knows how Cosmos blockchain works",
"Knows what actions should be called for token transfer, swapping or bridging",
"Knows that users might want to do specific actions multiple times and should help them by doing it again.",
"Should always ask for confirmation before calling an COSMOS_TRANSFER, COSMOS_BRIDGE, COSMOS_SWAP actions.",
"Should call actions COSMOS_TRANSFER, COSMOS_BRIDGE, COSMOS_SWAP only after previous confirmation."
"Should always ask for confirmation before calling an COSMOS_TRANSFER, COSMOS_BRIDGE, COSMOS_IBC_SWAP actions.",
"Should ask for confirmation ONCE and perform action after getting it. If user wants to change sth in data for transfer, should do it and ask again for confirmation of new data.",
"Should call actions COSMOS_TRANSFER, COSMOS_BRIDGE, COSMOS_IBC_SWAP only after previous confirmation."
],
"messageExamples": [
[
{
"user": "{{user1}}",
"content": { "text": "Show my balances of my wallet on {{mantrachaintestnet2}}" }
"content": {
"text": "Show my balances of my wallet on {{mantrachaintestnet2}}"
}
},
{
"user": "CosmosHelper",
Expand All @@ -45,7 +45,9 @@
[
{
"user": "{{user1}}",
"content": { "text": "How does IBC work?" }
"content": {
"text": "How does IBC work?"
}
},
{
"user": "CosmosHelper",
Expand All @@ -57,7 +59,9 @@
[
{
"user": "{{user1}}",
"content": { "text": "What is CosmWasm?" }
"content": {
"text": "What is CosmWasm?"
}
},
{
"user": "CosmosHelper",
Expand All @@ -69,7 +73,9 @@
[
{
"user": "{{user1}}",
"content": { "text": "Can you help me transfer tokens?" }
"content": {
"text": "Can you help me transfer tokens?"
}
},
{
"user": "CosmosHelper",
Expand All @@ -81,7 +87,9 @@
[
{
"user": "{{user1}}",
"content": { "text": "Make transfer 0.0001 OM to mantra13248w8dtnn07sxc3gq4l3ts4rvfyat6fks0ecj on mantrachaintestnet2" }
"content": {
"text": "Make transfer 0.0001 OM to mantra13248w8dtnn07sxc3gq4l3ts4rvfyat6fks0ecj on mantrachaintestnet2"
}
},
{
"user": "CosmosHelper",
Expand Down
72 changes: 72 additions & 0 deletions packages/plugin-cosmos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,78 @@ Yes

---

### Token IBC Swap
This action allows swapping tokens between chains. The implementation of swapping is based on the Skip API and uses the @skip-go/client library.
To place transactions on chains, they must first be added to the env file. Specifically:
```env
COSMOS_AVAILABLE_CHAINS=osmosis,neutron,axelar,cosmoshub,terra2,pryzm
```
Keep in mind that most swaps require intermediate chains. These chains must also be included in the env file.

You can check which chains are supported by the Skip API and this plugin here: [Skip API Documentation](https://docs.skip.build/).

The list below contains all supported chains extracted from the Skip API:
```env
COSMOS_AVAILABLE_CHAINS=terra2,quicksilver,coreum,regen,mars,passage,dhealth,lumnetwork,provenance,chihuahua,pryzm,fetchhub,comdex,kyve,xpla,umee,celestia,osmosis,empowerchain,migaloo,dymension,kujira,self,humans,gitopia,agoric,doravota,int3face,quasar,gravitybridge,kava,sifchain,seda,shentu,decentr,cronos,carbon,stride,haqq,jackal,omniflixhub,noble,sentinel,nolus,odin,assetmantle,dydx,beezee,axelar,nois,mantrachain,elys,crescent,synternet,xion,source,akash,sei,canto,teritori,stargaze,titan,aura,evmos,archway,gateway,cheqd,milkyway,nibiru,cosmoshub,ununifi,nyx,bitsong,cryptoorgchain,neutron,penumbra,terra,shido,sommelier,saga,secretnetwork,chain4energy,juno,andromeda,planq,lava,oraichain,injective,persistence,composable
```

#### Example prompts:

##### Default flow
An example of the default flow of a swap request:

1. User input:
```
Swap 10 OSMO on osmosis to ATOM on cosmoshub
```
2. Agent asks for confirmation:
```
About to swap 10 OSMO on osmosis to ATAOM on cosmoshub. I would like to confirm the transaction details. Please confirm that you would like to proceed with the swap.
```
3. User confiramation:
```
confirming / yes
```
4. Agent calls action and responds after completing:
```
Successfuly swapped 10 OSMO tokens to ATOM on chain cosmoshub.
Transaction Hash: E84F36D6EEFAA6D7B70827F34EDAB83258BB86EFE33AEA1F4559D00B30CD3B50
```

##### Special case handling
An example of a flow when there is more than one token with the same symbol on the destination chain (e.g., multiple USDC tokens):
1. User input:
```
Swap 10 OSMO on osmosis to ATOM on cosmoshub
```
2. Agent asks for confirmation:
```
About to swap 10 OSMO on osmosis to ATAOM on cosmoshub. I would like to confirm the transaction details. Please confirm that you would like to proceed with the swap.
```
3. User confirmation:
```
confirming / yes
```
4. Model response:
```
I will now initiate the swap of 10 OSMO on the Osmosis chain to USDC on the Axelar chain. Please wait for the transaction to complete. If you need to swap more assets, feel free to let me know.

Error occured. Swap was not performed. Please provide denom for coin: USDC, on Chain Name: axelar. It is necessary as the symbol USDC is not unique among coins on chain axelar.
Select one from found assets:
Symbol: USDC Desc: Circle's stablecoin on Axelar Denom: uusdc,
Symbol: USDC Desc: Circle's stablecoin from Polygon on Axelar Denom: polygon-uusdc,
Symbol: USDC Desc: Circle's stablecoin from Avalanche on Axelar Denom: avalanche-uusdc
```
5. User response:
```
Swap 10 OSMO on osmosis to USDC with denom uusdc on axelar
```
6. Action call and agent response:
```
Successfuly swapped 10 OSMO tokens to USDC uusdc on chain axelar.
Transaction Hash: E84F36D6EEFAA6D7B70827F34EDAB83258BB86EFE33AEA1F4559D00B30CD3B50
```

## Contribution

The plugin includes comprehensive tests. Before submitting any pull requests, ensure all tests pass.
Expand Down
196 changes: 196 additions & 0 deletions packages/plugin-cosmos/src/actions/ibc-swap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import {
composeContext,
generateObjectDeprecated,
HandlerCallback,
IAgentRuntime,
Memory,
ModelClass,
State,
} from "@elizaos/core";

import { initWalletChainsData } from "../../providers/wallet/utils";
import { cosmosIBCSwapTemplate } from "../../templates";
import type {
ICosmosPluginOptions,
ICosmosWalletChains,
} from "../../shared/interfaces";
import { IBCSwapActionParams } from "./types.ts";
import { IBCSwapAction } from "./services/ibc-swap-action-service.ts";
import { prepareAmbiguityErrorMessage } from "./services/ibc-swap-utils.ts";

export const createIBCSwapAction = (pluginOptions: ICosmosPluginOptions) => ({
name: "COSMOS_IBC_SWAP",
description: "Swaps tokens on cosmos chains",
handler: async (
_runtime: IAgentRuntime,
_message: Memory,
state: State,
_options: { [key: string]: unknown },
_callback?: HandlerCallback
) => {
const cosmosIBCSwapContext = composeContext({
state: state,
template: cosmosIBCSwapTemplate,
templatingEngine: "handlebars",
});

const cosmosIBCSwapContent = await generateObjectDeprecated({
runtime: _runtime,
context: cosmosIBCSwapContext,
modelClass: ModelClass.SMALL,
});

const paramOptions: IBCSwapActionParams = {
fromChainName: cosmosIBCSwapContent.fromChainName,
fromTokenSymbol: cosmosIBCSwapContent.fromTokenSymbol,
fromTokenAmount: cosmosIBCSwapContent.fromTokenAmount,
toTokenSymbol: cosmosIBCSwapContent.toTokenSymbol,
toChainName: cosmosIBCSwapContent.toChainName,
toTokenDenom: cosmosIBCSwapContent?.toTokenDenom || undefined,
fromTokenDenom: cosmosIBCSwapContent?.fromTokenDenom || undefined,
};

console.log(
"Parameters extracted from user prompt: ",
JSON.stringify(paramOptions, null, 2)
);

try {
const walletProvider: ICosmosWalletChains =
await initWalletChainsData(_runtime);

const action = new IBCSwapAction(walletProvider);

const customAssets = (pluginOptions?.customChainData ?? []).map(
(chainData) => chainData.assets
);

if (_callback) {

const swapResp = await action.execute(
paramOptions,
customAssets,
_callback
);

const text =
swapResp.status === "STATE_COMPLETED_SUCCESS"
? `Successfully swapped ${swapResp.fromTokenAmount} ${swapResp.fromTokenSymbol} tokens to ${swapResp.toTokenSymbol} on chain ${swapResp.toChainName}.\nTransaction Hash: ${swapResp.txHash}`
: `Error occured swapping ${swapResp.fromTokenAmount} ${swapResp.fromTokenSymbol} tokens to ${swapResp.toTokenSymbol} on chain ${swapResp.toChainName}.\nTransaction Hash: ${swapResp.txHash}, try again`;
await _callback({
text: text,
content: {
success:
swapResp.status === "STATE_COMPLETED_SUCCESS",
hash: swapResp.txHash,
fromTokenAmount: paramOptions.fromTokenAmount,
fromToken: paramOptions.fromTokenSymbol,
toToken: paramOptions.toTokenSymbol,
fromChain: paramOptions.fromChainName,
toChain: paramOptions.toChainName,
},
});
}
return true;
} catch (error) {
console.error("Error during ibc token swap:", error);

const regex =
/Ambiguity Error.*value:([^\s.]+)\s+chainName:([^\s.]+)/;
const match = error.message.match(regex);

if (match) {
const value = match[1];
const chainName = match[2];

if (_callback) {
await _callback({
text: prepareAmbiguityErrorMessage(value, chainName),
content: { error: error.message },
});
}
} else {
console.error("Unhandled error:", error);

if (_callback) {
await _callback({
text: `Error ibc swapping tokens: ${error.message}`,
content: { error: error.message },
});
}
}
return false;
}
},
template: cosmosIBCSwapTemplate,
validate: async (runtime: IAgentRuntime) => {
const mnemonic = runtime.getSetting("COSMOS_RECOVERY_PHRASE");
const availableChains = runtime.getSetting("COSMOS_AVAILABLE_CHAINS");
const availableChainsArray = availableChains?.split(",");

return !(mnemonic && availableChains && availableChainsArray.length);
},
examples: [
[
{
user: "{{user1}}",
content: {
text: "Swap {{0.0001 ATOM}} from {{cosmoshub}} to {{OM}} on {{mantrachain1}}",
action: "COSMOS_IBC_SWAP",
},
},
{
user: "{{user2}}",
content: {
text: "Do you confirm the swap?",
action: "COSMOS_IBC_SWAP",
},
},
{
user: "{{user1}}",
content: {
text: "Yes",
action: "COSMOS_IBC_SWAP",
},
},
{
user: "{{user2}}",
content: {
text: "Starting swap transaction. Keep in mind that it might take couple of minutes",
action: "COSMOS_IBC_SWAP",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Swap {{0.0001 OM}} from {{mantrachain}} to {{OSMO}} on {{osmosis}}",
action: "COSMOS_IBC_SWAP",
},
},
{
user: "{{user2}}",
content: {
text: "Do you confirm the swap?",
action: "COSMOS_IBC_SWAP",
},
},
{
user: "{{user1}}",
content: {
text: "Yes",
action: "COSMOS_IBC_SWAP",
},
},
{
user: "{{user2}}",
content: {
text: "Starting swap transaction. Keep in mind that it might take couple of minutes",
action: "COSMOS_IBC_SWAP",
},
},
],
],
similes: ["COSMOS_SWAP", "COSMOS_SWAP_IBC"],
});
11 changes: 11 additions & 0 deletions packages/plugin-cosmos/src/actions/ibc-swap/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {z} from "zod";

export const IBCSwapParamsSchema = z.object({
fromChainName: z.string().min(1),
fromTokenSymbol: z.string().regex(/^[A-Z0-9]+$/),
fromTokenAmount: z.string().regex(/^\d+$/),
toTokenSymbol: z.string().regex(/^[A-Z0-9]+$/),
toChainName: z.string().min(1),
toTokenDenom: z.string().regex(/^ibc\/[A-F0-9]{64}$/),
fromTokenDenom: z.string().regex(/^ibc\/[A-F0-9]{64}$/),
});
Loading
Loading