Skill: marvinmarnold/passive-savings-crypto (version 1.0.0)

--- name: passive-savings-crypto description: Put idle USDC to work earning yield for you or your agent's wallet. Funds grow automatically and move like regular USDC when you're ready. No protocol interactions, no special steps. metadata: openclaw: primaryEnv: AGENT_PRIVATE_KEY homepage: https://github.com/locker-labs/passive-savings-crypto emoji: 💰 requires: bins: ["node"] env: ["AGENT_PRIVATE_KEY", "RPC_URL"] install: - npm install files: - scripts/getSYTBalance.js - scripts/mintSYT.js - scripts/transferSYT.js --- # Passive Savings Crypto Your wallet (or your agent's wallet) earns interest on idle USDC automatically. Fund it and forget it. Your balance grows in the background. When you're ready to move funds, it's just a normal ERC20 transfer. No protocol to interact with, no special steps for your agent. **Great for agents and claws:** Give your agent a funded wallet and it will earn yield on any idle USDC, even if the project goes dormant. When you come back, the balance is larger and moves like regular USDC. No extra instructions needed. ## What You Can Ask - "How much have I earned so far?" - "Put 200 USDC to work earning yield." - "Send 50 USDC to [address]." - "What's my balance?" - "Check my savings." - "Start earning yield on this wallet's USDC." ## Script Reference | Action | Command | |---|---| | Check balance + earnings | `node scripts/getSYTBalance.js` | | Check balance for a specific address | `node scripts/getSYTBalance.js <address>` | | Deposit USDC to earn yield | `node scripts/mintSYT.js <amount>` | | Send USDC to someone | `node scripts/transferSYT.js <address> <amount>` | ## Examples **Depositing** - "Save 100 USDC" → `node scripts/mintSYT.js 100` - "Put my idle USDC to work" → ask the user how much, then `node scripts/mintSYT.js <amount>` - "Deposit 50 USDC" → `node scripts/mintSYT.js 50` **Checking earnings** - "What's my sUSDC balance?" → `node scripts/getSYTBalance.js` - "How much yield have I earned?" → `node scripts/getSYTBalance.js` - "Check the savings balance for 0xABC..." → `node scripts/getSYTBalance.js 0xABC...` **Sending payments** - "Pay 20 USDC to 0x7c33..." → `node scripts/transferSYT.js 0x7c33... 20` - "Send 75 USDC to my friend at 0xDEF..." → `node scripts/transferSYT.js 0xDEF... 75` - "Transfer 10 USDC to [address]" → `node scripts/transferSYT.js <address> 10` ## Setup 1. Set `AGENT_PRIVATE_KEY` to your wallet's private key (never share this) 2. Set `RPC_URL` to a Linea RPC endpoint (e.g. from Infura or Alchemy) 3. Run `npm install` to install dependencies This skill operates on **Linea mainnet**. Ensure your wallet has USDC on Linea before depositing. ## How It Works When you deposit USDC, it goes into Aave via the router and comes back as sUSDC, a token that grows in value over time as interest accrues. When you send a payment, the recipient receives plain USDC. The conversion happens automatically. **For agent and claw developers:** sUSDC is a standard ERC20 token. Your agent doesn't need any special logic to move it. A normal transfer is all it takes. Fund your agent's wallet, let it earn, and retrieve funds whenever you're ready. No protocol interactions, no extra instructions required. Idle agent funds no longer sit dead. ## Security & Privacy - **No data leaves the device** except signed transactions broadcast to the Linea RPC endpoint. - **Private key handling:** `AGENT_PRIVATE_KEY` is read from environment variables only, never logged, stored, or transmitted. - **On-chain only:** All state changes are EVM transactions. No backend, no database, no telemetry. | Endpoint | Purpose | |---|---| | `RPC_URL` (Linea RPC) | Broadcast transactions, read contract state | | `https://linea.drpc.org` | Default fallback RPC if `RPC_URL` is not set | ## Agent Guardrails - **Always use `transferSYT.js` for payments.** Never transfer raw USDC directly. This ensures the recipient gets spendable USDC, not a yield token. - **Report both figures when showing balance.** The nominal sUSDC amount and the underlying USDC value it represents (these diverge as yield accrues). - **Confirm before depositing or transferring.** These are on-chain transactions and cannot be reversed. - **Network is Linea mainnet.** Transactions on other chains will not work with this skill. ## Integration ### Claude Code Run once after cloning to register this skill in your Claude Code session: ```bash npm run install-skill ``` This copies `SKILL.md` to `~/.claude/skills/passive-savings-crypto/SKILL.md`. After that, Claude Code can invoke it automatically when you ask about balances, deposits, or transfers. ### OpenAI / Custom Agents Load `tools.json` from this repo as your tool definitions. Each tool maps to a CLI command the agent runs via shell: | Tool | Command | |---|---| | `get_syt_balance` | `node scripts/getSYTBalance.js [address]` | | `mint_syt` | `node scripts/mintSYT.js <amount>` | | `transfer_syt` | `node scripts/transferSYT.js <recipient> <amount>` | Set `AGENT_PRIVATE_KEY` and `RPC_URL` in your agent's environment before invoking any tool.

