|
| 1 | +# Hydrate |
| 2 | + |
| 3 | +Build `hydratePayload` bytes for **HydrateProxy** (`hydrateExecute` / `hydrateExecuteAndSweep` in trails-contracts) by resolving patch targets with [`abicalldata`](https://github.com/0xsequence/go-sequence/tree/master/lib/abicalldata) **`Selector`** and **`ByteRange`** instead of hand-counting calldata offsets. |
| 4 | + |
| 5 | +`hydrate` depends only on `abicalldata` for selector types. The example below builds an `abicalldata.Selector` using `Path` from `lib/sapient/malleable`; fixed offsets can use `abicalldata.NewRangeSelector` instead. |
| 6 | + |
| 7 | +## Usage |
| 8 | + |
| 9 | +```go |
| 10 | +payload := v3.NewCallsPayload(...) |
| 11 | + |
| 12 | +// Selector must resolve to exactly one range on the target call (same index as ForCall). |
| 13 | +permitOwner := malleable.NewPath(). |
| 14 | + CallData(0). |
| 15 | + ABI(trailsABI, "hydrateExecute"). |
| 16 | + ArgBytesData("packedPayload"). |
| 17 | + EncodedCallsPayload(). |
| 18 | + EncodedCallData(0). |
| 19 | + ABI(erc2612ABI, "permit"). |
| 20 | + ArgSlot("owner"). |
| 21 | + AsSelector() |
| 22 | + |
| 23 | +b := hydrate.NewBuilder(&payload) |
| 24 | + |
| 25 | +if err := b.ForCall(0).DataAddress(permitOwner, hydrate.SourceSelf()); err != nil { |
| 26 | + return err |
| 27 | +} |
| 28 | + |
| 29 | +hydratePayload, err := b.Build() |
| 30 | +if err != nil { |
| 31 | + return err |
| 32 | +} |
| 33 | +// nil/empty hydratePayload means "no hydration" (contract skips hydration). |
| 34 | + |
| 35 | +calldata, err := hydrate.PackHydrateExecute(packedPayload, hydratePayload) |
| 36 | +``` |
| 37 | + |
| 38 | +With sweep after execution: |
| 39 | + |
| 40 | +```go |
| 41 | +calldata, err := hydrate.PackHydrateExecuteAndSweep( |
| 42 | + packedPayload, |
| 43 | + hydratePayload, |
| 44 | + sweepTarget, |
| 45 | + tokensToSweep, |
| 46 | + sweepNative, |
| 47 | +) |
| 48 | +``` |
| 49 | + |
| 50 | +If ABI parameters are unnamed, use index-based steps such as `ArgSlotIndex` / `ArgBytesDataIndex` when your path builder provides them. |
| 51 | + |
| 52 | +## Call sections and ordering |
| 53 | + |
| 54 | +- Use `ForCall(tindex)` for each packed call you want to hydrate. Multiple methods on the same `ForCall` append commands to that call's section. |
| 55 | +- `Build()` emits sections in **ascending** `tindex` order, each as: `[tindex byte][commands…][0x00]`. That order matches how the proxy walks the stream while executing calls; sections must not be reordered arbitrarily. |
| 56 | + |
| 57 | +## Address sources |
| 58 | + |
| 59 | +Data patches and `CallTo` / `CallValue` use a low nibble on the command byte; optional literals append 20 bytes when needed: |
| 60 | + |
| 61 | +| Helper | Meaning at execution time | |
| 62 | +|--------|---------------------------| |
| 63 | +| `SourceSelf()` | `address(this)` (the proxy) | |
| 64 | +| `SourceMsgSender()` | `msg.sender` | |
| 65 | +| `SourceTxOrigin()` | `tx.origin` | |
| 66 | +| `SourceAddress(a)` | fixed `a` (encoded after the flag) | |
| 67 | + |
| 68 | +## Selectors |
| 69 | + |
| 70 | +Each `Data*` method requires an `abicalldata.Selector` that resolves to **exactly one** `abicalldata.ByteRange`, and that range's `CallIndex` must equal the `ForCall` index. The range's offset within that call's `data` becomes the patch offset (`uint16`); the builder checks that 20-byte (address) or 32-byte (uint256) replacements fit. |
| 71 | + |
| 72 | +For unit tests or fixed layouts, `abicalldata.NewRangeSelector(callIndex, offset, size)` returns a selector backed by an explicit range. |
| 73 | + |
| 74 | +When using a multi-step path into nested calldata, ABI context is cleared after any step that narrows the active range or switches to a new byte frame (including `.Slice`, `.ArgBytesData`, `.ArgBytesDataIndex`, `.ArgBytesEncoded`, `.EncodedCallsPayload`, `.EncodedCallData`). Call `.ABI(contractABI, method)` again before `.ArgSlot`, `.ArgSlotIndex`, `.ArgBytesData`, `.ArgBytesDataIndex`, or `.ArgBytesEncoded` on that frame. |
0 commit comments