forked from elizaOS/eliza
-
Notifications
You must be signed in to change notification settings - Fork 0
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: ibc transfer action #14
Merged
KacperKoza343
merged 9 commits into
develop
from
ELIZAAI-17-cross-chain-bridging-ibc-or-external-bridge
Jan 14, 2025
Merged
Changes from 1 commit
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
a9fecd3
feat: ibc transfer action
KacperKoza343 6a929a1
refactor: remove redundant services and use skipClient
KacperKoza343 b3d8b05
update: eliza package import
KacperKoza343 2c4b037
feat: add bridge data fetcher and update tests
KacperKoza343 8e467ff
update: add IBC transfer action to README
KacperKoza343 63acdd4
refactor: make bridge data fetcher reusable across the project
KacperKoza343 907ed03
fix: update similes for ibc transfer action
KacperKoza343 64e6cb2
fix: update toAddress in ibc transfer
KacperKoza343 3504675
Merge branch 'develop' into ELIZAAI-17-cross-chain-bridging-ibc-or-ex…
KacperKoza343 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
226 changes: 226 additions & 0 deletions
226
packages/plugin-cosmos/src/actions/ibc-transfer/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
import { | ||
composeContext, | ||
generateObjectDeprecated, | ||
HandlerCallback, | ||
IAgentRuntime, | ||
Memory, | ||
ModelClass, | ||
State, | ||
} from "@ai16z/eliza"; | ||
import { initWalletChainsData } from "../../providers/wallet/utils"; | ||
import { | ||
cosmosIBCTransferTemplate, | ||
cosmosTransferTemplate, | ||
} from "../../templates"; | ||
import type { | ||
ICosmosPluginOptions, | ||
ICosmosWalletChains, | ||
} from "../../shared/interfaces"; | ||
import { IBCTransferActionParams } from "./types"; | ||
import { CosmosIBCTransferAction } from "./services/ibc-transfer-action-service"; | ||
import { bridgeDataProvider } from "./services/bridge-data-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 CosmosIBCTransferAction(walletProvider); | ||
|
||
const customAssets = (pluginOptions?.customChainData ?? []).map( | ||
(chainData) => chainData.assets | ||
); | ||
|
||
const transferResp = await action.execute( | ||
paramOptions, | ||
bridgeDataProvider, | ||
customAssets | ||
); | ||
|
||
if (_callback) { | ||
await _callback({ | ||
text: `Successfully transferred ${paramOptions.amount} tokens from ${paramOptions.chainName} to ${paramOptions.toAddress} on ${paramOptions.targetChainName}\nGas paid: ${transferResp.gasPaid}\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.\n Gas paid: ${transferResp.gasPaid}. 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_TRANSFER", | ||
"COSMOS_SEND_TOKENS", | ||
"COSMOS_TOKEN_TRANSFER", | ||
"COSMOS_MOVE_TOKENS", | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { z } from "zod"; | ||
|
||
export const IBCTransferParamsSchema = z.object({ | ||
chainName: z.string(), | ||
symbol: z.string(), | ||
amount: z.string(), | ||
toAddress: z.string(), | ||
targetChainName: z.string(), | ||
}); | ||
|
||
export const bridgeDataProviderParamsSchema = z.object({ | ||
source_asset_denom: z.string(), | ||
source_asset_chain_id: z.string(), | ||
allow_multi_tx: z.boolean(), | ||
}); | ||
|
||
export const bridgeDataProviderResponseAssetsSchema = z.object({ | ||
denom: z.string(), | ||
chain_id: z.string(), | ||
origin_denom: z.string(), | ||
origin_chain_id: z.string(), | ||
trace: z.string(), | ||
symbol: z.string().optional(), | ||
name: z.string().optional(), | ||
logo_uri: z.string().optional(), | ||
decimals: z.number().optional(), | ||
recommended_symbol: z.string().optional(), | ||
}); | ||
|
||
export const bridgeDataProviderResponseSchema = z.object({ | ||
dest_assets: z.record( | ||
z.string(), | ||
z.object({ | ||
assets: z.array(bridgeDataProviderResponseAssetsSchema), | ||
}) | ||
), | ||
}); |
68 changes: 68 additions & 0 deletions
68
packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { bridgeDataProviderResponseSchema } from "../schema"; | ||
import { BridgeDataProviderParams, BridgeDataProviderResponse } from "../types"; | ||
import axios from "axios"; | ||
|
||
type CacheKey = `${string}_${string}`; | ||
|
||
export class BridgeDataFetcher { | ||
private static instance: BridgeDataFetcher; | ||
private cache: Map<CacheKey, BridgeDataProviderResponse>; | ||
private readonly apiUrl: string; | ||
|
||
private constructor() { | ||
this.cache = new Map(); | ||
this.apiUrl = "https://api.skip.build/v2/fungible/assets_from_source"; | ||
} | ||
|
||
public static getInstance(): BridgeDataFetcher { | ||
if (!BridgeDataFetcher.instance) { | ||
BridgeDataFetcher.instance = new BridgeDataFetcher(); | ||
} | ||
return BridgeDataFetcher.instance; | ||
} | ||
|
||
private generateCacheKey( | ||
sourceAssetDenom: string, | ||
sourceAssetChainId: string | ||
): CacheKey { | ||
return `${sourceAssetDenom}_${sourceAssetChainId}`; | ||
} | ||
|
||
public async fetchBridgeData( | ||
sourceAssetDenom: string, | ||
sourceAssetChainId: string | ||
): Promise<BridgeDataProviderResponse> { | ||
const cacheKey = this.generateCacheKey( | ||
sourceAssetDenom, | ||
sourceAssetChainId | ||
); | ||
|
||
if (this.cache.has(cacheKey)) { | ||
return this.cache.get(cacheKey)!; | ||
} | ||
|
||
const requestData: BridgeDataProviderParams = { | ||
source_asset_denom: sourceAssetDenom, | ||
source_asset_chain_id: sourceAssetChainId, | ||
allow_multi_tx: false, | ||
}; | ||
|
||
try { | ||
const response = await axios.post(this.apiUrl, requestData, { | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
}); | ||
|
||
const validResponse = bridgeDataProviderResponseSchema.parse( | ||
response.data | ||
); | ||
|
||
this.cache.set(cacheKey, validResponse); | ||
return response.data; | ||
} catch (error) { | ||
console.error("Error fetching assets:", error); | ||
throw error; | ||
} | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { IBridgeDataProvider } from "../../../shared/interfaces"; | ||
import { BridgeDataFetcher } from "./bridge-data-fetcher"; | ||
|
||
export const bridgeDataProvider: IBridgeDataProvider = async ( | ||
sourceAssetDenom: string, | ||
sourceAssetChainId: string | ||
) => { | ||
const bridgeDataFetcher = BridgeDataFetcher.getInstance(); | ||
const bridgeData = await bridgeDataFetcher.fetchBridgeData( | ||
sourceAssetDenom, | ||
sourceAssetChainId | ||
); | ||
|
||
const ibcAssetData = bridgeData.dest_assets[ | ||
sourceAssetChainId | ||
]?.assets?.find(({ origin_denom }) => origin_denom === sourceAssetDenom); | ||
|
||
if (!ibcAssetData) { | ||
throw new Error("No IBC asset data"); | ||
} | ||
|
||
const channelId = ibcAssetData.trace.split("/")[0]; | ||
|
||
if (!channelId) { | ||
throw new Error("No channel for bridge"); | ||
} | ||
|
||
return { | ||
channelId, | ||
ibcDenom: ibcAssetData.denom, | ||
portId: "transfer", | ||
}; | ||
}; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that these similes should be unique between actions but some of them are the same as for
COSMOS_TRANSFER
action (action from first PR).