@oracle-agent/oracle 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/SETUP.md +4 -4
- package/dist/action-receipts.mjs +1 -0
- package/dist/action-semantics.mjs +1 -0
- package/dist/address-book.mjs +2 -0
- package/dist/bin/desk-server.mjs +60 -0
- package/dist/bin/oracle-data-mcp.mjs +9 -0
- package/dist/bin/oracle-equities.mjs +20 -0
- package/dist/bin/oracle-init.mjs +24 -0
- package/dist/bin/oracle-public-server.mjs +5 -0
- package/dist/bin/oracle-route.mjs +22 -0
- package/dist/bin/oracle-scan.mjs +18 -0
- package/dist/bin/oracle-upgrade.mjs +21 -0
- package/dist/bin/oracle.mjs +63 -0
- package/dist/chains.mjs +1 -0
- package/dist/cli/commands/auth.mjs +16 -0
- package/dist/cli/commands/bootstrap.mjs +18 -0
- package/dist/cli/commands/chain.mjs +21 -0
- package/dist/cli/commands/chat.mjs +45 -0
- package/dist/cli/commands/credential.mjs +1 -0
- package/dist/cli/commands/data-mcp.mjs +2 -0
- package/dist/cli/commands/data.mjs +12 -0
- package/dist/cli/commands/doctor.mjs +5 -0
- package/dist/cli/commands/equities.mjs +15 -0
- package/dist/cli/commands/farm.mjs +59 -0
- package/dist/cli/commands/fees.mjs +10 -0
- package/dist/cli/commands/follow.mjs +28 -0
- package/dist/cli/commands/harness.mjs +15 -0
- package/dist/cli/commands/help.mjs +1 -0
- package/dist/cli/commands/init.mjs +3 -0
- package/dist/cli/commands/mcp.mjs +36 -0
- package/dist/cli/commands/model.mjs +54 -0
- package/dist/cli/commands/plugins.mjs +43 -0
- package/dist/cli/commands/prepare.mjs +2 -0
- package/dist/cli/commands/public.mjs +3 -0
- package/dist/cli/commands/resolve.mjs +23 -0
- package/dist/cli/commands/route.mjs +2 -0
- package/dist/cli/commands/runner.mjs +1 -0
- package/dist/cli/commands/scan.mjs +2 -0
- package/dist/cli/commands/setup.mjs +34 -0
- package/dist/cli/commands/sign.mjs +4 -0
- package/dist/cli/commands/signer.mjs +1 -0
- package/dist/cli/commands/strategy.mjs +18 -0
- package/dist/cli/commands/swap.mjs +36 -0
- package/dist/cli/commands/upgrade.mjs +2 -0
- package/dist/cli/commands/vault.mjs +1 -0
- package/dist/cli/commands/version.mjs +2 -0
- package/dist/data/desk-data.mjs +55 -0
- package/dist/data/names.mjs +2 -0
- package/dist/data/providers/farming.mjs +2 -0
- package/dist/equities/index.mjs +1 -0
- package/dist/index.mjs +60 -0
- package/dist/nft-gas-war-guard.mjs +1 -0
- package/dist/onboarding/harness-configs.mjs +4 -0
- package/dist/portfolio-risk.mjs +1 -0
- package/dist/prepare-envelope.mjs +1 -0
- package/dist/public-api/agent-grants.mjs +3 -0
- package/dist/public-control/policy-schema.mjs +1 -0
- package/dist/router/index.mjs +2 -0
- package/dist/scanner/index.mjs +4 -0
- package/dist/signals/index.mjs +1 -0
- package/dist/strategy/index.mjs +3 -0
- package/dist/watch-preferences.mjs +1 -0
- package/package.json +33 -78
- package/public/oracle-splash/_variants/ice.html +5 -18
- package/public/oracle-splash/_variants/ivory.html +5 -18
- package/public/oracle-splash/downloads/index.html +4 -4
- package/public/oracle-splash/index.html +113 -34
- package/CONTRIBUTING.md +0 -99
- package/bin/desk-server.mjs +0 -473
- package/bin/oracle-data-mcp.mjs +0 -1252
- package/bin/oracle-equities.mjs +0 -168
- package/bin/oracle-init.mjs +0 -525
- package/bin/oracle-public-server.mjs +0 -36
- package/bin/oracle-route.mjs +0 -254
- package/bin/oracle-scan.mjs +0 -192
- package/bin/oracle-upgrade.mjs +0 -42
- package/bin/oracle.mjs +0 -34
- package/docs/adding-a-chain.md +0 -229
- package/docs/architecture.md +0 -139
- package/docs/buzz-integration.md +0 -115
- package/docs/cli.md +0 -143
- package/docs/connectors.md +0 -13
- package/docs/hyperliquid-builder-code.md +0 -50
- package/docs/locals-only-fee-waiver.md +0 -34
- package/docs/oracle-pack-standard.md +0 -29
- package/docs/profiles.md +0 -151
- package/docs/public-surface.md +0 -206
- package/examples/add-a-chain.mjs +0 -65
- package/examples/oracle-pack-template.mjs +0 -38
- package/examples/research-a-token.mjs +0 -70
- package/plugins/oracle-owner-gate/__init__.py +0 -288
- package/plugins/oracle-owner-gate/plugin.yaml +0 -9
- package/protocols/templates/safe-erc20/SECURITY.md +0 -16
- package/protocols/templates/safe-erc20/foundry.toml +0 -12
- package/protocols/templates/safe-erc20/remappings.txt +0 -2
- package/protocols/templates/safe-erc20/src/SafeERC20.sol +0 -53
- package/protocols/templates/safe-erc20/test/SafeERC20.t.sol +0 -72
- package/scripts/adversarial-bench.mjs +0 -114
- package/scripts/build-dist.mjs +0 -119
- package/scripts/build-inscription.py +0 -230
- package/scripts/check-doc-drift.mjs +0 -123
- package/scripts/check-test-count.mjs +0 -105
- package/scripts/e2e-hl-markets.mjs +0 -21
- package/scripts/e2e-hl-perps.mjs +0 -48
- package/scripts/e2e-hypercore-staking.mjs +0 -128
- package/scripts/e2e-solana-bitcoin.mjs +0 -183
- package/scripts/guard-publish.mjs +0 -22
- package/scripts/protocol-template-gate.mjs +0 -15
- package/scripts/public-api-scan.mjs +0 -23
- package/scripts/publish-dist.mjs +0 -67
- package/scripts/record-hero-cli.sh +0 -80
- package/scripts/secret-scan.mjs +0 -169
- package/scripts/smoke-dist-bins.mjs +0 -89
- package/scripts/verify-dist.mjs +0 -29
- package/scripts/verify-v3-venues.mjs +0 -233
- package/skins/oracle.yaml +0 -56
- package/src/action-receipts.mjs +0 -265
- package/src/action-semantics.mjs +0 -62
- package/src/address-book.mjs +0 -208
- package/src/agent-auth.mjs +0 -206
- package/src/approval-guard.mjs +0 -282
- package/src/attestation-secret.mjs +0 -94
- package/src/audit-log.mjs +0 -196
- package/src/auth/oauth.mjs +0 -672
- package/src/auto-slippage.mjs +0 -378
- package/src/capability-posture.mjs +0 -125
- package/src/cards.mjs +0 -422
- package/src/chains.mjs +0 -62
- package/src/cli/chain-catalog.mjs +0 -215
- package/src/cli/chain-state.mjs +0 -74
- package/src/cli/commands/auth.mjs +0 -193
- package/src/cli/commands/bootstrap.mjs +0 -87
- package/src/cli/commands/chain.mjs +0 -143
- package/src/cli/commands/chat.mjs +0 -465
- package/src/cli/commands/credential.mjs +0 -9
- package/src/cli/commands/data-mcp.mjs +0 -18
- package/src/cli/commands/data.mjs +0 -75
- package/src/cli/commands/doctor.mjs +0 -136
- package/src/cli/commands/equities.mjs +0 -41
- package/src/cli/commands/farm.mjs +0 -129
- package/src/cli/commands/fees.mjs +0 -109
- package/src/cli/commands/follow.mjs +0 -130
- package/src/cli/commands/harness.mjs +0 -63
- package/src/cli/commands/help.mjs +0 -7
- package/src/cli/commands/init.mjs +0 -27
- package/src/cli/commands/mcp.mjs +0 -154
- package/src/cli/commands/model.mjs +0 -106
- package/src/cli/commands/plugins.mjs +0 -275
- package/src/cli/commands/prepare.mjs +0 -12
- package/src/cli/commands/public.mjs +0 -20
- package/src/cli/commands/resolve.mjs +0 -64
- package/src/cli/commands/route.mjs +0 -12
- package/src/cli/commands/runner.mjs +0 -9
- package/src/cli/commands/scan.mjs +0 -12
- package/src/cli/commands/setup.mjs +0 -294
- package/src/cli/commands/sign.mjs +0 -21
- package/src/cli/commands/signer.mjs +0 -9
- package/src/cli/commands/swap.mjs +0 -102
- package/src/cli/commands/upgrade.mjs +0 -12
- package/src/cli/commands/vault.mjs +0 -9
- package/src/cli/commands/version.mjs +0 -22
- package/src/cli/first-run.mjs +0 -23
- package/src/cli/kernel.mjs +0 -262
- package/src/cli/mcp-targets/chatgpt.mjs +0 -51
- package/src/cli/mcp-targets/claude-code.mjs +0 -39
- package/src/cli/mcp-targets/claude-desktop.mjs +0 -24
- package/src/cli/mcp-targets/codex.mjs +0 -41
- package/src/cli/mcp-targets/cursor.mjs +0 -32
- package/src/cli/mcp-targets/shared.mjs +0 -71
- package/src/cli/mcp-targets/vscode.mjs +0 -39
- package/src/cli/messaging-platforms.mjs +0 -217
- package/src/cli/model-config.mjs +0 -78
- package/src/cli/operator-dispatch.mjs +0 -194
- package/src/cli/oracle-harness.py +0 -415
- package/src/cli/paths.mjs +0 -107
- package/src/cli/runtime.mjs +0 -351
- package/src/cli/setup-state.mjs +0 -168
- package/src/cli/spawn-child.mjs +0 -43
- package/src/data/catalog.mjs +0 -858
- package/src/data/desk-data.mjs +0 -877
- package/src/data/http.mjs +0 -253
- package/src/data/names.mjs +0 -188
- package/src/data/provider-endpoint.mjs +0 -94
- package/src/data/providers/aave-v3.mjs +0 -139
- package/src/data/providers/across-bridge.mjs +0 -41
- package/src/data/providers/aerodrome.mjs +0 -245
- package/src/data/providers/aevo.mjs +0 -33
- package/src/data/providers/approval-indexer.mjs +0 -133
- package/src/data/providers/approvals.mjs +0 -594
- package/src/data/providers/babylon-staking.mjs +0 -149
- package/src/data/providers/balancer.mjs +0 -209
- package/src/data/providers/beefy-yields.mjs +0 -45
- package/src/data/providers/birdeye-tokens.mjs +0 -33
- package/src/data/providers/bisq-markets.mjs +0 -145
- package/src/data/providers/bitcoin-esplora.mjs +0 -216
- package/src/data/providers/bitcoin-meta.mjs +0 -378
- package/src/data/providers/blockscout.mjs +0 -14
- package/src/data/providers/bob-rootstock.mjs +0 -198
- package/src/data/providers/braiins-insights.mjs +0 -103
- package/src/data/providers/bridge-aggregator.mjs +0 -259
- package/src/data/providers/bridges.mjs +0 -242
- package/src/data/providers/bungee.mjs +0 -29
- package/src/data/providers/celestia-da.mjs +0 -38
- package/src/data/providers/changenow.mjs +0 -135
- package/src/data/providers/compound.mjs +0 -32
- package/src/data/providers/connext.mjs +0 -28
- package/src/data/providers/convex.mjs +0 -30
- package/src/data/providers/cow-protocol.mjs +0 -110
- package/src/data/providers/cowswap.mjs +0 -415
- package/src/data/providers/curve.mjs +0 -201
- package/src/data/providers/das-assets.mjs +0 -59
- package/src/data/providers/debridge.mjs +0 -109
- package/src/data/providers/defillama.mjs +0 -88
- package/src/data/providers/dexscreener.mjs +0 -43
- package/src/data/providers/drift.mjs +0 -32
- package/src/data/providers/dydx.mjs +0 -41
- package/src/data/providers/evm-rpc.mjs +0 -225
- package/src/data/providers/farming.mjs +0 -291
- package/src/data/providers/geckoterminal.mjs +0 -34
- package/src/data/providers/gmx.mjs +0 -496
- package/src/data/providers/hiro-stacks.mjs +0 -150
- package/src/data/providers/hl-assets.mjs +0 -165
- package/src/data/providers/hl-info.mjs +0 -108
- package/src/data/providers/hl-markets.mjs +0 -210
- package/src/data/providers/hl-outcome.mjs +0 -61
- package/src/data/providers/hl-perps.mjs +0 -419
- package/src/data/providers/hl-staking.mjs +0 -353
- package/src/data/providers/hl-ws.mjs +0 -119
- package/src/data/providers/hyperevm-dex.mjs +0 -49
- package/src/data/providers/jito-mev.mjs +0 -117
- package/src/data/providers/jupiter-venues.mjs +0 -119
- package/src/data/providers/jupiter.mjs +0 -315
- package/src/data/providers/kamino-strategies.mjs +0 -143
- package/src/data/providers/l2beat-tvl.mjs +0 -42
- package/src/data/providers/lifi.mjs +0 -166
- package/src/data/providers/liquid-esplora.mjs +0 -152
- package/src/data/providers/magiceden-ordinals.mjs +0 -141
- package/src/data/providers/magiceden-sol.mjs +0 -406
- package/src/data/providers/mempool-lightning.mjs +0 -144
- package/src/data/providers/mempool-mining.mjs +0 -142
- package/src/data/providers/meteora-dlmm.mjs +0 -168
- package/src/data/providers/morpho.mjs +0 -177
- package/src/data/providers/nft-gallery.mjs +0 -163
- package/src/data/providers/nft-portfolio.mjs +0 -494
- package/src/data/providers/odos.mjs +0 -156
- package/src/data/providers/ondo.mjs +0 -31
- package/src/data/providers/oneinch.mjs +0 -174
- package/src/data/providers/opensea-multichain.mjs +0 -136
- package/src/data/providers/opensea-nft.mjs +0 -371
- package/src/data/providers/orca.mjs +0 -29
- package/src/data/providers/ordinals-runes.mjs +0 -180
- package/src/data/providers/pancakeswap.mjs +0 -28
- package/src/data/providers/paraswap.mjs +0 -125
- package/src/data/providers/pendle.mjs +0 -188
- package/src/data/providers/poly-clob.mjs +0 -283
- package/src/data/providers/poly-public.mjs +0 -96
- package/src/data/providers/poly-ws.mjs +0 -103
- package/src/data/providers/polygon-staking.mjs +0 -227
- package/src/data/providers/portfolio-history.mjs +0 -394
- package/src/data/providers/portfolio.mjs +0 -600
- package/src/data/providers/pyth-price-feeds.mjs +0 -172
- package/src/data/providers/raydium.mjs +0 -35
- package/src/data/providers/relayscan-mev.mjs +0 -72
- package/src/data/providers/rfq.mjs +0 -15
- package/src/data/providers/rh-agent.mjs +0 -59
- package/src/data/providers/sanctum-lst.mjs +0 -180
- package/src/data/providers/satflow.mjs +0 -341
- package/src/data/providers/signed-material-guard.mjs +0 -108
- package/src/data/providers/solana-rpc.mjs +0 -197
- package/src/data/providers/solend-lending.mjs +0 -166
- package/src/data/providers/spark-lend.mjs +0 -42
- package/src/data/providers/squid-router.mjs +0 -29
- package/src/data/providers/stargate.mjs +0 -29
- package/src/data/providers/sushiswap.mjs +0 -30
- package/src/data/providers/symbiotic-restaking.mjs +0 -41
- package/src/data/providers/thorchain.mjs +0 -36
- package/src/data/providers/traderjoe.mjs +0 -28
- package/src/data/providers/twap.mjs +0 -152
- package/src/data/providers/uniswap-v3.mjs +0 -457
- package/src/data/providers/uniswap-v4.mjs +0 -414
- package/src/data/providers/vertex.mjs +0 -41
- package/src/data/providers/wallet-watch.mjs +0 -264
- package/src/data/providers/wormhole.mjs +0 -28
- package/src/data/providers/yearn.mjs +0 -31
- package/src/data/providers/zerox.mjs +0 -167
- package/src/data/public-api-scan.mjs +0 -61
- package/src/data/quote-placeholder.mjs +0 -31
- package/src/desk/swap-prepare.mjs +0 -194
- package/src/equities/bridge.mjs +0 -388
- package/src/equities/fixtures/bridge_routes.json +0 -159
- package/src/equities/fixtures/dst_transitions.json +0 -29
- package/src/equities/fixtures/equity_dex_pools.json +0 -154
- package/src/equities/fixtures/hip3_l2books.json +0 -2711
- package/src/equities/fixtures/nyse_holidays.json +0 -1
- package/src/equities/fixtures/oracles.json +0 -100
- package/src/equities/fixtures/rh_pool_state.json +0 -244
- package/src/equities/fixtures/solana_xstocks.json +0 -704
- package/src/equities/fixtures/ton_xstocks.json +0 -35
- package/src/equities/fixtures/universe.json +0 -411
- package/src/equities/fixtures/venues.json +0 -3542
- package/src/equities/fixtures.mjs +0 -405
- package/src/equities/funding.mjs +0 -142
- package/src/equities/index.mjs +0 -166
- package/src/equities/liveness.mjs +0 -148
- package/src/equities/marketHours.mjs +0 -249
- package/src/equities/normalize.mjs +0 -295
- package/src/equities/num.mjs +0 -146
- package/src/equities/prepare.mjs +0 -95
- package/src/equities/rank.mjs +0 -260
- package/src/equities/sourceNum.mjs +0 -69
- package/src/equities/types.mjs +0 -220
- package/src/equities/venues/arcus-perp.mjs +0 -84
- package/src/equities/venues/arcus-spot.mjs +0 -105
- package/src/equities/venues/hyperliquid-hip3.mjs +0 -224
- package/src/equities/venues/rh-uniswap.mjs +0 -216
- package/src/equities/venues/solana-xstocks.mjs +0 -139
- package/src/equities/venues/ton-stonfi.mjs +0 -139
- package/src/exact-integer.mjs +0 -72
- package/src/exec-policy.mjs +0 -419
- package/src/flags.mjs +0 -15
- package/src/fresh-window.mjs +0 -76
- package/src/gmx-attestation.mjs +0 -176
- package/src/index.mjs +0 -107
- package/src/licensing/locals-only.mjs +0 -25
- package/src/nft-gas-war-guard.mjs +0 -139
- package/src/onboarding/agent-keys.mjs +0 -161
- package/src/onboarding/harness-configs.mjs +0 -102
- package/src/onboarding/index.mjs +0 -18
- package/src/onboarding/tiers.mjs +0 -139
- package/src/oracle-env.mjs +0 -55
- package/src/package-manifest.mjs +0 -42
- package/src/plugins/agent-plugin-loader.mjs +0 -225
- package/src/portfolio-risk.mjs +0 -170
- package/src/prepare-envelope.mjs +0 -204
- package/src/profile-upgrade.mjs +0 -277
- package/src/protocol-execution.mjs +0 -84
- package/src/protocol-templates/gate.mjs +0 -179
- package/src/protocol-templates/prepare-deploy.mjs +0 -81
- package/src/public-api/agent-grants.mjs +0 -57
- package/src/public-api/buzz-integration.mjs +0 -267
- package/src/public-api/connect-agent.mjs +0 -424
- package/src/public-api/grants.mjs +0 -142
- package/src/public-api/http.mjs +0 -707
- package/src/public-api/metering.mjs +0 -111
- package/src/public-control/aa-adapter.mjs +0 -402
- package/src/public-control/build-registry.mjs +0 -227
- package/src/public-control/bundler-client.mjs +0 -357
- package/src/public-control/grant-indexer.mjs +0 -296
- package/src/public-control/policy-render.mjs +0 -69
- package/src/public-control/policy-schema.mjs +0 -322
- package/src/public-control/runtime-config.mjs +0 -275
- package/src/public-control/session-key-model.mjs +0 -379
- package/src/public-control/session-orchestrator.mjs +0 -418
- package/src/rfq/intent.mjs +0 -180
- package/src/rfq/sources.mjs +0 -181
- package/src/route-attestation.mjs +0 -132
- package/src/router/best-execution.mjs +0 -326
- package/src/router/index.mjs +0 -250
- package/src/router/integrator-fee.mjs +0 -135
- package/src/router/prepare-bridge.mjs +0 -293
- package/src/router/prepare-route.mjs +0 -398
- package/src/router/proposal.mjs +0 -311
- package/src/router/risk-classifier.mjs +0 -119
- package/src/router/route-sources.mjs +0 -364
- package/src/scanner/chains.config.mjs +0 -543
- package/src/scanner/contract.mjs +0 -270
- package/src/scanner/evm-scanner.mjs +0 -394
- package/src/scanner/index.mjs +0 -9
- package/src/scanner/v2-venue.mjs +0 -351
- package/src/scanner/v3-venue.mjs +0 -308
- package/src/scopes.mjs +0 -44
- package/src/sell-simulation.mjs +0 -167
- package/src/signals/engine.mjs +0 -146
- package/src/signals/index.mjs +0 -1
- package/src/token-transfer-guard.mjs +0 -188
- package/src/tui/app.mjs +0 -438
- package/src/tui/backend.mjs +0 -215
- package/src/tui/format.mjs +0 -272
- package/src/tui/gateway-client.mjs +0 -177
- package/src/tui/input.mjs +0 -501
- package/src/tui/memory.mjs +0 -105
- package/src/tui/meta-learn.mjs +0 -85
- package/src/tui/renderer.mjs +0 -443
- package/src/tui/scheduler.mjs +0 -205
- package/src/tui/skills-loader.mjs +0 -140
- package/src/tui/standalone-client.mjs +0 -752
- package/src/tui/theme.mjs +0 -81
- package/src/vault-attestation.mjs +0 -146
- package/src/venues.mjs +0 -206
- package/src/watch-preferences.mjs +0 -85
- /package/{profiles → dist/assets/profiles}/_template/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/_template/profile.json +0 -0
- /package/{profiles → dist/assets/profiles}/bitcoin-agent/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/bitcoin-agent/profile.json +0 -0
- /package/{profiles → dist/assets/profiles}/hyperliquid-agent/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/hyperliquid-agent/profile.json +0 -0
- /package/{profiles → dist/assets/profiles}/oracle/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/oracle/profile.json +0 -0
- /package/{profiles → dist/assets/profiles}/polymarket-agent/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/polymarket-agent/profile.json +0 -0
- /package/{profiles → dist/assets/profiles}/profile.schema.json +0 -0
- /package/{profiles → dist/assets/profiles}/protocol-builder/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/protocol-builder/profile.json +0 -0
- /package/{profiles → dist/assets/profiles}/robinhood-agent/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/robinhood-agent/profile.json +0 -0
- /package/{profiles → dist/assets/profiles}/solana-agent/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/solana-agent/profile.json +0 -0
- /package/{profiles → dist/assets/profiles}/stable-agent/SOUL.md +0 -0
- /package/{profiles → dist/assets/profiles}/stable-agent/profile.json +0 -0
- /package/{skills → dist/assets/skills}/balance/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/bitcoin-l1.md +0 -0
- /package/{skills → dist/assets/skills}/chain-defi-ecosystem-profiling.md +0 -0
- /package/{skills → dist/assets/skills}/evm-contract-research.md +0 -0
- /package/{skills → dist/assets/skills}/hyperliquid.md +0 -0
- /package/{skills → dist/assets/skills}/multichain-exec-desk.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-action-semantics/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-best-execution/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-best-execution.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-bitcoin/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-chain-graphs-telegram-cards/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-chat/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-chat/chain.SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-chat/setup.SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-circuit-breaker/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-contract-research/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-desk/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-desk.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-dex-launch/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-equities/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-grants/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-hypercore-staking/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-hyperliquid/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-meme-token-sniper/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-multichain-nft-launch/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-multichain-token-launch/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-nft-gacha-launch/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-nft-mint-gas-war/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-polymarket/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-protocol-builder/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-protocol-security/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-public-product/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-receipts/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-receipts.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-rfq-tokenized-assets/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-smart-wallet-scanner/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-solana/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-solana-nft/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/oracle-token-research/SKILL.md +0 -0
- /package/{skills → dist/assets/skills}/solana.md +0 -0
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
// Oracle Control — grant audit + active-permission indexer (Slice F).
|
|
2
|
-
//
|
|
3
|
-
// Deterministic, storage-agnostic reconstruction of grant state from an
|
|
4
|
-
// append-only event log. Oracle Control is DETERMINISTIC AUTHORIZATION: this
|
|
5
|
-
// module never guesses, never consults Oracle Router (advisory), and never
|
|
6
|
-
// keeps its own mutable state. The chain (or whatever emits the event log)
|
|
7
|
-
// is the sole source of truth; this file is a pure projection of that log
|
|
8
|
-
// plus an explicit reference `now`.
|
|
9
|
-
//
|
|
10
|
-
// Hard boundaries for this file:
|
|
11
|
-
// - No imports outside src/public-control/policy-schema.mjs (grant identity
|
|
12
|
-
// + expiry helpers are reused from there, never duplicated).
|
|
13
|
-
// - No import from the private executor stack, no RPC/chain calls, no I/O.
|
|
14
|
-
// - Pure functions only: reconstructGrantIndex(events, opts) always returns
|
|
15
|
-
// the same result for the same (events, opts.now) — same events replayed
|
|
16
|
-
// after a "restart" (fresh process, no cache, no prior in-memory state)
|
|
17
|
-
// reconstruct the identical active set. Input array order never matters.
|
|
18
|
-
// - Audit/output records are built from an explicit field whitelist only.
|
|
19
|
-
// Unknown/extra properties on raw events (or on a rejected grant payload)
|
|
20
|
-
// are NEVER copied into output, so a poisoned event can't smuggle a
|
|
21
|
-
// secret (API key, session key, keystore path, ...) into the index.
|
|
22
|
-
//
|
|
23
|
-
// Event log shape (each entry is a plain object; unknown extra fields are
|
|
24
|
-
// ignored, never echoed):
|
|
25
|
-
// { type: "grantCreated", at: <unix seconds>, grant: <raw grant>, grantId?: <string> }
|
|
26
|
-
// { type: "grantActivated", at: <unix seconds>, grantId: <string> }
|
|
27
|
-
// { type: "grantRevoked", at: <unix seconds>, grantId: <string>, reason?: <string> }
|
|
28
|
-
// { type: "grantExpired", at: <unix seconds>, grantId: <string> }
|
|
29
|
-
//
|
|
30
|
-
// `grantId` on a grantCreated event is optional: when omitted it is derived
|
|
31
|
-
// from the grant payload itself via policy-schema's grantId() (content
|
|
32
|
-
// addressed identity). When supplied it is cross-checked against the derived
|
|
33
|
-
// id and the event is rejected on mismatch (fail closed, no id spoofing).
|
|
34
|
-
//
|
|
35
|
-
// Status precedence (revocation beats expiry beats activity — mirrors the
|
|
36
|
-
// same precedence used by session-key-model.mjs's sessionStatus): a grant
|
|
37
|
-
// that was revoked and later would also have expired still reports revoked.
|
|
38
|
-
// REVOKED > EXPIRED > ACTIVE > PENDING > UNKNOWN
|
|
39
|
-
// Only ACTIVE grants are ever included in the `active` set.
|
|
40
|
-
|
|
41
|
-
import { normalizeGrant, grantId as computeGrantId, isExpired, GrantValidationError } from "./policy-schema.mjs";
|
|
42
|
-
|
|
43
|
-
export const GRANT_EVENT_TYPES = Object.freeze([
|
|
44
|
-
"grantCreated",
|
|
45
|
-
"grantActivated",
|
|
46
|
-
"grantRevoked",
|
|
47
|
-
"grantExpired",
|
|
48
|
-
]);
|
|
49
|
-
|
|
50
|
-
export const GRANT_STATUS = Object.freeze({
|
|
51
|
-
ACTIVE: "active",
|
|
52
|
-
PENDING: "pending",
|
|
53
|
-
REVOKED: "revoked",
|
|
54
|
-
EXPIRED: "expired",
|
|
55
|
-
UNKNOWN: "unknown",
|
|
56
|
-
INVALID: "invalid",
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
/** Fields ever allowed into an audit/output record. Nothing outside this
|
|
60
|
-
* whitelist can reach the returned index, no matter what a raw event or
|
|
61
|
-
* grant payload carries. All of these are public-by-design fields already
|
|
62
|
-
* enforced by policy-schema.mjs's REQUIRED_FIELDS. */
|
|
63
|
-
const GRANT_SUMMARY_FIELDS = Object.freeze([
|
|
64
|
-
"chainId",
|
|
65
|
-
"agentAddress",
|
|
66
|
-
"accountAddress",
|
|
67
|
-
"actions",
|
|
68
|
-
"targets",
|
|
69
|
-
"maxValueWei",
|
|
70
|
-
"maxGasWei",
|
|
71
|
-
"maxSlippageBps",
|
|
72
|
-
"expiresAt",
|
|
73
|
-
"nonce",
|
|
74
|
-
"revocationKey",
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
function isPlainObject(v) {
|
|
78
|
-
return v != null && typeof v === "object" && !Array.isArray(v);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function toUnixSeconds(v) {
|
|
82
|
-
if (typeof v === "bigint") return Number(v);
|
|
83
|
-
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
84
|
-
if (typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v.trim()))) {
|
|
85
|
-
return Number(v.trim());
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function toSafeString(v, maxLen = 512) {
|
|
91
|
-
if (typeof v !== "string") return null;
|
|
92
|
-
const s = v.trim();
|
|
93
|
-
if (!s) return null;
|
|
94
|
-
return s.length > maxLen ? s.slice(0, maxLen) : s;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function grantSummary(normalizedGrant) {
|
|
98
|
-
const out = {};
|
|
99
|
-
for (const field of GRANT_SUMMARY_FIELDS) out[field] = normalizedGrant[field];
|
|
100
|
-
return out;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Parse one raw event into a strictly-whitelisted internal shape. Never
|
|
105
|
-
* throws. Extra/unknown properties on `raw` are silently dropped (this is
|
|
106
|
-
* the point — it's the no-secret-leak boundary for whatever noise a chain
|
|
107
|
-
* indexer/relay might attach to an event object).
|
|
108
|
-
*
|
|
109
|
-
* Returns { ok: true, event } or { ok: false, error: { type, at, grantId, message } }.
|
|
110
|
-
*/
|
|
111
|
-
function parseEvent(raw, opts) {
|
|
112
|
-
if (!isPlainObject(raw)) {
|
|
113
|
-
return { ok: false, error: { type: null, at: null, grantId: null, message: "event must be a plain object" } };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const type = typeof raw.type === "string" ? raw.type : null;
|
|
117
|
-
const at = toUnixSeconds(raw.at);
|
|
118
|
-
const grantIdHint = typeof raw.grantId === "string" && raw.grantId.trim() ? raw.grantId.trim() : null;
|
|
119
|
-
|
|
120
|
-
if (!GRANT_EVENT_TYPES.includes(type)) {
|
|
121
|
-
return { ok: false, error: { type, at, grantId: grantIdHint, message: `unknown event type "${String(type)}"` } };
|
|
122
|
-
}
|
|
123
|
-
if (at == null) {
|
|
124
|
-
return { ok: false, error: { type, at: null, grantId: grantIdHint, message: "event.at must be a unix-seconds number" } };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (type === "grantCreated") {
|
|
128
|
-
if (!isPlainObject(raw.grant)) {
|
|
129
|
-
return { ok: false, error: { type, at, grantId: grantIdHint, message: "grantCreated requires a grant payload" } };
|
|
130
|
-
}
|
|
131
|
-
let normalized;
|
|
132
|
-
try {
|
|
133
|
-
// Identity + shape validation only — deliberately WITHOUT opts.now, so
|
|
134
|
-
// a grant that has since expired can still be identified/reconstructed
|
|
135
|
-
// (liveness is evaluated separately against the caller's `now`).
|
|
136
|
-
normalized = normalizeGrant(raw.grant, { allowWildcardActions: opts.allowWildcardActions });
|
|
137
|
-
} catch (e) {
|
|
138
|
-
const message = e instanceof GrantValidationError ? e.message : String(e?.message || e);
|
|
139
|
-
return { ok: false, error: { type, at, grantId: grantIdHint, message } };
|
|
140
|
-
}
|
|
141
|
-
const derivedId = computeGrantId(raw.grant, { allowWildcardActions: opts.allowWildcardActions });
|
|
142
|
-
if (grantIdHint && grantIdHint !== derivedId) {
|
|
143
|
-
return {
|
|
144
|
-
ok: false,
|
|
145
|
-
error: { type, at, grantId: grantIdHint, message: "grantId does not match content-derived grant identity (fail closed)" },
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
return { ok: true, event: { type, at, grantId: derivedId, grant: normalized } };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// grantActivated / grantRevoked / grantExpired all reference an existing
|
|
152
|
-
// grant purely by id — they never carry grant material.
|
|
153
|
-
if (!grantIdHint) {
|
|
154
|
-
return { ok: false, error: { type, at, grantId: null, message: `${type} requires a grantId` } };
|
|
155
|
-
}
|
|
156
|
-
const reason = type === "grantRevoked" ? toSafeString(raw.reason) : null;
|
|
157
|
-
return { ok: true, event: { type, at, grantId: grantIdHint, reason } };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function minOf(list) {
|
|
161
|
-
return list.length ? list.reduce((a, b) => Math.min(a, b)) : null;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Reconstruct the deterministic grant index from a raw event log.
|
|
166
|
-
*
|
|
167
|
-
* @param {object[]} events raw event log (any order, any origin)
|
|
168
|
-
* @param {object} opts
|
|
169
|
-
* @param {number} opts.now REQUIRED unix-seconds reference time. All
|
|
170
|
-
* reconstruction is relative to this instant;
|
|
171
|
-
* pass the same `now` to get the same result.
|
|
172
|
-
* @param {boolean} [opts.allowWildcardActions] forwarded to policy-schema.
|
|
173
|
-
*
|
|
174
|
-
* @returns {{
|
|
175
|
-
* active: object[], grants currently ACTIVE, sorted by grantId
|
|
176
|
-
* historical: object[], everything else (pending/revoked/expired/unknown/invalid), sorted by grantId
|
|
177
|
-
* all: object[], active + historical, sorted by grantId
|
|
178
|
-
* invalidEvents: object[] malformed/rejected raw events (whitelisted fields only)
|
|
179
|
-
* }}
|
|
180
|
-
*/
|
|
181
|
-
export function reconstructGrantIndex(events, opts = {}) {
|
|
182
|
-
const now = toUnixSeconds(opts.now);
|
|
183
|
-
if (now == null) throw new Error("grant-indexer: opts.now (unix seconds) is required");
|
|
184
|
-
if (!Array.isArray(events)) throw new Error("grant-indexer: events must be an array");
|
|
185
|
-
|
|
186
|
-
const invalidEvents = [];
|
|
187
|
-
/** grantId -> { created: [], activated: [], revoked: [], expired: [] } */
|
|
188
|
-
const byId = new Map();
|
|
189
|
-
|
|
190
|
-
function bucket(id) {
|
|
191
|
-
if (!byId.has(id)) {
|
|
192
|
-
byId.set(id, { created: [], activated: [], revoked: [], expired: [] });
|
|
193
|
-
}
|
|
194
|
-
return byId.get(id);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
for (const raw of events) {
|
|
198
|
-
const parsed = parseEvent(raw, opts);
|
|
199
|
-
if (!parsed.ok) {
|
|
200
|
-
invalidEvents.push(parsed.error);
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
const ev = parsed.event;
|
|
204
|
-
// Chain is source of truth for what has already happened: an event dated
|
|
205
|
-
// after the reconstruction instant hasn't occurred *yet* from this
|
|
206
|
-
// vantage point, so it is excluded rather than applied early.
|
|
207
|
-
if (ev.at > now) continue;
|
|
208
|
-
|
|
209
|
-
const b = bucket(ev.grantId);
|
|
210
|
-
if (ev.type === "grantCreated") {
|
|
211
|
-
// Same content-derived id => identical normalized grant; duplicate
|
|
212
|
-
// creations are idempotent. Keep the earliest-by-time occurrence so
|
|
213
|
-
// createdAt is stable and order-independent.
|
|
214
|
-
const existing = b.created[0];
|
|
215
|
-
if (!existing || ev.at < existing.at) b.created = [ev];
|
|
216
|
-
} else if (ev.type === "grantActivated") {
|
|
217
|
-
b.activated.push(ev);
|
|
218
|
-
} else if (ev.type === "grantRevoked") {
|
|
219
|
-
b.revoked.push(ev);
|
|
220
|
-
} else if (ev.type === "grantExpired") {
|
|
221
|
-
b.expired.push(ev);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const records = [];
|
|
226
|
-
for (const [id, b] of byId.entries()) {
|
|
227
|
-
const createdEvent = b.created[0] || null;
|
|
228
|
-
const createdAt = createdEvent ? createdEvent.at : null;
|
|
229
|
-
const activatedAt = minOf(b.activated.map((e) => e.at));
|
|
230
|
-
const revokedAt = minOf(b.revoked.map((e) => e.at));
|
|
231
|
-
const explicitExpiredAt = minOf(b.expired.map((e) => e.at));
|
|
232
|
-
|
|
233
|
-
const grant = createdEvent ? createdEvent.grant : null;
|
|
234
|
-
const schemaExpired = grant ? isExpired(grant, now) : false;
|
|
235
|
-
const expiredAt = explicitExpiredAt != null ? explicitExpiredAt : schemaExpired ? grant.expiresAt : null;
|
|
236
|
-
|
|
237
|
-
let status;
|
|
238
|
-
if (!createdEvent) {
|
|
239
|
-
// Lifecycle events (activated/revoked/expired) referencing a grantId
|
|
240
|
-
// we never saw a valid grantCreated for. Never active — there is no
|
|
241
|
-
// grant content to authorize against.
|
|
242
|
-
status = GRANT_STATUS.UNKNOWN;
|
|
243
|
-
} else if (b.revoked.length > 0) {
|
|
244
|
-
status = GRANT_STATUS.REVOKED;
|
|
245
|
-
} else if (explicitExpiredAt != null || schemaExpired) {
|
|
246
|
-
status = GRANT_STATUS.EXPIRED;
|
|
247
|
-
} else if (b.activated.length > 0) {
|
|
248
|
-
status = GRANT_STATUS.ACTIVE;
|
|
249
|
-
} else {
|
|
250
|
-
status = GRANT_STATUS.PENDING;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const record = {
|
|
254
|
-
grantId: id,
|
|
255
|
-
status,
|
|
256
|
-
createdAt,
|
|
257
|
-
activatedAt,
|
|
258
|
-
revokedAt,
|
|
259
|
-
expiredAt,
|
|
260
|
-
reason: b.revoked.length > 0 ? toSafeString(b.revoked.map((e) => e.reason).find(Boolean) || null) : null,
|
|
261
|
-
grant: grant ? grantSummary(grant) : null,
|
|
262
|
-
};
|
|
263
|
-
records.push(record);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
records.sort((a, b) => (a.grantId < b.grantId ? -1 : a.grantId > b.grantId ? 1 : 0));
|
|
267
|
-
invalidEvents.sort((a, b) => {
|
|
268
|
-
const at = (a.at ?? -Infinity) - (b.at ?? -Infinity);
|
|
269
|
-
if (at !== 0) return at;
|
|
270
|
-
const ai = a.grantId || "";
|
|
271
|
-
const bi = b.grantId || "";
|
|
272
|
-
return ai < bi ? -1 : ai > bi ? 1 : 0;
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
const active = records.filter((r) => r.status === GRANT_STATUS.ACTIVE);
|
|
276
|
-
const historical = records.filter((r) => r.status !== GRANT_STATUS.ACTIVE);
|
|
277
|
-
|
|
278
|
-
return Object.freeze({
|
|
279
|
-
active: Object.freeze(active),
|
|
280
|
-
historical: Object.freeze(historical),
|
|
281
|
-
all: Object.freeze(records),
|
|
282
|
-
invalidEvents: Object.freeze(invalidEvents),
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/** Convenience: is a single grant id active right now, per the given log? */
|
|
287
|
-
export function isGrantActive(events, grantId, opts = {}) {
|
|
288
|
-
const { active } = reconstructGrantIndex(events, opts);
|
|
289
|
-
return active.some((r) => r.grantId === grantId);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/** Convenience: look up one grant's reconstructed record (or null). */
|
|
293
|
-
export function getGrantRecord(events, grantId, opts = {}) {
|
|
294
|
-
const { all } = reconstructGrantIndex(events, opts);
|
|
295
|
-
return all.find((r) => r.grantId === grantId) || null;
|
|
296
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// Oracle Control — human-readable grant renderer (Slice A).
|
|
2
|
-
//
|
|
3
|
-
// Renders a validated public grant into deterministic plain text a user can
|
|
4
|
-
// review before approving. Rendering is a PRESENTATION of the deterministic
|
|
5
|
-
// policy only — it never adds, removes, or reinterprets authority, and it only
|
|
6
|
-
// ever surfaces public grant material (no keys, tokens, paths, or secrets).
|
|
7
|
-
//
|
|
8
|
-
// Output is byte-for-byte deterministic for a given grant: fixed field order,
|
|
9
|
-
// fixed labels, UTC timestamps derived purely from expiresAt, and the sha256
|
|
10
|
-
// grant id from the canonical form. Tests assert exact output.
|
|
11
|
-
|
|
12
|
-
import { normalizeGrant, grantId, isReadonlyAction, GRANT_VERSION } from "./policy-schema.mjs";
|
|
13
|
-
|
|
14
|
-
const LABEL_WIDTH = 14;
|
|
15
|
-
|
|
16
|
-
function row(label, value) {
|
|
17
|
-
return `${(label + ":").padEnd(LABEL_WIDTH)}${value}`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Deterministic UTC ISO string from unix seconds. */
|
|
21
|
-
function isoUtc(unixSeconds) {
|
|
22
|
-
return new Date(unixSeconds * 1000).toISOString();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** "50 bps (0.50%)" — deterministic fixed-2 percentage. */
|
|
26
|
-
export function formatBps(bps) {
|
|
27
|
-
return `${bps} bps (${(bps / 100).toFixed(2)}%)`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Group wei digits for readability: 1000000 -> "1,000,000". Deterministic. */
|
|
31
|
-
export function formatWei(weiString) {
|
|
32
|
-
const s = String(weiString);
|
|
33
|
-
return `${s.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} wei`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Render a grant as deterministic human-readable text.
|
|
38
|
-
* Validates/normalizes first (throws GrantValidationError on bad input), so a
|
|
39
|
-
* grant that cannot be rendered is a grant that cannot be authorized.
|
|
40
|
-
*/
|
|
41
|
-
export function renderGrant(input, opts = {}) {
|
|
42
|
-
const g = normalizeGrant(input, opts);
|
|
43
|
-
const id = grantId(g);
|
|
44
|
-
const readonlyOnly = g.actions.every((a) => isReadonlyAction(a));
|
|
45
|
-
|
|
46
|
-
const lines = [
|
|
47
|
-
"ORACLE CONTROL GRANT (deterministic authorization)",
|
|
48
|
-
"==================================================",
|
|
49
|
-
row("Grant ID", id),
|
|
50
|
-
row("Version", String(GRANT_VERSION)),
|
|
51
|
-
row("Chain", String(g.chainId)),
|
|
52
|
-
row("Agent", g.agentAddress),
|
|
53
|
-
row("Account", g.accountAddress),
|
|
54
|
-
row("Actions", g.actions.join(", ")),
|
|
55
|
-
row("Targets", g.targets.length ? g.targets.join(", ") : "(none — read/simulate only)"),
|
|
56
|
-
row("Max value", formatWei(g.maxValueWei)),
|
|
57
|
-
row("Max gas", formatWei(g.maxGasWei)),
|
|
58
|
-
row("Max slippage", formatBps(g.maxSlippageBps)),
|
|
59
|
-
row("Expires", `${isoUtc(g.expiresAt)} (unix ${g.expiresAt})`),
|
|
60
|
-
row("Nonce", g.nonce),
|
|
61
|
-
row("Revocation", g.revocationKey),
|
|
62
|
-
"--------------------------------------------------",
|
|
63
|
-
readonlyOnly
|
|
64
|
-
? "Scope: read/simulate only — this grant cannot move funds."
|
|
65
|
-
: "Scope: includes state-changing actions bounded by the caps above.",
|
|
66
|
-
"Self-custodial: this grant contains public data only and can be revoked at any time via the revocation key.",
|
|
67
|
-
];
|
|
68
|
-
return lines.join("\n");
|
|
69
|
-
}
|
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
// Oracle Control — public grant policy schema (Slice A).
|
|
2
|
-
//
|
|
3
|
-
// Oracle Control is DETERMINISTIC AUTHORIZATION: a grant either validates or it
|
|
4
|
-
// does not, with no advisory judgement involved (that is Oracle Router's job,
|
|
5
|
-
// and Router output must never gate anything here).
|
|
6
|
-
//
|
|
7
|
-
// This module is dependency-free (node builtins only, no npm packages) and
|
|
8
|
-
// fully deterministic: same input object => same normalized grant => same
|
|
9
|
-
// canonical string => same grant id, regardless of key order, address casing,
|
|
10
|
-
// array ordering, or numeric representation (number / bigint / decimal string).
|
|
11
|
-
//
|
|
12
|
-
// Self-custody boundary: a grant only ever references PUBLIC material —
|
|
13
|
-
// chain id, the agent's public address, the user's own account address,
|
|
14
|
-
// action scopes, target contracts, spend/gas/slippage ceilings, expiry,
|
|
15
|
-
// nonce, and a public revocation key identifier. Nothing here may carry or
|
|
16
|
-
// derive private keys, bearer tokens, keystore paths, or session secrets.
|
|
17
|
-
//
|
|
18
|
-
// Fail-closed rules enforced by validateGrant / normalizeGrant:
|
|
19
|
-
// - every required field must be present (expiry, chain, agent, account, ...)
|
|
20
|
-
// - unknown fields are rejected (no smuggling extra authority)
|
|
21
|
-
// - broad wildcard actions are rejected by default ("*", "*:*", "verb:*")
|
|
22
|
-
// - empty targets are only allowed when EVERY action is read/simulate scoped
|
|
23
|
-
// - expiresAt is required and, when a reference `now` is supplied, must be
|
|
24
|
-
// strictly in the future
|
|
25
|
-
|
|
26
|
-
import { createHash } from "node:crypto";
|
|
27
|
-
|
|
28
|
-
export const GRANT_VERSION = 1;
|
|
29
|
-
|
|
30
|
-
/** Every field is required. Order here is documentation only; canonical form
|
|
31
|
-
* always sorts keys alphabetically. */
|
|
32
|
-
export const REQUIRED_FIELDS = Object.freeze([
|
|
33
|
-
"chainId",
|
|
34
|
-
"agentAddress",
|
|
35
|
-
"accountAddress",
|
|
36
|
-
"actions",
|
|
37
|
-
"targets",
|
|
38
|
-
"maxValueWei",
|
|
39
|
-
"maxGasWei",
|
|
40
|
-
"maxSlippageBps",
|
|
41
|
-
"expiresAt",
|
|
42
|
-
"nonce",
|
|
43
|
-
"revocationKey",
|
|
44
|
-
]);
|
|
45
|
-
|
|
46
|
-
/** Action verbs that never touch state; only these may have empty targets. */
|
|
47
|
-
export const READONLY_ACTION_VERBS = Object.freeze(["read", "simulate"]);
|
|
48
|
-
|
|
49
|
-
export const MAX_SLIPPAGE_BPS = 10_000;
|
|
50
|
-
export const MAX_GRANT_TTL_SECONDS = 24 * 60 * 60;
|
|
51
|
-
export const MAX_NONCE_LENGTH = 128;
|
|
52
|
-
export const MAX_REVOCATION_KEY_LENGTH = 256;
|
|
53
|
-
export const MAX_ACTIONS = 64;
|
|
54
|
-
export const MAX_TARGETS = 256;
|
|
55
|
-
|
|
56
|
-
const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
57
|
-
// verb:resource — lowercase verb, resource may carry dots/dashes/underscores.
|
|
58
|
-
const ACTION_RE = /^[a-z][a-z0-9_-]*:[a-z0-9._-]+$/;
|
|
59
|
-
// wildcard-bearing action (only accepted when explicitly opted in, and never
|
|
60
|
-
// with a wildcard verb).
|
|
61
|
-
const ACTION_WILDCARD_RE = /^[a-z][a-z0-9_-]*:\*$/;
|
|
62
|
-
const NONCE_RE = /^[A-Za-z0-9._:-]+$/;
|
|
63
|
-
const REVOCATION_KEY_RE = /^[A-Za-z0-9._:-]+$/;
|
|
64
|
-
const DECIMAL_RE = /^(0|[1-9][0-9]*)$/;
|
|
65
|
-
|
|
66
|
-
export class GrantValidationError extends Error {
|
|
67
|
-
constructor(errors) {
|
|
68
|
-
const lines = errors.map((e) => `${e.field}: ${e.message}`);
|
|
69
|
-
super(`invalid grant (${errors.length} error${errors.length === 1 ? "" : "s"}): ${lines.join("; ")}`);
|
|
70
|
-
this.name = "GrantValidationError";
|
|
71
|
-
this.errors = errors;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function isPlainObject(v) {
|
|
76
|
-
return v != null && typeof v === "object" && !Array.isArray(v);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Parse a non-negative integer amount (number | bigint | decimal string)
|
|
80
|
-
* into a canonical decimal string. Returns null when unparseable. */
|
|
81
|
-
function toAmountString(v) {
|
|
82
|
-
if (typeof v === "bigint") {
|
|
83
|
-
return v >= 0n ? v.toString(10) : null;
|
|
84
|
-
}
|
|
85
|
-
if (typeof v === "number") {
|
|
86
|
-
if (!Number.isSafeInteger(v) || v < 0) return null;
|
|
87
|
-
return String(v);
|
|
88
|
-
}
|
|
89
|
-
if (typeof v === "string") {
|
|
90
|
-
const s = v.trim();
|
|
91
|
-
if (!DECIMAL_RE.test(s)) return null;
|
|
92
|
-
return s;
|
|
93
|
-
}
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function toSafeInt(v) {
|
|
98
|
-
if (typeof v === "bigint") {
|
|
99
|
-
if (v < 0n || v > BigInt(Number.MAX_SAFE_INTEGER)) return null;
|
|
100
|
-
return Number(v);
|
|
101
|
-
}
|
|
102
|
-
if (typeof v === "number") {
|
|
103
|
-
if (!Number.isSafeInteger(v) || v < 0) return null;
|
|
104
|
-
return v;
|
|
105
|
-
}
|
|
106
|
-
if (typeof v === "string") {
|
|
107
|
-
const s = v.trim();
|
|
108
|
-
if (!DECIMAL_RE.test(s)) return null;
|
|
109
|
-
const n = Number(s);
|
|
110
|
-
return Number.isSafeInteger(n) ? n : null;
|
|
111
|
-
}
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** True when the action verb is read-only (read/simulate). */
|
|
116
|
-
export function isReadonlyAction(action) {
|
|
117
|
-
const verb = String(action).split(":")[0];
|
|
118
|
-
return READONLY_ACTION_VERBS.includes(verb);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Validate a grant. Never throws; returns { ok, errors, grant } where `grant`
|
|
123
|
-
* is the normalized form when ok, else null.
|
|
124
|
-
*
|
|
125
|
-
* Options:
|
|
126
|
-
* allowWildcardActions (default false) — permit "verb:*" actions. Broad
|
|
127
|
-
* wildcards ("*", "*:*", wildcard verbs) are ALWAYS
|
|
128
|
-
* rejected regardless of this flag.
|
|
129
|
-
* now (unix seconds) — when provided, expiresAt must be
|
|
130
|
-
* strictly greater than it. Pass explicitly for
|
|
131
|
-
* deterministic validation; omitted means no liveness
|
|
132
|
-
* check (pure shape validation).
|
|
133
|
-
*/
|
|
134
|
-
export function validateGrant(input, opts = {}) {
|
|
135
|
-
const errors = [];
|
|
136
|
-
const push = (field, message) => errors.push({ field, message });
|
|
137
|
-
|
|
138
|
-
if (!isPlainObject(input)) {
|
|
139
|
-
push("grant", "grant must be a plain object");
|
|
140
|
-
return { ok: false, errors, grant: null };
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Fail closed on unknown fields.
|
|
144
|
-
for (const key of Object.keys(input)) {
|
|
145
|
-
if (!REQUIRED_FIELDS.includes(key)) push(key, "unknown field is not allowed");
|
|
146
|
-
}
|
|
147
|
-
for (const key of REQUIRED_FIELDS) {
|
|
148
|
-
if (!(key in input) || input[key] == null) push(key, "required field is missing");
|
|
149
|
-
}
|
|
150
|
-
if (errors.length) return { ok: false, errors, grant: null };
|
|
151
|
-
|
|
152
|
-
const out = {};
|
|
153
|
-
|
|
154
|
-
// chainId
|
|
155
|
-
const chainId = toSafeInt(input.chainId);
|
|
156
|
-
if (chainId == null || chainId <= 0) push("chainId", "must be a positive integer chain id");
|
|
157
|
-
else out.chainId = chainId;
|
|
158
|
-
|
|
159
|
-
// addresses
|
|
160
|
-
for (const field of ["agentAddress", "accountAddress"]) {
|
|
161
|
-
const v = input[field];
|
|
162
|
-
if (typeof v !== "string" || !ADDRESS_RE.test(v.trim())) {
|
|
163
|
-
push(field, "must be a 0x-prefixed 20-byte hex address");
|
|
164
|
-
} else {
|
|
165
|
-
out[field] = v.trim().toLowerCase();
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// actions
|
|
170
|
-
if (!Array.isArray(input.actions) || input.actions.length === 0) {
|
|
171
|
-
push("actions", "must be a non-empty array of action scopes");
|
|
172
|
-
} else if (input.actions.length > MAX_ACTIONS) {
|
|
173
|
-
push("actions", `too many actions (max ${MAX_ACTIONS})`);
|
|
174
|
-
} else {
|
|
175
|
-
const actions = [];
|
|
176
|
-
for (const raw of input.actions) {
|
|
177
|
-
const a = typeof raw === "string" ? raw.trim() : null;
|
|
178
|
-
if (!a) {
|
|
179
|
-
push("actions", "action entries must be non-empty strings");
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (a.includes("*")) {
|
|
183
|
-
if (opts.allowWildcardActions === true && ACTION_WILDCARD_RE.test(a)) {
|
|
184
|
-
actions.push(a);
|
|
185
|
-
} else {
|
|
186
|
-
push("actions", `broad wildcard action "${a}" is rejected (fail closed)`);
|
|
187
|
-
}
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
if (!ACTION_RE.test(a)) {
|
|
191
|
-
push("actions", `malformed action "${a}" (expected verb:resource)`);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
actions.push(a);
|
|
195
|
-
}
|
|
196
|
-
out.actions = [...new Set(actions)].sort();
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// targets
|
|
200
|
-
if (!Array.isArray(input.targets)) {
|
|
201
|
-
push("targets", "must be an array of contract addresses");
|
|
202
|
-
} else if (input.targets.length > MAX_TARGETS) {
|
|
203
|
-
push("targets", `too many targets (max ${MAX_TARGETS})`);
|
|
204
|
-
} else {
|
|
205
|
-
const targets = [];
|
|
206
|
-
for (const raw of input.targets) {
|
|
207
|
-
const t = typeof raw === "string" ? raw.trim() : null;
|
|
208
|
-
if (!t || !ADDRESS_RE.test(t)) {
|
|
209
|
-
push("targets", `target "${raw}" must be a 0x-prefixed 20-byte hex address (wildcards not allowed)`);
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
targets.push(t.toLowerCase());
|
|
213
|
-
}
|
|
214
|
-
out.targets = [...new Set(targets)].sort();
|
|
215
|
-
if (out.targets.length === 0 && Array.isArray(out.actions)) {
|
|
216
|
-
const nonReadonly = out.actions.filter((a) => !isReadonlyAction(a));
|
|
217
|
-
if (nonReadonly.length > 0 || out.actions.length === 0) {
|
|
218
|
-
push(
|
|
219
|
-
"targets",
|
|
220
|
-
`empty targets are only allowed for read/simulate scopes (blocked by: ${nonReadonly.join(", ") || "no valid actions"})`
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// amounts
|
|
227
|
-
for (const field of ["maxValueWei", "maxGasWei"]) {
|
|
228
|
-
const amt = toAmountString(input[field]);
|
|
229
|
-
if (amt == null) push(field, "must be a non-negative integer wei amount");
|
|
230
|
-
else out[field] = amt;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// slippage
|
|
234
|
-
const bps = toSafeInt(input.maxSlippageBps);
|
|
235
|
-
if (bps == null || bps > MAX_SLIPPAGE_BPS) {
|
|
236
|
-
push("maxSlippageBps", `must be an integer between 0 and ${MAX_SLIPPAGE_BPS}`);
|
|
237
|
-
} else {
|
|
238
|
-
out.maxSlippageBps = bps;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// expiry — required (checked above); must be a positive unix-seconds int and,
|
|
242
|
-
// when `now` is supplied, strictly in the future.
|
|
243
|
-
// Date-renderable ceiling in unix seconds. JS Date maxes at 8.64e15 ms, so a
|
|
244
|
-
// seconds value above 8.64e12 overflows the renderer (isoUtc does *1000).
|
|
245
|
-
// Also well within the AA lane's uint48 bound, so a schema-valid expiry always
|
|
246
|
-
// fits both the on-chain validUntil field and the human render. Reject rather
|
|
247
|
-
// than render a bogus far-future date (fail closed on the seconds-vs-ms mistake).
|
|
248
|
-
const MAX_UNIX_SECONDS = 8_640_000_000_000;
|
|
249
|
-
const expiresAt = toSafeInt(input.expiresAt);
|
|
250
|
-
if (expiresAt == null || expiresAt <= 0) {
|
|
251
|
-
push("expiresAt", "must be a positive unix-seconds timestamp");
|
|
252
|
-
} else if (expiresAt > MAX_UNIX_SECONDS) {
|
|
253
|
-
push("expiresAt", "exceeds the max unix-seconds timestamp (pass seconds, not milliseconds)");
|
|
254
|
-
} else {
|
|
255
|
-
out.expiresAt = expiresAt;
|
|
256
|
-
if (opts.now != null) {
|
|
257
|
-
const now = toSafeInt(opts.now);
|
|
258
|
-
if (now == null) push("expiresAt", "opts.now must be a unix-seconds integer");
|
|
259
|
-
else if (expiresAt <= now) push("expiresAt", `grant is expired (expiresAt ${expiresAt} <= now ${now})`);
|
|
260
|
-
else if (expiresAt - now > MAX_GRANT_TTL_SECONDS) {
|
|
261
|
-
push("expiresAt", `grant exceeds the hard 24-hour TTL (${MAX_GRANT_TTL_SECONDS} seconds)`);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// nonce
|
|
267
|
-
const nonce =
|
|
268
|
-
typeof input.nonce === "string"
|
|
269
|
-
? input.nonce.trim()
|
|
270
|
-
: toSafeInt(input.nonce) != null
|
|
271
|
-
? String(toSafeInt(input.nonce))
|
|
272
|
-
: null;
|
|
273
|
-
if (!nonce || nonce.length > MAX_NONCE_LENGTH || !NONCE_RE.test(nonce)) {
|
|
274
|
-
push("nonce", `must be a non-empty string (max ${MAX_NONCE_LENGTH} chars, [A-Za-z0-9._:-])`);
|
|
275
|
-
} else {
|
|
276
|
-
out.nonce = nonce;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// revocationKey — a PUBLIC identifier used to revoke this grant; never a secret.
|
|
280
|
-
const rk = typeof input.revocationKey === "string" ? input.revocationKey.trim() : null;
|
|
281
|
-
if (!rk || rk.length > MAX_REVOCATION_KEY_LENGTH || !REVOCATION_KEY_RE.test(rk)) {
|
|
282
|
-
push("revocationKey", `must be a non-empty public identifier (max ${MAX_REVOCATION_KEY_LENGTH} chars, [A-Za-z0-9._:-])`);
|
|
283
|
-
} else {
|
|
284
|
-
out.revocationKey = rk;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (errors.length) return { ok: false, errors, grant: null };
|
|
288
|
-
return { ok: true, errors: [], grant: out };
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/** Validate + normalize; throws GrantValidationError on any failure. */
|
|
292
|
-
export function normalizeGrant(input, opts = {}) {
|
|
293
|
-
const { ok, errors, grant } = validateGrant(input, opts);
|
|
294
|
-
if (!ok) throw new GrantValidationError(errors);
|
|
295
|
-
return grant;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Deterministic canonical JSON for a grant: validates/normalizes first, then
|
|
300
|
-
* serializes with alphabetically sorted keys (arrays already sorted + deduped
|
|
301
|
-
* by normalization). Identical grants — regardless of input key order, address
|
|
302
|
-
* casing, or numeric representation — always produce the identical string.
|
|
303
|
-
*/
|
|
304
|
-
export function canonicalizeGrant(input, opts = {}) {
|
|
305
|
-
const g = normalizeGrant(input, opts);
|
|
306
|
-
const keys = Object.keys(g).sort();
|
|
307
|
-
const parts = keys.map((k) => `${JSON.stringify(k)}:${JSON.stringify(g[k])}`);
|
|
308
|
-
return `{"version":${GRANT_VERSION},${parts.join(",")}}`;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/** sha256 hex of the canonical form — stable public identifier for a grant. */
|
|
312
|
-
export function grantId(input, opts = {}) {
|
|
313
|
-
return createHash("sha256").update(canonicalizeGrant(input, opts), "utf8").digest("hex");
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/** Deterministic expiry check against an explicit unix-seconds `now`. */
|
|
317
|
-
export function isExpired(grant, nowSeconds) {
|
|
318
|
-
const now = toSafeInt(nowSeconds);
|
|
319
|
-
const exp = toSafeInt(isPlainObject(grant) ? grant.expiresAt : null);
|
|
320
|
-
if (now == null || exp == null) return true; // fail closed
|
|
321
|
-
return exp <= now;
|
|
322
|
-
}
|