@dilukangelo/web3-ai-skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -0
- package/bin/cli.js +74 -0
- package/package.json +50 -0
- package/template/.agent/ARCHITECTURE.md +129 -0
- package/template/.agent/GEMINI.md +135 -0
- package/template/.agent/agents/contract-auditor.md +161 -0
- package/template/.agent/agents/rust-web3.md +153 -0
- package/template/.agent/agents/solidity-expert.md +164 -0
- package/template/.agent/agents/web3-frontend.md +192 -0
- package/template/.agent/agents/web3-infra.md +155 -0
- package/template/.agent/agents/web3-orchestrator.md +145 -0
- package/template/.agent/skills/clean-code/SKILL.md +142 -0
- package/template/.agent/skills/dapp-patterns/SKILL.md +184 -0
- package/template/.agent/skills/rainbowkit-wagmi/SKILL.md +262 -0
- package/template/.agent/skills/rpc-optimization/SKILL.md +194 -0
- package/template/.agent/skills/rust-smart-contracts/SKILL.md +202 -0
- package/template/.agent/skills/smart-contract-auditing/SKILL.md +198 -0
- package/template/.agent/skills/solidity-patterns/SKILL.md +192 -0
- package/template/.agent/skills/subgraph-indexing/SKILL.md +225 -0
- package/template/.agent/workflows/audit.md +126 -0
- package/template/.agent/workflows/create-contract.md +134 -0
- package/template/.agent/workflows/create-dapp.md +109 -0
- package/template/.agent/workflows/deploy-contract.md +120 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rainbowkit-wagmi
|
|
3
|
+
description: RainbowKit v2, Wagmi v2, viem v2, wallet integration patterns, SIWE, multi-chain configuration, contract interaction hooks. Use when building DApp frontends with wallet connectivity.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# RainbowKit + Wagmi — DApp Wallet Integration
|
|
7
|
+
|
|
8
|
+
Expert knowledge for building Web3 frontend applications with modern wallet integration libraries.
|
|
9
|
+
|
|
10
|
+
## Use this skill when
|
|
11
|
+
|
|
12
|
+
- Setting up wallet connection in a DApp
|
|
13
|
+
- Writing contract interaction hooks with Wagmi
|
|
14
|
+
- Configuring multi-chain support
|
|
15
|
+
- Implementing SIWE (Sign-In with Ethereum)
|
|
16
|
+
- Building transaction UIs with proper state management
|
|
17
|
+
|
|
18
|
+
## Do not use this skill when
|
|
19
|
+
|
|
20
|
+
- Writing smart contracts (use `solidity-patterns` or `rust-smart-contracts`)
|
|
21
|
+
- Setting up RPC infrastructure (use `rpc-optimization`)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Setup (Next.js 15 + App Router)
|
|
26
|
+
|
|
27
|
+
### Installation
|
|
28
|
+
```bash
|
|
29
|
+
npm install @rainbow-me/rainbowkit wagmi viem @tanstack/react-query
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Configuration
|
|
33
|
+
```typescript
|
|
34
|
+
// lib/wagmi.ts
|
|
35
|
+
import { getDefaultConfig } from '@rainbow-me/rainbowkit'
|
|
36
|
+
import { mainnet, polygon, optimism, arbitrum, base } from 'wagmi/chains'
|
|
37
|
+
|
|
38
|
+
export const config = getDefaultConfig({
|
|
39
|
+
appName: 'My DApp',
|
|
40
|
+
projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!, // WalletConnect
|
|
41
|
+
chains: [mainnet, polygon, optimism, arbitrum, base],
|
|
42
|
+
ssr: true,
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Provider Setup
|
|
47
|
+
```typescript
|
|
48
|
+
// app/providers.tsx
|
|
49
|
+
'use client'
|
|
50
|
+
|
|
51
|
+
import { WagmiProvider } from 'wagmi'
|
|
52
|
+
import { RainbowKitProvider, darkTheme } from '@rainbow-me/rainbowkit'
|
|
53
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
54
|
+
import { config } from '@/lib/wagmi'
|
|
55
|
+
import '@rainbow-me/rainbowkit/styles.css'
|
|
56
|
+
|
|
57
|
+
const queryClient = new QueryClient()
|
|
58
|
+
|
|
59
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
60
|
+
return (
|
|
61
|
+
<WagmiProvider config={config}>
|
|
62
|
+
<QueryClientProvider client={queryClient}>
|
|
63
|
+
<RainbowKitProvider theme={darkTheme()}>
|
|
64
|
+
{children}
|
|
65
|
+
</RainbowKitProvider>
|
|
66
|
+
</QueryClientProvider>
|
|
67
|
+
</WagmiProvider>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Root Layout
|
|
73
|
+
```typescript
|
|
74
|
+
// app/layout.tsx
|
|
75
|
+
import { Providers } from './providers'
|
|
76
|
+
|
|
77
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
78
|
+
return (
|
|
79
|
+
<html lang="en">
|
|
80
|
+
<body>
|
|
81
|
+
<Providers>{children}</Providers>
|
|
82
|
+
</body>
|
|
83
|
+
</html>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Contract Interaction Patterns
|
|
91
|
+
|
|
92
|
+
### Reading Contract Data
|
|
93
|
+
```typescript
|
|
94
|
+
import { useReadContract, useReadContracts } from 'wagmi'
|
|
95
|
+
import { formatEther } from 'viem'
|
|
96
|
+
|
|
97
|
+
// Single read
|
|
98
|
+
const { data: balance } = useReadContract({
|
|
99
|
+
address: TOKEN_ADDRESS,
|
|
100
|
+
abi: erc20Abi,
|
|
101
|
+
functionName: 'balanceOf',
|
|
102
|
+
args: [userAddress],
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Batch reads (Multicall under the hood)
|
|
106
|
+
const { data: results } = useReadContracts({
|
|
107
|
+
contracts: [
|
|
108
|
+
{ address: TOKEN, abi: erc20Abi, functionName: 'name' },
|
|
109
|
+
{ address: TOKEN, abi: erc20Abi, functionName: 'symbol' },
|
|
110
|
+
{ address: TOKEN, abi: erc20Abi, functionName: 'totalSupply' },
|
|
111
|
+
],
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Writing to Contracts
|
|
116
|
+
```typescript
|
|
117
|
+
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
|
|
118
|
+
import { parseEther } from 'viem'
|
|
119
|
+
|
|
120
|
+
function MintButton() {
|
|
121
|
+
const { writeContract, data: hash, isPending } = useWriteContract()
|
|
122
|
+
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<button
|
|
126
|
+
disabled={isPending || isConfirming}
|
|
127
|
+
onClick={() =>
|
|
128
|
+
writeContract({
|
|
129
|
+
address: NFT_ADDRESS,
|
|
130
|
+
abi: nftAbi,
|
|
131
|
+
functionName: 'mint',
|
|
132
|
+
args: [1n],
|
|
133
|
+
value: parseEther('0.05'),
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
>
|
|
137
|
+
{isPending ? 'Confirm in wallet...' :
|
|
138
|
+
isConfirming ? 'Confirming...' :
|
|
139
|
+
isSuccess ? 'Minted!' : 'Mint NFT'}
|
|
140
|
+
</button>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Event Watching
|
|
146
|
+
```typescript
|
|
147
|
+
import { useWatchContractEvent } from 'wagmi'
|
|
148
|
+
|
|
149
|
+
useWatchContractEvent({
|
|
150
|
+
address: TOKEN_ADDRESS,
|
|
151
|
+
abi: erc20Abi,
|
|
152
|
+
eventName: 'Transfer',
|
|
153
|
+
onLogs(logs) {
|
|
154
|
+
logs.forEach(log => {
|
|
155
|
+
console.log('Transfer:', log.args.from, '→', log.args.to, log.args.value)
|
|
156
|
+
})
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Account & Chain Hooks
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { useAccount, useChainId, useSwitchChain, useBalance } from 'wagmi'
|
|
167
|
+
|
|
168
|
+
// Current wallet state
|
|
169
|
+
const { address, isConnected, chain } = useAccount()
|
|
170
|
+
|
|
171
|
+
// Native balance
|
|
172
|
+
const { data: balance } = useBalance({ address })
|
|
173
|
+
|
|
174
|
+
// Chain switching
|
|
175
|
+
const { switchChain } = useSwitchChain()
|
|
176
|
+
switchChain({ chainId: base.id })
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## SIWE (Sign-In with Ethereum)
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { useSignMessage } from 'wagmi'
|
|
185
|
+
import { SiweMessage } from 'siwe'
|
|
186
|
+
|
|
187
|
+
const { signMessageAsync } = useSignMessage()
|
|
188
|
+
|
|
189
|
+
async function signIn() {
|
|
190
|
+
const message = new SiweMessage({
|
|
191
|
+
domain: window.location.host,
|
|
192
|
+
address: userAddress,
|
|
193
|
+
statement: 'Sign in to My DApp',
|
|
194
|
+
uri: window.location.origin,
|
|
195
|
+
version: '1',
|
|
196
|
+
chainId: chainId,
|
|
197
|
+
nonce: await fetchNonce(), // from your backend
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
const signature = await signMessageAsync({
|
|
201
|
+
message: message.prepareMessage(),
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Send signature + message to backend for verification
|
|
205
|
+
await verifyOnBackend(message, signature)
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Custom Chain Configuration
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { defineChain } from 'viem'
|
|
215
|
+
|
|
216
|
+
export const monad = defineChain({
|
|
217
|
+
id: 10143,
|
|
218
|
+
name: 'Monad Testnet',
|
|
219
|
+
nativeCurrency: { name: 'Monad', symbol: 'MON', decimals: 18 },
|
|
220
|
+
rpcUrls: {
|
|
221
|
+
default: { http: ['https://testnet-rpc.monad.xyz'] },
|
|
222
|
+
},
|
|
223
|
+
blockExplorers: {
|
|
224
|
+
default: { name: 'Monad Explorer', url: 'https://testnet.monadexplorer.com' },
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Alternative Wallet Libraries
|
|
232
|
+
|
|
233
|
+
### ConnectKit
|
|
234
|
+
```bash
|
|
235
|
+
npm install connectkit wagmi viem
|
|
236
|
+
```
|
|
237
|
+
Minimal, focused wallet modal with great defaults.
|
|
238
|
+
|
|
239
|
+
### Privy
|
|
240
|
+
```bash
|
|
241
|
+
npm install @privy-io/react-auth
|
|
242
|
+
```
|
|
243
|
+
Embedded wallets, email/social login for Web2 → Web3 onboarding.
|
|
244
|
+
|
|
245
|
+
### Dynamic
|
|
246
|
+
```bash
|
|
247
|
+
npm install @dynamic-labs/sdk-react-core
|
|
248
|
+
```
|
|
249
|
+
Enterprise-grade, supports MPC wallets.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Anti-Patterns
|
|
254
|
+
|
|
255
|
+
| ❌ Don't | ✅ Do |
|
|
256
|
+
|----------|-------|
|
|
257
|
+
| Use ethers.js | Use viem (type-safe, tree-shakeable) |
|
|
258
|
+
| Hardcode chain IDs | Use wagmi chain objects |
|
|
259
|
+
| Skip pending states | Show full tx lifecycle |
|
|
260
|
+
| Ignore wallet errors | Catch and display user-friendly messages |
|
|
261
|
+
| Fetch on every render | Use TanStack Query caching |
|
|
262
|
+
| Store wallet state manually | Let wagmi manage it |
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rpc-optimization
|
|
3
|
+
description: RPC provider management, Multicall3 batching, WebSocket streaming, MEV protection, retry strategies, load balancing, and caching for Web3 applications.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# RPC Optimization — Web3 Data Layer Performance
|
|
7
|
+
|
|
8
|
+
Expert knowledge for optimizing RPC communication between DApps and blockchains.
|
|
9
|
+
|
|
10
|
+
## Use this skill when
|
|
11
|
+
|
|
12
|
+
- Setting up RPC providers and failover
|
|
13
|
+
- Implementing Multicall3 for batch reads
|
|
14
|
+
- Optimizing DApp load times and data fetching
|
|
15
|
+
- Implementing MEV protection for transactions
|
|
16
|
+
- Setting up WebSocket event streaming
|
|
17
|
+
- Configuring multi-chain RPC infrastructure
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Multicall3
|
|
22
|
+
|
|
23
|
+
### Universal Address
|
|
24
|
+
All EVM chains: `0xcA11bde05977b3631167028862bE2a173976CA11`
|
|
25
|
+
|
|
26
|
+
### viem Multicall
|
|
27
|
+
```typescript
|
|
28
|
+
import { multicall } from 'viem/actions'
|
|
29
|
+
|
|
30
|
+
const results = await multicall(client, {
|
|
31
|
+
contracts: [
|
|
32
|
+
{ address: token, abi: erc20Abi, functionName: 'balanceOf', args: [user] },
|
|
33
|
+
{ address: token, abi: erc20Abi, functionName: 'totalSupply' },
|
|
34
|
+
{ address: nft, abi: erc721Abi, functionName: 'ownerOf', args: [1n] },
|
|
35
|
+
],
|
|
36
|
+
allowFailure: true, // Don't revert if one call fails
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// results[0].result → balance
|
|
40
|
+
// results[1].result → totalSupply
|
|
41
|
+
// results[2].result → owner
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Batch Size Guidelines
|
|
45
|
+
| Chain | Recommended Batch | Max Batch |
|
|
46
|
+
|-------|------------------|-----------|
|
|
47
|
+
| Ethereum | 50-100 calls | ~500 |
|
|
48
|
+
| Polygon | 100-200 calls | ~1000 |
|
|
49
|
+
| Arbitrum | 100-200 calls | ~1000 |
|
|
50
|
+
| Base | 100-200 calls | ~1000 |
|
|
51
|
+
| Solana | Use getProgramAccounts | N/A |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Provider Configuration
|
|
56
|
+
|
|
57
|
+
### Multi-Provider Failover
|
|
58
|
+
```typescript
|
|
59
|
+
import { createClient, http, fallback } from 'viem'
|
|
60
|
+
|
|
61
|
+
const client = createClient({
|
|
62
|
+
chain: mainnet,
|
|
63
|
+
transport: fallback([
|
|
64
|
+
http('https://eth-mainnet.g.alchemy.com/v2/KEY'),
|
|
65
|
+
http('https://mainnet.infura.io/v3/KEY'),
|
|
66
|
+
http('https://rpc.ankr.com/eth'),
|
|
67
|
+
], {
|
|
68
|
+
rank: true, // Auto-rank by latency
|
|
69
|
+
retryCount: 3, // Retry failed requests
|
|
70
|
+
retryDelay: 150, // ms between retries
|
|
71
|
+
}),
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### WebSocket for Real-Time
|
|
76
|
+
```typescript
|
|
77
|
+
import { createPublicClient, webSocket } from 'viem'
|
|
78
|
+
|
|
79
|
+
const wsClient = createPublicClient({
|
|
80
|
+
chain: mainnet,
|
|
81
|
+
transport: webSocket('wss://eth-mainnet.g.alchemy.com/v2/KEY'),
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Watch for new blocks
|
|
85
|
+
const unwatch = wsClient.watchBlocks({
|
|
86
|
+
onBlock: (block) => console.log('New block:', block.number),
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Watch contract events
|
|
90
|
+
wsClient.watchContractEvent({
|
|
91
|
+
address: contractAddress,
|
|
92
|
+
abi: contractAbi,
|
|
93
|
+
eventName: 'Transfer',
|
|
94
|
+
onLogs: (logs) => handleTransferEvents(logs),
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Caching Strategies
|
|
101
|
+
|
|
102
|
+
### Block Data Caching
|
|
103
|
+
```typescript
|
|
104
|
+
// Confirmed blocks are immutable — cache aggressively
|
|
105
|
+
const CACHE_DURATION = {
|
|
106
|
+
latestBlock: 12_000, // 12s (1 block)
|
|
107
|
+
confirmedBlock: Infinity, // Never expires
|
|
108
|
+
pendingTx: 0, // Never cache
|
|
109
|
+
balance: 15_000, // 15s
|
|
110
|
+
contractState: 30_000, // 30s
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### TanStack Query Integration
|
|
115
|
+
```typescript
|
|
116
|
+
const { data } = useReadContract({
|
|
117
|
+
...contractConfig,
|
|
118
|
+
query: {
|
|
119
|
+
staleTime: 30_000, // Consider fresh for 30s
|
|
120
|
+
gcTime: 5 * 60_000, // Keep in cache for 5min
|
|
121
|
+
refetchInterval: 15_000, // Refresh every 15s
|
|
122
|
+
},
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## MEV Protection
|
|
129
|
+
|
|
130
|
+
### Transaction Submission
|
|
131
|
+
```typescript
|
|
132
|
+
import { createWalletClient, http } from 'viem'
|
|
133
|
+
|
|
134
|
+
// Use Flashbots Protect RPC for private submission
|
|
135
|
+
const protectedClient = createWalletClient({
|
|
136
|
+
chain: mainnet,
|
|
137
|
+
transport: http('https://rpc.flashbots.net'),
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
// Transaction goes to private mempool → no sandwich attacks
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Protection Methods
|
|
144
|
+
| Method | Provider | Chains |
|
|
145
|
+
|--------|----------|--------|
|
|
146
|
+
| **Flashbots Protect** | Flashbots | Ethereum |
|
|
147
|
+
| **MEV Blocker** | CoW Protocol | Ethereum |
|
|
148
|
+
| **Bloxroute** | Bloxroute | Ethereum, BSC |
|
|
149
|
+
| **Private RPCs** | Direct builder | Various |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Rate Limiting & Error Handling
|
|
154
|
+
|
|
155
|
+
### Retry with Backoff
|
|
156
|
+
```typescript
|
|
157
|
+
async function rpcCallWithRetry<T>(
|
|
158
|
+
fn: () => Promise<T>,
|
|
159
|
+
maxRetries = 3,
|
|
160
|
+
baseDelay = 1000
|
|
161
|
+
): Promise<T> {
|
|
162
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
163
|
+
try {
|
|
164
|
+
return await fn()
|
|
165
|
+
} catch (error) {
|
|
166
|
+
if (attempt === maxRetries) throw error
|
|
167
|
+
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 500
|
|
168
|
+
await new Promise(resolve => setTimeout(resolve, delay))
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
throw new Error('Unreachable')
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Common RPC Error Codes
|
|
176
|
+
| Code | Meaning | Action |
|
|
177
|
+
|------|---------|--------|
|
|
178
|
+
| -32005 | Rate limited | Backoff and retry |
|
|
179
|
+
| -32000 | Execution error | Check tx params |
|
|
180
|
+
| -32602 | Invalid params | Fix request format |
|
|
181
|
+
| -32603 | Internal error | Try different provider |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Provider Comparison (2026)
|
|
186
|
+
|
|
187
|
+
| Provider | Free Tier | Best Feature |
|
|
188
|
+
|----------|-----------|-------------|
|
|
189
|
+
| **Alchemy** | 300M CU/month | Enhanced APIs, NFT data |
|
|
190
|
+
| **Infura** | 100K req/day | MetaMask integration |
|
|
191
|
+
| **QuickNode** | 10M credits | Addon marketplace |
|
|
192
|
+
| **Ankr** | Limited free | Decentralized |
|
|
193
|
+
| **dRPC** | Pay-per-use | Multi-provider |
|
|
194
|
+
| **Chainstack** | 3M req/month | Dedicated nodes |
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rust-smart-contracts
|
|
3
|
+
description: Rust blockchain program development for Solana (Anchor), CosmWasm, and Arbitrum Stylus. Account model, PDAs, CPI, compute budget optimization.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rust Smart Contracts — Solana, CosmWasm, Stylus
|
|
7
|
+
|
|
8
|
+
Expert knowledge for building secure, efficient blockchain programs in Rust.
|
|
9
|
+
|
|
10
|
+
## Use this skill when
|
|
11
|
+
|
|
12
|
+
- Writing Solana programs (Anchor or native)
|
|
13
|
+
- Building CosmWasm smart contracts
|
|
14
|
+
- Developing Arbitrum Stylus contracts
|
|
15
|
+
- Optimizing compute units on Solana
|
|
16
|
+
- Implementing cross-program invocations (CPI)
|
|
17
|
+
|
|
18
|
+
## Do not use this skill when
|
|
19
|
+
|
|
20
|
+
- Writing Solidity/EVM contracts (use `solidity-patterns`)
|
|
21
|
+
- Building DApp frontends (use `rainbowkit-wagmi`)
|
|
22
|
+
|
|
23
|
+
## Instructions
|
|
24
|
+
|
|
25
|
+
1. Identify the target runtime (Solana, CosmWasm, Stylus)
|
|
26
|
+
2. Design account/state structure appropriate for the runtime
|
|
27
|
+
3. Implement with proper validation and error handling
|
|
28
|
+
4. Write comprehensive tests
|
|
29
|
+
5. Deploy and verify
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Solana / Anchor Patterns
|
|
34
|
+
|
|
35
|
+
### Account Validation
|
|
36
|
+
```rust
|
|
37
|
+
#[derive(Accounts)]
|
|
38
|
+
pub struct Transfer<'info> {
|
|
39
|
+
#[account(mut, has_one = authority)]
|
|
40
|
+
pub from: Account<'info, TokenAccount>,
|
|
41
|
+
|
|
42
|
+
#[account(mut)]
|
|
43
|
+
pub to: Account<'info, TokenAccount>,
|
|
44
|
+
|
|
45
|
+
pub authority: Signer<'info>,
|
|
46
|
+
|
|
47
|
+
/// CHECK: Validated by constraint
|
|
48
|
+
#[account(
|
|
49
|
+
seeds = [b"vault", from.key().as_ref()],
|
|
50
|
+
bump = vault.bump,
|
|
51
|
+
)]
|
|
52
|
+
pub vault: Account<'info, Vault>,
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### PDA Derivation
|
|
57
|
+
```rust
|
|
58
|
+
// Deterministic address from seeds
|
|
59
|
+
let (pda, bump) = Pubkey::find_program_address(
|
|
60
|
+
&[b"user-stats", user.key().as_ref()],
|
|
61
|
+
program_id,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// In Anchor
|
|
65
|
+
#[account(
|
|
66
|
+
init,
|
|
67
|
+
payer = user,
|
|
68
|
+
space = 8 + UserStats::INIT_SPACE,
|
|
69
|
+
seeds = [b"user-stats", user.key().as_ref()],
|
|
70
|
+
bump,
|
|
71
|
+
)]
|
|
72
|
+
pub user_stats: Account<'info, UserStats>,
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Cross-Program Invocation (CPI)
|
|
76
|
+
```rust
|
|
77
|
+
// Transfer SOL via CPI
|
|
78
|
+
let cpi_context = CpiContext::new(
|
|
79
|
+
ctx.accounts.system_program.to_account_info(),
|
|
80
|
+
system_program::Transfer {
|
|
81
|
+
from: ctx.accounts.payer.to_account_info(),
|
|
82
|
+
to: ctx.accounts.recipient.to_account_info(),
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
system_program::transfer(cpi_context, amount)?;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Compute Unit Optimization
|
|
89
|
+
| Technique | CU Savings |
|
|
90
|
+
|-----------|-----------|
|
|
91
|
+
| Avoid `msg!` in production | ~100 CU each |
|
|
92
|
+
| Use `zero_copy` for large accounts | Significant |
|
|
93
|
+
| Minimize account validations | ~200 CU per check |
|
|
94
|
+
| Use `Box<Account<>>` for large accounts | Stack savings |
|
|
95
|
+
| Batch operations in single ix | Reduced overhead |
|
|
96
|
+
|
|
97
|
+
### Security Checklist
|
|
98
|
+
- ✅ Validate all account ownership
|
|
99
|
+
- ✅ Check signer authority
|
|
100
|
+
- ✅ Validate PDA seeds match expected
|
|
101
|
+
- ✅ Prevent reinitialization
|
|
102
|
+
- ✅ Use checked arithmetic
|
|
103
|
+
- ✅ Close accounts properly (return rent)
|
|
104
|
+
- ✅ Validate token mint matches expected
|
|
105
|
+
- ❌ Never trust account data without validation
|
|
106
|
+
- ❌ Never use unchecked arithmetic
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## CosmWasm Patterns
|
|
111
|
+
|
|
112
|
+
### Message Types
|
|
113
|
+
```rust
|
|
114
|
+
#[cw_serde]
|
|
115
|
+
pub enum ExecuteMsg {
|
|
116
|
+
Transfer { recipient: String, amount: Uint128 },
|
|
117
|
+
UpdateConfig { admin: Option<String> },
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#[cw_serde]
|
|
121
|
+
#[derive(QueryResponses)]
|
|
122
|
+
pub enum QueryMsg {
|
|
123
|
+
#[returns(BalanceResponse)]
|
|
124
|
+
Balance { address: String },
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Entry Points
|
|
129
|
+
```rust
|
|
130
|
+
#[entry_point]
|
|
131
|
+
pub fn instantiate(
|
|
132
|
+
deps: DepsMut,
|
|
133
|
+
_env: Env,
|
|
134
|
+
info: MessageInfo,
|
|
135
|
+
msg: InstantiateMsg,
|
|
136
|
+
) -> Result<Response, ContractError> {
|
|
137
|
+
let state = State { owner: info.sender, count: msg.count };
|
|
138
|
+
STATE.save(deps.storage, &state)?;
|
|
139
|
+
Ok(Response::new().add_attribute("method", "instantiate"))
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### IBC (Inter-Blockchain Communication)
|
|
144
|
+
- Implement `ibc_channel_open`, `ibc_channel_connect`
|
|
145
|
+
- Handle `ibc_packet_receive` for cross-chain messages
|
|
146
|
+
- Use `IbcMsg::Transfer` for token transfers
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Stylus (Rust on EVM)
|
|
151
|
+
|
|
152
|
+
### Key Pattern
|
|
153
|
+
```rust
|
|
154
|
+
use stylus_sdk::{alloy_primitives::*, prelude::*};
|
|
155
|
+
|
|
156
|
+
#[storage]
|
|
157
|
+
pub struct Counter {
|
|
158
|
+
count: StorageU256,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#[public]
|
|
162
|
+
impl Counter {
|
|
163
|
+
pub fn increment(&mut self) {
|
|
164
|
+
let count = self.count.get() + U256::from(1);
|
|
165
|
+
self.count.set(count);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
pub fn get(&self) -> U256 {
|
|
169
|
+
self.count.get()
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Testing
|
|
177
|
+
|
|
178
|
+
### Anchor Tests (TypeScript)
|
|
179
|
+
```typescript
|
|
180
|
+
it("initializes correctly", async () => {
|
|
181
|
+
await program.methods
|
|
182
|
+
.initialize()
|
|
183
|
+
.accounts({ state: statePda, user: provider.wallet.publicKey })
|
|
184
|
+
.rpc();
|
|
185
|
+
|
|
186
|
+
const state = await program.account.state.fetch(statePda);
|
|
187
|
+
expect(state.count.toNumber()).to.equal(0);
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Rust Unit Tests
|
|
192
|
+
```rust
|
|
193
|
+
#[cfg(test)]
|
|
194
|
+
mod tests {
|
|
195
|
+
use super::*;
|
|
196
|
+
|
|
197
|
+
#[test]
|
|
198
|
+
fn test_process_instruction() {
|
|
199
|
+
// Use solana-program-test or bankrun
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|