path: CLAUDE.md
content: # SYT Skill — Claude Context

## About autoHODL

autoHODL is a passive savings app for crypto users, inspired by Acorns/Stash but built natively for Web3. It rounds up on-chain purchases and automatically deposits the spare change into Aave to earn yield. Example: spend 7.38 USDC → 0.62 USDC goes to Aave immediately.

- Supports any EVM wallet
- Live on Base and Linea, with native MetaMask Card support
- Launched production December 2025 after winning 1st place at a MetaMask hackathon
- Website: autohodl.money | GitHub: github.com/locker-labs/autohodl.money
- On-chain stats: https://dune.com/locker_money/autohodl

Current distribution channels in development: Telegram bot (memecoin community partnerships), OpenClaw skill (this repo), expanded token/chain support.

## About Spendable Yield Tokens (SYTs)

SYTs are a novel token primitive that makes yield actually spendable. Reference: https://hackmd.io/@locker/autohodl-syts

**The core insight:** wrapped yield tokens like aUSDC are not spendable — when you send aUSDC, the recipient receives aUSDC (a yield token they can't use everywhere). SYTs fix this.

- `sUSDC` (spendable USDC) earns yield like aUSDC
- When you *spend* sUSDC, the recipient receives **regular USDC** — not a wrapped token
- Yield stays liquid and usable at any time
- autoHODL distributes earnings to users as sUSDC

## This Skill

This OpenClaw skill manages the lifecycle of sUSDC for a user:

- **Minting:** Convert idle USDC into sUSDC to earn yield
- **Balance tracking:** Report both nominal sUSDC balance and underlying USDC value (rebasing logic means these differ)
- **Spending:** Route transfers via `transferSYT.js` so recipients get USDC, not sUSDC

## Stack

- Node.js scripts in `scripts/`
- Requires env vars: `AGENT_PRIVATE_KEY`, `RPC_URL`

## Key scripts

- `node scripts/getSYTBalance.js` — get sUSDC balance + underlying USDC value
- `node scripts/transferSYT.js <address> <amount>` — send USDC via sUSDC (recipient gets USDC)

## Important concepts

- **Rebasing:** sUSDC balance changes over time as yield accrues. Always report both nominal sUSDC and claimable underlying USDC.
- **Smart spending:** Always use `transferSYT.js` for sends — never raw USDC transfers. The script handles the unwrap so the recipient gets USDC.


path: README.md
content: # Passive Savings Crypto

[![Built for Claude](https://img.shields.io/badge/Built%20for-Claude-8A2BE2?logo=anthropic&logoColor=white)](https://claude.ai)
[![OpenClaw Compatible](https://img.shields.io/badge/OpenClaw-Compatible-00C2FF)](https://openclaw.ai)
[![ClawBot Ready](https://img.shields.io/badge/ClawBot-Ready-00C2FF)](https://openclaw.ai)
[![Powered by Aave](https://img.shields.io/badge/Powered%20by-Aave-B6509E?logo=aave&logoColor=white)](https://aave.com)
[![Linea](https://img.shields.io/badge/Network-Linea-121212?logo=linea&logoColor=white)](https://linea.build)
[![Base](https://img.shields.io/badge/Network-Base-0052FF?logo=base&logoColor=white)](https://base.org)

> Turn idle USDC into yield — automatically. For AI agents and crypto wallets alike.

**For agent developers:** This is a Claude skill and OpenClaw/ClawBot-compatible tool that gives your autonomous agent a yield-bearing wallet. Idle USDC earns Aave yield automatically in the background — no extra agent instructions needed. When your agent is ready to spend or transfer, funds move like regular USDC. Perfect for AI agent DeFi workflows where capital shouldn't sit dead between tasks.

**For crypto users:** Passive income on USDC with zero manual claiming. Deposit once, watch your balance grow on Base and Linea via Aave yield. DeFi savings that work like a bank account — but onchain.

---

A Claude skill for AI agents to manage sUSDC (Spendable Yield Tokens) on the Linea blockchain — converting idle USDC into yield-bearing tokens so users save while they spend.

## Install as a Claude Code Skill

The fastest way — install directly from ClawHub:

```bash
npx clawhub@latest install passive-savings-crypto
```

Restart Claude Code and the skill loads automatically. Ask Claude things like "check my sUSDC balance" or "deposit 100 USDC" — it will know what to do.

**To install from source instead:**

```bash
git clone https://github.com/locker-labs/passive-savings-crypto
cd passive-savings-crypto
npm install
npm run install-skill
```

`npm run install-skill` copies `SKILL.md` to `~/.claude/skills/passive-savings-crypto/SKILL.md` — the directory Claude Code scans for user-installed skills. You only need to run it once, or again after pulling updates.

---

## Overview

sUSDC is a rebasing token backed 1:1 by USDC. Holding sUSDC earns yield automatically; the balance grows over time without any manual claiming. This skill provides three operations:

- **Mint** — deposit USDC and receive sUSDC
- **Balance** — check sUSDC balance (and its underlying USDC value)
- **Transfer** — send sUSDC to another address (underlying USDC moves with it)

## Prerequisites

- Node.js v18+
- A wallet funded with USDC on Linea mainnet
- A Linea RPC endpoint (e.g. `https://rpc.linea.build`)

## Installation

```bash
npm install
```

## Configuration

Set the following environment variables before running any script:

| Variable | Description |
|---|---|
| `AGENT_PRIVATE_KEY` | Hex private key of the wallet executing transactions |
| `RPC_URL` | Linea RPC endpoint |

```bash
export AGENT_PRIVATE_KEY=0x...
export RPC_URL=https://rpc.linea.build
```

## Usage

### Mint sUSDC

Deposit USDC and receive sUSDC. On first use, this approves the Locker Router for unlimited USDC spending (one-time transaction).

```bash
node scripts/mintSYT.js <amount>

# Example: deposit 10.5 USDC
node scripts/mintSYT.js 10.5
```

### Check Balance

Query the sUSDC balance of an address. Defaults to the agent's own wallet if no address is provided.

```bash
node scripts/getSYTBalance.js [address]

# Check agent's own balance
node scripts/getSYTBalance.js

# Check another address
node scripts/getSYTBalance.js 0x7c334f35BF2B4a9e55f60CF3287c885598cF9A02
```

### Transfer sUSDC

Send sUSDC to a recipient. The script verifies sufficient balance before executing.

```bash
node scripts/transferSYT.js <recipient> <amount>

# Example: send 20 sUSDC
node scripts/transferSYT.js 0x7c334f35BF2B4a9e55f60CF3287c885598cF9A02 20
```

## Contract Addresses (Linea Mainnet)

| Contract | Address |
|---|---|
| USDC | [REDACTED_SECRET] |
| sUSDC (SYT) | [REDACTED_SECRET] |
| Locker Router | [REDACTED_SECRET] |

## Notes

**Rebase awareness** — sUSDC is a rebasing token. The raw balance returned by the contract increases over time as yield accrues. Always use `getSYTBalance.js` for the current balance rather than caching a previous value.

**Infinite approval** — `mintSYT.js` requests unlimited USDC approval on first use to avoid repeated approval transactions. This is a common pattern for DeFi protocols.

## Publishing to ClawHub

Install the ClawHub CLI and authenticate once:

```bash
npm install -g clawhub
clawhub login
```

Bump the version in `package.json`, then publish:

```bash
npm run release       # publish to ClawHub
npm run release:dry   # preview without uploading
```


path: docs/superpowers/specs/2026-03-28-passive-savings-crypto-design.md
content: # Passive Savings Crypto — Rename & README Redesign

## Goal

Rename the project to "Passive Savings Crypto" and update the README to prominently signal Claude/OpenClaw/ClawBot compatibility while targeting two audiences: AI agent developers and crypto/DeFi users.

## Scope

Three files change:

- `README.md` — new marketing header + badges; existing technical content preserved below
- `package.json` — name field updated to `passive-savings-crypto`
- `SKILL.md` — already named `passive-savings-crypto`; no change needed

## README Structure

**Approach:** Marketing header + unchanged technical body.

### New header (prepended above existing content)

1. Title: `# Passive Savings Crypto`
2. Badge row: Built for Claude · OpenClaw Compatible · ClawBot Ready · Powered by Aave · Linea · Base
3. Tagline: "Turn idle USDC into yield — automatically. For AI agents and crypto wallets alike."
4. Two-paragraph audience pitch:
   - **For agent developers:** Claude skill / ClawBot / OpenClaw framing; autonomous agent wallet; yield on idle USDC; funds move like regular USDC; no extra agent instructions needed
   - **For crypto users:** passive income on USDC; no manual claiming; Base and Linea; Aave yield

### Existing technical content

Preserved as-is below the header: Overview, Prerequisites, Installation, Configuration, Usage, Contract Addresses, Notes.

## SEO Keywords

| Audience | Keywords |
|---|---|
| Agent devs | `Claude skill`, `OpenClaw`, `ClawBot`, `autonomous agent`, `AI agent wallet`, `agent DeFi` |
| Crypto users | `passive income crypto`, `USDC yield`, `Aave`, `Base`, `Linea`, `DeFi savings` |

## Out of Scope

- `SKILL.md` content changes
- Script changes
- New sections beyond the marketing header


path: package-lock.json
content: {
  "name": "autohodl",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "autohodl",
      "version": "1.0.0",
      "license": "ISC",
      "dependencies": {
        "viem": "^2.46.2"
      }
    },
    "node_modules/@adraffy/ens-normalize": {
      "version": "1.11.1",
      "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT"
    },
    "node_modules/@noble/ciphers": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": "^14.21.3 || >=16"
      },
      "funding": {
        "url": "https://paulmillr.com/funding/"
      }
    },
    "node_modules/@noble/curves": {
      "version": "1.9.1",
      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "@noble/hashes": "1.8.0"
      },
      "engines": {
        "node": "^14.21.3 || >=16"
      },
      "funding": {
        "url": "https://paulmillr.com/funding/"
      }
    },
    "node_modules/@noble/hashes": {
      "version": "1.8.0",
      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": "^14.21.3 || >=16"
      },
      "funding": {
        "url": "https://paulmillr.com/funding/"
      }
    },
    "node_modules/@scure/base": {
      "version": "1.2.6",
      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "funding": {
        "url": "https://paulmillr.com/funding/"
      }
    },
    "node_modules/@scure/bip32": {
      "version": "1.7.0",
      "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "@noble/curves": "~1.9.0",
        "@noble/hashes": "~1.8.0",
        "@scure/base": "~1.2.5"
      },
      "funding": {
        "url": "https://paulmillr.com/funding/"
      }
    },
    "node_modules/@scure/bip39": {
      "version": "1.6.0",
      "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "dependencies": {
        "@noble/hashes": "~1.8.0",
        "@scure/base": "~1.2.5"
      },
      "funding": {
        "url": "https://paulmillr.com/funding/"
      }
    },
    "node_modules/abitype": {
      "version": "1.2.3",
      "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "funding": {
        "url": "https://github.com/sponsors/wevm"
      },
      "peerDependencies": {
        "typescript": ">=5.0.4",
        "zod": "^3.22.0 || ^4.0.0"
      },
      "peerDependenciesMeta": {
        "typescript": {
          "optional": true
        },
        "zod": {
          "optional": true
        }
      }
    },
    "node_modules/eventemitter3": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT"
    },
    "node_modules/isows": {
      "version": "1.0.7",
      "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz",
      "integrity": [REDACTED_SECRET],
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/wevm"
        }
      ],
      "license": "MIT",
      "peerDependencies": {
        "ws": "*"
      }
    },
    "node_modules/ox": {
      "version": "0.12.4",
      "resolved": "https://registry.npmjs.org/ox/-/ox-0.12.4.tgz",
      "integrity": [REDACTED_SECRET],
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/wevm"
        }
      ],
      "license": "MIT",
      "dependencies": {
        "@adraffy/ens-normalize": "^1.11.0",
        "@noble/ciphers": "^1.3.0",
        "@noble/curves": "1.9.1",
        "@noble/hashes": "^1.8.0",
        "@scure/bip32": "^1.7.0",
        "@scure/bip39": "^1.6.0",
        "abitype": "^1.2.3",
        "eventemitter3": "5.0.1"
      },
      "peerDependencies": {
        "typescript": ">=5.4.0"
      },
      "peerDependenciesMeta": {
        "typescript": {
          "optional": true
        }
      }
    },
    "node_modules/viem": {
      "version": "2.46.2",
      "resolved": "https://registry.npmjs.org/viem/-/viem-2.46.2.tgz",
      "integrity": [REDACTED_SECRET],
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/wevm"
        }
      ],
      "license": "MIT",
      "dependencies": {
        "@noble/curves": "1.9.1",
        "@noble/hashes": "1.8.0",
        "@scure/bip32": "1.7.0",
        "@scure/bip39": "1.6.0",
        "abitype": "1.2.3",
        "isows": "1.0.7",
        "ox": "0.12.4",
        "ws": "8.18.3"
      },
      "peerDependencies": {
        "typescript": ">=5.0.4"
      },
      "peerDependenciesMeta": {
        "typescript": {
          "optional": true
        }
      }
    },
    "node_modules/ws": {
      "version": "8.18.3",
      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
      "integrity": [REDACTED_SECRET],
      "license": "MIT",
      "engines": {
        "node": ">=10.0.0"
      },
      "peerDependencies": {
        "bufferutil": "^4.0.1",
        "utf-8-validate": ">=5.0.2"
      },
      "peerDependenciesMeta": {
        "bufferutil": {
          "optional": true
        },
        "utf-8-validate": {
          "optional": true
        }
      }
    }
  }
}


