Skip to main content

Beyond ElizaOS: Additional Functionality

We are working on implementing features that are not available in Eliza. Current functionality includes:

  • Get Account Balance on Fuse blockchain
  • Transfer FUSE token with user-connected wallet instead of hardcoded private key

Get Account Balance on Fuse blockchain

We have implemented a custom action “getBalance” in the ElizaOS to retrieve the correct information from the Fuse blockchain.

export const getBalanceAction = {
name: "getBalance",
description: "Get the balance of a provided wallet address",
handler: async (
runtime: IAgentRuntime,
_message: Memory,
state: State,
_options: any,
callback?: (response: any) => void
) => {
const walletProvider = initWalletProvider(runtime);
const currentChain = walletProvider.getCurrentChain().name.toLowerCase() as SupportedChain;
const { address } = await buildAddressDetails(state, runtime);

try {
const client = walletProvider.getPublicClient(currentChain);
const balance = await client.getBalance({ address });
const formattedBalance = formatEther(balance);

if (callback) {
callback({
text: `Balance for address ${address}: ${formattedBalance}`,
content: { balance: formattedBalance },
});
}
return true;
} catch (error) {
console.error("Error getting balance:", error);
if (callback) {
callback({
text: `Error getting balance: ${error.message}`,
content: { error: error.message },
});
}
return false;
}
},
template: getAddressTemplate,
validate: async (runtime: IAgentRuntime) => {
const privateKey = runtime.getSetting("EVM_PRIVATE_KEY");
return typeof privateKey === "string" && privateKey.startsWith("0x");
},
examples: [
[
{
user: "assistant",
content: {
text: "I'll check the balance for 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
action: "GET_BALANCE",
},
},
{
user: "user",
content: {
text: "What's the balance of 0x742d35Cc6634C0532925a3b844Bc454e4438f44e?",
action: "GET_BALANCE",
},
},
],
],
similes: ["CHECK_BALANCE", "BALANCE_INQUIRY", "ACCOUNT_BALANCE"],
};

Transfer FUSE token with user-connected wallet

In this instance, we have replaced Eliza’s hardcoded private key with the user’s connected wallet, enabling the user to transfer the FUSE token on the blockchain via Edison themselves.

ElizaOS/Backend:

export const transferAction = {
name: "transfer",
description: "Transfer tokens between addresses on the same chain",
handler: async (
runtime: IAgentRuntime,
_message: Memory,
state: State,
_options: any,
callback?: HandlerCallback
) => {
const transferDetails = await buildTransferDetails(state, runtime);

try {
if (callback) {
callback({
text: `You are about to transfer ${transferDetails.amount} to ${transferDetails.toAddress}`,
content: transferDetails,
action: "SEND_TOKENS"
});
}
return true;
} catch (error) {
console.error("Error during token transfer:", error);
if (callback) {
callback({
text: `Error transferring tokens: ${error.message}`,
content: { error: error.message },
});
}
return false;
}
},
template: transferTemplate,
validate: async (runtime: IAgentRuntime) => true,
examples: [
[
{
user: "assistant",
content: {
text: "I'll help you transfer 1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
action: "SEND_TOKENS",
},
},
{
user: "user",
content: {
text: "Transfer 1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
action: "SEND_TOKENS",
},
},
],
],
similes: ["SEND_TOKENS", "TOKEN_TRANSFER", "MOVE_TOKENS"],
};

Frontend: 

type ChatMessageProps = {
message: TextResponse;
};

type SendTokensContent = {
amount: string;
toAddress: Address;
}

const AssistantIcon = () => (
<div className="relative shrink-0 w-[58px] h-[58px] -mt-[10px]">
<Image
src={edisonGradient}
alt="Assistant Icon"
width={58}
height={58}
className="w-[58px] h-[58px]"
/>
</div>
)

const Spinner = () => {
return (
<span className="animate-spin border-2 border-gray border-t-2 border-t-dark-gray rounded-full w-4 h-4"></span>
)
}

const Button = ({ message }: ChatMessageProps) => {
const dispatch = useAppDispatch();
const { isConnected } = useAccount();
const content = message.content as SendTokensContent;

const {
data: hash,
error,
isPending,
sendTransaction
} = useSendTransaction()

const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({
hash,
})

const isLoading = isPending || isConfirming;

useEffect(() => {
if (isConfirmed && hash) {
dispatch(updateMessage({
message: {
...message,
hash
}
}));
}
}, [dispatch, hash, isConfirmed, message]);

return (
<button
className={`transition ease-in-out flex items-center gap-2 w-fit px-4 py-3 text-lg leading-none font-semibold rounded-full hover:bg-[transparent] hover:text-black ${error?.message ? "bg-[#FFEBE9] border border-[#FD0F0F] text-[#FD0F0F]" : "bg-black border border-black text-white"}`}
onClick={() => {
if (message.hash) {
window.open(`${fuse.blockExplorers.default.url}/tx/${hash}`, "_blank");
} else if (isConnected) {
sendTransaction({
to: content.toAddress,
value: parseEther(content.amount),
});
} else {
dispatch(setIsWalletModalOpen(true));
}
}}
>
{isConnected ? "Sign Transaction" : "Connect Wallet"}
{isLoading && <Spinner />}
{message.hash && <ExternalArrow />}
</button>
);
};

const ChatMessage = ({ message }: ChatMessageProps) => {
const isUser = message.user === "user";
const isButton = message.action === "SEND_TOKENS";

return (
<div className={`flex gap-3 md:gap-0 ${isUser ? 'justify-end' : 'justify-start'}`}>
{!isUser && <AssistantIcon />}
<div
className={`rounded-2xl px-4 py-3 max-w-[85%] ${isUser
? 'bg-lightest-gray'
: 'bg-white border border-lightest-gray'
} ${!isUser && message.isLoading ? 'message-animate' : ''}`}
>
{message.isLoading ? (
<div className="flex gap-1">
<span className="animate-bounce">.</span>
<span className="animate-bounce delay-100">.</span>
<span className="animate-bounce delay-200">.</span>
</div>
) : (
<>
<div className="whitespace-pre-wrap leading-relaxed">{message.text}</div>
{message.hash ? (
<div className="mt-3 bg-[#ECFCE5] rounded-xl p-4">
<div className="flex items-center gap-2">
<div className="w-6 h-6 rounded-full bg-[#49E358] flex items-center justify-center">
<Check className="w-4 h-4 text-white" />
</div>
<span className="text-[#198155] font-medium">Transaction complete</span>
</div>
<a href={`${fuse.blockExplorers.default.url}/tx/${message.hash}`} className="text-[#198155] hover:underline mt-1 block">
View transaction &gt;
</a>
</div>
) : (
isButton && <Button message={message} />
)}
{!isUser && (
<div className="flex items-center gap-2 mt-2">
<button className="p-1 hover:bg-lightest-gray rounded">
<Copy className="w-4 h-4 text-text-dark-gray" />
</button>
<button className="p-1 hover:bg-lightest-gray rounded">
<ThumbsUp className="w-4 h-4 text-text-dark-gray" />
</button>
<button className="p-1 hover:bg-lightest-gray rounded">
<ThumbsDown className="w-4 h-4 text-text-dark-gray" />
</button>
</div>
)}
</>
)}
</div>
</div>
);
};

export default ChatMessage;