-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: Add Etherscan plugin with all endpoints Co-Authored-By: [email protected] <[email protected]> * chore: update pnpm lockfile Co-Authored-By: [email protected] <[email protected]> * fix: update tsconfig extends path Co-Authored-By: [email protected] <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: [email protected] <[email protected]>
- Loading branch information
1 parent
25dc4a7
commit f344c8b
Showing
10 changed files
with
576 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# GOAT Etherscan Plugin | ||
|
||
This plugin enables your agent to interact with the Ethereum blockchain data from Etherscan. | ||
|
||
## Installation | ||
|
||
```bash | ||
pnpm add @goat-sdk/plugin-etherscan | ||
``` | ||
|
||
## Configuration | ||
|
||
To use the Etherscan plugin, you'll need an API key from [Etherscan](https://etherscan.io/apis). | ||
|
||
```typescript | ||
import { etherscan } from "@goat-sdk/plugin-etherscan"; | ||
|
||
const tools = getOnChainTools({ | ||
plugins: [ | ||
etherscan({ | ||
apiKey: "YOUR_ETHERSCAN_API_KEY", | ||
}), | ||
], | ||
}); | ||
``` | ||
|
||
## Features | ||
|
||
- Account balance and transaction history | ||
- Contract ABI and source code retrieval | ||
- Transaction status and receipt information | ||
- Block data | ||
- Token balances and transfers | ||
- Gas price tracking | ||
- Event logs | ||
|
||
## Network Support | ||
|
||
The plugin supports both mainnet and testnet networks: | ||
- Ethereum Mainnet | ||
- Goerli Testnet | ||
- Sepolia Testnet | ||
|
||
## Example Usage | ||
|
||
```typescript | ||
// Get account balance | ||
const balance = await tools.get_account_balance({ | ||
address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", | ||
network: "mainnet", | ||
}); | ||
|
||
// Get contract ABI | ||
const abi = await tools.get_contract_abi({ | ||
address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", | ||
network: "mainnet", | ||
}); | ||
|
||
// Get gas price | ||
const gasPrice = await tools.get_gas_price({ | ||
network: "mainnet", | ||
}); | ||
``` | ||
|
||
## API Reference | ||
|
||
For detailed API documentation, please refer to the TypeScript types and JSDoc comments in the source code. |
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,32 @@ | ||
{ | ||
"name": "@goat-sdk/plugin-etherscan", | ||
"version": "0.1.0", | ||
"files": ["dist/**/*", "README.md", "package.json"], | ||
"scripts": { | ||
"build": "tsup", | ||
"clean": "rm -rf dist", | ||
"test": "vitest run --passWithNoTests" | ||
}, | ||
"sideEffects": false, | ||
"main": "./dist/index.js", | ||
"module": "./dist/index.mjs", | ||
"types": "./dist/index.d.ts", | ||
"dependencies": { | ||
"@goat-sdk/core": "workspace:*", | ||
"reflect-metadata": "^0.1.13", | ||
"zod": "catalog:" | ||
}, | ||
"peerDependencies": { | ||
"@goat-sdk/core": "workspace:*" | ||
}, | ||
"homepage": "https://ohmygoat.dev", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/goat-sdk/goat.git" | ||
}, | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/goat-sdk/goat/issues" | ||
}, | ||
"keywords": ["ai", "agents", "web3", "blockchain", "etherscan"] | ||
} |
20 changes: 20 additions & 0 deletions
20
typescript/packages/plugins/etherscan/src/etherscan.plugin.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,20 @@ | ||
import { Chain, PluginBase, WalletClientBase } from "@goat-sdk/core"; | ||
import { EtherscanService } from "./etherscan.service"; | ||
|
||
interface EtherscanPluginOptions { | ||
apiKey: string; | ||
} | ||
|
||
export class EtherscanPlugin extends PluginBase<WalletClientBase> { | ||
constructor({ apiKey }: EtherscanPluginOptions) { | ||
super("etherscan", [new EtherscanService(apiKey)]); | ||
} | ||
|
||
supportsChain(chain: Chain): boolean { | ||
return chain.type === "evm"; | ||
} | ||
} | ||
|
||
export function etherscan(options: EtherscanPluginOptions) { | ||
return new EtherscanPlugin(options); | ||
} |
257 changes: 257 additions & 0 deletions
257
typescript/packages/plugins/etherscan/src/etherscan.service.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,257 @@ | ||
import "reflect-metadata"; | ||
import { Tool, WalletClientBase } from "@goat-sdk/core"; | ||
import { | ||
AccountBalanceParameters, | ||
AccountTransactionsParameters, | ||
BlockByNumberParameters, | ||
ContractABIParameters, | ||
ContractSourceCodeParameters, | ||
EventLogsParameters, | ||
GasPriceParameters, | ||
TokenBalanceParameters, | ||
TransactionReceiptParameters, | ||
TransactionStatusParameters, | ||
} from "./parameters"; | ||
import { buildUrl, etherscanRequest } from "./request"; | ||
|
||
// Common response types | ||
export interface AccountBalance { | ||
status: string; | ||
message: string; | ||
result: string; | ||
} | ||
|
||
export interface TransactionList { | ||
status: string; | ||
message: string; | ||
result: Array<{ | ||
blockNumber: string; | ||
timeStamp: string; | ||
hash: string; | ||
from: string; | ||
to: string; | ||
value: string; | ||
gas: string; | ||
gasPrice: string; | ||
}>; | ||
} | ||
|
||
// Types for better type safety | ||
export type EtherscanResponse<T> = Promise<T>; | ||
|
||
export class EtherscanService { | ||
constructor(private readonly apiKey: string) {} | ||
|
||
@Tool({ | ||
name: "get_account_balance", | ||
description: "Get account balance from Etherscan", | ||
}) | ||
async getAccountBalance( | ||
_walletClient: WalletClientBase, | ||
parameters: AccountBalanceParameters, | ||
): EtherscanResponse<AccountBalance> { | ||
const { address, tag, network } = parameters; | ||
return etherscanRequest( | ||
buildUrl(network, "account", "balance", { | ||
address, | ||
tag, | ||
}), | ||
this.apiKey, | ||
); | ||
} | ||
|
||
@Tool({ | ||
name: "get_account_transactions", | ||
description: "Get account transactions from Etherscan", | ||
}) | ||
async getAccountTransactions( | ||
_walletClient: WalletClientBase, | ||
parameters: AccountTransactionsParameters, | ||
): EtherscanResponse<TransactionList> { | ||
const { address, startBlock, endBlock, page, offset, sort, network } = parameters; | ||
return etherscanRequest( | ||
buildUrl(network, "account", "txlist", { | ||
address, | ||
startblock: startBlock, | ||
endblock: endBlock, | ||
page, | ||
offset, | ||
sort, | ||
}), | ||
this.apiKey, | ||
); | ||
} | ||
|
||
@Tool({ | ||
name: "get_contract_abi", | ||
description: "Get contract ABI from Etherscan", | ||
}) | ||
async getContractABI( | ||
_walletClient: WalletClientBase, | ||
parameters: ContractABIParameters, | ||
): EtherscanResponse<string> { | ||
const { address, network } = parameters; | ||
return etherscanRequest( | ||
buildUrl(network, "contract", "getabi", { | ||
address, | ||
}), | ||
this.apiKey, | ||
); | ||
} | ||
|
||
@Tool({ | ||
name: "get_contract_source_code", | ||
description: "Get contract source code from Etherscan", | ||
}) | ||
async getContractSourceCode( | ||
_walletClient: WalletClientBase, | ||
parameters: ContractSourceCodeParameters, | ||
): EtherscanResponse<string> { | ||
const { address, network } = parameters; | ||
return etherscanRequest( | ||
buildUrl(network, "contract", "getsourcecode", { | ||
address, | ||
}), | ||
this.apiKey, | ||
); | ||
} | ||
|
||
@Tool({ | ||
name: "get_transaction_status", | ||
description: "Get transaction status from Etherscan", | ||
}) | ||
async getTransactionStatus( | ||
_walletClient: WalletClientBase, | ||
parameters: TransactionStatusParameters, | ||
): EtherscanResponse<{ status: string; message: string; result: { status: string } }> { | ||
const { txhash, network } = parameters; | ||
return etherscanRequest( | ||
buildUrl(network, "transaction", "getstatus", { | ||
txhash, | ||
}), | ||
this.apiKey, | ||
); | ||
} | ||
|
||
@Tool({ | ||
name: "get_transaction_receipt", | ||
description: "Get transaction receipt from Etherscan", | ||
}) | ||
async getTransactionReceipt( | ||
_walletClient: WalletClientBase, | ||
parameters: TransactionReceiptParameters, | ||
): EtherscanResponse<{ status: string; message: string; result: { status: string } }> { | ||
const { txhash, network } = parameters; | ||
return etherscanRequest( | ||
buildUrl(network, "transaction", "gettxreceiptstatus", { | ||
txhash, | ||
}), | ||
this.apiKey, | ||
); | ||
} | ||
|
||
@Tool({ | ||
name: "get_block_by_number", | ||
description: "Get block by number from Etherscan", | ||
}) | ||
async getBlockByNumber( | ||
_walletClient: WalletClientBase, | ||
parameters: BlockByNumberParameters, | ||
): EtherscanResponse<{ | ||
jsonrpc: string; | ||
id: number; | ||
result: { | ||
number: string; | ||
hash: string; | ||
parentHash: string; | ||
transactions: string[]; | ||
timestamp: string; | ||
nonce: string; | ||
difficulty: string; | ||
gasLimit: string; | ||
gasUsed: string; | ||
miner: string; | ||
extraData: string; | ||
size: string; | ||
}; | ||
}> { | ||
const { blockNumber, network } = parameters; | ||
return etherscanRequest( | ||
buildUrl(network, "proxy", "eth_getBlockByNumber", { | ||
tag: typeof blockNumber === "number" ? `0x${blockNumber.toString(16)}` : blockNumber, | ||
boolean: true, | ||
}), | ||
this.apiKey, | ||
); | ||
} | ||
|
||
@Tool({ | ||
name: "get_token_balance", | ||
description: "Get token balance from Etherscan", | ||
}) | ||
async getTokenBalance( | ||
_walletClient: WalletClientBase, | ||
parameters: TokenBalanceParameters, | ||
): EtherscanResponse<{ status: string; message: string; result: string }> { | ||
const { address, contractAddress, tag, network } = parameters; | ||
return etherscanRequest( | ||
buildUrl(network, "account", "tokenbalance", { | ||
address, | ||
contractaddress: contractAddress, | ||
tag, | ||
}), | ||
this.apiKey, | ||
); | ||
} | ||
|
||
@Tool({ | ||
name: "get_gas_price", | ||
description: "Get current gas price from Etherscan", | ||
}) | ||
async getGasPrice( | ||
_walletClient: WalletClientBase, | ||
parameters: GasPriceParameters, | ||
): EtherscanResponse<{ jsonrpc: string; id: number; result: string }> { | ||
const { network } = parameters; | ||
return etherscanRequest(buildUrl(network, "proxy", "eth_gasPrice", {}), this.apiKey); | ||
} | ||
|
||
@Tool({ | ||
name: "get_event_logs", | ||
description: "Get event logs from Etherscan", | ||
}) | ||
async getEventLogs( | ||
_walletClient: WalletClientBase, | ||
parameters: EventLogsParameters, | ||
): EtherscanResponse<{ | ||
status: string; | ||
message: string; | ||
result: Array<{ | ||
address: string; | ||
topics: string[]; | ||
data: string; | ||
blockNumber: string; | ||
timeStamp: string; | ||
gasPrice: string; | ||
gasUsed: string; | ||
logIndex: string; | ||
transactionHash: string; | ||
transactionIndex: string; | ||
blockHash: string; | ||
}>; | ||
}> { | ||
const { address, fromBlock, toBlock, topic0, topic1, topic2, topic3, network } = parameters; | ||
const params: Record<string, string | number> = { | ||
address, | ||
fromBlock: typeof fromBlock === "number" ? `0x${fromBlock.toString(16)}` : fromBlock, | ||
toBlock: typeof toBlock === "number" ? `0x${toBlock.toString(16)}` : toBlock, | ||
}; | ||
|
||
if (topic0) params.topic0 = topic0; | ||
if (topic1) params.topic1 = topic1; | ||
if (topic2) params.topic2 = topic2; | ||
if (topic3) params.topic3 = topic3; | ||
|
||
return etherscanRequest(buildUrl(network, "logs", "getLogs", params), this.apiKey); | ||
} | ||
} |
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,4 @@ | ||
export * from "./etherscan.plugin"; | ||
export * from "./etherscan.service"; | ||
export * from "./parameters"; | ||
export * from "./request"; |
Oops, something went wrong.