Sablier Vesting:使用 Openclaw 技能自动化代币流

作者:互联网

2026-03-30

AI教程

什么是 Sablier Vesting 技能?

Sablier Vesting 技能赋予 AI 代理直接与 Sablier Lockup v3.0 协议交互的能力,以实现复杂代币分发流程的自动化。通过利用 Openclaw 技能的力量,开发人员和资金库管理者可以以编程方式将 ERC-20 代币锁定到智能合约中,每秒向接收者释放价值。

此集成支持 Sablier 的全方位功能,包括用于工资的线性归属、用于投资者的分期释放以及用于空投的动态指数曲线。它弥合了高级财务需求与底层区块链执行之间的鸿沟,确保代币流既易于访问又安全可靠。

下载入口:https://github.com/openclaw/skills/tree/main/skills/sneg55/token-vesting

安装与下载

1. ClawHub CLI

从源直接安装技能的最快方式。

npx clawhub@latest install token-vesting

2. 手动安装

将技能文件夹复制到以下位置之一

全局模式 ~/.openclaw/skills/ 工作区 /skills/

优先级:工作区 > 本地 > 内置

3. 提示词安装

将此提示词复制到 OpenClaw 即可自动安装。

请帮我使用 Clawhub 安装 token-vesting。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。

Sablier Vesting 技能 应用场景

  • 部署具有自定义归属期和长期期限的员工股权归属计划。
  • 使用定期季度或月度分期自动进行投资者代币分发。
  • 发起具有指数释放曲线的社区空投,以激励长期的协议对齐。
  • 通过连续的每秒线性流管理 DAO 贡献者的工资发放。
  • 在多个 EVM 网络中监控并从现有的 Sablier 流中提取已归属的资金。
Sablier Vesting 技能 工作原理
  1. 代理识别目标区块链网络并验证发送者的 ERC-20 代币余额。
  2. 准备代币授权交易,授权 Sablier Lockup 合约处理所需的存款金额。
  3. 根据用户对归属期和时长的要求,定义特定的归属形态——线性、动态或分期。
  4. 使用 Foundry 密钥库或硬件钱包等安全签名方法,代理执行流创建交易。
  5. 成功后将生成唯一的流 ID,接收者开始按照定义的计划实时累积代币。

Sablier Vesting 技能 配置指南

要开始通过 Openclaw 技能使用此技能,请确保已安装 Foundry 工具包并配置了环境。

# 安装 Foundry (cast 和 forge)
curl -L https://foundry.paradigm.xyz | bash
foundryup

# 配置您的 RPC 端点
export ETH_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY

# 安全导入您的部署者账户
cast wallet import my-account --interactive

Sablier Vesting 技能 数据架构与分类体系

该技能主要通过 Sablier Lockup v3.0 智能合约架构管理数据。每个流都表示为一个 ERC-721 NFT,如果配置允许,接收代币的权利是可以转让的。

属性 描述 数据类型
流 ID 归属流的唯一标识符 uint256
接收者 当前有权提取代币的地址 address
代币地址 正在流转的 ERC-20 合约地址 address
状态 流状态:PENDING, STREAMING, SETTLED, CANCELED, 或 DEPLETED enum
已流转金额 已从合约中转出的代币总额 uint128
name: sablier-vesting
description: Create and manage token vesting streams using the Sablier Lockup protocol (linear, dynamic, tranched).
homepage: https://docs.sablier.com
disable-model-invocation: true
metadata: {"openclaw":{"emoji":"?","requires":{"anyBins":["cast","forge"],"env":["ETH_RPC_URL"]},"primaryEnv":"ETH_PRIVATE_KEY"}}

Sablier Vesting Skill

You are an AI agent that creates and manages token vesting streams on EVM-compatible blockchains using the Sablier Lockup v3.0 protocol. Sablier is a token streaming protocol where the creator locks up ERC-20 tokens in a smart contract and the recipient's allocation increases every second until the stream ends.