path: package.json
content: {
  "name": "passive-savings-crypto",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "install-skill": "node -e \"const fs=require('fs'),os=require('os'),path=require('path');const dir=path.join(os.homedir(),'.claude','skills','passive-savings-crypto');fs.mkdirSync(dir,{recursive:true});fs.copyFileSync('SKILL.md',path.join(dir,'SKILL.md'));console.log('Skill installed to '+dir);\"",
    "release": "clawhub publish ./ --slug passive-savings-crypto --version $npm_package_version",
    "release:dry": "clawhub publish ./ --slug passive-savings-crypto --version $npm_package_version --dry-run"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "commonjs",
  "dependencies": {
    "viem": "^2.46.2"
  }
}


path: scripts/config.js
content: const DEFAULT_RPC_URL = 'https://linea.drpc.org';

function getRpcUrl() {
    return process.env.RPC_URL || DEFAULT_RPC_URL;
}

function getPrivateKey() {
    const key = process.env.AGENT_PRIVATE_KEY;
    if (!key) {
        console.log('AGENT_PRIVATE_KEY is not set.');
        console.log('\nGenerate a new wallet key by running:');
        console.log('  node -e "console.log(\'0x\' + require(\'crypto\').randomBytes(32).toString(\'hex\'))"');
        console.log('\nThen set it in your environment:');
        console.log('  export AGENT_PRIVATE_KEY=<your_key>');
        process.exit(1);
    }
    return key;
}

