Skip to content

Commit

Permalink
Merge pull request #14 from blockydevs/ELIZAAI-17-cross-chain-bridgin…
Browse files Browse the repository at this point in the history
…g-ibc-or-external-bridge

feat: ibc transfer action
  • Loading branch information
KacperKoza343 authored Jan 14, 2025
2 parents a11dd5a + 3504675 commit 480e4f6
Show file tree
Hide file tree
Showing 21 changed files with 3,669 additions and 2,369 deletions.
30 changes: 30 additions & 0 deletions packages/plugin-cosmos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,36 @@ Yes

4. Action executed.

### Token IBC Transfer

This plugin supports a token transfer action, which allows users to transfer tokens between addresses on Cosmos-compatible blockchains between different chains.

#### Example Prompts

Below are examples of how the ibc transfer action can be initiated and confirmed:

**Example**

1. User input:

```
Make an IBC transfer 0.0001 OSMO to neutron1nk3uuw6zt5t5aqw5fvujkd54sa4uws9xg2nk82 from osmosistestnet to neutrontestnet
```

2. Plugin response:

```
Before making the IBC transfer, I would like to confirm the details. You would like to transfer 0.0001 OSMO from osmosistestnet to neutrontestnet, specifically to the address neutron1nk3uuw6zt5t5aqw5fvujkd54sa4uws9xg2nk82, is that correct?
```

3. User confirmation:

```
Yes
```

4. Action executed.

---

## Contribution
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-cosmos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"@cosmjs/cosmwasm-stargate": "^0.32.4",
"@cosmjs/proto-signing": "^0.32.4",
"@cosmjs/stargate": "^0.32.4",
"@skip-go/client": "^0.16.3",
"axios": "^1.7.9",
"bignumber.js": "9.1.2",
"chain-registry": "^1.69.68",
"tsup": "8.3.5",
Expand Down
226 changes: 226 additions & 0 deletions packages/plugin-cosmos/src/actions/ibc-transfer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import {
composeContext,
generateObjectDeprecated,
HandlerCallback,
IAgentRuntime,
Memory,
ModelClass,
State,
} from "@elizaos/core";
import { initWalletChainsData } from "../../providers/wallet/utils";
import {
cosmosIBCTransferTemplate,
cosmosTransferTemplate,
} from "../../templates";
import type {
ICosmosPluginOptions,
ICosmosWalletChains,
} from "../../shared/interfaces";
import { IBCTransferActionParams } from "./types";
import { IBCTransferAction } from "./services/ibc-transfer-action-service";
import { bridgeDenomProvider } from "./services/bridge-denom-provider";