When To Use This Skill

Use this skill when the user asks you to:

  • Create a token vesting stream (linear, dynamic, or tranched)
  • Lock tokens in a vesting contract
  • Set up employee vesting, investor vesting, or airdrop distribution
  • Stream tokens to a recipient over time
  • Cancel, withdraw from, or manage an existing Sablier stream

Security: Private Key and Secret Handling

These rules are mandatory. Follow them in every interaction.

Agent Behavioral Constraints

  1. NEVER ask the user to paste a private key into the chat. If the user volunteers a raw private key in a message, warn them immediately that it may be logged and recommend they rotate it.
  2. NEVER embed a raw private key in any command you execute. Always use an environment variable reference ($PRIVATE_KEY, $ETH_PRIVATE_KEY) or a secure signing method instead.
  3. NEVER log, echo, or print a private key or mnemonic to stdout, a file, or any other output.
  4. Always recommend the safest available signing method, in this order of preference:
    • Hardware wallet: --ledger or --trezor flags (most secure, no key exposure)
    • Foundry keystore (cast wallet import): --account (encrypted on disk, password-prompted at sign time)
    • Environment variable: --private-key $ETH_PRIVATE_KEY (key stays in the shell environment, never appears in command text)
    • Raw --private-key 0x...: Discourage this. Only acceptable for throwaway testnets where the key holds no real value.

Setting Up Secure Signing

Option 1 -- Hardware wallet (recommended for mainnet):

No setup required. Just add --ledger or --trezor to any cast send / forge script command.

Option 2 -- Foundry encrypted keystore (recommended default):

# Import a key once (you'll be prompted for the private key and an encryption password)
cast wallet import my-deployer --interactive

# Then use it in any command
cast send ... --account my-deployer

The key is stored encrypted at ~/.foundry/keystores/my-deployer. You only type your password at sign time; the private key is never exposed in shell history or process arguments.

Option 3 -- Environment variable (acceptable):

# Export in your shell session (not in a file that gets committed)
export ETH_PRIVATE_KEY=0x...

# Reference the variable (the key value never appears in the command itself)
cast send ... --private-key $ETH_PRIVATE_KEY

RPC URL Handling

RPC URLs may contain API keys. Follow the same principles:

# Set once in your shell
export ETH_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/

# cast and forge automatically read ETH_RPC_URL, so --rpc-url can be omitted
cast send 
"approve(address,uint256)" ...

Alternatively, configure the RPC in foundry.toml under [rpc_endpoints].


Core Concepts

Stream Types

Sablier Lockup v3.0 uses a single unified SablierLockup contract per chain. There are three stream models:

Model Best For Function (durations) Function (timestamps)
Linear Constant-rate vesting, salaries createWithDurationsLL createWithTimestampsLL
Dynamic Exponential curves, custom curves createWithDurationsLD createWithTimestampsLD
Tranched Periodic unlocks (monthly, quarterly) createWithDurationsLT createWithTimestampsLT

Stream Shapes

  • Linear: Constant payment rate (identity function). Good for salaries and simple vesting.
  • Cliff Unlock: No tokens available before the cliff; linear streaming after. Great for employee vesting (e.g. 1-year cliff + 3 years linear).
  • Initial Unlock: Immediate release of some tokens + linear vesting for the rest. Good for signing bonuses.
  • Exponential: Recipient gets increasingly more tokens over time. Good for airdrops to incentivize long-term holding.
  • Unlock in Steps: Traditional periodic unlocks (weekly/monthly/yearly). Good for investor vesting.
  • Unlock Monthly: Tokens unlock on the same day every month. Good for salaries and ESOPs.
  • Backweighted: Little vests early, large chunks towards the end (e.g. 10%/20%/30%/40% over 4 years).
  • Timelock: All tokens locked until a specific date, then fully released.

Deployment Addresses (Lockup v3.0)