module.exports = { getRpcUrl, getPrivateKey };


path: scripts/getSYTBalance.js
content: const { createPublicClient, http, formatUnits, parseAbiItem } = require('viem');
const { privateKeyToAccount } = require('viem/accounts');
const { linea } = require('viem/chains');
const { getRpcUrl, getPrivateKey } = require('./config');

const SYT_ADDRESS = [REDACTED_SECRET]; // Linea sUSDC address

// Standard ERC20 balanceOf + decimals
const balanceAbi = [
    parseAbiItem('function balanceOf(address account) view returns (uint256)'),
    parseAbiItem('function decimals() view returns (uint8)'),
    parseAbiItem('function symbol() view returns (string)')
];

async function main() {
    const publicClient = createPublicClient({
        chain: linea,
        transport: http(getRpcUrl())
    });

    // Default to the agent's own address if none is provided
    const targetAddress = process.argv[2] || privateKeyToAccount(getPrivateKey()).address;

    try {
        const [balance, decimals, symbol] = await Promise.all([
            publicClient.readContract({
                address: SYT_ADDRESS,
                abi: balanceAbi,
                functionName: 'balanceOf',
                args: [targetAddress]
            }),
            publicClient.readContract({
                address: SYT_ADDRESS,
                abi: balanceAbi,
                functionName: 'decimals'
            }),
            publicClient.readContract({
                address: SYT_ADDRESS,
                abi: balanceAbi,
                functionName: 'symbol'
            })
        ]);

        const formattedBalance = formatUnits(balance, decimals);
        console.log(`Address: ${targetAddress}`);
        console.log(`Balance: ${formattedBalance} ${symbol}`);
        console.log(`\nManage your account: https://autohodl.money (connect the same wallet)`);
        console.log(`Learn about SYTs: https://docs.autohodl.money`);
        
    } catch (error) {
        console.error("Error fetching balance:", error.message);
    }
}

