@osiris-ai/web3-evm-sdk
Version:
Osiris Web3 EVM SDK
375 lines (305 loc) • 11.2 kB
Markdown
# -ai/web3-evm-sdk
EVM wallet client for building Web3 MCPs with secure blockchain operations and policy enforcement.
[](https://badge.fury.io/js/@osiris-ai%2Fweb3-evm-sdk)
[](https://opensource.org/licenses/MIT)
## Overview
The EVM SDK provides secure blockchain wallet operations for the [Osiris](https://docs.osirislabs.xyz) ecosystem. Build powerful Web3 MCPs that can interact with Ethereum and EVM-compatible chains with enterprise-grade security, policy enforcement, and zero-configuration wallet management.
**Key Features:**
- 🔐 **Policy Enforcement** - User-defined transaction policies automatically validated
- 💼 **Embedded Wallets** - Turnkey-powered enterprise wallet infrastructure
- ⛓️ **Multi-Chain Support** - Ethereum, Polygon, BSC, and other EVM chains
- 📝 **Viem Integration** - Full compatibility with viem for Web3 operations
- 🛡️ **Enterprise Security** - Hardware-backed signing with HSM support
- 📝 **Full TypeScript** - Complete type safety and IDE support
## Installation
```bash
npm install -ai/web3-evm-sdk @osiris-ai/sdk viem
```
## Quick Start
### Wallet Operations
```typescript
import { createMcpServer, getAuthContext } from '-ai/sdk';
import { EVMWalletClient } from '-ai/web3-evm-sdk';
import { createSuccessResponse, createErrorResponse } from '../utils/types.js';
import { z } from 'zod';
await createMcpServer({
name: 'web3-mcp',
version: '1.0.0',
auth: {
useHub: true,
hubConfig: {
baseUrl: process.env.HUB_BASE_URL!,
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET!,
}
},
configure: (server) => {
// Get user wallet addresses
server.tool(
'get_wallet_addresses',
'Get available wallet addresses',
{},
async () => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
const walletClient = new EVMWalletClient(
process.env.HUB_BASE_URL!,
token.access_token,
context.deploymentId
);
const walletRecords = await walletClient.getWalletRecords();
const addresses = walletRecords.map(record =>
record.accounts.addresses.map(addr => ({
address: addr.address,
chains: addr.chains
}))
).flat();
return createSuccessResponse('Wallet addresses retrieved', { addresses });
} catch (error) {
return createErrorResponse(error);
}
}
);
// Sign message
server.tool(
'sign_message',
'Sign a message with user wallet',
{
message: z.string(),
address: z.string(),
chainId: z.string().default('evm:eip155:1')
},
async ({ message, address, chainId }) => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
const walletClient = new EVMWalletClient(
process.env.HUB_BASE_URL!,
token.access_token,
context.deploymentId
);
const signature = await walletClient.signMessage(message, chainId, address);
return createSuccessResponse('Message signed successfully', { signature });
} catch (error) {
return createErrorResponse(error);
}
}
);
}
});
```
## API Reference
### EVMWalletClient
Main wallet client for EVM blockchain operations.
```typescript
class EVMWalletClient
```
#### Constructor
```typescript
new EVMWalletClient(hubBaseUrl: string, accessToken: string, deploymentId: string)
```
#### Methods
##### `getWalletRecords(): Promise<Wallet[]>`
Get user's available wallet records with addresses and supported chains.
##### `signMessage(message: string, chainId: string, walletAddress: string): Promise<string>`
Sign a message with the specified wallet address.
##### `signTransaction(abi: any, transaction: any, chainId: string, walletAddress: string): Promise<string>`
Sign a transaction with automatic policy validation.
##### `signTypedData(typedData: any, chainId: string, walletAddress: string, metadata?: any): Promise<string>`
Sign typed data (EIP-712) for smart contract interactions.
##### `getViemAccount(address: string, chainId: string): Promise<Account>`
Get a viem-compatible account for use with viem client operations.
## Usage Examples
### Token Transfer
```typescript
server.tool(
'transfer_tokens',
'Transfer tokens to another address',
{
to: z.string(),
amount: z.string(),
tokenAddress: z.string().optional(),
walletAddress: z.string()
},
async ({ to, amount, tokenAddress, walletAddress }) => {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
const walletClient = new EVMWalletClient(
process.env.HUB_BASE_URL!,
token.access_token,
context.deploymentId
);
try {
// Get viem account for transaction
const account = await walletClient.getViemAccount(walletAddress, 'evm:eip155:1');
// Build transaction (simplified)
const transaction = {
to: to as `0x${string}`,
value: BigInt(amount),
// Add gas estimation and other transaction fields
};
// Sign transaction (policy validation happens automatically)
const signedTx = await walletClient.signTransaction(
[], // ERC20 ABI needed
transaction,
'evm:eip155:1',
walletAddress
);
return createSuccessResponse('Transaction signed', { signedTransaction: signedTx });
} catch (error) {
if (error.message.includes('policy')) {
return createErrorResponse(`Transaction blocked by wallet policy: ${error.message}`);
}
return createErrorResponse(`Transfer failed: ${error.message}`);
}
}
);
```
### Smart Contract Interaction
```typescript
server.tool(
'interact_with_contract',
'Interact with a smart contract',
{
contractAddress: z.string(),
functionName: z.string(),
args: z.array(z.any()),
walletAddress: z.string()
},
async ({ contractAddress, functionName, args, walletAddress }) => {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("User not authenticated");
}
const walletClient = new EVMWalletClient(
process.env.HUB_BASE_URL!,
token.access_token,
context.deploymentId
);
try {
// Example: ERC-20 transfer
const typedData = {
domain: {
name: 'Token Contract',
version: '1',
chainId: 1,
verifyingContract: contractAddress
},
types: {
Transfer: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' }
]
},
message: {
to: args[0],
amount: args[1]
}
};
const signature = await walletClient.signTypedData(
typedData,
'evm:eip155:1',
walletAddress
);
return createSuccessResponse('Contract interaction signed', { signature });
} catch (error) {
return createErrorResponse(`Contract interaction failed: ${error.message}`);
}
}
);
```
## Policy Enforcement
All wallet operations are automatically validated against user-defined policies:
```typescript
// Users set policies in their wallet settings:
// - Maximum transaction amount
// - Allowed recipient addresses
// - Smart contract interaction rules
// - Time-based restrictions
// - Multi-signature requirements
// Example policy validation scenarios:
server.tool('policy_aware_transfer', 'Transfer with policy validation', schema, async (params) => {
try {
// Transaction is automatically validated against:
// 1. Amount limits (e.g., max $1000 per transaction)
// 2. Recipient whitelist (only approved addresses)
// 3. Time restrictions (e.g., no transfers on weekends)
// 4. Velocity limits (e.g., max $5000 per day)
const result = await walletClient.signTransaction(/* transaction */);
return createSuccessResponse('Transaction approved by policies', result);
} catch (error) {
if (error.message.includes('amount exceeds limit')) {
return createErrorResponse('🚫 Transaction amount exceeds your daily limit');
}
if (error.message.includes('recipient not approved')) {
return createErrorResponse('🚫 Recipient address not in your approved list');
}
return createErrorResponse(`Policy violation: ${error.message}`);
}
});
```
## Error Handling
```typescript
server.tool('robust_wallet_tool', 'Wallet tool with error handling', schema, async (params) => {
try {
const { token, context } = getAuthContext("osiris");
if (!token || !context) {
return createErrorResponse("🔐 Please connect your wallet first");
}
const walletClient = new EVMWalletClient(
process.env.HUB_BASE_URL!,
token.access_token,
context.deploymentId
);
const walletRecords = await walletClient.getWalletRecords();
return createSuccessResponse('Wallet data retrieved', walletRecords);
} catch (error: any) {
if (error.message.includes('No wallet record found')) {
return createErrorResponse("❌ No wallet found. Please create a wallet first.");
}
if (error.message.includes('policy')) {
return createErrorResponse(`🚫 Operation blocked: ${error.message}`);
}
if (error.message.includes('insufficient funds')) {
return createErrorResponse("💰 Insufficient funds for this transaction.");
}
return createErrorResponse(`Wallet error: ${error.message}`);
}
});
```
## Getting Started
1. **Install the Osiris CLI:**
```bash
npm install -g -ai/cli
```
2. **Set up authentication:**
```bash
npx -ai/cli register
npx -ai/cli create-client
npx -ai/cli connect-auth
```
3. **Create your Web3 MCP:**
```bash
npx -ai/cli create-mcp my-web3-mcp
```
4. **Add Web3 integration:**
```bash
npm install -ai/web3-evm-sdk viem
```
## Contributing
We welcome contributions! Please see our [Contributing Guide](https://github.com/osiris-labs/osiris/blob/main/CONTRIBUTING.md) for details.
## Support
- **Documentation:** [https://docs.osirislabs.xyz](https://docs.osirislabs.xyz)
- **GitHub Issues:** [https://github.com/fetcchx/osiris-ai/issues](https://github.com/fetcchx/osiris-ai/issues)
- **Discord Community:** [Join our Discord](https://discord.osirislabs.xyz)
## License
MIT License - see [LICENSE](LICENSE) file for details.
---
Built with ❤️ by the [Osiris Labs](https://osirislabs.xyz) team.