@plumenetwork/onchainos-nest-plugin
v0.0.5
Published
OnchainOS plugin for Nest RWA yield vaults — calldata builder, vault discovery, and position queries. Used by the okx-rwa-nest skill.
Maintainers
Readme
@plumenetwork/onchainos-nest-plugin
CLI for depositing into and withdrawing from Nest — a protocol for tokenized real-world-asset yield (US Treasuries, regulated funds, private credit, CLO).
The CLI builds the on-chain transaction calldata. It does not sign or broadcast anything; private keys never enter this process. Pipe its output to your wallet of choice — OKX's TEE-managed wallet via onchainos wallet contract-call, or any signer that takes raw calldata (Foundry's cast, Frame, MetaMask, etc.).
Install
npm install -g @plumenetwork/onchainos-nest-plugin
onchainos-nest --versionRequires Node.js ≥ 20.
Configuration
Public RPC defaults work out of the box. Override with paid endpoints if you hit rate limits:
| Env var | Default | Purpose |
|---|---|---|
| ETH_RPC_URL | https://ethereum-rpc.publicnode.com | Reads ERC-20 decimals() / balanceOf() / allowance() and Nest's Accountant.getRate() |
| BSC_RPC_URL, ARBITRUM_RPC_URL, POLYGON_RPC_URL, BASE_RPC_URL, OPTIMISM_RPC_URL, AVAX_RPC_URL | public defaults | Same, per chain |
| NEST_API_BASE | https://api.nest.credit/v1 | Nest's REST API |
Subcommands
All commands write JSON to stdout on success and JSON to stderr on failure. Exit codes: 0 success, 1 user-actionable error, 2 transient/retryable.
vaults [--slug <slug>] [--no-live]
List the active Nest vaults. Each vault's depositChains and sharesChains are the chains that work for your wallet (taken from your wallet's chain list and what each vault accepts).
onchainos-nest vaults --slug nest-treasury-vault{
"vaults": [
{
"slug": "nest-treasury-vault",
"symbol": "nTBILL",
"vaultType": "boringNest",
"vaultAddress": "0xe72fe64840f4ef80e3ec73a1c749491b5c938cb9",
"accountantAddress": "0x0b738cd187872b265a689e8e4130c336e76892ec",
"decimals": 6,
"depositChains": [1],
"sharesChains": [1],
"tvl": 642517.88
}
]
}Without --slug, returns all currently-live vaults. The full response also includes liquidAssets[], nestVaults[], tellerContractAddress, and other fields.
recommend --capital <usd> --risk <tier> [--mode simple|advanced]
Pick a vault for a given capital + risk preference. Risk tiers: conservative | balanced | aggressive. Mode simple returns the top match; advanced returns the full ranked list.
onchainos-nest recommend --capital 100 --risk conservative --mode simple{
"recommendation": {
"slug": "nest-treasury-vault",
"vaultType": "boringNest",
"sec30d": 0.0386,
"tvl": 642517.88
},
"capital": 100,
"risk": "conservative"
}eligibility --address <0x...> --chain-id <n> [--country <ISO2>] [--is-new-proxy] [--is-deposit-and-bridge]
Compliance check. Returns a signed predicateMessage that the on-chain Nest contract validates at deposit time. The CLI hard-blocks --country US before any network call.
onchainos-nest eligibility --address 0xYourAddressHere000000000000000000000000 \
--chain-id 1 --country DE{
"eligible": true,
"predicateMessage": {
"taskId": "...",
"expireByBlockNumber": 1777912619,
"expireByTime": 1777912619,
"signerAddresses": ["..."],
"signatures": ["..."]
}
}For ineligible addresses or US:
{ "eligible": false, "reason": "..." }Pass --is-new-proxy for nest / boringNest vault types; omit it for boring vaults.
build-approve --token <0x...> --spender <0x...> --amount <ui> --chain <n>
Build ERC-20 approve(spender, amount) calldata. Reads the token's decimals() to convert from UI units. Hard-rejects amounts above 10^60 base units (no MaxUint256-style "infinite" approvals).
onchainos-nest build-approve \
--token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
--spender 0x6104fe10ca937a086ba7adbd0910a4733d380cb6 \
--amount 1 --chain 1{
"to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"inputData": "0x095ea7b3...",
"value": "0",
"description": "Approve 1 of 0xA0b86991... for 0x6104fe10..."
}build-deposit --vault <slug> --asset <0x...> --amount <ui> --address <0x...> --predicate-message <jsonOrAtPath> [--chain <n>] [--slippage-bps <bps>]
Build deposit calldata for the chosen vault. The CLI inspects vaultType and emits the right call — OLD_PREDICATE_PROXY.deposit(...) for boring vaults, NEW_PREDICATE_PROXY.deposit(...) for nest / boringNest vaults.
--predicate-message accepts inline JSON ('{...}') or a file path prefixed with @ (@./predicate.json).
# Save the predicate, then build deposit
onchainos-nest eligibility --address $USER --chain-id 1 --country DE --is-new-proxy \
| jq '.predicateMessage' > /tmp/predicate.json
onchainos-nest build-deposit \
--vault nest-treasury-vault \
--asset 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
--amount 1 \
--address $USER \
--predicate-message @/tmp/predicate.json \
--chain 1 \
--slippage-bps 50{
"to": "0xfC0c4222B3A0c9B060C0B959DEc62442036b9035",
"inputData": "0xa46ea103...",
"value": "0",
"description": "Deposit 1 USDC into nTBILL",
"expectedShares": "estimate-via-convertToAssets",
"slippageBps": 50,
"requestType": "deposit"
}Sequencing. A deposit needs an
approvefirst. Runbuild-approveagainst the spender returned bybuild-deposit'stofield, broadcast it, wait for confirmation, then broadcast the deposit.Predicate is time-bound. Refetch via
eligibilityif the user takes more than ~50 minutes between steps.Cross-chain. When
--chainis something other than1(Ethereum),build-depositproduces adepositAndBridge(...)call that bridges your deposit to Plume via LayerZero. The response carriesrequestType: "depositAndBridge",valueset to the LayerZero fee in source-chain native token (with a 10% buffer), and the sameto: OLD_PREDICATE_PROXYyou'd use on Ethereum. Currently supported for vault typeboringonly —nestandboringNesttypes return an error suggesting same-chain Ethereum.
build-withdraw --vault <slug> --shares <ui> --address <0x...> [--want-token <0x...>] [--chain <n>] [--claim]
Build withdrawal calldata.
| Vault type | Flag | Calls |
|---|---|---|
| boring | — | AtomicQueue.updateAtomicRequest(...) — async, solver fulfills typically within ~24h |
| nest / boringNest | (no --claim) | NestVault.requestRedeem(shares, owner, controller) — starts cooldown |
| nest / boringNest | --claim | NestVault.redeem(shares, receiver, owner) — only valid after cooldown |
onchainos-nest build-withdraw \
--vault nest-treasury-vault \
--shares 0.5 \
--address 0xYourAddressHere000000000000000000000000 \
--want-token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48{
"to": "0x250c2d14ed6376fb392fba1edd2cfd11d2bf7f12",
"inputData": "0x7d41c86e...",
"value": "0",
"description": "Request redeem of 0.5 from nTBILL",
"requestType": "requestRedeem",
"prerequisites": [
{
"to": "0xe72fe64840f4ef80e3ec73a1c749491b5c938cb9",
"inputData": "0x095ea7b3...",
"value": "0",
"description": "Approve 0.5 of <share token> for <vault>"
}
]
}Auto-approve.
build-withdrawreads your share-token allowance to the relevant spender (AtomicQueue for boring; the vault contract for nest / boringNest). If the allowance is short, the response includes aprerequisites: [...]array with the approve calldata — broadcast each prerequisite first (in order), then the main tx.The
--claimpath doesn't need an approve —redeemdoesn'ttransferFromthe user.
build-bridge --vault <slug> --shares <ui> --address <0x...> --source-chain <num> --dest-chain <num>
Build calldata to move already-owned vault shares from one chain to another via LayerZero. Use this when you've already deposited and now want your shares on a different chain.
| Vault type | Calls | Routes to |
|---|---|---|
| nest / boringNest | nestVaultOft.send(sendParam, fee, refundAddress) (LayerZero OFT) | the vault's OFT contract |
| boring | multiChainLayerZeroTeller.bridge(amount, BridgeData) | the vault's multi-chain Teller |
The CLI quotes the LayerZero native fee on-chain (OFT.quoteSend or Teller.previewFee), adds a 10% safety buffer, and emits the calldata with value set to the buffered fee. Pay this fee in native gas of the source chain.
onchainos-nest build-bridge \
--vault nest-treasury-vault \
--shares 0.5 \
--address 0xYourAddressHere000000000000000000000000 \
--source-chain 1 \
--dest-chain 98866{
"to": "0xe72fe64840f4ef80e3ec73a1c749491b5c938cb9",
"inputData": "0x...",
"value": "1100000000000000",
"description": "Bridge 0.5 nTBILL from chain 1 to chain 98866 via LayerZero OFT",
"requestType": "oftSend"
}Pre-deposit only. This command bridges shares — not assets. To get shares in the first place, run
build-deposit(same-chain) on whichever chain you hold the deposit asset on. To bridge assets cross-chain into a deposit in one tx, seebuild-deposit --chain <non-default>forboringvaults (the legacydepositAndBridgepath).
status --address <0x...> [--vault <slug>]
Aggregate position. Without --vault, returns total TVL + weighted APY across all the user's Nest holdings. With --vault, also reads the user's share-token balance for that vault on-chain.
onchainos-nest status --address 0xYourAddressHere000000000000000000000000{ "totalValueUSD": 0, "weightedApy": 0, "positions": [] }pending-redemptions --address <0x...> [--vault <slug>]
User's outstanding withdrawal requests. Each entry's currentClaimableAssets field tells you when a --claim will succeed (must be > 0 — cooldown done, solver fulfilled, etc.).
onchainos-nest pending-redemptions \
--address 0xYourAddressHere000000000000000000000000 \
--vault nest-treasury-vaulthistory --vault <slug> [--days <n>]
Vault performance: rolling 7d / 30d APY, 30-day TVL change, recent transaction count, recent on-chain price points.
onchainos-nest history --vault nest-treasury-vault --days 30{
"vaultSlug": "nest-treasury-vault",
"currentApy": { "rolling7d": 0.0210, "rolling30d": 0.0367, "sec30d": 0.0386 },
"tvl30DayChange": -1321853.12,
"pricePoints": [ /* per-chain accountant price updates */ ],
"recentTxBreakdown": { "count7d": 7, "transactions": [/* ... */] }
}End-to-end deposit walkthrough
USER=0xYourAddressHere000000000000000000000000
USDC=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
VAULT=nest-treasury-vault
# 1. Pick a vault
onchainos-nest recommend --capital 100 --risk conservative --mode simple
# 2. Get a fresh predicate (~1-hour validity)
onchainos-nest eligibility --address $USER --chain-id 1 --country DE --is-new-proxy \
| jq '.predicateMessage' > /tmp/predicate.json
# 3. Build deposit calldata
onchainos-nest build-deposit \
--vault $VAULT --asset $USDC --amount 1 \
--address $USER --predicate-message @/tmp/predicate.json --chain 1
# 4. Build approve for the spender returned in step 3's "to" field
PROXY=0xfC0c4222B3A0c9B060C0B959DEc62442036b9035 # NEW_PREDICATE_PROXY for boringNest
onchainos-nest build-approve --token $USDC --spender $PROXY --amount 1 --chain 1
# 5. Hand the calldata to your signer:
# OKX TEE wallet: onchainos wallet contract-call --to ... --input-data ... --chain 1
# Self-custody: sign locally; broadcast via your own RPC
# Foundry: cast send --private-key <KEY> --rpc-url <URL> <to> <inputData>Security
- No private keys handled. The CLI reads public APIs and on-chain state, returns calldata. Signing happens in your wallet of choice.
- No unbounded approvals.
build-approverejects amounts above10^60base units. - Compliance is on-chain. The eligibility predicate is signed by Nest and validated by their
PredicateProxycontract at deposit time. We don't validate signatures locally; the contract does. - API responses are validated. Every response from Nest is parsed through a strict schema before any field is used.
Development
git clone https://github.com/plumenetwork/onchainos-nest-plugin
cd onchainos-nest-plugin
npm install
npm test
npm run typecheck
npm run build
# Local install for hands-on testing
npm link
onchainos-nest --versionIssues / contributions
- Bugs and feature requests: https://github.com/plumenetwork/onchainos-nest-plugin/issues
- Source: https://github.com/plumenetwork/onchainos-nest-plugin