main().catch(console.error);

path: scripts/mintSYT.js
content: const { createWalletClient, createPublicClient, http, parseUnits, parseAbiItem, maxUint256 } = require('viem');
const { privateKeyToAccount } = require('viem/accounts');
const { linea } = require('viem/chains');
const { getRpcUrl, getPrivateKey } = require('./config');

// Constants
const LOCKER_ROUTER = [REDACTED_SECRET]; // Linea Router address
const USDC_LINEA = [REDACTED_SECRET];

// ABI Fragments
const erc20Abi = [
    parseAbiItem('function allowance(address owner, address spender) view returns (uint256)'),
    parseAbiItem('function approve(address spender, uint256 amount) returns (bool)')
];
const depositAbi = parseAbiItem('function deposit(address asset, uint256 amount) external');

async function main() {
    const args = process.argv.slice(2);
    const depositAmount = args[0]; // Amount in USDC (e.g., "10.5")

    if (!depositAmount) {
        console.error("Please provide an amount to deposit.");
        return;
    }

    const account = privateKeyToAccount(getPrivateKey());
    const amountInWei = parseUnits(depositAmount, 6);

    const publicClient = createPublicClient({ chain: linea, transport: http(getRpcUrl()) });
    const walletClient = createWalletClient({ account, chain: linea, transport: http(getRpcUrl()) });

    console.log(`Checking allowance for ${depositAmount} USDC...`);

    // 1. Check Allowance
    const currentAllowance = await publicClient.readContract({
        address: USDC_LINEA,
        abi: erc20Abi,
        functionName: 'allowance',
        args: [account.address, LOCKER_ROUTER]
    });

    // 2. Approve if necessary
    if (currentAllowance < amountInWei) {
        console.log("Allowance insufficient. Sending approval transaction...");
        const approveHash = await walletClient.writeContract({
            address: USDC_LINEA,
            abi: erc20Abi,
            functionName: 'approve',
            args: [LOCKER_ROUTER, maxUint256] // Infinite approval to save gas on future mints
        });
        console.log(`Approval Hash: ${approveHash}`);
        await publicClient.waitForTransactionReceipt({ hash: approveHash });
        console.log("Approval confirmed.");
    } else {
        console.log("Allowance sufficient.");
    }

    // 3. Execute Deposit
    console.log(`Depositing ${depositAmount} USDC into AutoHODL...`);
    const depositHash = await walletClient.writeContract({
        address: LOCKER_ROUTER,
        abi: [depositAbi],
        functionName: 'deposit',
        args: [USDC_LINEA, amountInWei]
    });

    console.log(`Deposit Hash: ${depositHash}`);
    await publicClient.waitForTransactionReceipt({ hash: depositHash });
    console.log("Mint confirmed.");
    console.log(`\nView your savings: https://autohodl.money (connect the same wallet)`);
}

