Skip to main content
Glama

Hashkey MCP Server

Official
by HashkeyHSK
handlers.ts•4.06 kB
import { encodeFunctionData, erc20Abi, formatUnits, type Account, type PublicActions, type WalletClient, } from 'viem'; import { waitForTransactionReceipt } from 'viem/actions'; import { base } from 'viem/chains'; import type { z } from 'zod'; import { USDC_ADDRESS, USDC_DECIMALS } from '../../lib/constants.js'; import { COINBASE_COMMERCE_ABI } from '../../lib/contracts/coinbase-commerce.js'; import type { OpenRouterTransferIntentResponse } from '../types.js'; import { checkToolSupportsChain, constructBaseScanUrl, } from '../utils/index.js'; import type { BuyOpenRouterCreditsSchema } from './schemas.js'; export async function buyOpenRouterCreditsHandler( wallet: WalletClient & PublicActions, args: z.infer<typeof BuyOpenRouterCreditsSchema>, ): Promise<string> { const { amountUsd } = args; checkToolSupportsChain({ chainId: wallet.chain?.id, supportedChains: [base], }); if (!process.env.OPENROUTER_API_KEY) { throw new Error('OPENROUTER_API_KEY is not set'); } const address = wallet.account?.address; if (!address) { throw new Error('No address found'); } // Ensure user has enough USDC for txn const usdcBalance = await wallet.readContract({ address: USDC_ADDRESS, abi: erc20Abi, functionName: 'balanceOf', args: [address], }); const parsedUnits = formatUnits(usdcBalance, USDC_DECIMALS); if (Number(parsedUnits) < amountUsd) { throw new Error('Insufficient USDC balance'); } const response = await fetch( 'https://openrouter.ai/api/v1/credits/coinbase', { method: 'POST', headers: { Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount: amountUsd, sender: address, chain_id: base.id, // only Base supported }), }, ); const responseJSON: OpenRouterTransferIntentResponse = await response.json(); const { data: { web3_data: { transfer_intent: { call_data }, }, }, } = responseJSON; console.error(responseJSON); // Generate transactions based off intent const atomicUnits = BigInt(call_data.recipient_amount) + BigInt(call_data.fee_amount); const approvalTxCalldata = encodeFunctionData({ abi: erc20Abi, functionName: 'approve', args: [ responseJSON.data.web3_data.transfer_intent.metadata .contract_address as `0x${string}`, atomicUnits, ], }); const transferTokenPreApprovedTxCalldata = encodeFunctionData({ abi: COINBASE_COMMERCE_ABI, functionName: 'transferTokenPreApproved', args: [ { id: call_data.id as `0x${string}`, deadline: BigInt( Math.floor(new Date(call_data.deadline).getTime() / 1000), ), recipient: call_data.recipient as `0x${string}`, recipientAmount: BigInt(call_data.recipient_amount), recipientCurrency: call_data.recipient_currency as `0x${string}`, refundDestination: call_data.refund_destination as `0x${string}`, feeAmount: BigInt(call_data.fee_amount), operator: call_data.operator as `0x${string}`, signature: call_data.signature as `0x${string}`, prefix: call_data.prefix as `0x${string}`, }, ], }); const approval = await wallet.sendTransaction({ to: USDC_ADDRESS, data: approvalTxCalldata, account: wallet.account as Account, chain: wallet.chain, }); await waitForTransactionReceipt(wallet, { hash: approval, }); const transfer = await wallet.sendTransaction({ to: responseJSON.data.web3_data.transfer_intent.metadata .contract_address as `0x${string}`, data: transferTokenPreApprovedTxCalldata, account: wallet.account as Account, chain: wallet.chain, }); const { transactionHash } = await waitForTransactionReceipt(wallet, { hash: transfer, }); return JSON.stringify({ hash: transactionHash, url: constructBaseScanUrl(wallet.chain ?? base, transactionHash), }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/HashkeyHSK/hsk-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server