@dev.sail.money/sailor 0.0.2-23 → 0.0.2-27
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/AGENTS.md +1 -2
- package/README.md +62 -11
- package/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +6 -8
- package/examples/permissions/SailCalldata.sol +118 -0
- package/package.json +9 -3
- package/packages/cli/dist/index.cjs +326 -116
- package/packages/cli/dist/server.cjs +586 -1401
- package/packages/sdk/dist/chains.d.ts +12 -0
- package/packages/sdk/dist/chains.d.ts.map +1 -0
- package/packages/sdk/dist/chains.js +88 -0
- package/packages/sdk/dist/chains.js.map +1 -0
- package/packages/sdk/dist/index.d.ts +2 -1
- package/packages/sdk/dist/index.d.ts.map +1 -1
- package/packages/sdk/dist/index.js +2 -1
- package/packages/sdk/dist/index.js.map +1 -1
- package/packages/sdk/dist/intelligence.d.ts +1 -1
- package/packages/sdk/dist/intelligence.js +1 -1
- package/packages/sdk/dist/lifi.d.ts +17 -0
- package/packages/sdk/dist/lifi.d.ts.map +1 -1
- package/packages/sdk/dist/lifi.js +24 -0
- package/packages/sdk/dist/lifi.js.map +1 -1
- package/packages/sdk/dist/types.d.ts +17 -1
- package/packages/sdk/dist/types.d.ts.map +1 -1
- package/packages/sdk/package.json +28 -0
- package/packages/ui/dist/assets/{add-BcGCle88.js → add-qTuQMFTK.js} +1 -1
- package/packages/ui/dist/assets/{all-wallets-8Jcfw5Qj.js → all-wallets-C-2_6Yfv.js} +1 -1
- package/packages/ui/dist/assets/{app-store-R1-af7b6.js → app-store-DIz363Ij.js} +1 -1
- package/packages/ui/dist/assets/{apple-Cxitz1aK.js → apple-Bm2XNMO1.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-n-1aGCMa.js → arrow-bottom-D7B-UpVg.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-circle-BGQ9w6zi.js → arrow-bottom-circle-XB0ks3C3.js} +1 -1
- package/packages/ui/dist/assets/{arrow-left-BngH26cQ.js → arrow-left-_d9tAksG.js} +1 -1
- package/packages/ui/dist/assets/{arrow-right-Bd1vyPNZ.js → arrow-right-By10t6nk.js} +1 -1
- package/packages/ui/dist/assets/{arrow-top-nst6Ttr2.js → arrow-top-CrLIWHvo.js} +1 -1
- package/packages/ui/dist/assets/{bank-3aeQOng_.js → bank-DZC26LyI.js} +1 -1
- package/packages/ui/dist/assets/{basic-Ds-ESc-H.js → basic-C2XYdmOJ.js} +1 -1
- package/packages/ui/dist/assets/{browser-COcw6X9p.js → browser-URMwBp02.js} +1 -1
- package/packages/ui/dist/assets/{card-B8clsijS.js → card-DI1X7fa2.js} +1 -1
- package/packages/ui/dist/assets/{ccip-SgLYYQWq.js → ccip-CkI0EKXG.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-CxpPWG40.js → checkmark-DRPP3O33.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-bold-3THr8VIu.js → checkmark-bold-BXlTXuID.js} +1 -1
- package/packages/ui/dist/assets/{chevron-bottom-HfWcF1bE.js → chevron-bottom-BtIA5-Zw.js} +1 -1
- package/packages/ui/dist/assets/{chevron-left-CkXbcOst.js → chevron-left-CR6HwOU6.js} +1 -1
- package/packages/ui/dist/assets/{chevron-right-Bt7ATlLQ.js → chevron-right-DubfEKW-.js} +1 -1
- package/packages/ui/dist/assets/{chevron-top-BZc4xycM.js → chevron-top-BrZ5ZZx1.js} +1 -1
- package/packages/ui/dist/assets/{chrome-store-IT1ftwJy.js → chrome-store-DdtEbnZQ.js} +1 -1
- package/packages/ui/dist/assets/{clock-DpBvmJiH.js → clock-CbGzJbXx.js} +1 -1
- package/packages/ui/dist/assets/{close-BoRwHijk.js → close-BdZW5KIv.js} +1 -1
- package/packages/ui/dist/assets/{coinPlaceholder-Dba1nosr.js → coinPlaceholder-BXu1Nsdu.js} +1 -1
- package/packages/ui/dist/assets/{compass-D4QnmoWi.js → compass-CZQzEYcb.js} +1 -1
- package/packages/ui/dist/assets/{copy-iBRvR2f1.js → copy-fCcrYd5O.js} +1 -1
- package/packages/ui/dist/assets/{core-B0JxSbAV.js → core-B_rlQlCa.js} +3 -3
- package/packages/ui/dist/assets/cursor-CNWux1oe.js +3 -0
- package/packages/ui/dist/assets/{cursor-transparent-Djp2Lulv.js → cursor-transparent-BvuhytdN.js} +1 -1
- package/packages/ui/dist/assets/{desktop-DF6t43QS.js → desktop-AwX0UzWw.js} +1 -1
- package/packages/ui/dist/assets/{disconnect-BUeUrh3r.js → disconnect-B2DnHNge.js} +1 -1
- package/packages/ui/dist/assets/{discord-DWtvIwBI.js → discord-CcFyxhBQ.js} +1 -1
- package/packages/ui/dist/assets/{etherscan-CrPMVPiO.js → etherscan-D5kNPWk4.js} +1 -1
- package/packages/ui/dist/assets/{events-DC84dMPF.js → events-C8ePWdzE.js} +1 -1
- package/packages/ui/dist/assets/{exclamation-triangle-9IcrwUru.js → exclamation-triangle-DyQ_1Efw.js} +1 -1
- package/packages/ui/dist/assets/{extension-_qe-89Jo.js → extension-jz2s06tW.js} +1 -1
- package/packages/ui/dist/assets/{external-link-Bsar6cmv.js → external-link-2ybQxumz.js} +1 -1
- package/packages/ui/dist/assets/{facebook-CZnM2tIe.js → facebook-DS8jxA10.js} +1 -1
- package/packages/ui/dist/assets/{fallback-BOfZ_bwu.js → fallback-B9fs84IX.js} +1 -1
- package/packages/ui/dist/assets/{farcaster-DyBlkt7c.js → farcaster-BBJafyOu.js} +1 -1
- package/packages/ui/dist/assets/{filters-DwCepBxH.js → filters-CNeaqp_S.js} +1 -1
- package/packages/ui/dist/assets/{github-DgFcdJPt.js → github-Bc07zhCy.js} +1 -1
- package/packages/ui/dist/assets/{google-Csj-pWAm.js → google-v8uzPHNZ.js} +1 -1
- package/packages/ui/dist/assets/{help-circle-CF8XrZBx.js → help-circle-DxtX1OZF.js} +1 -1
- package/packages/ui/dist/assets/{id-YqFg_Hnr.js → id-Br98ANoH.js} +1 -1
- package/packages/ui/dist/assets/{image-DvX7Dg9U.js → image-Bmk8NyWu.js} +1 -1
- package/packages/ui/dist/assets/{index-DsS2DJdh.js → index-C3xXe8Pu.js} +1 -1
- package/packages/ui/dist/assets/{index-Da0fOMbp.js → index-C8u3uWA-.js} +1 -1
- package/packages/ui/dist/assets/{index-BiN726SD.js → index-D8e304bv.js} +1 -1
- package/packages/ui/dist/assets/{index-3OLndEW6.js → index-DK4KEqZ6.js} +3 -3
- package/packages/ui/dist/assets/{index-BR_6qS4k.js → index-noDAXths.js} +1 -1
- package/packages/ui/dist/assets/index-tz2OP5vg.js +1775 -0
- package/packages/ui/dist/assets/{index.es-BiIWW5o1.js → index.es-DO70eR0a.js} +4 -4
- package/packages/ui/dist/assets/{info-zvmQXfcd.js → info-Dm75IcvR.js} +1 -1
- package/packages/ui/dist/assets/{info-circle-poBAGMBs.js → info-circle-COqN3U18.js} +1 -1
- package/packages/ui/dist/assets/{lightbulb-Dce_oD8K.js → lightbulb-oXBKSpqT.js} +1 -1
- package/packages/ui/dist/assets/{mail-dmvvhqN6.js → mail-CzH77S-9.js} +1 -1
- package/packages/ui/dist/assets/{metamask-sdk-BnCx3I3r.js → metamask-sdk-kTTPRXnX.js} +1 -1
- package/packages/ui/dist/assets/{mobile-B8JxvnR1.js → mobile-DC8p7uWG.js} +1 -1
- package/packages/ui/dist/assets/{more-BwL7SLTo.js → more-CW76Ufwh.js} +1 -1
- package/packages/ui/dist/assets/{network-placeholder-BGXRLaM_.js → network-placeholder-C-jgFto6.js} +1 -1
- package/packages/ui/dist/assets/{nftPlaceholder-YKLlOwZU.js → nftPlaceholder-CMNOse1o.js} +1 -1
- package/packages/ui/dist/assets/{off-BdCZJUYq.js → off-BXAtst7s.js} +1 -1
- package/packages/ui/dist/assets/{parseSignature-D9i4CKzP.js → parseSignature-huIOIOFv.js} +1 -1
- package/packages/ui/dist/assets/{play-store-3UXzYs09.js → play-store-CAA_V4_P.js} +1 -1
- package/packages/ui/dist/assets/{plus-42VUC7wg.js → plus-o-2XxIRf.js} +1 -1
- package/packages/ui/dist/assets/{qr-code-W99JyxAx.js → qr-code-BucbLbnn.js} +1 -1
- package/packages/ui/dist/assets/{recycle-horizontal-B2t10e4m.js → recycle-horizontal-CzM8G600.js} +1 -1
- package/packages/ui/dist/assets/{refresh-DEFqVlG3.js → refresh-UBxc9Ujg.js} +1 -1
- package/packages/ui/dist/assets/{reown-logo-BpYt7vTo.js → reown-logo-DouNSg_O.js} +1 -1
- package/packages/ui/dist/assets/{search-C38Hy_cf.js → search-B9_0X948.js} +1 -1
- package/packages/ui/dist/assets/{secp256k1-IKD5pd64.js → secp256k1-CnLx1wuv.js} +1 -1
- package/packages/ui/dist/assets/{send-gSwkftFg.js → send-D2ETNZ0F.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontal-C1XbgPr5.js → swapHorizontal-6R0tJDBj.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalBold-CtEf5r93.js → swapHorizontalBold-YzWWnx8n.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalMedium-CHP_nvzn.js → swapHorizontalMedium-DD4yeAY1.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalRoundedBold-Crnco-Af.js → swapHorizontalRoundedBold-DmL4h2Mk.js} +1 -1
- package/packages/ui/dist/assets/{swapVertical-Ozkl0BQE.js → swapVertical-DWUQf-aB.js} +1 -1
- package/packages/ui/dist/assets/{telegram-BdfzLFDW.js → telegram-BWYqex9D.js} +1 -1
- package/packages/ui/dist/assets/{three-dots-H2XRHPIG.js → three-dots-kuxAOnjJ.js} +1 -1
- package/packages/ui/dist/assets/{twitch-DvbgB-BL.js → twitch-Cqjm9Lmi.js} +1 -1
- package/packages/ui/dist/assets/{twitterIcon-BBsTYQzn.js → twitterIcon-qeJ4RH6T.js} +1 -1
- package/packages/ui/dist/assets/{verify-3A_7IJxL.js → verify-C2pfbtI7.js} +1 -1
- package/packages/ui/dist/assets/{verify-filled-B4sP0yi_.js → verify-filled-DxEdt6vx.js} +1 -1
- package/packages/ui/dist/assets/{w3m-modal-Bfua7kiP.js → w3m-modal-Dm1gaw9i.js} +1 -1
- package/packages/ui/dist/assets/{wallet-BbJ989Xh.js → wallet-B7p1_8EB.js} +1 -1
- package/packages/ui/dist/assets/{wallet-placeholder-CQ2v8t-c.js → wallet-placeholder-BY8k3n1-.js} +1 -1
- package/packages/ui/dist/assets/{walletconnect-BuShZt17.js → walletconnect-Bg6lRwqy.js} +1 -1
- package/packages/ui/dist/assets/{warning-circle-DMs8QNCr.js → warning-circle-DqLCpUJ0.js} +1 -1
- package/packages/ui/dist/assets/{x-DzP75KD1.js → x-WtAqjAcW.js} +1 -1
- package/packages/ui/dist/index.html +1 -1
- package/templates/custom-mandate/README.md +31 -0
- package/templates/custom-mandate/mandates/BoundedCallPermission.sol +8 -2
- package/templates/custom-mandate/mandates/SailCalldata.sol +118 -0
- package/templates/default/AGENTS.md +51 -2
- package/templates/default/examples/dca/agent.ts +1 -1
- package/templates/default/examples/dca/mandate.ts +1 -1
- package/templates/default/src/agent.ts +1 -1
- package/templates/default/tsconfig.json +5 -2
- package/templates/lifi-permissions/README.md +1 -1
- package/packages/ui/dist/assets/cursor-D3cYdnOt.js +0 -3
- package/packages/ui/dist/assets/index-yQSvDbVa.js +0 -1775
package/AGENTS.md
CHANGED
|
@@ -8,9 +8,8 @@ tooling to create SMAs, register permission contracts, and run strategy agents.
|
|
|
8
8
|
|
|
9
9
|
| Package / path | Name | Role |
|
|
10
10
|
|---|---|---|
|
|
11
|
-
| `packages/sdk` | `@sail/sdk` | SailorClient, LocalKeyring, kernel ABIs, EIP-712 builders, deployment registry |
|
|
11
|
+
| `packages/sdk` | `@sail/sdk` | SailorClient, LocalKeyring, kernel ABIs, EIP-712 builders, deployment registry, per-chain address registry |
|
|
12
12
|
| `packages/cli` | `sailor` | CLI: init, keys, account, mandate, onboard, station, ui, run, session, scan, status, owner, doctor, capabilities |
|
|
13
|
-
| `packages/chains` | `@sail/chains` | Per-chain address registry (kernel, mandateFactory, governance) |
|
|
14
13
|
| `packages/ui` | `sailor-ui` | Local dashboard + browser-driven onboarding wizard at localhost:3333 |
|
|
15
14
|
| `templates/default` | — | Default agent starter: neutral blank scaffold + Foundry workspace + onboarding guide (AGENTS.md) |
|
|
16
15
|
| `templates/custom-mandate` | — | Solidity reference: IPermission scaffold (not a project template) |
|
package/README.md
CHANGED
|
@@ -10,8 +10,8 @@ Sailor is the operator layer for [Sail Protocol](../SailProtocol): the tooling a
|
|
|
10
10
|
|
|
11
11
|
| Package | Name | Role |
|
|
12
12
|
|---|---|---|
|
|
13
|
-
| `packages/sdk` | `@sail/sdk` | TypeScript library
|
|
14
|
-
| `packages/cli` |
|
|
13
|
+
| `packages/sdk` | `@sail/sdk` (internal) | TypeScript library: SailorClient, EIP-712 helpers, ABIs, chain registry |
|
|
14
|
+
| `packages/cli` | `@sail.money/sailor` | CLI for account setup, mandate signing, and agent execution |
|
|
15
15
|
| `packages/chains` | `@sail/chains` | Per-chain address registry (EVM-compatible) |
|
|
16
16
|
| `packages/ui` | `sailor-ui` | Local dashboard running on localhost:3333 |
|
|
17
17
|
| `templates/default` | — | Default agent starter (neutral; what `sailor init` scaffolds) |
|
|
@@ -74,7 +74,7 @@ Prerequisites:
|
|
|
74
74
|
- Node.js 18+
|
|
75
75
|
- A wallet (MetaMask or Rabby)
|
|
76
76
|
- An RPC URL (e.g. Alchemy free tier)
|
|
77
|
-
- A supported chain: **Base, Base Sepolia, Arbitrum, or Unichain** — verified deployments are bundled in `@sail
|
|
77
|
+
- A supported chain: **Base, Base Sepolia, Arbitrum, or Unichain** — verified deployments are bundled in `@sail.money/sailor`.
|
|
78
78
|
|
|
79
79
|
### Recommended — assistant-driven
|
|
80
80
|
|
|
@@ -187,8 +187,7 @@ sailor ui stop
|
|
|
187
187
|
|
|
188
188
|
## Agent-driven onboarding & custom mandates
|
|
189
189
|
|
|
190
|
-
For chains with a bundled Sail deployment (Base, Base Sepolia, Arbitrum, Unichain
|
|
191
|
-
in `@sail/sdk`, no `@sail/chains` entry required), an agent can drive the whole
|
|
190
|
+
For chains with a bundled Sail deployment (Base, Base Sepolia, Arbitrum, Unichain), an agent can drive the whole
|
|
192
191
|
setup through a browser **signing station**. The station is a local HTTP +
|
|
193
192
|
WebSocket daemon that bridges the CLI and the owner's wallet: the agent never
|
|
194
193
|
holds the owner key — it pushes signing requests, the owner approves them in the
|
|
@@ -246,6 +245,60 @@ non-interactively. No private key ever appears in the workflow file or in secret
|
|
|
246
245
|
|
|
247
246
|
---
|
|
248
247
|
|
|
248
|
+
## Packages
|
|
249
|
+
|
|
250
|
+
Sailor ships as a **single npm package** — the SDK is bundled inside it and exposed via a subpath export:
|
|
251
|
+
|
|
252
|
+
| Package | Contents |
|
|
253
|
+
|---|---|
|
|
254
|
+
| `@sail.money/sailor` | CLI binary, UI server, templates, examples, and SDK |
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
npm install @sail.money/sailor # latest stable
|
|
258
|
+
npm install @sail.money/sailor@dev # latest dev build
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The SDK is available as a subpath export for use in agent code:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import type { Agent, AgentContext, Dispatch } from '@sail.money/sailor/sdk'
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Dev builds carry a prerelease version suffix and are published under the `dev` dist-tag; production releases use `latest`:
|
|
268
|
+
|
|
269
|
+
| Trigger | Version | dist-tag |
|
|
270
|
+
|---|---|---|
|
|
271
|
+
| Tag push (`v*`) | `1.0.0` | `latest` |
|
|
272
|
+
| Manual dispatch | `1.0.0-42` | `dev` |
|
|
273
|
+
|
|
274
|
+
### Publishing flow
|
|
275
|
+
|
|
276
|
+
A single CI job builds sailor and publishes it to npm. No separate SDK publish step — the SDK dist lives inside the sailor package and is accessed via the `./sdk` subpath export.
|
|
277
|
+
|
|
278
|
+
The workflow (`publish-npm.yml`) calls the reusable `publish-npm-public-pkg.yaml`:
|
|
279
|
+
- Tag push → version unchanged, dist-tag `latest`
|
|
280
|
+
- Manual dispatch → version stamped with run number (e.g. `1.0.0-42`), dist-tag `dev`
|
|
281
|
+
|
|
282
|
+
### GitHub Packages
|
|
283
|
+
|
|
284
|
+
`@sailagent/sailor` is also published to GitHub Packages for internal testing — no public npm registry required. GitHub Packages requires the `@sailagent` scope; install it with:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
npm install @sailagent/sailor --registry https://npm.pkg.github.com
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
To keep `@sail.money/sailor/sdk` imports working in agent code when installing from GitHub Packages, add a package alias to your project's `package.json`:
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
"dependencies": {
|
|
294
|
+
"@sail.money/sailor": "npm:@sailagent/sailor"
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
This resolves `@sail.money/sailor` (and its `./sdk` subpath) to the `@sailagent/sailor` package transparently.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
249
302
|
## Architecture
|
|
250
303
|
|
|
251
304
|
```
|
|
@@ -268,7 +321,7 @@ non-interactively. No private key ever appears in the workflow file or in secret
|
|
|
268
321
|
│ (register perms) │ │ (custody) │ │ (named, per-call) │
|
|
269
322
|
└────────────────────┘ └────────────────────┘ └────────────────────┘
|
|
270
323
|
|
|
271
|
-
sailor CLI / @sail/sdk drive both signing paths above.
|
|
324
|
+
sailor CLI / @sail.money/sailor/sdk drive both signing paths above.
|
|
272
325
|
.sail/ (account · mandate · activity) ──→ sailor-ui (localhost:3333)
|
|
273
326
|
```
|
|
274
327
|
|
|
@@ -290,13 +343,13 @@ The CLI and SDK sit between the operator and SailKernel: they build the EIP-712
|
|
|
290
343
|
|
|
291
344
|
Sailor is functional and published as [`@sail.money/sailor`](https://www.npmjs.com/package/@sail.money/sailor) on npm (v0.0.1). The SDK, CLI, keystore, mandate flows, agent runner, and dashboard are implemented and have been exercised end to end against Base Sepolia.
|
|
292
345
|
|
|
293
|
-
The Sail Protocol trusted core is deployed on Base, Base Sepolia, Arbitrum, and Unichain as staging deployments for testing ahead of a formal launch. All four run the selective dispatch model, with verified deployments bundled in `@sail/
|
|
346
|
+
The Sail Protocol trusted core is deployed on Base, Base Sepolia, Arbitrum, and Unichain as staging deployments for testing ahead of a formal launch. All four run the selective dispatch model, with verified deployments bundled in `@sail.money/sailor`. These deployments are under an ongoing external audit by [Octane Security](https://octane.security) and are not final — do not use them with funds you are not prepared to lose. Permission templates are not yet deployed against the Base, Arbitrum, and Base Sepolia kernels; **Unichain** ships the full template suite (7 shared + 12 standalone, source-verified) and its template registries are populated. The remaining template registries will be filled in as templates are deployed on the other chains and at mainnet launch.
|
|
294
347
|
|
|
295
348
|
---
|
|
296
349
|
|
|
297
350
|
## Deployments
|
|
298
351
|
|
|
299
|
-
The Sail Protocol trusted core is live on the following chains as **staging deployments** ahead of a formal launch
|
|
352
|
+
The Sail Protocol trusted core is live on the following chains as **staging deployments** ahead of a formal launch. All addresses are bundled in `@sail.money/sailor` and run the selective dispatch model with zero fees. Permission templates are not yet deployed against the Base, Arbitrum, and Base Sepolia kernels; **Unichain** ships the full template suite (7 shared + 12 standalone, source-verified on uniscan.xyz) and has its onboarding allowlists seeded at genesis.
|
|
300
353
|
|
|
301
354
|
### Base (8453)
|
|
302
355
|
|
|
@@ -348,9 +401,7 @@ First chain to ship the full permission-template suite (7 shared + 12 standalone
|
|
|
348
401
|
| SafeModuleEnabler | `0xFE9227A9F2baf704060c604466df354a5A137b9B` |
|
|
349
402
|
| Treasury | `0xB01dCE443d052e44b7D13726c0EC9fFB7f5815B6` |
|
|
350
403
|
|
|
351
|
-
The 19 template addresses are in
|
|
352
|
-
|
|
353
|
-
Addresses are sourced from `@sail/sdk` (`packages/sdk/src/deployments.ts`), the canonical registry.
|
|
404
|
+
The 19 template addresses are in `packages/sdk/src/deployments.ts` (`knownTemplates` + `standaloneTemplates` for chain 130).
|
|
354
405
|
|
|
355
406
|
---
|
|
356
407
|
|
|
@@ -33,6 +33,7 @@ pragma solidity 0.8.26;
|
|
|
33
33
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
34
|
|
|
35
35
|
import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
|
|
36
|
+
import {SailCalldata} from "./SailCalldata.sol";
|
|
36
37
|
|
|
37
38
|
contract BoundedSupply_AaveV3_Arbitrum is IPermission {
|
|
38
39
|
bytes32 private constant DISCRIMINATOR = keccak256("BoundedSupply_AaveV3_Arbitrum");
|
|
@@ -63,15 +64,12 @@ contract BoundedSupply_AaveV3_Arbitrum is IPermission {
|
|
|
63
64
|
if (ctx.target != AAVE_POOL) return false;
|
|
64
65
|
if (ctx.selector != SEL_SUPPLY) return false;
|
|
65
66
|
// supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)
|
|
66
|
-
|
|
67
|
-
if (txData.length < 4 + 4 * 32) return false;
|
|
67
|
+
if (!SailCalldata.hasParams(txData, 4)) return false;
|
|
68
68
|
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/* uint16 referralCode — not bounded */
|
|
74
|
-
) = abi.decode(txData[4:], (address, uint256, address, uint16));
|
|
69
|
+
address asset = SailCalldata.asAddress(txData, 0);
|
|
70
|
+
uint256 amount = SailCalldata.asUint256(txData, 1);
|
|
71
|
+
address onBehalfOf = SailCalldata.asAddress(txData, 2);
|
|
72
|
+
// slot 3 = referralCode (uint16) — informational, not bounded
|
|
75
73
|
|
|
76
74
|
if (!isAllowedAsset[asset]) return false;
|
|
77
75
|
if (amount > MAX_SUPPLY_AMOUNT) return false;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
// SailCalldata — safe static-parameter extraction for IPermission.evaluate()
|
|
6
|
+
//
|
|
7
|
+
// PROBLEM
|
|
8
|
+
// evaluate(bytes calldata txData, Context calldata ctx) receives raw ABI-
|
|
9
|
+
// encoded calldata. Extracting parameters by hand is the most dangerous part
|
|
10
|
+
// of permission writing:
|
|
11
|
+
// • Forgetting the length check before decoding → silent wrong value or
|
|
12
|
+
// revert instead of clean `return false`.
|
|
13
|
+
// • Wrong slot index → decoding the wrong parameter (type-checks, still wrong).
|
|
14
|
+
// • Truncation bugs when casting uint256 slots to address (padding bits).
|
|
15
|
+
//
|
|
16
|
+
// SOLUTION
|
|
17
|
+
// This library centralises the three operations into named helpers:
|
|
18
|
+
// 1. hasParams() — explicit length guard (call once, at the top of evaluate)
|
|
19
|
+
// 2. asAddress() — extract + mask to 20 bytes
|
|
20
|
+
// 3. asUint256(), asInt256(), asBytes32(), asBool(), asUint128() — typed slots
|
|
21
|
+
//
|
|
22
|
+
// All helpers are view/pure and add zero gas overhead beyond the slice itself.
|
|
23
|
+
//
|
|
24
|
+
// USAGE (replace abi.decode pattern)
|
|
25
|
+
//
|
|
26
|
+
// // Before:
|
|
27
|
+
// if (txData.length < 4 + 3 * 32) return false;
|
|
28
|
+
// (address asset, uint256 amount, address onBehalfOf) =
|
|
29
|
+
// abi.decode(txData[4:], (address, uint256, address));
|
|
30
|
+
//
|
|
31
|
+
// // After:
|
|
32
|
+
// if (!SailCalldata.hasParams(txData, 3)) return false;
|
|
33
|
+
// address asset = SailCalldata.asAddress(txData, 0);
|
|
34
|
+
// uint256 amount = SailCalldata.asUint256(txData, 1);
|
|
35
|
+
// address onBehalfOf = SailCalldata.asAddress(txData, 2);
|
|
36
|
+
//
|
|
37
|
+
// LIMITATIONS
|
|
38
|
+
// Only covers static (fixed-size) ABI types. Dynamic types (bytes, string,
|
|
39
|
+
// arrays) use pointer indirection — decode them with abi.decode(txData[4:], ...)
|
|
40
|
+
// after the hasParams() guard, or write a dedicated extractor.
|
|
41
|
+
//
|
|
42
|
+
// SLOT INDEXING
|
|
43
|
+
// Slots are 0-indexed from the first constructor parameter (after the 4-byte
|
|
44
|
+
// selector). Slot 0 = bytes [4..35], slot 1 = bytes [36..67], etc.
|
|
45
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
library SailCalldata {
|
|
48
|
+
// ── Guards ────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
/// @notice Returns true when txData is long enough to hold `params` static
|
|
51
|
+
/// 32-byte ABI slots after the 4-byte selector.
|
|
52
|
+
/// @dev Call this once at the top of evaluate() before any slot access.
|
|
53
|
+
/// Returns false (not revert) so evaluate() can return false cleanly.
|
|
54
|
+
function hasParams(bytes calldata txData, uint256 params) internal pure returns (bool) {
|
|
55
|
+
return txData.length >= 4 + params * 32;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Static-type extractors ────────────────────────────────────────────────
|
|
59
|
+
// All assume hasParams() has already been checked for the relevant slot.
|
|
60
|
+
// Accessing an out-of-bounds slot will cause the calldata slice to revert —
|
|
61
|
+
// always guard with hasParams() first.
|
|
62
|
+
|
|
63
|
+
/// @notice Extract an address from ABI slot `i` (0-indexed after selector).
|
|
64
|
+
/// Masks to 20 bytes, discarding the ABI zero-padding in the upper 12.
|
|
65
|
+
function asAddress(bytes calldata txData, uint256 i) internal pure returns (address) {
|
|
66
|
+
return address(uint160(uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]))));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// @notice Extract a uint256 from ABI slot `i`.
|
|
70
|
+
function asUint256(bytes calldata txData, uint256 i) internal pure returns (uint256) {
|
|
71
|
+
return uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// @notice Extract an int256 from ABI slot `i`.
|
|
75
|
+
function asInt256(bytes calldata txData, uint256 i) internal pure returns (int256) {
|
|
76
|
+
return int256(uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32])));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// @notice Extract a bytes32 from ABI slot `i`.
|
|
80
|
+
function asBytes32(bytes calldata txData, uint256 i) internal pure returns (bytes32) {
|
|
81
|
+
return bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// @notice Extract a bool from ABI slot `i` (true when slot value is non-zero).
|
|
85
|
+
function asBool(bytes calldata txData, uint256 i) internal pure returns (bool) {
|
|
86
|
+
return asUint256(txData, i) != 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// @notice Extract a uint128 from ABI slot `i` (lower 128 bits of the slot).
|
|
90
|
+
function asUint128(bytes calldata txData, uint256 i) internal pure returns (uint128) {
|
|
91
|
+
return uint128(asUint256(txData, i));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// @notice Extract a uint64 from ABI slot `i`.
|
|
95
|
+
function asUint64(bytes calldata txData, uint256 i) internal pure returns (uint64) {
|
|
96
|
+
return uint64(asUint256(txData, i));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// @notice Extract a uint32 from ABI slot `i`.
|
|
100
|
+
function asUint32(bytes calldata txData, uint256 i) internal pure returns (uint32) {
|
|
101
|
+
return uint32(asUint256(txData, i));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// @notice Extract a uint24 from ABI slot `i` (e.g. Uniswap fee tier).
|
|
105
|
+
function asUint24(bytes calldata txData, uint256 i) internal pure returns (uint24) {
|
|
106
|
+
return uint24(asUint256(txData, i));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// @notice Extract a uint16 from ABI slot `i` (e.g. Aave referral code).
|
|
110
|
+
function asUint16(bytes calldata txData, uint256 i) internal pure returns (uint16) {
|
|
111
|
+
return uint16(asUint256(txData, i));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// @notice Extract bytes4 (a function selector) from ABI slot `i`.
|
|
115
|
+
function asBytes4(bytes calldata txData, uint256 i) internal pure returns (bytes4) {
|
|
116
|
+
return bytes4(asBytes32(txData, i));
|
|
117
|
+
}
|
|
118
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev.sail.money/sailor",
|
|
3
|
-
"version": "0.0.2-
|
|
3
|
+
"version": "0.0.2-27",
|
|
4
4
|
"description": "Operator toolkit for Sail Protocol",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sailor": "packages/cli/dist/index.cjs"
|
|
7
7
|
},
|
|
8
|
+
"exports": {
|
|
9
|
+
"./sdk": {
|
|
10
|
+
"import": "./packages/sdk/dist/index.js",
|
|
11
|
+
"types": "./packages/sdk/dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
8
14
|
"files": [
|
|
9
15
|
"packages/cli/dist",
|
|
10
16
|
"packages/sdk/dist",
|
|
@@ -18,12 +24,12 @@
|
|
|
18
24
|
"AGENTS.md"
|
|
19
25
|
],
|
|
20
26
|
"scripts": {
|
|
21
|
-
"build": "pnpm --filter @sail/sdk build && pnpm --filter
|
|
27
|
+
"build": "pnpm --filter @sail/sdk build && pnpm --filter sailor build && pnpm --filter sailor-ui build",
|
|
22
28
|
"test": "pnpm --filter sailor-ui test",
|
|
23
29
|
"test:ui": "pnpm --filter sailor-ui test:ui",
|
|
24
30
|
"link:cli": "pnpm link --global",
|
|
25
31
|
"postinstall": "node scripts/postinstall.js",
|
|
26
|
-
"typecheck": "pnpm --filter @sail/sdk build && pnpm
|
|
32
|
+
"typecheck": "pnpm --filter @sail/sdk build && pnpm -r typecheck",
|
|
27
33
|
"docs:check": "node scripts/check-docs.mjs",
|
|
28
34
|
"init:check": "node scripts/check-init.mjs",
|
|
29
35
|
"eval": "node evals/run.mjs",
|