|
| 1 | +# AGENTS.md - snap-7715-permissions Project Guide |
| 2 | + |
| 3 | +This document provides agents with essential information for working on the snap-7715-permissions monorepo. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +This is a monorepo implementing ERC-7715 permissions for MetaMask Snaps. It contains: |
| 8 | + |
| 9 | +- **@metamask/permissions-kernel-snap** (`packages/permissions-kernel-snap`) - Kernel snap managing the permissions offer registry |
| 10 | +- **@metamask/gator-permissions-snap** (`packages/gator-permissions-snap`) - DeleGator permissions snap that creates delegation accounts |
| 11 | +- **@metamask/shared** (`packages/shared`) - Shared utilities, types, constants, and testing utilities across snaps |
| 12 | +- **Development/test site** (`packages/site`) - Local testing environment for dApp development |
| 13 | + |
| 14 | +### Architecture Summary |
| 15 | + |
| 16 | +- **Kernel Snap**: Manages a `permissions offer registry` listing all permissions a user is willing to grant via ERC-7715 requests |
| 17 | +- **Gator Snap**: Creates DeleGator accounts and enables sites to request ERC-7715 permissions with user review via custom UI dialogs |
| 18 | + |
| 19 | +## Technology Stack |
| 20 | + |
| 21 | +- **Node Version**: 20.x or 22.x (see `.nvmrc`: `^20 || >=22`) |
| 22 | +- **Package Manager**: Yarn 4.10.1 with workspaces |
| 23 | +- **Language**: TypeScript 5.8.3 (strict mode) |
| 24 | +- **Testing**: Jest with @metamask/snaps-jest |
| 25 | +- **Linting**: ESLint 9 (flat config - `eslint.config.mjs`) |
| 26 | +- **Formatting**: Prettier 3.6.2 |
| 27 | +- **Runtime**: MetaMask Snaps (Flask >= 12.14.2) |
| 28 | + |
| 29 | +## Package Structure |
| 30 | + |
| 31 | +``` |
| 32 | +packages/ |
| 33 | +├── permissions-kernel-snap/ # Kernel snap (port 8081) |
| 34 | +├── gator-permissions-snap/ # DeleGator snap (port 8082) |
| 35 | +├── shared/ # Shared utilities and types |
| 36 | +└── site/ # Development testing site (port 8000) |
| 37 | +``` |
| 38 | + |
| 39 | +## Prerequisites |
| 40 | + |
| 41 | +1. **MetaMask Flask**: >= 12.14.2 |
| 42 | +2. **Node.js**: 20.x or 22.x (use `nvm use` to switch) |
| 43 | +3. **Yarn**: 4.10.1 (managed via `packageManager` field) |
| 44 | +4. **Environment variables**: See `.env.example` in each package |
| 45 | + |
| 46 | +## Quick Start |
| 47 | + |
| 48 | +```bash |
| 49 | +# Install dependencies and set up snap submodules |
| 50 | +yarn prepare:snap |
| 51 | + |
| 52 | +# Build all packages |
| 53 | +yarn build |
| 54 | + |
| 55 | +# Start development servers (site on port 8000, snaps on 8081/8082) |
| 56 | +yarn start |
| 57 | +``` |
| 58 | + |
| 59 | +## Build Commands |
| 60 | + |
| 61 | +```bash |
| 62 | +# Install dependencies |
| 63 | +yarn install |
| 64 | + |
| 65 | +# Full setup with snap submodules |
| 66 | +yarn prepare:snap |
| 67 | + |
| 68 | +# Build all packages (parallel, topological) |
| 69 | +yarn build |
| 70 | + |
| 71 | +# Build and pack for distribution |
| 72 | +yarn build:pack |
| 73 | + |
| 74 | +# Update and validate changelogs |
| 75 | +yarn changelog:update |
| 76 | +yarn changelog:validate |
| 77 | +``` |
| 78 | + |
| 79 | +## Development Commands |
| 80 | + |
| 81 | +```bash |
| 82 | +# Start all development servers |
| 83 | +yarn start |
| 84 | +# Access at http://localhost:8000/ |
| 85 | +# - permissions-kernel-snap: local:http://localhost:8081 |
| 86 | +# - gator-permissions-snap: local:http://localhost:8082 |
| 87 | + |
| 88 | +# Run tests (parallel across workspaces) |
| 89 | +yarn test |
| 90 | + |
| 91 | +# Watch mode for tests |
| 92 | +yarn test --watch |
| 93 | + |
| 94 | +# Run tests in specific package |
| 95 | +yarn workspace @metamask/gator-permissions-snap test |
| 96 | + |
| 97 | +# Linting |
| 98 | +yarn lint # Run all linters |
| 99 | +yarn lint:eslint # ESLint only |
| 100 | +yarn lint:fix # Fix linting issues |
| 101 | +yarn lint:misc # Check markdown, JSON, etc. |
| 102 | +``` |
| 103 | + |
| 104 | +## Environment Variables |
| 105 | + |
| 106 | +All packages throw build-time errors if required env vars are missing. Check `.env.example` in each package. |
| 107 | + |
| 108 | +### Common Variables |
| 109 | + |
| 110 | +| Variable | Description | Package | |
| 111 | +|----------|-------------|---------| |
| 112 | +| `SNAP_ENV` | Environment (development/production) | All | |
| 113 | +| `KERNEL_SNAP_ID` | Snap ID of permissions kernel snap | gator-snap | |
| 114 | +| `STORE_PERMISSIONS_ENABLED` | Feature flag for storage ("true"/"false") | All snaps | |
| 115 | + |
| 116 | +### Package-Specific Setup |
| 117 | + |
| 118 | +Each package requires specific environment variables. See: |
| 119 | +- `packages/permissions-kernel-snap/.env.example` |
| 120 | +- `packages/gator-permissions-snap/.env.example` |
| 121 | +- `packages/site/.env.example` |
| 122 | + |
| 123 | +## Code Style and Standards |
| 124 | + |
| 125 | +### Formatting Requirements |
| 126 | + |
| 127 | +- **Prettier** automatically formats code with: |
| 128 | + - Single quotes (`'`) |
| 129 | + - 2-space indentation |
| 130 | + - Trailing commas throughout |
| 131 | + - Quote props as-needed |
| 132 | + |
| 133 | +All code must be formatted before committing. Run `yarn lint:fix` to auto-fix. |
| 134 | + |
| 135 | +### TypeScript Strictness |
| 136 | + |
| 137 | +The project uses strict TypeScript configuration: |
| 138 | + |
| 139 | +- `strict: true` - Full type checking |
| 140 | +- `exactOptionalPropertyTypes: true` - No implicit undefined on optional properties |
| 141 | +- `noUncheckedIndexedAccess: true` - Require null checks on indexed access |
| 142 | +- Target: ES2020, Module: Node16 |
| 143 | + |
| 144 | +### ESLint Configuration |
| 145 | + |
| 146 | +The project uses ESLint 9 with flat config (`eslint.config.mjs`). Key rules: |
| 147 | + |
| 148 | +- No `console.log` in production code |
| 149 | +- No unused variables |
| 150 | +- No untyped `any` without `@ts-expect-error` |
| 151 | +- Imports must be properly sorted |
| 152 | +- JSDoc comments required for public APIs (classes and function declarations) |
| 153 | +- Only allow throwing Snap SDK error objects |
| 154 | + |
| 155 | +## Testing Standards |
| 156 | + |
| 157 | +- **Test files**: Use `*.test.ts` suffix (not `*.spec.ts`) |
| 158 | +- **Structure**: Co-located with source or in `test/` directories |
| 159 | +- **Framework**: Jest with @metamask/snaps-jest |
| 160 | +- **Pattern**: Arrange-Act-Assert |
| 161 | +- **Coverage**: Test happy paths AND error cases |
| 162 | +- **Mocking**: Mock external dependencies (no real HTTP calls) |
| 163 | + |
| 164 | +Example: |
| 165 | +```typescript |
| 166 | +describe('parsePermission', () => { |
| 167 | + it('parses valid permission objects', () => { |
| 168 | + const input = { name: 'test', args: [] }; |
| 169 | + const result = parsePermission(input); |
| 170 | + expect(result).toEqual({ name: 'test', args: [] }); |
| 171 | + }); |
| 172 | + |
| 173 | + it('throws an error with invalid input', () => { |
| 174 | + expect(() => parsePermission({ name: '', args: [] })).toThrow(); |
| 175 | + }); |
| 176 | +}); |
| 177 | +``` |
| 178 | + |
| 179 | +> **Important**: `@metamask/snaps-jest` requires the snap to be built first. Always run `yarn build` before testing. |
| 180 | +
|
| 181 | +## Error Handling |
| 182 | + |
| 183 | +- Use MetaMask SDK error types: `InternalError`, `InvalidParamsError`, etc. |
| 184 | +- Always provide meaningful error messages with context |
| 185 | +- Never silently fail - log or throw on unexpected conditions |
| 186 | +- Validate all inputs, especially at snap RPC boundaries |
| 187 | +- Reference: https://docs.metamask.io/snaps/how-to/communicate-errors/ |
| 188 | + |
| 189 | +Example: |
| 190 | +```typescript |
| 191 | +if (!snapId) { |
| 192 | + throw new InternalError('Snap ID is required'); |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +## Type Design |
| 197 | + |
| 198 | +- Declare types explicitly for all public APIs |
| 199 | +- Use discriminated unions for complex variants |
| 200 | +- Avoid `any` - use `unknown` and narrow types |
| 201 | +- Leverage `const` assertions for literal types |
| 202 | + |
| 203 | +Example: |
| 204 | +```typescript |
| 205 | +// Good: Discriminated union |
| 206 | +type Result = |
| 207 | + | { success: true; data: string } |
| 208 | + | { success: false; error: Error }; |
| 209 | + |
| 210 | +// Bad: Optional properties without discrimination |
| 211 | +type Result = { |
| 212 | + success: boolean; |
| 213 | + data?: string; |
| 214 | + error?: Error; |
| 215 | +}; |
| 216 | +``` |
| 217 | + |
| 218 | +## Snap Development Guidelines |
| 219 | + |
| 220 | +### Entry Points |
| 221 | + |
| 222 | +- Single entry point per snap (e.g., `src/index.ts`) |
| 223 | +- Export `onRpcRequest` handler and optional `onInstall`, `onUpdate`, etc. |
| 224 | +- Use snap manifest (`snap.manifest.ts`) for metadata |
| 225 | + |
| 226 | +### RPC Handler Pattern |
| 227 | + |
| 228 | +```typescript |
| 229 | +export const onRpcRequest: OnRpcRequestHandler = async (request) => { |
| 230 | + const { method, params } = request; |
| 231 | + |
| 232 | + switch (method) { |
| 233 | + case 'method1': |
| 234 | + return handleMethod1(params); |
| 235 | + case 'method2': |
| 236 | + return handleMethod2(params); |
| 237 | + default: |
| 238 | + throw new InvalidParamsError(`Unknown method: ${method}`); |
| 239 | + } |
| 240 | +}; |
| 241 | +``` |
| 242 | + |
| 243 | +### State Management |
| 244 | + |
| 245 | +- Use `snap.request()` with `snap_manageState` for persistence |
| 246 | +- State must be JSON-serializable |
| 247 | +- Keep state minimal - only store what's necessary |
| 248 | +- Always validate state when retrieving it |
| 249 | + |
| 250 | +## Code Comments |
| 251 | + |
| 252 | +- Write comments explaining **why**, not **what** |
| 253 | +- Use JSDoc for public APIs (functions and classes) |
| 254 | +- Keep comments up-to-date with code |
| 255 | + |
| 256 | +Good example: |
| 257 | +```typescript |
| 258 | +// Filter out archived accounts to show only active users |
| 259 | +const activeAccounts = accounts.filter(a => !a.archived); |
| 260 | +``` |
| 261 | + |
| 262 | +Bad example: |
| 263 | +```typescript |
| 264 | +const name = user.name; // Get the user's name |
| 265 | +``` |
| 266 | + |
| 267 | +## Git Workflow |
| 268 | + |
| 269 | +### Commit Messages |
| 270 | + |
| 271 | +Follow the conventional commits style: |
| 272 | +- Present tense ("add feature" not "added feature") |
| 273 | +- Reference issues when relevant (#123) |
| 274 | +- Keep first line under 72 characters |
| 275 | + |
| 276 | +Types: `fix:`, `feat:`, `docs:`, `chore:`, `refactor:`, etc. |
| 277 | + |
| 278 | +Example: `fix: update intro permission control message for clarity (#269)` |
| 279 | + |
| 280 | +### Branches |
| 281 | + |
| 282 | +- Feature branches: `feat/feature-name` |
| 283 | +- Bug fix branches: `fix/issue-name` |
| 284 | +- Target: `main` branch for PRs |
| 285 | + |
| 286 | +### PR Guidelines |
| 287 | + |
| 288 | +1. **Must have an open issue** - All PRs must be paired with an issue |
| 289 | +2. **Clear description** - Explain what and why (not just what) |
| 290 | +3. **Link issues** - Reference related issues (#123) |
| 291 | +4. **Test coverage** - Include tests for all new code |
| 292 | +5. **Follow style guide** - See `docs/styleGuide.md` |
| 293 | +6. **CI passes** - All linting and tests must pass |
| 294 | + |
| 295 | +## Dependency Management |
| 296 | + |
| 297 | +- Add dependencies with `yarn add` in the appropriate workspace package |
| 298 | +- Prefer existing dependencies in shared packages (`@metamask/shared`) |
| 299 | +- Avoid duplication across workspaces |
| 300 | +- Check root `package.json` `resolutions` for pinned versions |
| 301 | +- Scripts are disabled by default for security; use `lavamoat.allowScripts` |
| 302 | + |
| 303 | +## Security Considerations |
| 304 | + |
| 305 | +1. **No hardcoded secrets** - Use environment variables |
| 306 | +2. **Validate all inputs** - Especially RPC parameters |
| 307 | +3. **Minimize permissions** - Request only necessary snap permissions |
| 308 | +4. **No arbitrary code execution** - Don't use `eval` or `Function` |
| 309 | +5. **Sanitize logs** - Never log sensitive data |
| 310 | + |
| 311 | +## Performance Guidelines |
| 312 | + |
| 313 | +1. **Minimize state size** - Snap state persists to storage |
| 314 | +2. **Batch RPC calls** - Group related requests to reduce overhead |
| 315 | +3. **Lazy load dependencies** - Don't import unused modules |
| 316 | +4. **Efficient serialization** - Keep JSON payloads small |
| 317 | + |
| 318 | +## Troubleshooting |
| 319 | + |
| 320 | +### Build Errors |
| 321 | + |
| 322 | +- Verify environment variables are set correctly |
| 323 | +- Check Node version matches `.nvmrc` (`nvm use`) |
| 324 | +- Clear yarn cache: `yarn cache clean` |
| 325 | +- Rebuild: `yarn build` |
| 326 | + |
| 327 | +### Test Failures |
| 328 | + |
| 329 | +- Ensure snaps are built before testing: `yarn build` |
| 330 | +- Check test environment variables are configured |
| 331 | +- Run specific test: `yarn test --testNamePattern="test name"` |
| 332 | +- Build specific package: `yarn workspace @metamask/gator-permissions-snap build` |
| 333 | + |
| 334 | +### Snap Loading Issues |
| 335 | + |
| 336 | +- Verify localhost ports (8000, 8081, 8082) are available |
| 337 | +- Check Flask version is 12.14.2+ |
| 338 | +- Clear Flask cache in settings |
| 339 | +- Validate snap manifest is correct |
| 340 | + |
| 341 | +## Key Files and Directories |
| 342 | + |
| 343 | +- `AGENTS.md` - Project guidelines for agents (this file) |
| 344 | +- `CONTRIBUTING.md` - Contribution guidelines |
| 345 | +- `README.md` - Project overview and quick start |
| 346 | +- `docs/styleGuide.md` - Detailed coding style guide |
| 347 | +- `packages/*/src/index.ts` - Snap entry points |
| 348 | +- `packages/*/snap.manifest.ts` - Snap metadata |
| 349 | +- `packages/*/.env.example` - Environment variable examples |
| 350 | +- `tsconfig.json` - Root TypeScript configuration |
| 351 | +- `.prettierrc.js` - Prettier formatting rules |
| 352 | +- `eslint.config.mjs` - ESLint flat config |
| 353 | +- `yarn.lock` - Dependency lockfile |
| 354 | + |
| 355 | +## Additional Documentation |
| 356 | + |
| 357 | +- `docs/addingNewPermissionTypes.md` - How to add new permission types |
| 358 | +- `docs/manifest-management.md` - Snap manifest management |
| 359 | +- `docs/release.md` - Release process |
| 360 | +- `docs/snapPreinstall.md` - Pre-installation setup |
| 361 | +- `packages/gator-permissions-snap/docs/architecture.md` - Gator snap architecture |
| 362 | + |
| 363 | +## References |
| 364 | + |
| 365 | +- [ERC-7715 Specification](https://eip.tools/eip/7715) |
| 366 | +- [ERC-7710 Specification](https://eip.tools/eip/7710) |
| 367 | +- [MetaMask Snaps Docs](https://docs.metamask.io/snaps/) |
| 368 | +- [MetaMask Snaps Repository](https://github.com/MetaMask/snaps) |
| 369 | +- [Delegation Framework](https://github.com/MetaMask/delegation-framework) |
| 370 | +- [MetaMask Snaps Error Handling](https://docs.metamask.io/snaps/how-to/communicate-errors/) |
0 commit comments