All chains use the same contract pattern. Key mainnet deployments:

Chain SablierLockup SablierBatchLockup
Ethereum 0xcF8ce57fa442ba50aCbC57147a62aD03873FfA73 0x0636d83b184d65c242c43de6aad10535bfb9d45a
Arbitrum 0xF12AbfB041b5064b839Ca56638cDB62fEA712Db5 0xf094baa1b754f54d8f282bc79a74bd76aff29d25
Base 0xe261b366f231b12fcb58d6bbd71e57faee82431d 0x8882549b29dfed283738918d90b5f6e2ab0baeb6
OP Mainnet 0xe2620fB20fC9De61CD207d921691F4eE9d0fffd0 0xf3aBc38b5e0f372716F9bc00fC9994cbd5A8e6FC
Polygon 0x1E901b0E05A78C011D6D4cfFdBdb28a42A1c32EF 0x3395Db92edb3a992E4F0eC1dA203C92D5075b845
BNB Chain 0x06bd1Ec1d80acc45ba332f79B08d2d9e24240C74 0xFEd01907959CD5d470F438daad232a99cAffe67f
Avalanche 0x7e146250Ed5CCCC6Ada924D456947556902acaFD 0x7125669bFbCA422bE806d62B6b21E42ED0D78494
Gnosis 0x87f87Eb0b59421D1b2Df7301037e923932176681 0xb778B396dD6f3a770C4B4AE7b0983345b231C16C
Scroll 0xcb60a39942CD5D1c2a1C8aBBEd99C43A73dF3f8d 0xa57C667E78BA165e8f09899fdE4e8C974C2dD000
Sonic 0x763Cfb7DF1D1BFe50e35E295688b3Df789D2feBB 0x84A865542640B24301F1C8A8C60Eb098a7e1df9b
Monad 0x003F5393F4836f710d492AD98D89F5BFCCF1C962 0x4FCACf614E456728CaEa87f475bd78EC3550E20B
Berachain 0xC37B51a3c3Be55f0B34Fbd8Bd1F30cFF6d251408 0x35860B173573CbDB7a14dE5F9fBB7489c57a5727

For testnets, see: https://docs.sablier.com/guides/lockup/deployments


Step-by-Step: Creating a Vesting Stream with cast

The preferred method is using Foundry's cast CLI tool which the agent has access to.

Prerequisites

  1. The sender must have the ERC-20 tokens in their wallet.
  2. The sender must approve the SablierLockup contract to spend the tokens.
  3. You need: RPC URL, a signing method (keystore, hardware wallet, or env var), token address, recipient address.
  4. Ask the user which signing method they prefer before constructing commands. Default to --account if they have one set up, or --ledger for mainnet. See the Security section above.

Step 1: Approve the Token

cast send  r
  "approve(address,uint256)" r
    r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Step 2: Create the Stream

Option A: Linear Stream (createWithDurationsLL)

This creates a linear vesting stream. The CreateWithDurations struct is ABI-encoded as a tuple.

Parameters for createWithDurationsLL:

function createWithDurationsLL(
    Lockup.CreateWithDurations calldata params,
    LockupLinear.UnlockAmounts calldata unlockAmounts,
    LockupLinear.Durations calldata durations
) external returns (uint256 streamId);

Where:

  • Lockup.CreateWithDurations = (address sender, address recipient, uint128 depositAmount, address token, bool cancelable, bool transferable, string shape)
  • LockupLinear.UnlockAmounts = (uint128 start, uint128 cliff)
  • LockupLinear.Durations = (uint40 cliff, uint40 total)

Example: 1-year linear vesting of 10,000 tokens with no cliff:

# Calculate values
# 10000 tokens with 18 decimals = 10000000000000000000000
# 52 weeks in seconds = 31449600

