diff --git a/.env.example b/.env.example index c3e3e820967..4be3e5a8385 100644 --- a/.env.example +++ b/.env.example @@ -91,6 +91,13 @@ STARKNET_ADDRESS= STARKNET_PRIVATE_KEY= STARKNET_RPC_URL= +# Conflux Configuration +CONFLUX_CORE_PRIVATE_KEY= +CONFLUX_CORE_SPACE_RPC_URL= +CONFLUX_ESPACE_PRIVATE_KEY= +CONFLUX_ESPACE_RPC_URL= +CONFLUX_MEME_CONTRACT_ADDRESS= + # Coinbase COINBASE_COMMERCE_KEY= # from coinbase developer portal COINBASE_API_KEY= # from coinbase developer portal @@ -98,3 +105,4 @@ COINBASE_PRIVATE_KEY= # from coinbase developer portal # if not configured it will be generated and written to runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_ID and runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_HEX_SEED COINBASE_GENERATED_WALLET_ID= # not your address but the wallet id from generating a wallet through the plugin COINBASE_GENERATED_WALLET_HEX_SEED= # not your address but the wallet hex seed from generating a wallet through the plugin and calling export + diff --git a/agent/package.json b/agent/package.json index 744d39b39f3..8404f5f8035 100644 --- a/agent/package.json +++ b/agent/package.json @@ -21,6 +21,7 @@ "@ai16z/client-twitter": "workspace:*", "@ai16z/eliza": "workspace:*", "@ai16z/plugin-bootstrap": "workspace:*", + "@ai16z/plugin-conflux": "workspace:*", "@ai16z/plugin-image-generation": "workspace:*", "@ai16z/plugin-node": "workspace:*", "@ai16z/plugin-solana": "workspace:*", diff --git a/agent/src/index.ts b/agent/src/index.ts index f76ae1d8889..fd18d45b82b 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -23,6 +23,7 @@ import { validateCharacterConfig, } from "@ai16z/eliza"; import { bootstrapPlugin } from "@ai16z/plugin-bootstrap"; +import { confluxPlugin } from "@ai16z/plugin-conflux"; import { solanaPlugin } from "@ai16z/plugin-solana"; import { nodePlugin } from "@ai16z/plugin-node"; import { @@ -252,6 +253,9 @@ export function createAgent( character, plugins: [ bootstrapPlugin, + character.settings.secrets?.CONFLUX_CORE_PRIVATE_KEY + ? confluxPlugin + : null, nodePlugin, character.settings.secrets?.WALLET_PUBLIC_KEY ? solanaPlugin : null, character.settings.secrets?.COINBASE_COMMERCE_KEY || diff --git a/packages/plugin-conflux/README.md b/packages/plugin-conflux/README.md new file mode 100644 index 00000000000..faa68cfa76a --- /dev/null +++ b/packages/plugin-conflux/README.md @@ -0,0 +1,25 @@ +# @ai16z/plugin-conflux + +This plugin provides actions and providers for interacting with the [Conflux network](https://www.confluxdocs.com/docs/general). + +## Actions + +### ConfiPump + +Buy and sell tokens on Conflux's implementation of pump.fun (ConfiPump). + +### Transfer + +Transfer tokens from one address to another within Conflux core space. + +### Bridge Transfer + +Transfer tokens from one address to Conflux eSpace. + +### Sponsor (TBD) + +Provide gas for Conflux core space contracts so they can be called without the need to have Conflux in user's wallet. + +### Swap (TBD) + +Swap tokens on Conflux DEXs. diff --git a/packages/plugin-conflux/package.json b/packages/plugin-conflux/package.json new file mode 100644 index 00000000000..59c3ed8f796 --- /dev/null +++ b/packages/plugin-conflux/package.json @@ -0,0 +1,15 @@ +{ + "name": "@ai16z/plugin-conflux", + "version": "0.0.1", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "cive": "^0.7.1", + "@ai16z/eliza": "workspace:*" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --watch" + } +} diff --git a/packages/plugin-conflux/src/abi/crossSpaceCall.ts b/packages/plugin-conflux/src/abi/crossSpaceCall.ts new file mode 100644 index 00000000000..f9ad2a67a07 --- /dev/null +++ b/packages/plugin-conflux/src/abi/crossSpaceCall.ts @@ -0,0 +1,184 @@ +const CrossSpaceCallAbi = [ + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes20", + name: "sender", + type: "bytes20", + }, + { + indexed: true, + internalType: "bytes20", + name: "receiver", + type: "bytes20", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "data", + type: "bytes", + }, + ], + name: "Call", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes20", + name: "sender", + type: "bytes20", + }, + { + indexed: true, + internalType: "bytes20", + name: "contract_address", + type: "bytes20", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "init", + type: "bytes", + }, + ], + name: "Create", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "bool", + name: "success", + type: "bool", + }, + ], + name: "Outcome", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes20", + name: "sender", + type: "bytes20", + }, + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + ], + name: "Withdraw", + type: "event", + }, + { + inputs: [{ internalType: "bytes", name: "init", type: "bytes" }], + name: "createEVM", + outputs: [{ internalType: "bytes20", name: "", type: "bytes20" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "bytes20", name: "to", type: "bytes20" }], + name: "transferEVM", + outputs: [{ internalType: "bytes", name: "output", type: "bytes" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes20", name: "to", type: "bytes20" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "callEVM", + outputs: [{ internalType: "bytes", name: "output", type: "bytes" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes20", name: "to", type: "bytes20" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "staticCallEVM", + outputs: [{ internalType: "bytes", name: "output", type: "bytes" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "deployEip1820", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "value", type: "uint256" }], + name: "withdrawFromMapped", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "addr", type: "address" }], + name: "mappedBalance", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "addr", type: "address" }], + name: "mappedNonce", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +]; + +export default CrossSpaceCallAbi; diff --git a/packages/plugin-conflux/src/abi/erc20.ts b/packages/plugin-conflux/src/abi/erc20.ts new file mode 100644 index 00000000000..fa3a4262668 --- /dev/null +++ b/packages/plugin-conflux/src/abi/erc20.ts @@ -0,0 +1,119 @@ +const ERC20ABI = [ + { + constant: true, + inputs: [], + name: 'name', + outputs: [{ name: '', type: 'string' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { name: '_spender', type: 'address' }, + { name: '_value', type: 'uint256' }, + ], + name: 'approve', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'totalSupply', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { name: '_from', type: 'address' }, + { name: '_to', type: 'address' }, + { name: '_value', type: 'uint256' }, + ], + name: 'transferFrom', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'decimals', + outputs: [{ name: '', type: 'uint8' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [{ name: '_owner', type: 'address' }], + name: 'balanceOf', + outputs: [{ name: 'balance', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'symbol', + outputs: [{ name: '', type: 'string' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { name: '_to', type: 'address' }, + { name: '_value', type: 'uint256' }, + ], + name: 'transfer', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [ + { name: '_owner', type: 'address' }, + { name: '_spender', type: 'address' }, + ], + name: 'allowance', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { payable: true, stateMutability: 'payable', type: 'fallback' }, + { + anonymous: false, + inputs: [ + { indexed: true, name: 'owner', type: 'address' }, + { indexed: true, name: 'spender', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, name: 'from', type: 'address' }, + { indexed: true, name: 'to', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + name: 'Transfer', + type: 'event', + }, +] as const; + +export default ERC20ABI; \ No newline at end of file diff --git a/packages/plugin-conflux/src/abi/meme.ts b/packages/plugin-conflux/src/abi/meme.ts new file mode 100644 index 00000000000..0a1e5044154 --- /dev/null +++ b/packages/plugin-conflux/src/abi/meme.ts @@ -0,0 +1,1671 @@ +const MEMEABI = [ + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "tokenImpl_", + type: "address", + }, + { + internalType: "address", + name: "tokenImplV2_", + type: "address", + }, + { + internalType: "uint256", + name: "feeRate_", + type: "uint256", + }, + { + internalType: "address", + name: "feeReceiver_", + type: "address", + }, + { + internalType: "address", + name: "dexLauncher_", + type: "address", + }, + { + internalType: "enum IConfiPumpTypes.DexThreshType", + name: "defaultDexThreshType_", + type: "uint8", + }, + { + internalType: "enum IConfiPumpTypes.CurveType", + name: "defaultCurveType_", + type: "uint8", + }, + { + internalType: "enum IConfiPumpTypes.TokenVersion", + name: "defaultTokenVersion_", + type: "uint8", + }, + { + internalType: "address", + name: "v2Factory_", + type: "address", + }, + { + internalType: "bytes32", + name: "v2InitCodeHash_", + type: "bytes32", + }, + { + internalType: "address", + name: "weth_", + type: "address", + }, + { + internalType: "uint256", + name: "creation_fee_", + type: "uint256", + }, + { + internalType: "uint256", + name: "lpEth_", + type: "uint256", + }, + { + internalType: "uint256", + name: "lpEthTokenCreator_", + type: "uint256", + }, + ], + internalType: "struct ConfiPumpBase.ConfiPumpInitParams", + name: "params", + type: "tuple", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [ + { + internalType: "uint256", + name: "actualAmount", + type: "uint256", + }, + { + internalType: "uint256", + name: "amount1", + type: "uint256", + }, + ], + name: "ActualAmountMustLTEAmount", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "AmountTooSmall", + type: "error", + }, + { + inputs: [], + name: "CallReverted", + type: "error", + }, + { + inputs: [], + name: "FeatureDisabled", + type: "error", + }, + { + inputs: [], + name: "GameNotLive", + type: "error", + }, + { + inputs: [], + name: "GameNotPaused", + type: "error", + }, + { + inputs: [], + name: "GameNotPending", + type: "error", + }, + { + inputs: [], + name: "GameNotStarted", + type: "error", + }, + { + inputs: [], + name: "InvalidDEXSupplyThreshold", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "threshold", + type: "uint256", + }, + ], + name: "InvalidDexThreshold", + type: "error", + }, + { + inputs: [ + { + internalType: "enum IConfiPumpTypes.DexThreshType", + name: "threshold", + type: "uint8", + }, + ], + name: "InvalidDexThresholdType", + type: "error", + }, + { + inputs: [], + name: "InvalidGameSupplyThreshold", + type: "error", + }, + { + inputs: [], + name: "InvalidLocks", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "expected", + type: "uint256", + }, + { + internalType: "uint256", + name: "actual", + type: "uint256", + }, + ], + name: "InvalidPiggybackLength", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "id", + type: "uint256", + }, + ], + name: "InvalidRoundID", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "signer", + type: "address", + }, + ], + name: "InvalidSigner", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "InvalidTokenForBattle", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "enum IConfiPumpTypes.TokenMode", + name: "mode", + type: "uint8", + }, + ], + name: "InvalidTokenModeForGame", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "enum IConfiPumpTypes.TokenMode", + name: "from", + type: "uint8", + }, + { + internalType: "enum IConfiPumpTypes.TokenMode", + name: "to", + type: "uint8", + }, + ], + name: "InvalidTokenModeTransition", + type: "error", + }, + { + inputs: [], + name: "LastRoundNotResolved", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "expected", + type: "address", + }, + { + internalType: "address", + name: "actual", + type: "address", + }, + ], + name: "MismatchedAddressInProof", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "srcToken", + type: "address", + }, + { + internalType: "address", + name: "dstToken", + type: "address", + }, + ], + name: "NoConversionPath", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "created", + type: "uint256", + }, + { + internalType: "uint256", + name: "max", + type: "uint256", + }, + ], + name: "NoQuotaForCreator", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "collection", + type: "address", + }, + ], + name: "NonPositionNFTReceived", + type: "error", + }, + { + inputs: [], + name: "NotImplemented", + type: "error", + }, + { + inputs: [], + name: "NotRoller", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "NotUniswapV3Pool", + type: "error", + }, + { + inputs: [], + name: "PermissionlessCreateDisabled", + type: "error", + }, + { + inputs: [ + { + internalType: "uint160", + name: "sqrtPriceA", + type: "uint160", + }, + { + internalType: "uint160", + name: "sqrtPriceB", + type: "uint160", + }, + ], + name: "PriceAMustLTPriceB", + type: "error", + }, + { + inputs: [], + name: "ProtocolDisabled", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "requiredToken", + type: "uint256", + }, + { + internalType: "uint256", + name: "reserveToken", + type: "uint256", + }, + ], + name: "RequiredTokenMustLTE", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "id", + type: "uint256", + }, + ], + name: "RoundNotFound", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "tokenA", + type: "address", + }, + ], + name: "SameToken", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "seq", + type: "uint256", + }, + ], + name: "SeqNotFound", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "actualAmount", + type: "uint256", + }, + { + internalType: "uint256", + name: "minAmount", + type: "uint256", + }, + ], + name: "SlippageTooHigh", + type: "error", + }, + { + inputs: [], + name: "StakingDisabled", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "newSupply", + type: "uint256", + }, + ], + name: "SupplyExceedsTotalSupply", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "TokenAlreadyDEXed", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "TokenAlreadyInGame", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "TokenInDuel", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "TokenKilled", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "TokenNotDEXed", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "TokenNotFound", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "TokenNotKilled", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "TokenNotTradable", + type: "error", + }, + { + inputs: [], + name: "TradeDisabled", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "pool", + type: "address", + }, + { + internalType: "uint256", + name: "liquidity", + type: "uint256", + }, + ], + name: "UniswapV2PoolNotZero", + type: "error", + }, + { + inputs: [], + name: "UniswapV3Slot0Failed", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "next", + type: "uint256", + }, + ], + name: "cannotCheckInUntil", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "oldFlags", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newFlags", + type: "uint256", + }, + ], + name: "BitFlagsChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "user", + type: "address", + }, + ], + name: "CheckedIn", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "newSupply", + type: "uint256", + }, + ], + name: "FlapTokenCirculatingSupplyChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint8", + name: "version", + type: "uint8", + }, + ], + name: "Initialized", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "pool", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "eth", + type: "uint256", + }, + ], + name: "LaunchedToDEX", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "previousAdminRole", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "newAdminRole", + type: "bytes32", + }, + ], + name: "RoleAdminChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleGranted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleRevoked", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "ts", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "buyer", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "eth", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "fee", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "postPrice", + type: "uint256", + }, + ], + name: "TokenBought", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "ts", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "creator", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "string", + name: "name", + type: "string", + }, + { + indexed: false, + internalType: "string", + name: "symbol", + type: "string", + }, + { + indexed: false, + internalType: "string", + name: "meta", + type: "string", + }, + ], + name: "TokenCreated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "curve", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "curveParameter", + type: "uint256", + }, + ], + name: "TokenCurveSet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "dexSupplyThresh", + type: "uint256", + }, + ], + name: "TokenDexSupplyThreshSet", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "ts", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "srcToken", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "dstToken", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "srcAmount", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "dstAmount", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "who", + type: "address", + }, + ], + name: "TokenRedeemed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "ts", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "seller", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "eth", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "fee", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "postPrice", + type: "uint256", + }, + ], + name: "TokenSold", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "token", + type: "address", + }, + { + indexed: false, + internalType: "enum IConfiPumpTypes.TokenVersion", + name: "version", + type: "uint8", + }, + ], + name: "TokenVersionSet", + type: "event", + }, + { + stateMutability: "nonpayable", + type: "fallback", + }, + { + inputs: [], + name: "DEFAULT_ADMIN_ROLE", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "address", + name: "recipient", + type: "address", + }, + { + internalType: "uint256", + name: "minAmount", + type: "uint256", + }, + { + internalType: "bool", + name: "isCreator", + type: "bool", + }, + ], + name: "buy", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "checkIn", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + ], + name: "getRoleAdmin", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "getToken", + outputs: [ + { + components: [ + { + internalType: "enum IConfiPumpTypes.TokenStatus", + name: "status", + type: "uint8", + }, + { + internalType: "uint256", + name: "reserve", + type: "uint256", + }, + { + internalType: "uint256", + name: "circulatingSupply", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "bool", + name: "inGame", + type: "bool", + }, + { + internalType: "uint256", + name: "seqInGame", + type: "uint256", + }, + ], + internalType: "struct IConfiPumpTypes.TokenState", + name: "", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "getTokenEx", + outputs: [ + { + components: [ + { + internalType: "enum IConfiPumpTypes.TokenStatus", + name: "status", + type: "uint8", + }, + { + internalType: "uint256", + name: "reserve", + type: "uint256", + }, + { + internalType: "uint256", + name: "circulatingSupply", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "bool", + name: "inGame", + type: "bool", + }, + { + internalType: "uint256", + name: "seqInGame", + type: "uint256", + }, + { + internalType: "enum IConfiPumpTypes.TokenMode", + name: "mode", + type: "uint8", + }, + ], + internalType: "struct IConfiPumpTypes.TokenStateEx", + name: "", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + name: "getTokenV2", + outputs: [ + { + components: [ + { + internalType: "enum IConfiPumpTypes.TokenStatus", + name: "status", + type: "uint8", + }, + { + internalType: "uint256", + name: "reserve", + type: "uint256", + }, + { + internalType: "uint256", + name: "circulatingSupply", + type: "uint256", + }, + { + internalType: "uint256", + name: "price", + type: "uint256", + }, + { + internalType: "enum IConfiPumpTypes.TokenVersion", + name: "tokenVersion", + type: "uint8", + }, + { + internalType: "uint256", + name: "r", + type: "uint256", + }, + { + internalType: "uint256", + name: "dexSupplyThresh", + type: "uint256", + }, + ], + internalType: "struct IConfiPumpTypes.TokenStateV2", + name: "state", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "grantRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "hasRole", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "admin", + type: "address", + }, + ], + name: "initialize", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "lastCheckIn", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "string", + name: "name", + type: "string", + }, + { + internalType: "string", + name: "symbol", + type: "string", + }, + { + internalType: "string", + name: "meta", + type: "string", + }, + ], + name: "newToken", + outputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "string", + name: "name", + type: "string", + }, + { + internalType: "string", + name: "symbol", + type: "string", + }, + { + internalType: "string", + name: "meta", + type: "string", + }, + ], + name: "newTokenNoDuel", + outputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "string", + name: "name", + type: "string", + }, + { + internalType: "string", + name: "symbol", + type: "string", + }, + { + internalType: "string", + name: "meta", + type: "string", + }, + { + internalType: "enum IConfiPumpTypes.DexThreshType", + name: "dexTreshType", + type: "uint8", + }, + ], + name: "newTokenWithDexSupplyThresh", + outputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "nonce", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "uint256", + name: "eth", + type: "uint256", + }, + ], + name: "previewBuy", + outputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "srcToken", + type: "address", + }, + { + internalType: "address", + name: "dstToken", + type: "address", + }, + { + internalType: "uint256", + name: "srcAmount", + type: "uint256", + }, + ], + name: "previewRedeem", + outputs: [ + { + internalType: "uint256", + name: "dstAmount", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "previewSell", + outputs: [ + { + internalType: "uint256", + name: "eth", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "srcToken", + type: "address", + }, + { + internalType: "address", + name: "dstToken", + type: "address", + }, + { + internalType: "uint256", + name: "srcAmount", + type: "uint256", + }, + ], + name: "redeem", + outputs: [ + { + internalType: "uint256", + name: "dstAmount", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "renounceRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "revokeRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "minEth", + type: "uint256", + }, + ], + name: "sell", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "flags", + type: "uint256", + }, + ], + name: "setBitFlags", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes4", + name: "interfaceId", + type: "bytes4", + }, + ], + name: "supportsInterface", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "tokenCreators", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "tokenCreatorsFeeBalance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + stateMutability: "payable", + type: "receive", + }, +] as const; + +export default MEMEABI; diff --git a/packages/plugin-conflux/src/actions/bridgeTransfer.ts b/packages/plugin-conflux/src/actions/bridgeTransfer.ts new file mode 100644 index 00000000000..347b8b980e1 --- /dev/null +++ b/packages/plugin-conflux/src/actions/bridgeTransfer.ts @@ -0,0 +1,141 @@ +import { + Action, + IAgentRuntime, + Memory, + State, + HandlerCallback, +} from "@ai16z/eliza"; +import { generateObjectV2, composeContext, ModelClass, Content } from "@ai16z/eliza"; +import { createPublicClient, createWalletClient, http, parseCFX, encodeFunctionData } from "cive"; +import { hexAddressToBase32 } from "cive/utils"; +import { privateKeyToAccount } from "cive/accounts"; +import { testnet } from "cive/chains"; +import { confluxBridgeTransferTemplate } from "../templates/bridgeTransfer"; +import { TransferSchema, isTransferContent } from "../types"; +import CrossSpaceCallAbi from "../abi/crossSpaceCall"; + +const bridgeSendCFX = async ( + secretKey: `0x${string}`, + rpcUrl: string, + espaceTo: `0x${string}`, + amount: string +) => { + const client = createPublicClient({ + transport: http(rpcUrl), + }); + const networkId = await client.getChainId(); + const account = privateKeyToAccount(secretKey, { networkId }); + + const walletClient = createWalletClient({ + transport: http(rpcUrl), + chain: testnet, + }); + + const toAddress = hexAddressToBase32({ + hexAddress: "0x0888000000000000000000000000000000000006", + networkId, + }); // crossSpaceCall Address + + const hash = await walletClient.sendTransaction({ + account, + to: toAddress, + value: parseCFX(amount), + chain: testnet, + data: encodeFunctionData({ + abi: CrossSpaceCallAbi, + functionName: "transferEVM", + args: [espaceTo], + }), + }); + + // await client.waitForTransactionReceipt({ + // hash, + // }); + return hash; +}; + +export const bridgeTransfer: Action = { + name: "BRIDGE_SEND_CFX", + description: + "Bridge transfer CFX from Conflux Core Space to another in Conflux eSpace. The address is a 0x-prefix address", + similes: ["BRIDGE_SEND_CONFLUX", "CROSS_SPACE_SEND_CFX", "BRIDGE_TRANSFER_CFX", "CROSS_SPACE_TRANSFER_CFX"], + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Send 1 CFX to eSpace Address 0x119DA8bbe74B1C5c987D0c64D10eC1dB301d4752", + }, + }, + { + user: "{{user2}}", + content: { + text: "1 CFX sent to espace Address 0x119DA8bbe74B1C5c987D0c64D10eC1dB301d4752: 0x1234567890abcdef", + content: { + to: "0x119DA8bbe74B1C5c987D0c64D10eC1dB301d4752", + amount: "1", + }, + }, + }, + ], + ], + validate: async (runtime: IAgentRuntime, message: Memory) => { + // no extra validation needed + return true; + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state?: State, + options?: { [key: string]: unknown }, + callback?: HandlerCallback + ) => { + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + const context = composeContext({ + state, + template: confluxBridgeTransferTemplate, + }); + + const content = await generateObjectV2({ + runtime, + context, + modelClass: ModelClass.SMALL, + schema: TransferSchema, + }); + + if (!isTransferContent(content.object)) { + throw new Error("Invalid content"); + } + + const secretKey = runtime.getSetting("CONFLUX_CORE_PRIVATE_KEY") as `0x${string}`; + const rpcUrl = runtime.getSetting("CONFLUX_CORE_SPACE_RPC_URL"); + + let success = false; + + try { + const hash = await bridgeSendCFX(secretKey, rpcUrl, content.object.to as `0x${string}`, content.object.amount.toString()); + success = true; + if (!callback) { + return success; + } + callback({ + text: `${content.object.amount} CFX sent to ${content.object.to}: ${hash}`, + content: content.object, + }); + } catch (error) { + console.error(`Error sending CFX: ${error}`); + if (!callback) { + return success; + } + callback({ + text: `Failed to send ${content.object.amount} CFX to ${content.object.to}: ${error}`, + }); + } + return success; + }, +}; diff --git a/packages/plugin-conflux/src/actions/confiPump.ts b/packages/plugin-conflux/src/actions/confiPump.ts new file mode 100644 index 00000000000..a852b96227e --- /dev/null +++ b/packages/plugin-conflux/src/actions/confiPump.ts @@ -0,0 +1,331 @@ +import { + Action, + IAgentRuntime, + Memory, + State, + HandlerCallback, +} from "@ai16z/eliza"; +import { + generateObjectV2, + composeContext, + ModelClass, +} from "@ai16z/eliza"; +import { + createPublicClient, + createWalletClient, + http, + parseEther, + encodeFunctionData, + WalletClient, + Account +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { confluxESpaceTestnet, confluxESpace } from "viem/chains"; +import { parseUnits, getAddress } from "viem/utils"; +import { confluxTransferTemplate } from "../templates/transfer"; +import { + PumpSchema, + isPumpContent, + isPumpBuyContent, + isPumpCreateContent, + isPumpSellContent, +} from "../types"; +import MEMEABI from "../abi/meme"; +import ERC20ABI from "../abi/erc20"; + +// Helper function to check and approve token allowance if needed +async function ensureAllowance( + walletClient: WalletClient, + rpcUrl: string, + account: Account, + tokenAddress: `0x${string}`, + memeAddress: `0x${string}`, + amount: bigint +) { + console.log(`Checking allowance: token: ${tokenAddress} meme: ${memeAddress} amount: ${amount}`); + + const publicClient = createPublicClient({ + transport: http(rpcUrl), + chain: confluxESpaceTestnet, + }); + + const allowance = await publicClient.readContract({ + address: tokenAddress, + abi: ERC20ABI, + functionName: "allowance", + args: [account.address, memeAddress], + }); + + console.log("allowance:", allowance); + + if (allowance < amount) { + console.log(`allowance(${allowance}) is less than amount(${amount}), approving...`); + + const hash = await walletClient.sendTransaction({ + account, + to: tokenAddress, + data: encodeFunctionData({ + abi: ERC20ABI, + functionName: "approve", + args: [memeAddress, amount - allowance], + }), + chain: confluxESpaceTestnet, + kzg: null, + }); + + console.log(`Approving hash: ${hash}`); + await publicClient.waitForTransactionReceipt({ hash }); + console.log(`Approving success: ${hash}`); + } else { + console.log(`No need to approve`); + } +} + +// Main ConfiPump action definition +export const confiPump: Action = { + name: "CONFI_PUMP", + description: "Perform actions on ConfiPump, for example create a new token, buy a token, or sell a token.", + similes: ["SELL_TOKEN", "BUY_TOKEN", "CREATE_TOKEN"], + examples: [ + // Create token example + [ + { + user: "{{user1}}", + content: { + text: "Create a new token called GLITCHIZA with symbol GLITCHIZA and generate a description about it.", + }, + }, + { + user: "{{user2}}", + content: { + text: "Token GLITCHIZA (GLITCHIZA) created successfully!\nContract Address: 0x1234567890abcdef\n", + action: "CREATE_TOKEN", + content: { + tokenInfo: { + symbol: "GLITCHIZA", + address: "EugPwuZ8oUMWsYHeBGERWvELfLGFmA1taDtmY8uMeX6r", + creator: "9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa", + name: "GLITCHIZA", + description: "A GLITCHIZA token", + }, + amount: "1", + }, + }, + }, + ], + // Buy token example + [ + { + user: "{{user1}}", + content: { + text: "Buy 0.00069 CFX worth of GLITCHIZA(0x1234567890abcdef)", + }, + }, + { + user: "{{user2}}", + content: { + text: "0.00069 CFX bought successfully!", + action: "BUY_TOKEN", + content: { + address: "0x1234567890abcdef", + amount: "0.00069", + }, + }, + }, + ], + // Sell token example + [ + { + user: "{{user1}}", + content: { + text: "Sell 0.00069 CFX worth of GLITCHIZA(0x1234567890abcdef)", + }, + }, + { + user: "{{user2}}", + content: { + text: "0.00069 CFX sold successfully: 0x1234567890abcdef", + action: "SELL_TOKEN", + content: { + address: "0x1234567890abcdef", + amount: "0.00069", + }, + }, + }, + ], + ], + + validate: async (runtime: IAgentRuntime, message: Memory) => { + return true; // No extra validation needed + }, + + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state?: State, + options?: { [key: string]: unknown }, + callback?: HandlerCallback + ) => { + let success = false; + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + // Generate content based on template + const context = composeContext({ + state, + template: confluxTransferTemplate, + }); + + const content = await generateObjectV2({ + runtime, + context, + modelClass: ModelClass.LARGE, + schema: PumpSchema, + }); + + if (!isPumpContent(content.object)) { + throw new Error("Invalid content"); + } + + // Setup clients and account + const rpcUrl = runtime.getSetting("CONFLUX_ESPACE_RPC_URL"); + const account = privateKeyToAccount( + runtime.getSetting("CONFLUX_ESPACE_PRIVATE_KEY") as `0x${string}` + ); + const walletClient = createWalletClient({ + transport: http(rpcUrl), + }); + + const contentObject = content.object; + let data: any; + let value: bigint; + + try { + // Handle different action types + switch (contentObject.action) { + case "CREATE_TOKEN": + if (!isPumpCreateContent(contentObject)) { + throw new Error("Invalid content"); + } + console.log( + "creating: ", + contentObject.params.name, + contentObject.params.symbol, + contentObject.params.description + ); + data = encodeFunctionData({ + abi: MEMEABI, + functionName: "newToken", + args: [ + contentObject.params.name, + contentObject.params.symbol, + contentObject.params.description, + ], + }); + value = parseEther("10"); + break; + + case "BUY_TOKEN": + if (!isPumpBuyContent(contentObject)) { + throw new Error("Invalid content"); + } + value = parseUnits(contentObject.params.value.toString(), 18); + console.log("buying: ", contentObject.params.tokenAddress, value); + data = encodeFunctionData({ + abi: MEMEABI, + functionName: "buy", + args: [ + contentObject.params.tokenAddress as `0x${string}`, + account.address, + 0n, + false, + ], + }); + break; + + case "SELL_TOKEN": + if (!isPumpSellContent(contentObject)) { + throw new Error("Invalid content"); + } + const tokenAddress = getAddress( + contentObject.params.tokenAddress as `0x${string}` + ); + console.log( + "selling: ", + tokenAddress, + account.address, + contentObject.params.value + ); + const amountUnits = parseUnits( + contentObject.params.value.toString(), + 18 + ); + + await ensureAllowance( + walletClient, + rpcUrl, + account, + tokenAddress as `0x${string}`, + runtime.getSetting("CONFLUX_MEME_CONTRACT_ADDRESS") as `0x${string}`, + amountUnits + ); + + data = encodeFunctionData({ + abi: MEMEABI, + functionName: "sell", + args: [tokenAddress, amountUnits, 0n], + }); + value = 0n; + break; + } + + // Simulate and execute transaction + const publicClient = createPublicClient({ + transport: http(rpcUrl), + chain: confluxESpaceTestnet, + }); + + const memeContractAddress = runtime.getSetting("CONFLUX_MEME_CONTRACT_ADDRESS") as `0x${string}`; + + const simulate = await publicClient.call({ + to: memeContractAddress, + data, + value, + account, + }); + console.log("simulate: ", simulate); + + const hash = await walletClient.sendTransaction({ + account, + to: memeContractAddress, + data, + chain: confluxESpaceTestnet, + kzg: null, + value, + }); + + success = true; + + if (callback) { + callback({ + text: `Perform the action successfully: ${content.object.action}: ${hash}`, + content: content.object, + }); + } + } catch (error) { + console.error(`Error performing the action: ${error}`); + if (callback) { + callback({ + text: `Failed to perform the action: ${content.object.action}: ${error}`, + }); + } + } + + return success; + }, +}; diff --git a/packages/plugin-conflux/src/actions/transfer.ts b/packages/plugin-conflux/src/actions/transfer.ts new file mode 100644 index 00000000000..d24749492ae --- /dev/null +++ b/packages/plugin-conflux/src/actions/transfer.ts @@ -0,0 +1,129 @@ +import { + Action, + IAgentRuntime, + Memory, + State, + HandlerCallback, +} from "@ai16z/eliza"; +import { generateObjectV2, composeContext, ModelClass, Content } from "@ai16z/eliza"; +import { createPublicClient, createWalletClient, http, parseCFX } from "cive"; +import { privateKeyToAccount } from "cive/accounts"; +import { testnet } from "cive/chains"; +import { confluxTransferTemplate } from "../templates/transfer"; +import { TransferSchema, isTransferContent } from "../types"; + +const sendCFX = async ( + secretKey: `0x${string}`, + rpcUrl: string, + to: string, + amount: string +) => { + const client = createPublicClient({ + transport: http(rpcUrl), + }); + const networkId = await client.getChainId(); + const account = privateKeyToAccount(secretKey, { networkId }); + + const walletClient = createWalletClient({ + transport: http(rpcUrl), + chain: testnet, + }); + + const hash = await walletClient.sendTransaction({ + account, + to, + value: parseCFX(amount), + chain: testnet, + }); + + // await client.waitForTransactionReceipt({ + // hash, + // }); + return hash; +}; + +export const transfer: Action = { + name: "SEND_CFX", + description: + "Transfer CFX to another address in Conflux Core Space. The address starts with `cfx:` or `cfxtest:`", + similes: ["SEND_CONFLUX", "SEND_CFX_CORE_SPACE", "TRANSFER_CFX"], + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Send 1 CFX to cfx:aaejuaaaaaaaaaaaaaaaaaaaaaaaaaaaa2eaeg85p5", + }, + }, + { + user: "{{user2}}", + content: { + text: "1 CFX sent to cfx:aaejuaaaaaaaaaaaaaaaaaaaaaaaaaaaa2eaeg85p5: 0x1234567890abcdef", + content: { + to: "cfx:aaejuaaaaaaaaaaaaaaaaaaaaaaaaaaaa2eaeg85p5", + amount: "1", + }, + }, + }, + ], + ], + validate: async (runtime: IAgentRuntime, message: Memory) => { + // no extra validation needed + return true; + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state?: State, + options?: { [key: string]: unknown }, + callback?: HandlerCallback + ) => { + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + const context = composeContext({ + state, + template: confluxTransferTemplate, + }); + + const content = await generateObjectV2({ + runtime, + context, + modelClass: ModelClass.SMALL, + schema: TransferSchema, + }); + + if (!isTransferContent(content.object)) { + throw new Error("Invalid content"); + } + + const secretKey = runtime.getSetting("CONFLUX_CORE_PRIVATE_KEY") as `0x${string}`; + const rpcUrl = runtime.getSetting("CONFLUX_CORE_SPACE_RPC_URL"); + + let success = false; + + try { + const hash = await sendCFX(secretKey, rpcUrl, content.object.to, content.object.amount.toString()); + success = true; + if (!callback) { + return success; + } + callback({ + text: `${content.object.amount} CFX sent to ${content.object.to}: ${hash}`, + content: content.object, + }); + } catch (error) { + console.error(`Error sending CFX: ${error}`); + if (!callback) { + return success; + } + callback({ + text: `Failed to send ${content.object.amount} CFX to ${content.object.to}: ${error}`, + }); + } + return success; + }, +}; diff --git a/packages/plugin-conflux/src/index.ts b/packages/plugin-conflux/src/index.ts new file mode 100644 index 00000000000..1c6e65989e3 --- /dev/null +++ b/packages/plugin-conflux/src/index.ts @@ -0,0 +1,11 @@ +import { Plugin } from "@ai16z/eliza"; +import { transfer } from "./actions/transfer"; +import { bridgeTransfer } from "./actions/bridgeTransfer"; +import { confiPump } from "./actions/confiPump"; + +export const confluxPlugin: Plugin = { + name: "conflux", + description: "Conflux Plugin for Eliza", + actions: [transfer, bridgeTransfer, confiPump], + providers: [], +}; diff --git a/packages/plugin-conflux/src/templates/bridgeTransfer.ts b/packages/plugin-conflux/src/templates/bridgeTransfer.ts new file mode 100644 index 00000000000..ca5fdea32ba --- /dev/null +++ b/packages/plugin-conflux/src/templates/bridgeTransfer.ts @@ -0,0 +1,7 @@ +export const confluxBridgeTransferTemplate = ` +Extract Conflux Cross Space Transfer Parameters from the latest message: + +{{recentMessages}} + +The to address should be the Conflux eSpace address, starting with "0x". +`; diff --git a/packages/plugin-conflux/src/templates/confiPump.ts b/packages/plugin-conflux/src/templates/confiPump.ts new file mode 100644 index 00000000000..b3047fc8027 --- /dev/null +++ b/packages/plugin-conflux/src/templates/confiPump.ts @@ -0,0 +1,9 @@ +export const confiPumpTemplate = ` +Extract Conflux ConfiPump Parameters, including token creation, buy, and sell, from the latest messages: + +{{recentMessages}} + +For token creation, should come up with a name, symbol, and description. +For token buy, should come up with the amount of CFX to buy which token (with token address starting with 0x). +For token sell, should come up with the amount of token to sell (with token address starting with 0x). +`; diff --git a/packages/plugin-conflux/src/templates/transfer.ts b/packages/plugin-conflux/src/templates/transfer.ts new file mode 100644 index 00000000000..57fef7ad0d4 --- /dev/null +++ b/packages/plugin-conflux/src/templates/transfer.ts @@ -0,0 +1,7 @@ +export const confluxTransferTemplate = ` +Extract Conflux Core Space Transfer Parameters from the latest message: + +{{recentMessages}} + +The to address should be the Conflux Core Space address, starting with "cfx:" or "cfxtest:". +`; diff --git a/packages/plugin-conflux/src/types.ts b/packages/plugin-conflux/src/types.ts new file mode 100644 index 00000000000..5f033da3f47 --- /dev/null +++ b/packages/plugin-conflux/src/types.ts @@ -0,0 +1,85 @@ +import { z } from "zod"; +import { Content } from "@ai16z/eliza"; + +export const TransferSchema = z.object({ + to: z.string(), + amount: z.number(), // use number ignoring decimals issue +}); + +export interface TransferContent { + to: string; + amount: number; +} + +export const isTransferContent = (object: any): object is TransferContent => { + if (TransferSchema.safeParse(object).success) { + return true; + } + console.error("Invalid content: ", object); + return false; +}; + +export const PumpCreateSchema = z.object({ + action: z.literal("CREATE_TOKEN"), + params: z.object({ + symbol: z.string(), + name: z.string(), + description: z.string(), + }), +}); + +export const PumpBuySchema = z.object({ + action: z.literal("BUY_TOKEN"), + params: z.object({ + tokenAddress: z.string(), + value: z.number(), + }), +}); + +export const PumpSellSchema = z.object({ + action: z.literal("SELL_TOKEN"), + params: z.object({ + tokenAddress: z.string(), + value: z.number(), + }), +}); + +export const PumpSchema = z.union([PumpCreateSchema, PumpBuySchema, PumpSellSchema]); + +export type PumpContent = z.infer; +export type PumpCreateContent = z.infer; +export type PumpBuyContent = z.infer; +export type PumpSellContent = z.infer; + +export function isPumpContent(object: any): object is PumpContent { + if (PumpSchema.safeParse(object).success) { + return true; + } + console.error("Invalid content: ", object); + return false; +} + +export function isPumpCreateContent(object: any): object is PumpCreateContent { + if (PumpCreateSchema.safeParse(object).success) { + return true; + } + console.error("Invalid content: ", object); + return false; +} + +export function isPumpBuyContent(object: any): object is PumpBuyContent { + if (PumpBuySchema.safeParse(object).success) { + return true; + } + console.error("Invalid content: ", object); + return false; +} + +export function isPumpSellContent(object: any): object is PumpSellContent { + if (PumpSellSchema.safeParse(object).success) { + return true; + } + console.error("Invalid content: ", object); + return false; +} + diff --git a/packages/plugin-conflux/tsconfig.json b/packages/plugin-conflux/tsconfig.json new file mode 100644 index 00000000000..eaa78145aa3 --- /dev/null +++ b/packages/plugin-conflux/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "./src" + }, + "include": ["src"] +} diff --git a/packages/plugin-conflux/tsup.config.ts b/packages/plugin-conflux/tsup.config.ts new file mode 100644 index 00000000000..f63d4d37fcf --- /dev/null +++ b/packages/plugin-conflux/tsup.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "cive", + // Add other modules you want to externalize + ], +}); diff --git a/scripts/build.sh b/scripts/build.sh index 9fbbe5f2fb0..a6cb245aeef 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -24,6 +24,7 @@ PACKAGES=( "plugin-trustdb" "plugin-solana" "plugin-starknet" + "plugin-conflux" "adapter-postgres" "adapter-sqlite" "adapter-sqljs"