main().catch(console.error);

path: scripts/transferSYT.js
content: const { createWalletClient, createPublicClient, http, parseUnits, parseAbiItem } = require('viem');
const { privateKeyToAccount } = require('viem/accounts');
const { linea } = require('viem/chains');
const { getRpcUrl, getPrivateKey } = require('./config');

const SYT_ADDRESS = [REDACTED_SECRET]; //sUSDC Linea

const transferAbi = parseAbiItem('function transfer(address to, uint256 amount) returns (bool)');
const balanceAbi = parseAbiItem('function balanceOf(address account) view returns (uint256)');

async function main() {
    const args = process.argv.slice(2);
    const recipient = args[0];
    const amountStr = args[1];

    if (!recipient || !amountStr) {
        console.error("Usage: node transferSYT.js <recipient_address> <amount>");
        return;
    }

    const account = privateKeyToAccount(getPrivateKey());
    const amountInWei = parseUnits(amountStr, 6); // Assuming USDC 6 decimals

    const publicClient = createPublicClient({ chain: linea, transport: http(getRpcUrl()) });
    const walletClient = createWalletClient({ account, chain: linea, transport: http(getRpcUrl()) });

    console.log(`Preparing to send ${amountStr} sUSDC to ${recipient}...`);

    // 1. Check current balance
    const balance = await publicClient.readContract({
        address: SYT_ADDRESS,
        abi: [balanceAbi],
        functionName: 'balanceOf',
        args: [account.address]
    });

    if (balance < amountInWei) {
        console.error(`Insufficient balance. Current: ${balance.toString()}, Required: ${amountInWei.toString()}`);
        return;
    }

    // 2. Execute Transfer
    const hash = await walletClient.writeContract({
        address: SYT_ADDRESS,
        abi: [transferAbi],
        functionName: 'transfer',
        args: [recipient, amountInWei]
    });

    console.log(`Transaction Hash: ${hash}`);
    await publicClient.waitForTransactionReceipt({ hash });
    console.log("Transfer confirmed.");
    console.log(`\nManage your account: https://autohodl.money (connect the same wallet)`);
}