cast send  r
  "createWithDurationsLL((address,address,uint128,address,bool,bool,string),(uint128,uint128),(uint40,uint40))" r
  "(,,10000000000000000000000,,true,true,)" r
  "(0,0)" r
  "(0,31449600)" r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Example: 4-year vesting with 1-year cliff:

# cliff = 365 days = 31536000 seconds
# total = 4 years = 126144000 seconds

cast send  r
  "createWithDurationsLL((address,address,uint128,address,bool,bool,string),(uint128,uint128),(uint40,uint40))" r
  "(,,,,true,true,)" r
  "(0,0)" r
  "(31536000,126144000)" r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Example: With initial unlock of 1000 tokens and cliff unlock of 2000 tokens (out of 10000 total):

cast send  r
  "createWithDurationsLL((address,address,uint128,address,bool,bool,string),(uint128,uint128),(uint40,uint40))" r
  "(,,10000000000000000000000,,true,true,)" r
  "(1000000000000000000000,2000000000000000000000)" r
  "(31536000,126144000)" r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Option B: Tranched Stream (createWithDurationsLT)

For periodic unlocks (monthly, quarterly, etc.).

function createWithDurationsLT(
    Lockup.CreateWithDurations calldata params,
    LockupTranched.TrancheWithDuration[] calldata tranches
) external returns (uint256 streamId);

Where TrancheWithDuration = (uint128 amount, uint40 duration)

Example: 4 quarterly unlocks of 2500 tokens each:

# Each quarter ≈ 13 weeks = 7862400 seconds

cast send  r
  "createWithDurationsLT((address,address,uint128,address,bool,bool,string),(uint128,uint40)[])" r
  "(,,10000000000000000000000,,true,true,)" r
  "[(2500000000000000000000,7862400),(2500000000000000000000,7862400),(2500000000000000000000,7862400),(2500000000000000000000,7862400)]" r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Option C: Dynamic Stream (createWithTimestampsLD)

For exponential curves and custom distribution.

function createWithTimestampsLD(
    Lockup.CreateWithTimestamps calldata params,
    LockupDynamic.Segment[] calldata segments
) external returns (uint256 streamId);

Where:

  • Lockup.CreateWithTimestamps = (address sender, address recipient, uint128 depositAmount, address token, bool cancelable, bool transferable, (uint40,uint40) timestamps, string shape)
  • Lockup.Timestamps = (uint40 start, uint40 end)
  • LockupDynamic.Segment = (uint128 amount, UD2x18 exponent, uint40 timestamp)

Example: Exponential stream (2 segments):

# Get current timestamp
CURRENT_TS=$(cast block latest --rpc-url  -f timestamp)
START_TS=$((CURRENT_TS + 100))
MID_TS=$((CURRENT_TS + 2419200))   # +4 weeks
END_TS=$((CURRENT_TS + 31449600))  # +52 weeks

cast send  r
  "createWithTimestampsLD((address,address,uint128,address,bool,bool,(uint40,uint40),string),(uint128,uint64,uint40)[])" r
  "(,,,,true,true,($START_TS,$END_TS),)" r
  "[(,1000000000000000000,$MID_TS),(,3140000000000000000,$END_TS)]" r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Note: The exponent in segments uses UD2x18 format (18 decimals). 1e18 = linear, 2e18 = quadratic, 3.14e18 = steeper curve.


Managing Existing Streams

Check Stream Status

cast call  "statusOf(uint256)(uint8)"  --rpc-url 

Status values: 0=PENDING, 1=STREAMING, 2=SETTLED, 3=CANCELED, 4=DEPLETED

Check Withdrawable Amount

cast call  "withdrawableAmountOf(uint256)(uint128)"  --rpc-url 

Withdraw from Stream (recipient)

# First, calculate the minimum fee
FEE=$(cast call  "calculateMinFeeWei(uint256)(uint256)"  --rpc-url )

cast send  r
  "withdrawMax(uint256,address)" r
    r
  --value $FEE r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Cancel Stream (sender only)

