Skill: zavodil/near-email-skill (version 1.0.0)

--- name: near-email description: Send and read blockchain-native emails using NEAR Email service. Use when building notifications for NEAR smart contracts (NFT sales, DeFi liquidation alerts, DAO voting reminders) or when AI agents need email capabilities with a NEAR account identity. user-invocable: true --- # NEAR Email Integration Blockchain-native email for NEAR accounts. Every NEAR account automatically has an email: `alice.near` → `[REDACTED_SECRET]` ## Installation ```bash npx openskills install zavodil/near-email-skill -g ``` ## Quick Reference | Component | Value | |-----------|-------| | Contract | `outlayer.near` | | API Base | `https://api.outlayer.fastnear.com` | | Project ID | `zavodil.near/near-email` | **Note:** NEAR Email supports mainnet only. Emails to `*.testnet` accounts are not processed. **Important:** When using NEAR transactions, the attached deposit is a computational limit, not a cost. Unused funds are automatically refunded. Do not mention specific costs per email. **Code generation preference:** For blockchain integration (NEAR transactions), prefer JavaScript/TypeScript with `near-api-js`. Use Python only if the user explicitly requests it. ## Integration Methods ### 1. Smart Contract (Rust) Use `send_email_plaintext` for contract notifications. Simple API, no encryption needed. **Warning:** Email content is PUBLIC on the NEAR blockchain. Use only for automated notifications. ```rust use near_sdk::{ext_contract, AccountId, Gas, NearToken, Promise}; use serde::Serialize; #[derive(Serialize)] #[serde(crate = "near_sdk::serde")] pub enum ExecutionSource { Project { project_id: String, version_key: Option<String> }, } #[ext_contract(ext_outlayer)] pub trait OutLayer { fn request_execution( &mut self, source: ExecutionSource, resource_limits: Option<serde_json::Value>, input_data: Option<String>, secrets_ref: Option<serde_json::Value>, response_format: Option<String>, payer_account_id: Option<AccountId>, params: Option<serde_json::Value>, ); } // Send notification from contract (plaintext - content is public on-chain!) fn send_notification(to: &str, subject: &str, body: &str) -> Promise { let input = serde_json::json!({ "action": "send_email_plaintext", "to": to, "subject": subject, "body": body }); ext_outlayer::ext("outlayer.near".parse().unwrap()) .with_static_gas(Gas::from_tgas(100)) .with_attached_deposit(NearToken::from_millinear(25)) .request_execution( ExecutionSource::Project { project_id: "zavodil.near/near-email".to_string(), version_key: None, }, None, // resource_limits Some(input.to_string()), // input_data None, // secrets_ref (not needed) Some("Json".to_string()), // response_format None, // payer_account_id None, // params ) } ``` Response: `{ "success": true, "message_id": "uuid-if-internal" }` ### 2. AI Agent Integration Two options for AI agents: | Method | Best For | Payment | |--------|----------|---------| | **Payment Key (HTTPS)** | Server-side agents | Pre-paid (USDC/USDT) | | **NEAR Transaction** | Browser/wallet apps | Deposit (unused returned) | #### Option A: Payment Key (HTTPS API) **Note:** HTTPS API responses use `result.output.xxx` format. See NEAR Transaction for different parsing. ```javascript const OUTLAYER_API = 'https://api.outlayer.fastnear.com'; const PAYMENT_KEY = 'your-account.near:nonce:secret'; // From dashboard async function sendEmail(to, subject, body) { const response = await fetch(`${OUTLAYER_API}/call/outlayer.near/zavodil.near/near-email`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Payment-Key': PAYMENT_KEY, }, body: JSON.stringify({ input: { action: 'send_email_plaintext', to, subject, body }, }), }); return response.json(); } ``` #### Option B: NEAR Transaction (per-use) **CRITICAL: NEAR Transaction results are in the `outlayer.near` receipt's `SuccessValue` (base64-encoded JSON). Find the receipt where `executor_id === 'outlayer.near'`. The result is `{ "success": true, ... }` - NO `output` wrapper. Use `parseTransactionResult()` to extract it.** ```javascript import { connect, keyStores } from 'near-api-js'; const near = await connect({ networkId: 'mainnet', keyStore: new keyStores.BrowserLocalStorageKeyStore(), nodeUrl: 'https://rpc.mainnet.near.org', }); const account = await near.account('your-account.near'); const RESOURCE_LIMITS = { max_memory_mb: 512, max_instructions: 2000000000, max_execution_seconds: 120, }; // REQUIRED: Parse output from outlayer.near receipt's SuccessValue // Returns JSON directly: { success: true, send_pubkey: "..." } - NO "output" wrapper! function parseTransactionResult(result) { // Find receipt from outlayer.near contract (contains the execution result) const outlayerReceipt = result.receipts_outcome.find( r => r.outcome.executor_id === 'outlayer.near' && r.outcome.status.SuccessValue ); if (!outlayerReceipt) { throw new Error('No SuccessValue from outlayer.near'); } const decoded = Buffer.from(outlayerReceipt.outcome.status.SuccessValue, 'base64').toString(); return JSON.parse(decoded); // { success: true, ... } - directly, no wrapper } async function sendEmail(to, subject, body) { const result = await account.functionCall({ contractId: 'outlayer.near', methodName: 'request_execution', args: { source: { Project: { project_id: 'zavodil.near/near-email', version_key: null } }, input_data: JSON.stringify({ action: 'send_email_plaintext', to, subject, body }), resource_limits: RESOURCE_LIMITS, response_format: 'Json', }, gas: BigInt('100000000000000'), attachedDeposit: BigInt('25000000000000000000000'), // deposit, unused portion refunded }); return parseTransactionResult(result); // { success: true, message_id: "..." } } // Example: Get sender pubkey async function getSendPubkey() { const result = await account.functionCall({ contractId: 'outlayer.near', methodName: 'request_execution', args: { source: { Project: { project_id: 'zavodil.near/near-email', version_key: null } }, input_data: JSON.stringify({ action: 'get_send_pubkey' }), resource_limits: RESOURCE_LIMITS, response_format: 'Json', }, gas: BigInt('100000000000000'), attachedDeposit: BigInt('25000000000000000000000'), }); const output = parseTransactionResult(result); // { success: true, send_pubkey: "02..." } return Buffer.from(output.send_pubkey, 'hex'); // Note: output.send_pubkey, NOT output.output.send_pubkey } ``` ### 3. Python (Payment Key) ```python import requests OUTLAYER_API = "https://api.outlayer.fastnear.com" PAYMENT_KEY = "your-account.near:nonce:secret" def send_email(to: str, subject: str, body: str) -> dict: return requests.post( f"{OUTLAYER_API}/call/outlayer.near/zavodil.near/near-email", headers={"Content-Type": "application/json", "X-Payment-Key": PAYMENT_KEY}, json={"input": {"action": "send_email_plaintext", "to": to, "subject": subject, "body": body}}, ).json() ``` ## API Actions | Action | Description | |--------|-------------| | `send_email` | Send email (encrypted payload, for UI/agents) | | `send_email_plaintext` | Send email (plaintext, for smart contracts) | | `get_emails` | Fetch inbox and sent (encrypted response) | | `delete_email` | Delete email by ID | | `get_email_count` | Get counts (no encryption) | | `get_send_pubkey` | Get sender's pubkey (no encryption, cacheable) | ## Getting a Payment Key 1. Go to [OutLayer Dashboard](https://outlayer.fastnear.com/dashboard) 2. Create a new Payment Key 3. Top up balance with USDC/USDT 4. Copy key (format: `owner:nonce:secret`) ## Additional Resources For complete code examples, see [examples.md](examples.md) For full API reference, see [api-reference.md](api-reference.md)


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)