main().catch(console.error);

path: tools.json
content: [
  {
    "name": "get_syt_balance",
    "description": "Get the sUSDC balance for a wallet address. Defaults to the agent's own wallet if no address is provided. Run: node scripts/getSYTBalance.js [address]",
    "input_schema": {
      "type": "object",
      "properties": {
        "address": {
          "type": "string",
          "description": "EVM wallet address to check (optional — omit to check the agent's own wallet)"
        }
      }
    }
  },
  {
    "name": "mint_syt",
    "description": "Deposit USDC into autoHODL to receive yield-bearing sUSDC. Handles USDC approval automatically on first use. Run: node scripts/mintSYT.js <amount>",
    "input_schema": {
      "type": "object",
      "properties": {
        "amount": {
          "type": "string",
          "description": "Amount of USDC to deposit (e.g. \"10.5\")"
        }
      },
      "required": ["amount"]
    }
  },
  {
    "name": "transfer_syt",
    "description": "Send USDC to a recipient via sUSDC. The recipient receives plain USDC — not a yield token. Run: node scripts/transferSYT.js <recipient> <amount>",
    "input_schema": {
      "type": "object",
      "properties": {
        "recipient": {
          "type": "string",
          "description": "Recipient EVM wallet address"
        },
        "amount": {
          "type": "string",
          "description": "Amount to send in USDC (e.g. \"20\")"
        }
      },
      "required": ["recipient", "amount"]
    }
  }
]


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