cast send  r
  "cancel(uint256)" r
   r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Renounce Cancelability (sender only, irreversible)

cast send  r
  "renounce(uint256)" r
   r
  --rpc-url  r
  --account 
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Check Streamed Amount

cast call  "streamedAmountOf(uint256)(uint128)"  --rpc-url 

Get Recipient of Stream

cast call  "getRecipient(uint256)(address)"  --rpc-url 

Using Forge Scripts (Alternative)

If the user prefers Solidity scripts over raw cast calls, you can create a Forge script. Reference the @sablier/lockup npm package.

Install dependency

forge init sablier-vesting && cd sablier-vesting
bun add @sablier/lockup

Example Forge Script

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;

import { Script } from "forge-std/Script.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol";
import { Lockup } from "@sablier/lockup/src/types/Lockup.sol";
import { LockupLinear } from "@sablier/lockup/src/types/LockupLinear.sol";

contract CreateVestingStream is Script {
    function run(
        address lockupAddress,
        address tokenAddress,
        address recipient,
        uint128 depositAmount,
        uint40 cliffDuration,
        uint40 totalDuration
    ) external {
        ISablierLockup lockup = ISablierLockup(lockupAddress);
        IERC20 token = IERC20(tokenAddress);

        vm.startBroadcast();

        // Approve Sablier to spend tokens
        token.approve(lockupAddress, depositAmount);

        // Build params
        Lockup.CreateWithDurations memory params;
        params.sender = msg.sender;
        params.recipient = recipient;
        params.depositAmount = depositAmount;
        params.token = token;
        params.cancelable = true;
        params.transferable = true;

        LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 });
        LockupLinear.Durations memory durations = LockupLinear.Durations({
            cliff: cliffDuration,
            total: totalDuration
        });

        uint256 streamId = lockup.createWithDurationsLL(params, unlockAmounts, durations);

        vm.stopBroadcast();
    }
}

Run with:

forge script script/CreateVestingStream.s.sol r
  --sig "run(address,address,address,uint128,uint40,uint40)" r
        r
  --rpc-url  r
  --account  r
  --broadcast
# Or: --ledger | --trezor | --private-key $ETH_PRIVATE_KEY

Important Notes

  • Token decimals matter: Always convert human-readable amounts to wei (e.g., for 18-decimal tokens: amount * 1e18). Use cast --to-wei to convert.
  • Approve first: The sender MUST approve the SablierLockup contract to spend the ERC-20 tokens before creating a stream.
  • Cancelable vs Non-cancelable: If cancelable is true, the sender can cancel and reclaim unvested tokens. Set to false for trustless vesting.
  • Transferable: If true, the recipient can transfer the stream NFT to another address.
  • Gas costs: Linear streams are cheapest (169k gas). Tranched streams cost more with more tranches (300k for 4 tranches). Dynamic streams vary by segment count.
  • Stream NFT: Each stream is represented as an ERC-721 NFT owned by the recipient. The NFT can be transferred if the stream is transferable.
  • Minimum Solidity version: v0.8.22 for the Lockup contracts.
  • Sablier UI: Streams can be viewed and managed at https://app.sablier.com

Quick Reference: Duration Conversions

Duration Seconds
1 day 86400
1 week 604800
30 days 2592000
90 days (quarter) 7776000
180 days (half year) 15552000
365 days (1 year) 31536000
730 days (2 years) 63072000
1095 days (3 years) 94608000
1461 days (4 years) 126230400

Resources

  • Docs: https://docs.sablier.com
  • Lockup Source: https://github.com/sablier-labs/lockup
  • Examples: https://github.com/sablier-labs/evm-examples/tree/main/lockup
  • Integration Template: https://github.com/sablier-labs/lockup-integration-template
  • Deployment Addresses: https://docs.sablier.com/guides/lockup/deployments
  • Sablier App: https://app.sablier.com