export const createIBCTransferAction = (
pluginOptions: ICosmosPluginOptions
) => ({
name: "COSMOS_IBC_TRANSFER",
description: "Transfer tokens between addresses on cosmos chains",
handler: async (
_runtime: IAgentRuntime,
_message: Memory,
state: State,
_options: { [key: string]: unknown },
_callback?: HandlerCallback
) => {
const cosmosIBCTransferContext = composeContext({
state: state,
template: cosmosIBCTransferTemplate,
templatingEngine: "handlebars",
});

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

const paramOptions: IBCTransferActionParams = {
chainName: cosmosIBCTransferContent.chainName,
symbol: cosmosIBCTransferContent.symbol,
amount: cosmosIBCTransferContent.amount,
toAddress: cosmosIBCTransferContent.toAddress,
targetChainName: cosmosIBCTransferContent.targetChainName,
};

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

const action = new IBCTransferAction(walletProvider);

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

const transferResp = await action.execute(
paramOptions,
bridgeDenomProvider,
customAssets
);

if (_callback) {
await _callback({
text: `Successfully transferred ${paramOptions.amount} tokens from ${paramOptions.chainName} to ${paramOptions.toAddress} on ${paramOptions.targetChainName}\nTransaction Hash: ${transferResp.txHash}`,
content: {
success: true,
hash: transferResp.txHash,
amount: paramOptions.amount,
recipient: transferResp.to,
fromChain: paramOptions.chainName,
toChain: paramOptions.targetChainName,
},
});

const newMemory: Memory = {
userId: _message.agentId,
agentId: _message.agentId,
roomId: _message.roomId,
content: {
text: `Transaction ${paramOptions.amount} ${paramOptions.symbol} to address ${paramOptions.toAddress} from chain ${paramOptions.chainName} to ${paramOptions.targetChainName} was successfully transferred. Tx hash: ${transferResp.txHash}`,
},
};

await _runtime.messageManager.createMemory(newMemory);
}
return true;
} catch (error) {
console.error("Error during ibc token transfer:", error);

if (_callback) {
await _callback({
text: `Error ibc transferring tokens: ${error.message}`,
content: { error: error.message },
});
}

const newMemory: Memory = {
userId: _message.agentId,
agentId: _message.agentId,
roomId: _message.roomId,
content: {
text: `Transaction ${paramOptions.amount} ${paramOptions.symbol} to address ${paramOptions.toAddress} on chain ${paramOptions.chainName} to ${paramOptions.targetChainName} was unsuccessful.`,
},
};

await _runtime.messageManager.createMemory(newMemory);

return false;
}
},
template: cosmosTransferTemplate,
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: "Make an IBC transfer {{0.0001 ATOM}} to {{osmosis1pcnw46km8m5amvf7jlk2ks5std75k73aralhcf}} from {{cosmoshub}} to {{osmosis}}",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user2}}",
content: {
text: "Do you confirm the IBC transfer action?",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user1}}",
content: {
text: "Yes",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user2}}",
content: {
text: "",
action: "COSMOS_IBC_TRANSFER",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Send {{50 OSMO}} to {{juno13248w8dtnn07sxc3gq4l3ts4rvfyat6f4qkdd6}} from {{osmosis}} to {{juno}}",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user2}}",
content: {
text: "Do you confirm the IBC transfer action?",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user1}}",
content: {
text: "Yes",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user2}}",
content: {
text: "",
action: "COSMOS_IBC_TRANSFER",
},
},
],
[
{
user: "{{user1}}",
content: {
text: "Transfer {{0.005 JUNO}} from {{juno}} to {{cosmos1n0xv7z2pkl4eppnm7g2rqhe2q8q6v69h7w93fc}} on {{cosmoshub}}",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user2}}",
content: {
text: "Do you confirm the IBC transfer action?",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user1}}",
content: {
text: "Yes",
action: "COSMOS_IBC_TRANSFER",
},
},
{
user: "{{user2}}",
content: {
text: "",
action: "COSMOS_IBC_TRANSFER",
},
},
],
],
similes: [
"COSMOS_BRIDGE_TOKEN",
"COSMOS_IBC_SEND_TOKEN",
"COSMOS_TOKEN_IBC_TRANSFER",
"COSMOS_MOVE_IBC_TOKENS",
],
});
9 changes: 9 additions & 0 deletions packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { z } from "zod";

export const IBCTransferParamsSchema = z.object({
chainName: z.string(),
symbol: z.string(),
amount: z.string(),
toAddress: z.string(),
targetChainName: z.string(),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IDenomProvider } from "../../../shared/interfaces";
import { SkipApiAssetsFromSourceFetcher } from "../../../shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher";

export const bridgeDenomProvider: IDenomProvider = async (
sourceAssetDenom: string,
sourceAssetChainId: string,
destChainId: string
) => {
const skipApiAssetsFromSourceFetcher =
SkipApiAssetsFromSourceFetcher.getInstance();
const bridgeData = await skipApiAssetsFromSourceFetcher.fetch(
sourceAssetDenom,
sourceAssetChainId
);

const ibcAssetData = bridgeData.dest_assets[destChainId]?.assets?.find(
({ origin_denom }) => origin_denom === sourceAssetDenom
);

if (!ibcAssetData.denom) {
throw new Error("No IBC asset data");
}

return {
denom: ibcAssetData.denom,
};
};
Loading

0 comments on commit 480e4f6

Please sign in to comment.