@eco-incorp/sauce 0.99.5 → 0.99.6

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.
Files changed (81) hide show
  1. package/docs/concepts/architecture.md +4 -2
  2. package/docs/concepts/saucescript.md +19 -2
  3. package/docs/guides/builders-and-actions.md +4 -1
  4. package/docs/guides/compiling.md +5 -3
  5. package/docs/guides/intents.md +32 -2
  6. package/docs/guides/protocols-and-tokens.md +21 -1
  7. package/package.json +1 -1
  8. package/sdk/dist/embedded/skills.generated.js +6 -6
  9. package/sdk/dist/embedded/skills.generated.js.map +1 -1
  10. package/sdk/dist/embedded/sources.generated.js +2 -2
  11. package/sdk/dist/embedded/sources.generated.js.map +1 -1
  12. package/sdk/dist/plugin/index.d.ts +3 -2
  13. package/sdk/dist/plugin/index.d.ts.map +1 -1
  14. package/sdk/dist/plugin/index.js.map +1 -1
  15. package/sdk/dist/protocols/cbeth/functions.d.ts +2 -2
  16. package/sdk/dist/protocols/cbeth/functions.d.ts.map +1 -1
  17. package/sdk/dist/protocols/cbeth/functions.js +4 -6
  18. package/sdk/dist/protocols/cbeth/functions.js.map +1 -1
  19. package/sdk/dist/protocols/crvusd/functions.d.ts +2 -2
  20. package/sdk/dist/protocols/crvusd/functions.d.ts.map +1 -1
  21. package/sdk/dist/protocols/crvusd/functions.js +6 -6
  22. package/sdk/dist/protocols/erc20/functions.d.ts +3 -3
  23. package/sdk/dist/protocols/erc20/functions.d.ts.map +1 -1
  24. package/sdk/dist/protocols/erc20/functions.js +6 -9
  25. package/sdk/dist/protocols/erc20/functions.js.map +1 -1
  26. package/sdk/dist/protocols/frax/functions.d.ts +2 -2
  27. package/sdk/dist/protocols/frax/functions.d.ts.map +1 -1
  28. package/sdk/dist/protocols/frax/functions.js +6 -6
  29. package/sdk/dist/protocols/gho/functions.d.ts +2 -2
  30. package/sdk/dist/protocols/gho/functions.d.ts.map +1 -1
  31. package/sdk/dist/protocols/gho/functions.js +4 -6
  32. package/sdk/dist/protocols/gho/functions.js.map +1 -1
  33. package/sdk/dist/protocols/liquity-v2/functions.d.ts +2 -2
  34. package/sdk/dist/protocols/liquity-v2/functions.d.ts.map +1 -1
  35. package/sdk/dist/protocols/liquity-v2/functions.js +4 -6
  36. package/sdk/dist/protocols/liquity-v2/functions.js.map +1 -1
  37. package/sdk/dist/recipes/cctp-split.sauce.ts +3 -1
  38. package/sdk/dist/recipes/plain-transfer.sauce.ts +3 -0
  39. package/sdk/dist/routes/ast-walk.d.ts +4 -0
  40. package/sdk/dist/routes/ast-walk.d.ts.map +1 -1
  41. package/sdk/dist/routes/ast-walk.js +9 -2
  42. package/sdk/dist/routes/ast-walk.js.map +1 -1
  43. package/sdk/dist/routes/chain-exports.generated.d.ts.map +1 -1
  44. package/sdk/dist/routes/chain-exports.generated.js.map +1 -1
  45. package/sdk/dist/routes/index.d.ts +2 -2
  46. package/sdk/dist/routes/index.d.ts.map +1 -1
  47. package/sdk/dist/routes/index.js +1 -1
  48. package/sdk/dist/routes/index.js.map +1 -1
  49. package/sdk/dist/routes/intent-sugar.d.ts.map +1 -1
  50. package/sdk/dist/routes/intent-sugar.js +40 -23
  51. package/sdk/dist/routes/intent-sugar.js.map +1 -1
  52. package/sdk/dist/routes/sauce-route.d.ts.map +1 -1
  53. package/sdk/dist/routes/sauce-route.js +26 -23
  54. package/sdk/dist/routes/sauce-route.js.map +1 -1
  55. package/sdk/dist/routes/source-globals.generated.d.ts +7 -3
  56. package/sdk/dist/routes/source-globals.generated.d.ts.map +1 -1
  57. package/sdk/dist/routes/token-registry.d.ts.map +1 -1
  58. package/sdk/dist/routes/token-registry.js +6 -1
  59. package/sdk/dist/routes/token-registry.js.map +1 -1
  60. package/sdk/dist/routes/token-rewrite.d.ts +63 -28
  61. package/sdk/dist/routes/token-rewrite.d.ts.map +1 -1
  62. package/sdk/dist/routes/token-rewrite.js +390 -122
  63. package/sdk/dist/routes/token-rewrite.js.map +1 -1
  64. package/sdk/dist/token/api.d.ts +7 -4
  65. package/sdk/dist/token/api.d.ts.map +1 -1
  66. package/sdk/dist/token/api.js.map +1 -1
  67. package/sdk/dist/token/evm.js +2 -2
  68. package/sdk/dist/token/index.d.ts +3 -3
  69. package/sdk/dist/token/index.js +3 -3
  70. package/sdk/src/protocols/cbeth/functions.ts +4 -6
  71. package/sdk/src/protocols/crvusd/functions.ts +6 -6
  72. package/sdk/src/protocols/erc20/functions.ts +6 -9
  73. package/sdk/src/protocols/frax/functions.ts +6 -6
  74. package/sdk/src/protocols/gho/functions.ts +4 -6
  75. package/sdk/src/protocols/liquity-v2/functions.ts +4 -6
  76. package/sdk/src/skills/cbeth.md +4 -6
  77. package/sdk/src/skills/crvusd.md +6 -6
  78. package/sdk/src/skills/erc20.md +8 -10
  79. package/sdk/src/skills/frax.md +6 -6
  80. package/sdk/src/skills/gho.md +4 -6
  81. package/sdk/src/skills/liquity-v2.md +4 -6
@@ -28,8 +28,10 @@ USDC.approve(Uniswap.UniversalRouter, 1_000_000n);
28
28
  ```
29
29
 
30
30
  The destination chain picks which `USDC` and which router that means. Before compiling, the SDK
31
- rewrites those names into ABI-bound contract calls, supplies the matching ABIs and injects the
32
- addresses. The compiler sees the equivalent `IERC20.at(...).approve(...)`.
31
+ rewrites those names, supplies the matching ABIs and injects the addresses. A token write becomes a
32
+ call to the `@sauce/token` library, whose writes follow OpenZeppelin `SafeERC20`, so the compiler
33
+ sees the equivalent of `approve(USDC, <the router's address>, 1_000_000n)`. A token read and a
34
+ protocol call become ABI-bound calls such as `IERC20.at(USDC).balanceOf(...)`.
33
35
 
34
36
  These names belong to the SDK's route path, not to the compiler. A direct `compile()` call resolves
35
37
  nothing for you: bind ABIs and supply addresses yourself. See
@@ -153,8 +153,8 @@ A direct compiler program can bind an EVM ABI explicitly:
153
153
  ```ts
154
154
  import ERC20 from "./erc20.abi.json";
155
155
 
156
- function main(token: Address, spender: Address, amount: Uint256): void {
157
- ERC20.at(token).approve(spender, amount);
156
+ function main(token: Address, owner: Address): Uint256 {
157
+ return ERC20.at(token).balanceOf(owner);
158
158
  }
159
159
  ```
160
160
 
@@ -162,6 +162,23 @@ A bare JSON import binds a contract ABI. A JSON data import uses
162
162
  `with { type: "json" }`. The host resolver must return the named file's bytes in
163
163
  either case.
164
164
 
165
+ An ABI-bound call decodes what the ABI declares it returns. For an ERC-20 write
166
+ that is a `bool`, so `ERC20.at(token).approve(...)` reverts on a token that
167
+ returns nothing, such as USDT, and accepts one that returns `false`. Write token
168
+ transfers and approvals with `@sauce/token` instead, which follows OpenZeppelin
169
+ `SafeERC20`:
170
+
171
+ ```ts
172
+ import { approve } from "@sauce/token";
173
+
174
+ function main(token: Address, spender: Address, amount: Uint256): void {
175
+ approve(token, spender, amount);
176
+ }
177
+ ```
178
+
179
+ A raw compile resolves `@sauce/token` by passing `resolveSdkPackage` and
180
+ `resolveSdkModule` from `@eco-incorp/sauce/compiler`.
181
+
165
182
  The compiler's `ambient` option broadcasts a SauceScript module's exports without
166
183
  explicit imports. Its `defines` option injects scalar constants into every
167
184
  module. Their precedence differs: ambient exports yield to declarations;
@@ -37,7 +37,10 @@ returns `false` reverts rather than passing silently, and so do a write to an ad
37
37
  and a return shorter than one ABI word. `approve` adds `forceApprove`'s reset — it reads the current
38
38
  allowance and zeroes it first when it is non-zero, which USDT requires and a shared Pot makes
39
39
  likely. The `@sauce/token` functions a program calls directly (`transfer`, `transferFrom`,
40
- `approve`) behave the same way.
40
+ `approve`) behave the same way, and so does the route-body syntax — `USDC.transfer(...)`,
41
+ `Token(address).approve(...)` — which compiles to those functions. Only an ABI binding you write
42
+ yourself, `IERC20.at(address).transfer(...)`, is the raw unchecked call; see
43
+ [protocols and tokens](protocols-and-tokens.md#writes-follow-safeerc20).
41
44
 
42
45
  The visible consequence is that a literal amount is encoded into the calldata rather than appearing
43
46
  as a decimal in the emitted source.
@@ -18,9 +18,11 @@ execution time, a Solana account manifest, or full control over module resolutio
18
18
  | `compile({ target, resolve, ... })` | All source and dependency resolution, and compiler settings | Nothing — you get the compiler's own result |
19
19
 
20
20
  Bare `USDC`, `Token(address)` and protocol members only exist on the route path. There,
21
- `USDC.approve(Uniswap.UniversalRouter, 1_000_000n)` becomes an ERC-20 ABI import and
22
- `IERC20.at(USDC).approve(<the router's Base address>, 1_000_000n)`, with `USDC` defined as Base's
23
- USDC. Raw `compile()` expects you to write that yourself.
21
+ `USDC.approve(Uniswap.UniversalRouter, 1_000_000n)` becomes an import of `@sauce/token`'s
22
+ `approve` and `approve(USDC, <the router's Base address>, 1_000_000n)`, with `USDC` defined as
23
+ Base's USDC. That is the `SafeERC20` write; see
24
+ [protocols and tokens](protocols-and-tokens.md#writes-follow-safeerc20). Raw `compile()` expects
25
+ you to write it yourself, resolving `@sauce/token` with `resolveSdkPackage`.
24
26
 
25
27
  ## Compile an EVM route
26
28
 
@@ -177,12 +177,42 @@ they cost different things.
177
177
 
178
178
  | The body is… | Becomes… | Needs |
179
179
  | ------------------------------- | ------------------------------------------------------ | ----------------------- |
180
- | only token `transfer`/`approve` | one `route.calls` entry per statement | nothing further |
180
+ | only token `transfer`/`approve` | plain `route.calls` the Portal's Executor runs | nothing further |
181
181
  | anything else | a program compiled for the destination, run on its Pot | a Pot and a Kitchen fee |
182
182
 
183
183
  The first shape is free: a route already _is_ a list of calls, so a cook would only add a fee. The
184
184
  second is strictly more general.
185
185
 
186
+ A body takes the first shape only when **every** statement in the braces is a
187
+ `transfer(to, amount)` or `approve(spender, amount)` call, on a registered token symbol such as
188
+ `USDC` or on `Token(address)` with a literal or compile-time address, and each argument is a
189
+ literal, a compile-time define, or a variable of the enclosing program. A single statement of any
190
+ other kind — a local, a balance read, a condition, a loop, a `transferFrom`, a protocol call, an
191
+ intent of its own — makes the whole body a program.
192
+
193
+ #### Token-only bodies run on the Executor
194
+
195
+ A token-only body is not a Sauce program. The Portal's Executor runs each call itself, and checks
196
+ only that the call did not revert and that the target has code. It does not decode what a token
197
+ returns:
198
+
199
+ - **USDT is fully supported.** A token that returns nothing from `transfer` or `approve` works.
200
+ Every `approve` becomes two calls, `approve(spender, 0)` and then `approve(spender, amount)`,
201
+ because USDT refuses to change one non-zero allowance to another and one Executor runs every
202
+ intent on a chain, so an earlier intent's allowance may still be standing. A transfer stays one
203
+ call. The reset adds about 8,000 gas per approve.
204
+ - **A token that returns `false` instead of reverting is not detected.** The tokens the solver
205
+ delivers are pulled by the Portal with OpenZeppelin's `safeTransferFrom`, so one that returns
206
+ `false` fails delivery. But when the Executor's own `transfer` or `approve` returns `false`, the
207
+ intent is still fulfilled, with nothing moved.
208
+
209
+ For full `SafeERC20` checks, make the body a program by adding any statement that is not a token
210
+ write, such as `const held = USDC.balanceOf(ctx.self());`. A program's token writes are
211
+ [`SafeERC20`](protocols-and-tokens.md#writes-follow-safeerc20), and a `false` return reverts the
212
+ fulfilment. A program needs the destination's Kitchen fee and a deployed Pot, as the table says,
213
+ and the solver's delivery named in the second argument, because it is not derived for a program
214
+ (see below).
215
+
186
216
  ```js
187
217
  function main() {
188
218
  const amount = USDC.balanceOf(ctx.self());
@@ -332,7 +362,7 @@ Use literals for self-contained callbacks, or source strings plus `defines` for
332
362
 
333
363
  Callbacks must be synchronous and take no parameters. Bound/native functions, generators, and async functions are rejected. If a bundler transforms function source, ship an explicit SauceScript string or source asset. A TypeScript type declaration does not create a captured value.
334
364
 
335
- `Base(...)` adds the SDK's default ambient token module, enabling function forms such as `approve(tokenAddress, spender, amount)`. Direct `compileSauceRoute(...)` does not automatically add that ambient module. The token and protocol member syntax described in [protocols and tokens](protocols-and-tokens.md) is a separate rewrite.
365
+ `Base(...)` adds the SDK's default ambient token module, enabling function forms such as `approve(tokenAddress, spender, amount)`. Direct `compileSauceRoute(...)` does not automatically add that ambient module. The token and protocol member syntax described in [protocols and tokens](protocols-and-tokens.md) is a separate rewrite that works on both paths; its writes import the same module's functions themselves, so they have the same `SafeERC20` semantics either way.
336
366
 
337
367
  ## Submit the source reward
338
368
 
@@ -64,7 +64,27 @@ For a token absent from the registry, use `Token(address)`:
64
64
  Token(0x1111111111111111111111111111111111111111n).approve(Uniswap.UniversalRouter, 1_000_000n);
65
65
  ```
66
66
 
67
- Replace that illustrative address with your ERC-20. The wrapper accepts a literal, a local variable, or a compile-time define. The token member syntax supports `transfer`, `approve`, and `balanceOf`. Use an explicit ABI binding for additional ERC-20 methods. The shorthand `self.USDC` reads the executing contract's balance; `USDC.balanceOf(self)` makes the same intent explicit. `self` refers to the executing contract, which is the Pot in the EVM route path.
67
+ Replace that illustrative address with your ERC-20. The wrapper accepts a literal, a local variable, or a compile-time define. It can also be bound once and called, `const t = Token(address); t.approve(spender, amount);`, as long as `t` is never reassigned or used other than as `t.<method>(...)`. The token member syntax supports `transfer`, `approve`, `transferFrom`, and `balanceOf`. Use an explicit ABI binding for additional ERC-20 methods. The shorthand `self.USDC` reads the executing contract's balance; `USDC.balanceOf(self)` makes the same intent explicit. `self` refers to the executing contract, which is the Pot in the EVM route path.
68
+
69
+ ### Writes follow SafeERC20
70
+
71
+ `transfer`, `approve` and `transferFrom` have OpenZeppelin `SafeERC20` semantics however you spell
72
+ them: `USDC.transfer(to, amount)`, `Token(address).transfer(to, amount)`, a bound `t.transfer(...)`,
73
+ or the `@sauce/token` function `transfer(token, to, amount)`. The first three compile to exactly the
74
+ fourth, so they are one implementation, not four. A token that returns nothing, such as USDT, is
75
+ accepted when it has code. A `false` return, a malformed return, or an address with no code reverts
76
+ the program. `approve` resets a non-zero allowance to zero before setting the new one, as
77
+ `forceApprove` does, because USDT refuses to change one non-zero allowance to another.
78
+
79
+ A write returns nothing: it succeeds or reverts the program, so there is no `bool` to read, and
80
+ `const ok = USDC.transfer(...)` does not compile. Each write lowers to `@sauce/token` imported under
81
+ a reserved `__sauce_<method>` name, so a program's own `transfer` or `approve` function keeps its
82
+ name.
83
+
84
+ An explicit `IERC20.at(address).transfer(...)`, with the ABI imported yourself, is **not** SafeERC20.
85
+ It is the raw ABI call: it decodes the declared `bool`, so it reverts on a token that returns nothing
86
+ and accepts one that returns `false`. Use it only when that is the behavior you want; otherwise use
87
+ one of the spellings above.
68
88
 
69
89
  Use `tokenDefines` on an intent to add or override symbols, or `tokens` on `compileSauceRoute`'s compile options. Overrides merge per symbol with the registry. These maps contain **addresses**; `IntentOptions.tokens` instead declares the **token amounts** a solver must deliver for the route.
70
90
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eco-incorp/sauce",
3
- "version": "0.99.5",
3
+ "version": "0.99.6",
4
4
  "description": "Sauce protocol tooling: SDK, action primitives, and a local dev environment.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -22,7 +22,7 @@ export const SKILL_DOCUMENTS = {
22
22
  "beefy": "# Beefy Finance\n\nMulti-chain yield optimizer. Auto-compounds rewards from LP tokens and other yield sources across many chains.\n\n## Category\n\nyield | Chains: Ethereum, BSC, Polygon, Arbitrum, Optimism, Base, Avalanche\n\n## Key Operations\n\n- **deposit**: Deposit want tokens into Beefy vault\n- **depositAll**: Deposit entire balance of want token\n- **withdraw**: Withdraw by specifying share amount\n- **withdrawAll**: Withdraw entire position\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/beefy\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Deposit into Beefy vault\nimport { BeefyVaultABI as IBeefyVault } from \"./abis\";\nfunction main(vaultAddress: Address, amount: Uint256): Uint256 {\n const vault = IBeefyVault.at(vaultAddress);\n vault.deposit(amount);\n return 1;\n}\n\n// Deposit entire balance\nimport { BeefyVaultABI as IBeefyVault } from \"./abis\";\nfunction main(vaultAddress: Address): Uint256 {\n const vault = IBeefyVault.at(vaultAddress);\n vault.depositAll();\n return 1;\n}\n\n// Withdraw shares\nimport { BeefyVaultABI as IBeefyVault } from \"./abis\";\nfunction main(vaultAddress: Address, shares: Uint256): Uint256 {\n const vault = IBeefyVault.at(vaultAddress);\n vault.withdraw(shares);\n return 1;\n}\n\n// Withdraw all\nimport { BeefyVaultABI as IBeefyVault } from \"./abis\";\nfunction main(vaultAddress: Address): Uint256 {\n const vault = IBeefyVault.at(vaultAddress);\n vault.withdrawAll();\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| --------- | -------- | -------------------------------------------- |\n| Ethereum | BIFI | `0xB1F1ee126e9c96231Cc3d3fAD7C08b4cf873b1f1` |\n| BSC | BIFI | `0xCa3F508B8e4Dd382eE878A314789373D80A5190A` |\n| Polygon | BIFI | `0xFbdd194376de19a88F4A68671C339563c427310d` |\n| Arbitrum | BIFI | `0x99C409E5f62E4bd2AC142f17caFb6810B8F0BAAE` |\n| Optimism | BIFI | `0x4E720DD3Ac5CFe1e1fbDE4935f386Bb1C66F4642` |\n| Base | BIFI | `0xc55E93C62874D8100dBd2DfE307EDc1036ad5434` |\n| Avalanche | BIFI | `0xd6070ae98b8069de6B494332d1A1a81B6179D960` |\n\n## ABI Methods\n\n### BeefyVaultABI\n\n- `deposit(uint256)` - Deposit want tokens, receive mooTokens (vault shares)\n- `depositAll()` - Deposit entire want token balance\n- `withdraw(uint256)` - Withdraw by burning shares\n- `withdrawAll()` - Withdraw entire position\n- `getPricePerFullShare()` - Current share price (18 decimals)\n- `balance()` - Total want tokens in vault\n- `balanceOf(address)` - Query mooToken (share) balance\n- `want()` - Address of the underlying want token\n\n## Notes\n\n- TVL: $300M+. Each vault has a unique address per strategy\n- Approve want token to vault before depositing\n- Use want() to discover which token a vault accepts\n- getPricePerFullShare() returns the exchange rate between shares and want tokens\n- Auto-compounds rewards - no manual claiming needed\n",
23
23
  "benqi": "# Benqi\n\nLeading lending and borrowing protocol on Avalanche. Compound V2 fork with additional liquid staking (sAVAX) functionality.\n\n## Category\n\nlending | Chains: Avalanche (43114)\n\n## SauceScript Functions\n\n### supply\n\nSupply assets by minting qiTokens. Exchange rate grows as interest accrues.\n\n```typescript\nimport { QiTokenABI as IQiToken } from \"./abis\";\n\nfunction main(qiTokenAddress: Address, amount: Uint256): Uint256 {\n const qiToken = IQiToken.at(qiTokenAddress);\n return qiToken.mint(amount);\n}\n```\n\n- `qiTokenAddress`: The qiToken market contract (each asset has its own qiToken)\n- Returns 0 on success, error code on failure\n- Requires ERC-20 approval of underlying to the qiToken\n\n### withdraw\n\nWithdraw underlying assets by specifying the exact amount.\n\n```typescript\nimport { QiTokenABI as IQiToken } from \"./abis\";\n\nfunction main(qiTokenAddress: Address, amount: Uint256): Uint256 {\n const qiToken = IQiToken.at(qiTokenAddress);\n return qiToken.redeemUnderlying(amount);\n}\n```\n\n### borrow\n\nBorrow assets against qiToken collateral. Must call enterMarkets first.\n\n```typescript\nimport { QiTokenABI as IQiToken } from \"./abis\";\n\nfunction main(qiTokenAddress: Address, amount: Uint256): Uint256 {\n const qiToken = IQiToken.at(qiTokenAddress);\n return qiToken.borrow(amount);\n}\n```\n\n- Must enable collateral via `BenqiComptroller.enterMarkets([qiTokenAddress])` first\n\n### repay\n\nRepay borrowed assets.\n\n```typescript\nimport { QiTokenABI as IQiToken } from \"./abis\";\n\nfunction main(qiTokenAddress: Address, amount: Uint256): Uint256 {\n const qiToken = IQiToken.at(qiTokenAddress);\n return qiToken.repayBorrow(amount);\n}\n```\n\n- Requires ERC-20 approval of underlying to the qiToken\n\n## Deployed Addresses\n\n| Chain | Contract | Address |\n| --------- | ----------- | -------------------------------------------- |\n| Avalanche | comptroller | `0x486Af39519B4Dc9a7fCcd318217352830E8AD9b4` |\n\n## ABI Reference\n\n### QiTokenABI\n\n- `mint(uint256 mintAmount) returns (uint256)` - Supply underlying, receive qiTokens\n- `redeem(uint256 redeemTokens) returns (uint256)` - Redeem qiTokens for underlying\n- `redeemUnderlying(uint256 redeemAmount) returns (uint256)` - Redeem exact underlying amount\n- `borrow(uint256 borrowAmount) returns (uint256)` - Borrow underlying\n- `repayBorrow(uint256 repayAmount) returns (uint256)` - Repay borrow debt\n\n### BenqiComptrollerABI\n\n- `enterMarkets(address[] qiTokens) returns (uint256[])` - Enable qiTokens as collateral\n- `exitMarket(address qiToken) returns (uint256)` - Remove qiToken from collateral\n\n## Notes\n\n- Same interface as Compound V2 (forked codebase)\n- Must call `enterMarkets` on Comptroller before borrowing against any market\n- Also offers sAVAX liquid staking product (separate contract)\n- Return values: 0 = success, non-zero = error code\n- Avalanche-only deployment. TVL: $500M+. Audited\n",
24
24
  "camelot": "# Camelot\n\nNative Arbitrum DEX with dual AMM: V2 constant product pools (with native fee-on-transfer token support) and V3 concentrated liquidity pools (Algebra-based with dynamic fees, no fixed fee tiers).\n\n## Category\n\ndex | Chains: Arbitrum\n\n## Key Operations\n\n- **swapV2**: Swap via V2 router with fee-on-transfer token support and referral tracking\n- **swapV3**: Swap via V3 concentrated liquidity router (Algebra-based, dynamic fees)\n- **addLiquidity**: Add liquidity to V2 pools\n- **removeLiquidity**: Remove liquidity from V2 pools\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/camelot\";\n```\n\n## SauceScript Examples\n\n### swapV2\n\n```typescript\nimport { CamelotV2RouterABI as ICamelotRouter } from \"./abis\";\n\nfunction main(\n routerAddress: Address,\n path: Address[],\n amountIn: Uint256,\n amountOutMin: Uint256,\n recipient: Address,\n referrer: Address,\n): Uint256 {\n const router = ICamelotRouter.at(routerAddress);\n router.swapExactTokensForTokensSupportingFeeOnTransferTokens(\n amountIn,\n amountOutMin,\n path,\n recipient,\n referrer,\n 99999999999,\n );\n return 1;\n}\n```\n\n- `routerAddress`: Camelot V2 Router on Arbitrum\n- `path`: Ordered token address array for the swap route\n- `amountIn`: Exact input amount (in wei)\n- `amountOutMin`: Minimum output for slippage protection\n- `recipient`: Address to receive output tokens\n- `referrer`: Address of the referrer for fee sharing (use zero address if none)\n- This method supports fee-on-transfer (rebasing) tokens natively\n\n### swapV3\n\n```typescript\nimport { CamelotV3SwapRouterABI as ICamelotV3Router } from \"./abis\";\n\nfunction main(\n routerAddress: Address,\n tokenIn: Address,\n tokenOut: Address,\n amountIn: Uint256,\n amountOutMin: Uint256,\n recipient: Address,\n): Uint256 {\n const router = ICamelotV3Router.at(routerAddress);\n return router.exactInputSingle({\n tokenIn: tokenIn,\n tokenOut: tokenOut,\n recipient: recipient,\n deadline: 99999999999,\n amountIn: amountIn,\n amountOutMinimum: amountOutMin,\n limitSqrtPrice: 0,\n });\n}\n```\n\n- `routerAddress`: Camelot V3 SwapRouter on Arbitrum\n- No `fee` parameter needed: Camelot V3 uses dynamic fees (Algebra-based), not fixed fee tiers\n- `limitSqrtPrice`: Set to `0` for no price limit\n\n### addLiquidity\n\n```typescript\nimport { CamelotV2RouterABI as ICamelotRouter } from \"./abis\";\n\nfunction main(\n routerAddress: Address,\n tokenA: Address,\n tokenB: Address,\n amountADesired: Uint256,\n amountBDesired: Uint256,\n amountAMin: Uint256,\n amountBMin: Uint256,\n recipient: Address,\n): { amountA: Uint256; amountB: Uint256; liquidity: Uint256 } {\n const router = ICamelotRouter.at(routerAddress);\n return router.addLiquidity(\n tokenA,\n tokenB,\n amountADesired,\n amountBDesired,\n amountAMin,\n amountBMin,\n recipient,\n 99999999999,\n );\n}\n```\n\n- Both tokens must be approved to the V2 router\n\n### removeLiquidity\n\n```typescript\nimport { CamelotV2RouterABI as ICamelotRouter } from \"./abis\";\n\nfunction main(\n routerAddress: Address,\n tokenA: Address,\n tokenB: Address,\n liquidity: Uint256,\n amountAMin: Uint256,\n amountBMin: Uint256,\n recipient: Address,\n): { amountA: Uint256; amountB: Uint256 } {\n const router = ICamelotRouter.at(routerAddress);\n return router.removeLiquidity(\n tokenA,\n tokenB,\n liquidity,\n amountAMin,\n amountBMin,\n recipient,\n 99999999999,\n );\n}\n```\n\n- LP token must be approved to the V2 router\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | ------------- | -------------------------------------------- |\n| Arbitrum | V2 Factory | `0x6EcCab422D763aC031210895C81787E87B43A652` |\n| Arbitrum | V2 Router | `0xc873fEcbd354f5A56E00E710B90EF4201db2448d` |\n| Arbitrum | V3 Factory | `0x1a3c9B1d2F0529D97f2afC5136Cc23e58f1FD35B` |\n| Arbitrum | V3 SwapRouter | `0x1F721E2E82F6676FCE4eA07A5958cF098D339e18` |\n\n## ABI Methods\n\n### CamelotV2RouterABI\n\n- `swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, address referrer, uint256 deadline)` - V2 swap with fee-on-transfer token support and referral. Note: no return value\n- `addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) -> (uint256 amountA, uint256 amountB, uint256 liquidity)` - Add V2 LP\n- `removeLiquidity(address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) -> (uint256 amountA, uint256 amountB)` - Remove V2 LP\n\n### CamelotV3SwapRouterABI\n\n- `exactInputSingle(tuple(address tokenIn, address tokenOut, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 limitSqrtPrice)) -> uint256 amountOut` - V3 concentrated liquidity swap with dynamic fees\n\n## Notes\n\n- V2 router uses `swapExactTokensForTokensSupportingFeeOnTransferTokens` (not standard `swapExactTokensForTokens`) which natively handles rebasing/tax tokens\n- V2 router includes a `referrer` parameter for referral fee sharing (unique to Camelot)\n- V3 uses Algebra protocol (dynamic fees that adjust based on volatility, NOT fixed fee tiers like Uniswap V3)\n- V3 uses `limitSqrtPrice` instead of `sqrtPriceLimitX96`\n- Arbitrum-only deployment; the native DEX for Arbitrum ecosystem projects\n",
25
- "cbeth": "# Coinbase Wrapped Staked ETH\n\nCoinbase's liquid staking token for Ethereum. cbETH represents staked ETH plus accrued staking rewards. Non-rebasing.\n\n## Category\n\nliquid-staking | Chains: Ethereum, Base\n\n## Key Operations\n\n- **transfer**: Transfer cbETH tokens\n- **approve**: Approve spender for cbETH\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/cbeth\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer cbETH\nimport { CbETHABI as ICbETH } from \"./abis\";\nfunction main(cbethAddress: Address, to: Address, amount: Uint256): Uint256 {\n const cbeth = ICbETH.at(cbethAddress);\n cbeth.transfer(to, amount);\n return 1;\n}\n\n// Approve cbETH\nimport { CbETHABI as ICbETH } from \"./abis\";\nfunction main(cbethAddress: Address, spender: Address, amount: Uint256): Uint256 {\n const cbeth = ICbETH.at(cbethAddress);\n cbeth.approve(spender, amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | cbETH | `0xBe9895146f7AF43049ca1c1AE358B0541Ea49704` |\n| Base | cbETH | `0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22` |\n\n## ABI Methods\n\n### CbETHABI\n\n- `mint(address to, uint256 amount) returns (bool)` - Mint cbETH (restricted to Coinbase's minters)\n- `exchangeRate()` - Current cbETH/ETH exchange rate (Ethereum only; Base cbETH is a bridged token without it)\n- `balanceOf(address)` - Query cbETH balance\n- `approve(address,uint256)` - Approve spender\n- `transfer(address,uint256)` - Transfer cbETH\n\n## Notes\n\n- TVL: $2.5B+. Non-rebasing - exchange rate increases over time\n- Minted by Coinbase (mint is permissioned). On-chain operations are transfer/approve\n- Compatible with EigenLayer restaking (cbETH strategy available)\n- Available natively on Base L2\n",
25
+ "cbeth": "# Coinbase Wrapped Staked ETH\n\nCoinbase's liquid staking token for Ethereum. cbETH represents staked ETH plus accrued staking rewards. Non-rebasing.\n\n## Category\n\nliquid-staking | Chains: Ethereum, Base\n\n## Key Operations\n\n- **transfer**: Transfer cbETH tokens\n- **approve**: Approve spender for cbETH\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/cbeth\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer cbETH\nimport { transfer } from \"@sauce/token\";\nfunction main(cbethAddress: Address, to: Address, amount: Uint256): Uint256 {\n transfer(cbethAddress, to, amount);\n return 1;\n}\n\n// Approve cbETH\nimport { approve } from \"@sauce/token\";\nfunction main(cbethAddress: Address, spender: Address, amount: Uint256): Uint256 {\n approve(cbethAddress, spender, amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | cbETH | `0xBe9895146f7AF43049ca1c1AE358B0541Ea49704` |\n| Base | cbETH | `0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22` |\n\n## ABI Methods\n\n### CbETHABI\n\n- `mint(address to, uint256 amount) returns (bool)` - Mint cbETH (restricted to Coinbase's minters)\n- `exchangeRate()` - Current cbETH/ETH exchange rate (Ethereum only; Base cbETH is a bridged token without it)\n- `balanceOf(address)` - Query cbETH balance\n- `approve(address,uint256)` - Approve spender\n- `transfer(address,uint256)` - Transfer cbETH\n\n## Notes\n\n- TVL: $2.5B+. Non-rebasing - exchange rate increases over time\n- Minted by Coinbase (mint is permissioned). On-chain operations are transfer/approve\n- Compatible with EigenLayer restaking (cbETH strategy available)\n- Available natively on Base L2\n",
26
26
  "celer": "# Celer Network\n\nMulti-chain bridging protocol using SGN (State Guardian Network) for cross-chain message validation and token transfers.\n\n## Category\n\nbridge | Direction: any-to-any (L1-to-L2, L2-to-L2, L2-to-L1) | Chains: Ethereum (1), Arbitrum (42161), Optimism (10), Polygon (137), BSC (56), Avalanche (43114)\n\n## SauceScript Functions\n\n### bridge\n\nSend ERC-20 tokens cross-chain via cBridge.\n\n```typescript\nimport { CelerBridgeABI as IBridge } from \"./abis\";\n\nfunction main(\n bridgeAddress: Address,\n receiver: Address,\n token: Address,\n amount: Uint256,\n dstChainId: Uint256,\n maxSlippage: Uint256,\n): Uint256 {\n const bridge = IBridge.at(bridgeAddress);\n bridge.send(receiver, token, amount, dstChainId, 0, maxSlippage);\n return 1;\n}\n```\n\n- `receiver`: Address to receive tokens on destination chain\n- `token`: ERC-20 token to bridge\n- `dstChainId`: Destination EVM chain ID (as uint64)\n- `nonce`: Set to 0 (auto-generated)\n- `maxSlippage`: Maximum slippage in basis points (e.g. 500 = 5%). Applied during pool-based bridging\n- Requires ERC-20 approval to the Bridge\n\n## Deployed Addresses\n\n| Chain | Contract | Address |\n| --------- | -------- | -------------------------------------------- |\n| Ethereum | bridge | `0x5427FEFA711Eff984124bFBB1AB6fbf5E3DA1820` |\n| Arbitrum | bridge | `0x1619DE6B6B20eD217a58d00f37B9d47C7663feca` |\n| Optimism | bridge | `0x9D39Fc627A6d9d9F8C831c16995b209548cc3401` |\n| Polygon | bridge | `0x88DCDC47D2f83a99CF0000FDF667A468bB958a78` |\n| BSC | bridge | `0xdd90E5E87A2081Dcf0391920868eBc2FFB81a1aF` |\n| Avalanche | bridge | `0xef3c714c9425a8F3697A9C969Dc1af30ba82e5d4` |\n\n## ABI Reference\n\n### CelerBridgeABI\n\n- `send(address _receiver, address _token, uint256 _amount, uint64 _dstChainId, uint64 _nonce, uint32 _maxSlippage)` - Send ERC-20 tokens cross-chain via liquidity pool\n- `sendNative(address _receiver, uint256 _amount, uint64 _dstChainId, uint64 _nonce, uint32 _maxSlippage)` [payable] - Send native token (ETH/BNB/etc.) cross-chain\n\n## Notes\n\n- `send` for ERC-20 tokens, `sendNative` for native tokens (ETH, BNB, AVAX, etc.)\n- `nonce` is used for unique transfer identification -- can set to 0 or use block.timestamp\n- `maxSlippage` is in basis points (1 = 0.01%, 100 = 1%, 5000 = 50%)\n- Uses SGN validators to verify cross-chain messages\n- Finality: typically 5-20 minutes depending on chain confirmation requirements\n- Requires ERC-20 approval to the Bridge address\n- TVL: $150M+. Audited\n",
27
27
  "chainlink-ccip": "# Chainlink CCIP\n\nCross-Chain Interoperability Protocol by Chainlink. Enterprise-grade cross-chain messaging with DON (Decentralized Oracle Network) security and token transfers.\n\n## Category\n\ncross-chain messaging + token transfer | Direction: any-to-any | Chains: Ethereum (1), Arbitrum (42161), Optimism (10), Base (8453), Polygon (137), Arc (5042)\n\n## SauceScript Functions\n\n### sendMessage\n\nSend a cross-chain message via CCIP Router.\n\n```typescript\nimport { CCIPRouterABI as ICCIPRouter } from \"./abis\";\n\nfunction main(\n routerAddress: Address,\n destinationChainSelector: Uint256,\n receiver: bytes,\n data: bytes,\n): Uint256 {\n const router = ICCIPRouter.at(routerAddress);\n return router.ccipSend(destinationChainSelector, {\n receiver: receiver,\n data: data,\n tokenAmounts: [],\n feeToken: 0x0000000000000000000000000000000000000000,\n extraArgs: 0x00,\n });\n}\n```\n\n- `destinationChainSelector`: CCIP chain selector (NOT EVM chain ID). These are unique uint64 identifiers per chain\n- `receiver`: ABI-encoded destination address (bytes, not raw address)\n- `data`: Arbitrary message payload (bytes)\n- `tokenAmounts`: Array of `{token, amount}` tuples for cross-chain token transfers. Empty array `[]` for message-only\n- `feeToken`: Address of token to pay fees in. `address(0)` = pay in native token (ETH). Can also use LINK token address\n- `extraArgs`: Optional encoded extra arguments (gas limits, etc.). `0x00` for defaults\n- Requires native token (ETH) as msg.value when `feeToken` is `address(0)`\n- Requires ERC-20 approval to the Router for any tokens in `tokenAmounts`\n\n## Deployed Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | router | `0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D` |\n| Arbitrum | router | `0x141fa059441E0ca23ce184B6A78bafD2A517DdE8` |\n| Optimism | router | `0x3206695CaE29952f4b0c22a169725a865bc8Ce0f` |\n| Base | router | `0x881e3A65B4d4a04dD529061dd0071cf975F58bCD` |\n| Polygon | router | `0x849c5ED5a80F5B408Dd4969b78c2C8fdf0565Bfe` |\n\n## ABI Reference\n\n### CCIPRouterABI\n\n- `ccipSend(uint64 destinationChainSelector, EVM2AnyMessage message) returns (bytes32 messageId)` [payable] - Send cross-chain message and/or tokens. Returns message ID for tracking\n- `getFee(uint64 destinationChainSelector, EVM2AnyMessage message) returns (uint256 fee)` - Estimate fee for a CCIP message before sending (view)\n- `isChainSupported(uint64 chainSelector) returns (bool)` - Check if a destination chain selector is supported (view)\n\nEVM2AnyMessage tuple: `(bytes receiver, bytes data, EVMTokenAmount[] tokenAmounts, address feeToken, bytes extraArgs)`\nEVMTokenAmount tuple: `(address token, uint256 amount)`\n\n## Notes\n\n- Uses CCIP chain selectors (uint64), NOT EVM chain IDs. Each supported chain has a unique selector\n- Fees can be paid in native token (ETH) or LINK token\n- Use `getFee()` to estimate costs before sending\n- Supports both message-only and message+token transfers in a single call\n- DON-based security with Chainlink oracle network -- no external validators needed\n- Finality: typically 5-20 minutes depending on source chain finality\n- Rate limits apply per lane (source-destination pair)\n- TVL: $1B+. Audited\n- On Arc (5042) a native-paid fee is USDC at 18 decimals, from the same balance as the USDC ERC-20 `0x3600…0000`. The router's `getWrappedNative()` is `0x8DFa585699CB46ca2a5fA649700f09839B4b8743` (`CCIP_USDC`, a WETH9-style wrapper over native USDC), CCIP's own fee token rather than a canonical wrapped native\n",
28
28
  "chainlink": "# Chainlink\n\nIndustry-standard decentralized oracle network providing price feeds, VRF randomness, automation, and cross-chain interoperability (CCIP).\n\n## Category\n\noracle | Chains: Ethereum, Arc\n\n## Key Operations\n\n- **getLatestPrice**: Read latest price from a Chainlink price feed aggregator\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/chainlink\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Get latest price from feed\nimport { AggregatorV3ABI as IAggregatorV3 } from \"./abis\";\nfunction main(feedAddress: Address): {\n roundId: Uint256;\n answer: Uint256;\n startedAt: Uint256;\n updatedAt: Uint256;\n answeredInRound: Uint256;\n} {\n const feed = IAggregatorV3.at(feedAddress);\n return feed.latestRoundData();\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | ---------------- | -------------------------------------------- |\n| Ethereum | ethUsdFeed | `0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419` |\n| Ethereum | feedRegistry | `0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf` |\n| Ethereum | vrfV2Coordinator | `0x271682DEB8C4E0901D1a1550aD2e64D568E69909` |\n\n## ABI Methods\n\n### AggregatorV3ABI\n\n- `latestRoundData()` - Get latest price data. Returns tuple: (roundId uint80, answer int256, startedAt uint256, updatedAt uint256, answeredInRound uint80). answer is the price with feed-specific decimals\n- `decimals()` - Get price feed decimal precision. Returns uint8 (usually 8 for USD feeds, 18 for ETH feeds)\n\n### FeedRegistryABI\n\n- `latestRoundData(address,address)` - Get latest price for a base/quote pair. Params: base (token address), quote (denomination address, use 0x348... for USD). Returns same tuple as AggregatorV3\n- `getFeed(address,address)` - Look up aggregator address for a pair. Params: base, quote. Returns aggregator address\n\n## Notes\n\n- Price feeds return answer in int256 with feed-specific decimals (usually 8 for USD pairs)\n- Always check updatedAt timestamp for staleness - stale prices can cause issues\n- Feed Registry is Ethereum-only; on L2s, use individual feed addresses directly\n- VRF provides verifiable randomness for on-chain applications\n- Common feed addresses vary by chain - check Chainlink docs for specific chain deployments\n",
@@ -32,13 +32,13 @@ export const SKILL_DOCUMENTS = {
32
32
  "connext": "# Connext (Everclear)\n\nCross-chain liquidity protocol rebranded as Everclear. Uses intents and a clearing layer for capital-efficient cross-chain transfers.\n\n## Category\n\nbridge | Direction: any-to-any (L2-to-L2, L1-to-L2, L2-to-L1) | Chains: Ethereum (1), Arbitrum (42161), Optimism (10), Base (8453)\n\n## SauceScript Functions\n\n### bridge\n\nCreate a new cross-chain intent via EverclearSpoke.\n\n```typescript\nimport { EverclearSpokeABI as IEverclearSpoke } from \"./abis\";\n\nfunction main(\n spokeAddress: Address,\n destinations: Uint256[],\n recipient: Address,\n inputAsset: Address,\n outputAsset: Address,\n amount: Uint256,\n amountOutMin: Uint256,\n): Uint256 {\n const spoke = IEverclearSpoke.at(spokeAddress);\n const created = spoke.newIntent(\n destinations,\n recipient,\n inputAsset,\n outputAsset,\n amount,\n amountOutMin,\n 86400,\n \"\",\n );\n return created.intentId;\n}\n```\n\n- `destinations`: Array of destination domain IDs (uint32[]) -- can specify multiple possible destinations\n- `recipient`: Address to receive tokens on destination\n- `inputAsset`: Token to send on source chain\n- `outputAsset`: Token to receive on destination chain (can differ from input for cross-chain swaps)\n- `amountOutMin`: Minimum amount the recipient must receive\n- `ttl`: Time-to-live in seconds (86400 = 24 hours). Intent expires if unfilled\n- Requires ERC-20 approval to the EverclearSpoke\n\n## Deployed Addresses\n\n| Chain | Contract | Address |\n| -------- | -------------- | -------------------------------------------- |\n| Ethereum | everclearSpoke | `0xa05A3380889115bf313f1Db9d5f335157Be4D816` |\n| Arbitrum | everclearSpoke | `0xa05A3380889115bf313f1Db9d5f335157Be4D816` |\n| Optimism | everclearSpoke | `0xa05A3380889115bf313f1Db9d5f335157Be4D816` |\n| Base | everclearSpoke | `0xa05A3380889115bf313f1Db9d5f335157Be4D816` |\n\n## ABI Reference\n\n### EverclearSpokeABI\n\n- `newIntent(uint32[] destinations, address receiver, address inputAsset, address outputAsset, uint256 amount, uint256 amountOutMin, uint48 ttl, bytes data) returns (bytes32 intentId, Intent intent)` - Create a cross-chain transfer intent. Solvers compete to fill the intent on the destination chain. A `bytes32`-typed overload takes non-EVM receivers and assets\n\n## Notes\n\n- Intent-based architecture: users express desired outcome, solvers compete to fill\n- Rebranded from Connext to Everclear -- same contracts\n- Same EverclearSpoke address deployed across all chains\n- Uses domain IDs (uint32) for destination chains, NOT EVM chain IDs\n- Supports cross-chain swaps (inputAsset != outputAsset)\n- `ttl` determines how long the intent is valid -- unfilled intents can be cancelled after expiry\n- Finality: typically 2-15 minutes depending on solver activity\n- Requires ERC-20 approval to the EverclearSpoke\n- TVL: $50M+. Audited\n",
33
33
  "convex": "# Convex Finance\n\nYield optimizer for Curve Finance LP tokens. Deposit Curve LP tokens to earn boosted CRV rewards plus CVX incentives without needing to lock CRV.\n\n## Category\n\nyield | Chains: Ethereum\n\n## Key Operations\n\n- **deposit**: Deposit Curve LP tokens into Convex pool (with auto-staking)\n- **withdraw**: Withdraw Curve LP tokens from Convex pool\n- **getReward**: Claim accumulated CRV + CVX + extra rewards\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/convex\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Deposit Curve LP into Convex (auto-stake in reward pool)\nimport { BoosterABI as IBooster } from \"./abis\";\nfunction main(boosterAddress: Address, pid: Uint256, amount: Uint256): Uint256 {\n const booster = IBooster.at(boosterAddress);\n booster.deposit(pid, amount, true);\n return 1;\n}\n\n// Withdraw Curve LP from Convex\nimport { BoosterABI as IBooster } from \"./abis\";\nfunction main(boosterAddress: Address, pid: Uint256, amount: Uint256): Uint256 {\n const booster = IBooster.at(boosterAddress);\n booster.withdraw(pid, amount);\n return 1;\n}\n\n// Claim rewards from reward pool\nimport { BaseRewardPoolABI as IBaseRewardPool } from \"./abis\";\nfunction main(rewardPoolAddress: Address, account: Address): Uint256 {\n const pool = IBaseRewardPool.at(rewardPoolAddress);\n pool.getReward(account, true);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | ------------- | -------------------------------------------- |\n| Ethereum | booster | `0xF403C135812408BFbE8713b5A23a04b3D48AAE31` |\n| Ethereum | cvxRewardPool | `0xCF50b810E57Ac33B91dCF525C6ddd9881B139332` |\n\n## ABI Methods\n\n### BoosterABI\n\n- `deposit(uint256,uint256,bool)` - Deposit LP tokens. Params: pool ID (pid), amount, stake in reward pool (true recommended)\n- `withdraw(uint256,uint256)` - Withdraw LP tokens. Params: pool ID, amount\n- `poolLength()` - Total number of pools\n- `poolInfo(uint256)` - Get pool info: (lptoken, token, gauge, crvRewards, stash, shutdown)\n\n### BaseRewardPoolABI\n\n- `getReward(address,bool)` - Claim rewards. Params: account, claimExtras (true = claim extra reward tokens too)\n- `earned(address)` - Query pending CRV rewards\n- `balanceOf(address)` - Query staked balance in reward pool\n- `withdrawAndUnwrap(uint256,bool)` - Withdraw and unwrap in one tx. Params: amount, claim rewards\n\n## Notes\n\n- TVL: $2B+. Pool IDs (pid) are sequential integers\n- Third param in deposit = auto-stake in reward pool (always pass true for yield)\n- Each pool has its own BaseRewardPool contract (get from poolInfo.crvRewards)\n- Approve Curve LP token to Booster before depositing\n- getReward with claimExtras=true claims CRV + CVX + any extra reward tokens\n",
34
34
  "cowswap": "# CoW Swap\n\nMEV-protected DEX aggregator using batch auctions and Coincidence of Wants (CoW) to find optimal prices while protecting users from frontrunning.\n\n## Category\n\naggregator | Chains: Ethereum, Arbitrum\n\n## Key Operations\n\n- **preSignOrder**: Pre-sign an order on-chain for execution by solvers\n- **invalidateOrder**: Cancel/invalidate a pending order\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/cowswap\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Pre-sign order on-chain\nimport { GPv2SettlementABI as IGPv2Settlement } from \"./abis\";\nfunction main(settlementAddress: Address, orderUid: bytes): Uint256 {\n const settlement = IGPv2Settlement.at(settlementAddress);\n settlement.setPreSignature(orderUid, true);\n return 1;\n}\n\n// Invalidate/cancel order\nimport { GPv2SettlementABI as IGPv2Settlement } from \"./abis\";\nfunction main(settlementAddress: Address, orderUid: bytes): Uint256 {\n const settlement = IGPv2Settlement.at(settlementAddress);\n settlement.invalidateOrder(orderUid);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------------- | -------------------------------------------- |\n| Ethereum | gpv2Settlement | `0x9008D19f58AAbD9eD0D60971565AA8510560ab41` |\n| Arbitrum | gpv2Settlement | `0x9008D19f58AAbD9eD0D60971565AA8510560ab41` |\n\n## ABI Methods\n\n### GPv2SettlementABI\n\n- `setPreSignature(bytes,bool)` - Pre-sign order on-chain. Params: orderUid (unique order identifier bytes), signed (true to sign, false to unsign). Used for smart contract wallets that cannot sign off-chain\n- `invalidateOrder(bytes)` - Invalidate/cancel a pending order. Params: orderUid (order to cancel)\n\n## Notes\n\n- MEV-protected: batch auctions match Coincidence of Wants (overlapping orders) first\n- Orders are typically signed off-chain and submitted to CoW Protocol API\n- setPreSignature is for smart contracts/multisigs that cannot produce ECDSA signatures\n- orderUid encodes: order hash + owner address + validTo timestamp\n- Same settlement contract on both Ethereum and Arbitrum\n- Approve tokens to the GPv2VaultRelayer (not settlement contract) before trading\n",
35
- "crvusd": "# crvUSD\n\nCurve Finance native stablecoin using LLAMMA (Lending-Liquidating AMM Algorithm) for soft liquidations.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **transfer**: Transfer crvUSD tokens\n- **approve**: Approve crvUSD token spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/crvusd\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer crvUSD\nimport { CrvUSDERC20ABI as ICrvUSD } from \"./abis\";\nfunction main(crvusdAddress: Address, to: Address, amount: Uint256): Uint256 {\n const crvusd = ICrvUSD.at(crvusdAddress);\n return crvusd.transfer(to, amount);\n}\n\n// Approve crvUSD spender\nimport { CrvUSDERC20ABI as ICrvUSD } from \"./abis\";\nfunction main(crvusdAddress: Address, spender: Address, amount: Uint256): Uint256 {\n const crvusd = ICrvUSD.at(crvusdAddress);\n return crvusd.approve(spender, amount);\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | crvusd | `0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E` |\n\n## ABI Methods\n\n### CrvUSDERC20ABI\n\n- `transfer(address,uint256)` - Transfer crvUSD. Params: to, amount. Returns bool\n- `approve(address,uint256)` - Approve spender. Params: spender, amount. Returns bool\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256\n\n## Notes\n\n- TVL: $500M+. LLAMMA provides soft liquidations - collateral gradually converted to crvUSD during price drops\n- Soft liquidation = collateral is progressively swapped rather than instant liquidation\n- Borrowing happens through Curve lending controllers (per-market contracts)\n- Supported collateral includes ETH, wstETH, sfrxETH, tBTC, wBTC\n- crvUSD has a peg keeper mechanism to maintain $1 peg via Curve pools\n",
35
+ "crvusd": "# crvUSD\n\nCurve Finance native stablecoin using LLAMMA (Lending-Liquidating AMM Algorithm) for soft liquidations.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **transfer**: Transfer crvUSD tokens\n- **approve**: Approve crvUSD token spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/crvusd\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer crvUSD\nimport { transfer } from \"@sauce/token\";\nfunction main(crvusdAddress: Address, to: Address, amount: Uint256): Uint256 {\n transfer(crvusdAddress, to, amount);\n return 1;\n}\n\n// Approve crvUSD spender\nimport { approve } from \"@sauce/token\";\nfunction main(crvusdAddress: Address, spender: Address, amount: Uint256): Uint256 {\n approve(crvusdAddress, spender, amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | crvusd | `0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E` |\n\n## ABI Methods\n\n### CrvUSDERC20ABI\n\n- `transfer(address,uint256)` - Transfer crvUSD. Params: to, amount. Returns bool\n- `approve(address,uint256)` - Approve spender. Params: spender, amount. Returns bool\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256\n\n## Notes\n\n- TVL: $500M+. LLAMMA provides soft liquidations - collateral gradually converted to crvUSD during price drops\n- Soft liquidation = collateral is progressively swapped rather than instant liquidation\n- Borrowing happens through Curve lending controllers (per-market contracts)\n- Supported collateral includes ETH, wstETH, sfrxETH, tBTC, wBTC\n- crvUSD has a peg keeper mechanism to maintain $1 peg via Curve pools\n",
36
36
  "curve": "# Curve Finance\n\nStableSwap AMM optimized for low-slippage swaps between pegged assets (stablecoins, wrapped tokens). Uses a specialized invariant that provides near-zero slippage for like-kind assets while maintaining AMM properties.\n\n## Category\n\ndex | Chains: Ethereum, Arbitrum, Optimism, Base, Polygon, Avalanche, Fantom, Gnosis\n\n## Key Operations\n\n- **swap**: Exchange tokens within a pool using index-based routing\n- **addLiquidity**: Add liquidity with flexible token amounts (can be imbalanced)\n- **removeLiquidity**: Remove liquidity proportionally or single-sided\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/curve\";\n```\n\n## SauceScript Examples\n\n### swap\n\n```typescript\nimport { CurveStableSwapABI as IStableSwap } from \"./abis\";\n\nfunction main(\n poolAddress: Address,\n i: Uint256,\n j: Uint256,\n amountIn: Uint256,\n minAmountOut: Uint256,\n): Uint256 {\n const pool = IStableSwap.at(poolAddress);\n return pool.exchange(i, j, amountIn, minAmountOut);\n}\n```\n\n- `poolAddress`: The specific Curve pool contract address (each pool is a separate contract)\n- `i`: Index of the input token within the pool (0-based)\n- `j`: Index of the output token within the pool (0-based)\n- `amountIn`: Exact amount of input token (in wei)\n- `minAmountOut`: Minimum output for slippage protection\n- Token indices vary per pool\n- For StableSwap-NG pools. 3pool's `exchange` returns nothing, so reading back a `uint256` reverts there: use `CurveThreePoolABI` for 3pool\n\n### addLiquidity\n\n```typescript\nimport { CurveStableSwapABI as IStableSwap } from \"./abis\";\n\nfunction main(poolAddress: Address, amounts: Uint256[], minMintAmount: Uint256): Uint256 {\n const pool = IStableSwap.at(poolAddress);\n return pool.add_liquidity(amounts, minMintAmount);\n}\n```\n\n- `amounts`: Array of deposit amounts for each token in pool order (can include zeros for imbalanced deposits)\n- `minMintAmount`: Minimum LP tokens to receive (slippage protection)\n- All deposited tokens must be approved to the pool contract\n\n### removeLiquidity\n\n```typescript\nimport { CurveStableSwapABI as IStableSwap } from \"./abis\";\n\nfunction main(poolAddress: Address, amount: Uint256, minAmounts: Uint256[]): Uint256[] {\n const pool = IStableSwap.at(poolAddress);\n return pool.remove_liquidity(amount, minAmounts);\n}\n```\n\n- `amount`: Amount of LP tokens to burn\n- `minAmounts`: Minimum amounts of each token to receive (array in pool token order)\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| --------- | --------------------- | -------------------------------------------- |\n| Ethereum | RouterNG | `0x16C6521Dff6baB339122a0FE25a9116693265353` |\n| Ethereum | AddressProvider | `0x0000000022D53366457F9d5E68Ec105046FC4383` |\n| Ethereum | CRV Token | `0xD533a949740bb3306d119CC777fa900bA034cd52` |\n| Ethereum | 3pool (DAI/USDC/USDT) | `0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7` |\n| Arbitrum | RouterNG | `0x2191718CD32d02B8E60BAdFFeA33E4B5DD9A0A0D` |\n| Arbitrum | AddressProvider | `0x0000000022D53366457F9d5E68Ec105046FC4383` |\n| Optimism | RouterNG | `0x0DCDED3545D565bA3B19E683431381007245d983` |\n| Base | RouterNG | `0x4f37A9d177470499A2dD084621020b023fcffc1F` |\n| Polygon | AddressProvider | `0x0000000022D53366457F9d5E68Ec105046FC4383` |\n| Avalanche | AddressProvider | `0x0000000022D53366457F9d5E68Ec105046FC4383` |\n| Fantom | AddressProvider | `0x0000000022D53366457F9d5E68Ec105046FC4383` |\n| Gnosis | AddressProvider | `0x0000000022D53366457F9d5E68Ec105046FC4383` |\n\n## ABI Methods\n\n### CurveStableSwapABI\n\nThe StableSwap-NG pool interface (plain and meta pools). Classic pools such as 3pool have their own shape: see `CurveThreePoolABI` below.\n\n- `exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) -> uint256` - Swap between two tokens in the pool by index\n- `exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) -> uint256` - Swap between underlying tokens (metapools only)\n- `add_liquidity(uint256[] amounts, uint256 min_mint_amount) -> uint256` - Deposit tokens and receive LP tokens (can be imbalanced)\n- `remove_liquidity(uint256 _amount, uint256[] min_amounts) -> uint256[]` - Burn LP tokens and withdraw all tokens proportionally\n- `remove_liquidity_one_coin(uint256 _token_amount, int128 i, uint256 min_amount) -> uint256` - Burn LP tokens and withdraw as a single token\n- `get_dy(int128 i, int128 j, uint256 dx) -> uint256` - Quote: estimate output for a given input (view)\n- `get_virtual_price() -> uint256` - Get the virtual price of the LP token (view, useful for pricing)\n\n### CurveThreePoolABI\n\n3pool (`threePool` in the addresses), a classic StableSwap pool. Its liquidity arrays are fixed-size, and its state-changing functions return nothing: read amounts as balance changes.\n\n- `exchange(int128 i, int128 j, uint256 dx, uint256 min_dy)` - Swap between two of DAI(0), USDC(1), USDT(2)\n- `add_liquidity(uint256[3] amounts, uint256 min_mint_amount)` - Deposit tokens and receive 3CRV\n- `remove_liquidity(uint256 _amount, uint256[3] min_amounts)` - Burn 3CRV and withdraw all three tokens proportionally\n- `remove_liquidity_one_coin(uint256 _token_amount, int128 i, uint256 min_amount)` - Burn 3CRV and withdraw one token\n- `get_dy(int128 i, int128 j, uint256 dx) -> uint256` - Quote (view)\n- `get_virtual_price() -> uint256` - Virtual price of 3CRV (view)\n\n### CurveRouterNGABI\n\n- `exchange(address[11] _route, uint256[5][5] _swap_params, uint256 _amount, uint256 _expected, address[5] _pools) -> uint256` - Multi-pool routed swap for optimal execution\n- `exchange(address[11] _route, uint256[5][5] _swap_params, uint256 _amount, uint256 _min_dy, address[5] _pools, address _receiver) -> uint256` - The same routed swap, paying `_receiver` instead of the caller\n- `get_dy(address[11] _route, uint256[5][5] _swap_params, uint256 _amount, address[5] _pools) -> uint256` - Quote routed swap output (view)\n\n### CurveAddressProviderABI\n\n- `get_registry() -> address` - Get the main registry contract address (view)\n- `get_address(uint256 _id) -> address` - Get a specific system contract by ID (view)\n\n## Notes\n\n- Pool tokens are indexed (i, j) not addressed; you must know the token order for each pool\n- Common pool token orders: 3pool = [DAI(0), USDC(1), USDT(2)]\n- `exchange_underlying` is used for metapools or lending pools where tokens wrap underlying assets\n- `remove_liquidity_one_coin` is useful for single-sided withdrawal (higher slippage than proportional)\n- Each pool is a separate contract; use the AddressProvider or registry to discover pools\n- RouterNG enables cross-pool routing for multi-hop swaps across different pools\n- Curve excels at stablecoin swaps (much lower slippage than Uniswap for like-kind pairs)\n- The `get_virtual_price` never decreases and represents the LP token value growth over time\n- Tokens must be ERC20-approved to the pool contract (not a router) for direct pool swaps\n- For Router NG swaps, approve tokens to the Router NG contract\n",
37
37
  "debridge": "# deBridge\n\nCross-chain trading infrastructure with DLN (DeBridge Liquidity Network). Supports limit orders and market makers for cross-chain swaps.\n\n## Category\n\nbridge + cross-chain trading | Direction: any-to-any | Chains: Ethereum (1), Arbitrum (42161), Optimism (10), Base (8453), Polygon (137), BSC (56), Avalanche (43114), Arc (5042)\n\n## SauceScript Functions\n\n### bridge\n\nCreate a cross-chain order via DLN (DeBridge Liquidity Network).\n\n```typescript\nimport { DlnSourceABI as IDlnSource } from \"./abis\";\n\nfunction main(\n dlnSourceAddress: Address,\n giveToken: Address,\n giveAmount: Uint256,\n takeToken: bytes,\n takeAmount: Uint256,\n takeChainId: Uint256,\n receiver: bytes,\n): Uint256 {\n const dln = IDlnSource.at(dlnSourceAddress);\n return dln.createOrder(\n {\n giveTokenAddress: giveToken,\n giveAmount: giveAmount,\n takeTokenAddress: takeToken,\n takeAmount: takeAmount,\n takeChainId: takeChainId,\n receiverDst: receiver,\n givePatchAuthoritySrc: ctx.msgSender(),\n orderAuthorityAddressDst: receiver,\n allowedTakerDst: 0x00,\n externalCall: 0x00,\n allowedCancelBeneficiarySrc: 0x00,\n },\n 0x00,\n 0,\n 0x00,\n );\n}\n```\n\n- `giveToken`: ERC-20 token address to send on source chain\n- `giveAmount`: Amount of source token to send\n- `takeToken`: Token address on destination chain (as bytes, since it may be on a non-EVM chain)\n- `takeAmount`: Minimum amount to receive on destination. Set slightly below `giveAmount` to account for market maker spread\n- `takeChainId`: Destination chain ID (uses EVM chain IDs)\n- `receiver`: Recipient address on destination chain (as bytes)\n- `givePatchAuthoritySrc`: Address allowed to increase give amount (set to `msg.sender`)\n- `orderAuthorityAddressDst`: Address that can cancel/modify order on destination (set to `receiver`)\n- `allowedTakerDst`: Restrict which market maker can fill (empty `0x00` = any taker)\n- `externalCall`: Optional calldata to execute on destination after fill\n- `allowedCancelBeneficiarySrc`: Restrict who receives refund on cancellation (empty `0x00` = order creator)\n- Requires ERC-20 approval to DlnSource\n\n## Deployed Addresses\n\n| Chain | Contract | Address |\n| --------- | -------------- | -------------------------------------------- |\n| Ethereum | dlnSource | `0xeF4fB24aD0916217251F553c0596F8Edc630EB66` |\n| Ethereum | dlnDestination | `0xE7351Fd770A37282b91D153Ee690B63579D6dd7f` |\n| Arbitrum | dlnSource | `0xeF4fB24aD0916217251F553c0596F8Edc630EB66` |\n| Arbitrum | dlnDestination | `0xE7351Fd770A37282b91D153Ee690B63579D6dd7f` |\n| Optimism | dlnSource | `0xeF4fB24aD0916217251F553c0596F8Edc630EB66` |\n| Optimism | dlnDestination | `0xE7351Fd770A37282b91D153Ee690B63579D6dd7f` |\n| Base | dlnSource | `0xeF4fB24aD0916217251F553c0596F8Edc630EB66` |\n| Base | dlnDestination | `0xE7351Fd770A37282b91D153Ee690B63579D6dd7f` |\n| Polygon | dlnSource | `0xeF4fB24aD0916217251F553c0596F8Edc630EB66` |\n| Polygon | dlnDestination | `0xE7351Fd770A37282b91D153Ee690B63579D6dd7f` |\n| BSC | dlnSource | `0xeF4fB24aD0916217251F553c0596F8Edc630EB66` |\n| BSC | dlnDestination | `0xE7351Fd770A37282b91D153Ee690B63579D6dd7f` |\n| Avalanche | dlnSource | `0xeF4fB24aD0916217251F553c0596F8Edc630EB66` |\n| Avalanche | dlnDestination | `0xE7351Fd770A37282b91D153Ee690B63579D6dd7f` |\n\n## ABI Reference\n\n### DlnSourceABI\n\n- `createOrder(OrderCreation _orderCreation, bytes _affiliateFee, uint32 _referralCode, bytes _permitEnvelope) returns (bytes32 orderId)` [payable] - Create a cross-chain limit order. Market makers compete to fill on destination\n\nOrderCreation tuple: `(address giveTokenAddress, uint256 giveAmount, bytes takeTokenAddress, uint256 takeAmount, uint256 takeChainId, bytes receiverDst, address givePatchAuthoritySrc, bytes orderAuthorityAddressDst, bytes allowedTakerDst, bytes externalCall, bytes allowedCancelBeneficiarySrc)`\n\n### DlnDestinationABI\n\n- `fulfillOrder(Order _order, uint256 _fulFillAmount, bytes32 _orderId, bytes _permitEnvelope, address _unlockAuthority)` [payable] - Fill an order on the destination chain (called by market makers/solvers)\n\nOrder tuple: `(uint64 makerOrderNonce, bytes makerSrc, uint256 giveChainId, bytes giveTokenAddress, uint256 giveAmount, uint256 takeChainId, bytes receiverDst, address takeTokenAddress, uint256 takeAmount, bytes givePatchAuthoritySrc, address orderAuthorityAddressDst, bytes allowedTakerDst, bytes allowedCancelBeneficiarySrc, bytes externalCall)`\n\n## Notes\n\n- Intent/order-based architecture: users create orders specifying desired outcome, market makers compete to fill\n- Uses EVM chain IDs for destination (unlike LayerZero/Wormhole which use their own IDs)\n- Same DlnSource and DlnDestination addresses deployed across all supported chains\n- `takeAmount` should be set slightly below market rate to incentivize market makers\n- Supports cross-chain swaps natively (giveToken and takeToken can be different assets)\n- `externalCall` enables arbitrary contract execution on destination after the fill\n- Finality: typically 1-5 minutes (market maker fills immediately, then settles asynchronously)\n- Requires ERC-20 approval to DlnSource for the give token\n- TVL: $200M+. Audited\n- On Arc (5042) the DLN `globalFixedNativeFee()` is `1000000000000000000` wei, which is 1 USDC: Arc's native coin is USDC at 18 decimals, paid from the same balance as the USDC ERC-20 `0x3600…0000` (read 2026-09-28)\n",
38
38
  "dodo": "# DODO\n\nProactive Market Maker (PMM) DEX with capital-efficient liquidity provision. Unlike constant product AMMs, DODO uses oracle-guided pricing to concentrate liquidity near market price. Features single-token LP, customizable price curves, and smart routing across DODO pools.\n\n## Category\n\ndex | Chains: Ethereum, BSC\n\n## Key Operations\n\n- **swap**: Swap tokens via DODO V2 Proxy with pair-based routing\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/dodo\";\n```\n\n## SauceScript Examples\n\n### swap\n\n```typescript\nimport { DODOV2ProxyABI as IDODOProxy } from \"./abis\";\n\nfunction main(\n proxyAddress: Address,\n fromToken: Address,\n toToken: Address,\n fromAmount: Uint256,\n minReturn: Uint256,\n dodoPairs: Address[],\n direction: Uint256,\n): Uint256 {\n const proxy = IDODOProxy.at(proxyAddress);\n return proxy.dodoSwapV2TokenToToken(\n fromToken,\n toToken,\n fromAmount,\n minReturn,\n dodoPairs,\n direction,\n false,\n 99999999999,\n );\n}\n```\n\n- `proxyAddress`: DODO V2 Proxy address for the target chain\n- `fromToken`: Input token address\n- `toToken`: Output token address\n- `fromAmount`: Exact input amount (in wei)\n- `minReturn`: Minimum output for slippage protection\n- `dodoPairs`: Array of DODO pool addresses to route through (ordered for the swap path)\n- `direction`: Bitmask encoding which side of each pool to use. Each bit represents a pool: `0` = sell base token, `1` = sell quote token. For a single pool, use `0` or `1`. For multi-pool, combine bits (e.g., `0b01` = first pool sell quote, second pool sell base)\n- `isIncentive`: Set to `false` (incentive mining flag, usually disabled)\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | V2 Proxy | `0xa356867fDCeA8e71AEaf87805808803806231FDc` |\n| BSC | V2 Proxy | `0x8F8Dd7DB1bDA5eD3da8C9dAf3bFA471c12d58486` |\n\n## ABI Methods\n\n### DODOV2ProxyABI\n\n- `dodoSwapV2TokenToToken(address fromToken, address toToken, uint256 fromTokenAmount, uint256 minReturnAmount, address[] dodoPairs, uint256 directions, bool isIncentive, uint256 deadLine) -> uint256 returnAmount` - Execute a token-to-token swap through one or more DODO pools\n\n## Notes\n\n- PMM (Proactive Market Maker) provides better capital efficiency than constant product by concentrating liquidity near oracle price\n- `dodoPairs` is an array of DODO pool addresses defining the swap route (each pool has a base token and a quote token)\n- `directions` is a bitmask: for each pool in the path, a bit value of `0` means \"sell base token\" and `1` means \"sell quote token\". The least significant bit corresponds to the first pool\n- For a single-pool swap: if you're selling the base token of that pool, `directions = 0`; if selling the quote token, `directions = 1`\n- `isIncentive`: Flag for DODO mining incentives, typically set to `false`\n- Pool discovery requires off-chain lookup via DODO's API or subgraph to find the right pool addresses\n- Input token must be ERC20-approved to the V2 Proxy contract\n- DODO also supports single-token LP (provide only one side of liquidity) which is unique among AMMs\n",
39
39
  "eigenlayer": "# EigenLayer\n\nRestaking protocol that enables staked ETH to secure additional protocols (AVS). Deposit LSTs into strategies to earn restaking rewards on top of staking yield.\n\n## Category\n\nrestaking | Chains: Ethereum\n\n## Key Operations\n\n- **depositIntoStrategy**: Deposit LSTs (stETH, rETH, cbETH) into a restaking strategy\n- **delegateTo**: Delegate restaked assets to an operator (via DelegationManager)\n- **undelegate**: Undelegate from an operator, initiating withdrawal\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/eigenlayer\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Deposit LST into restaking strategy\nimport { StrategyManagerABI as IStrategyManager } from \"./abis\";\nfunction main(\n strategyManagerAddress: Address,\n strategy: Address,\n token: Address,\n amount: Uint256,\n): Uint256 {\n const sm = IStrategyManager.at(strategyManagerAddress);\n return sm.depositIntoStrategy(strategy, token, amount);\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | ----------------- | -------------------------------------------- |\n| Ethereum | strategyManager | `0x858646372CC42E1A627fcE94aa7A7033e7CF075A` |\n| Ethereum | delegationManager | `0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A` |\n| Ethereum | stETHStrategy | `0x93c4b944D05dfe6df7645A86cd2206016c51564D` |\n| Ethereum | rETHStrategy | `0x1BeE69b7dFFfA4E2d53C2a2Df135C388AD25dCD2` |\n| Ethereum | cbETHStrategy | `0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc` |\n\n## ABI Methods\n\n### StrategyManagerABI\n\n- `depositIntoStrategy(address,address,uint256)` - Deposit token into strategy. Params: strategy address, token address, amount. Returns shares\n- `stakerDepositShares(address staker, address strategy)` - Query staker's deposit shares in a strategy (renamed from `stakerStrategyShares`)\n\n### DelegationManagerABI\n\n- `delegateTo(address,tuple,bytes32)` - Delegate to operator. Tuple is (signature bytes, expiry uint256). Use empty sig + max expiry\n- `undelegate(address)` - Undelegate staker. Returns withdrawal root bytes32[]\n- `isDelegated(address)` - Check if address is delegated to an operator\n\n### StrategyABI\n\n- `sharesToUnderlyingView(uint256)` - Convert shares to underlying token amount\n- `underlyingToSharesView(uint256)` - Convert underlying amount to shares\n\n## Notes\n\n- TVL: $13B+. Foundation of the restaking ecosystem\n- Approve LST token to StrategyManager before depositing\n- Each LST has its own strategy contract (stETH, rETH, cbETH listed above)\n- Withdrawal has a 7-day delay period after undelegating\n- delegateTo requires approverSignatureAndExpiry tuple - use empty bytes + far-future expiry for typical cases\n",
40
40
  "ens": "# ENS\n\nEthereum Name Service - the decentralized naming system for wallets, websites, and resources. Maps human-readable names to Ethereum addresses.\n\n## Category\n\ninfrastructure | Chains: Ethereum\n\n## Key Operations\n\n- **setResolver**: Set the resolver contract for an ENS name\n- **setOwner**: Transfer ownership of an ENS name\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/ens\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Set resolver for ENS name\nimport { ENSRegistryABI as IENSRegistry } from \"./abis\";\nfunction main(registryAddress: Address, node: Uint256, resolver: Address): Uint256 {\n const registry = IENSRegistry.at(registryAddress);\n registry.setResolver(node, resolver);\n return 1;\n}\n\n// Transfer ENS name ownership\nimport { ENSRegistryABI as IENSRegistry } from \"./abis\";\nfunction main(registryAddress: Address, node: Uint256, owner: Address): Uint256 {\n const registry = IENSRegistry.at(registryAddress);\n registry.setOwner(node, owner);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | ------------- | -------------------------------------------- |\n| Ethereum | registry | `0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e` |\n| Ethereum | baseRegistrar | `0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85` |\n\n## ABI Methods\n\n### ENSRegistryABI\n\n- `owner(bytes32)` - Query owner of a name node. Params: node (namehash). Returns owner address\n- `resolver(bytes32)` - Query resolver of a name node. Params: node. Returns resolver address\n- `setOwner(bytes32,address)` - Transfer name ownership. Params: node, owner (new owner)\n- `setResolver(bytes32,address)` - Set resolver contract. Params: node, resolver (new resolver)\n\n### BaseRegistrarABI\n\n- `nameExpires(uint256)` - Check when a .eth name expires. Params: id (label hash as uint256). Returns expiry timestamp\n- `reclaim(uint256,address)` - Reclaim ENS registry ownership. Params: id (label hash), owner. Only callable by registrant\n\n## Notes\n\n- node = namehash of the ENS name (e.g. namehash(\"vitalik.eth\") = keccak256 chain)\n- .eth names are ERC-721 NFTs held in the baseRegistrar contract\n- Resolver contract stores the address/content records for a name\n- Only the owner of a node can setResolver or setOwner\n- Registration/renewal happens via the ETHRegistrarController (not in this ABI set)\n",
41
- "erc20": "# ERC-20\n\nThe standard interface for fungible tokens on EVM chains (EIP-20). Defines transfer, approve, transferFrom, balanceOf, allowance, totalSupply, name, symbol, and decimals.\n\n## Category\n\ninfrastructure | Chains: (standard interface, any chain)\n\n## Key Operations\n\n- **transfer**: Send tokens to an address\n- **approve**: Authorize a spender to transfer tokens on your behalf\n- **transferFrom**: Transfer tokens from one address to another (requires approval)\n- **balanceOf**: Query token balance of an address\n- **allowance**: Query remaining approved amount for a spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/erc20\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer tokens\nimport { ERC20ABI as IERC20 } from \"./abis\";\nfunction main(token: Address, to: Address, amount: Uint256): Uint256 {\n const t = IERC20.at(token);\n t.transfer(to, amount);\n return 1;\n}\n\n// Approve spender\nimport { ERC20ABI as IERC20 } from \"./abis\";\nfunction main(token: Address, spender: Address, amount: Uint256): Uint256 {\n const t = IERC20.at(token);\n t.approve(spender, amount);\n return 1;\n}\n\n// Transfer from (requires prior approval)\nimport { ERC20ABI as IERC20 } from \"./abis\";\nfunction main(token: Address, from: Address, to: Address, amount: Uint256): Uint256 {\n const t = IERC20.at(token);\n t.transferFrom(from, to, amount);\n return 1;\n}\n\n// Check balance\nimport { ERC20ABI as IERC20 } from \"./abis\";\nfunction main(token: Address, account: Address): Uint256 {\n const t = IERC20.at(token);\n return t.balanceOf(account);\n}\n\n// Storage-based transfer (demonstrates storage, crypto, abi encode, events)\nfunction balanceSlot(account: Address): Uint256 {\n return crypto.keccak256(abi.encode(account, 1));\n}\nfunction main(to: Address, amount: Uint256): Uint256 {\n const from = ctx.msgSender();\n const fromSlot = balanceSlot(from);\n const fromBalance = evm.sload(fromSlot);\n if (fromBalance < amount) throw \"insufficient balance\";\n evm.sstore(fromSlot, fromBalance - amount);\n const toSlot = balanceSlot(to);\n evm.sstore(toSlot, evm.sload(toSlot) + amount);\n log(abi.encode(from, to, amount), crypto.keccak256(\"Transfer(address,address,uint256)\"));\n return 1;\n}\n```\n\n## ABI Methods\n\n### ERC20ABI\n\n- `transfer(address,uint256)` - Transfer tokens. Params: to (recipient), amount (token amount). Returns bool success\n- `approve(address,uint256)` - Approve spender. Params: spender (authorized address), amount (max transfer amount). Returns bool success\n- `transferFrom(address,address,uint256)` - Transfer from approved address. Params: from (source), to (destination), amount. Returns bool success\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256 balance\n- `allowance(address,address)` - Query allowance. Params: owner, spender. Returns uint256 remaining allowance\n- `totalSupply()` - Total token supply. Returns uint256\n- `name()` - Token name. Returns string\n- `symbol()` - Token symbol. Returns string\n- `decimals()` - Token decimals. Returns uint8 (typically 18; USDC/USDT use 6)\n\n### Events\n\n- `Transfer(address indexed from, address indexed to, uint256 value)` - Emitted on transfer and transferFrom\n- `Approval(address indexed owner, address indexed spender, uint256 value)` - Emitted on approve\n\n## Notes\n\n- Standard interface (EIP-20) - not a specific deployment, no fixed addresses\n- Every fungible token on every EVM chain implements this interface\n- approve before transferFrom: spender must be approved by the owner first\n- Common pattern: approve max (type(uint256).max) for DeFi interactions, or use exact amounts for security\n- decimals varies: most tokens use 18, USDC/USDT use 6, WBTC uses 8\n- Some tokens (USDT) do not return bool from transfer/approve -- use SafeERC20 wrapper for compatibility\n- The storageTransfer example demonstrates how to implement ERC-20 logic entirely in SauceScript using storage operations\n",
41
+ "erc20": "# ERC-20\n\nThe standard interface for fungible tokens on EVM chains (EIP-20). Defines transfer, approve, transferFrom, balanceOf, allowance, totalSupply, name, symbol, and decimals.\n\n## Category\n\ninfrastructure | Chains: (standard interface, any chain)\n\n## Key Operations\n\n- **transfer**: Send tokens to an address\n- **approve**: Authorize a spender to transfer tokens on your behalf\n- **transferFrom**: Transfer tokens from one address to another (requires approval)\n- **balanceOf**: Query token balance of an address\n- **allowance**: Query remaining approved amount for a spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/erc20\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer tokens\nimport { transfer } from \"@sauce/token\";\nfunction main(token: Address, to: Address, amount: Uint256): Uint256 {\n transfer(token, to, amount);\n return 1;\n}\n\n// Approve spender\nimport { approve } from \"@sauce/token\";\nfunction main(token: Address, spender: Address, amount: Uint256): Uint256 {\n approve(token, spender, amount);\n return 1;\n}\n\n// Transfer from (requires prior approval)\nimport { transferFrom } from \"@sauce/token\";\nfunction main(token: Address, from: Address, to: Address, amount: Uint256): Uint256 {\n transferFrom(token, from, to, amount);\n return 1;\n}\n\n// Check balance\nimport { ERC20ABI as IERC20 } from \"./abis\";\nfunction main(token: Address, account: Address): Uint256 {\n const t = IERC20.at(token);\n return t.balanceOf(account);\n}\n\n// Storage-based transfer (demonstrates storage, crypto, abi encode, events)\nfunction balanceSlot(account: Address): Uint256 {\n return crypto.keccak256(abi.encode(account, 1));\n}\nfunction main(to: Address, amount: Uint256): Uint256 {\n const from = ctx.msgSender();\n const fromSlot = balanceSlot(from);\n const fromBalance = evm.sload(fromSlot);\n if (fromBalance < amount) throw \"insufficient balance\";\n evm.sstore(fromSlot, fromBalance - amount);\n const toSlot = balanceSlot(to);\n evm.sstore(toSlot, evm.sload(toSlot) + amount);\n log(abi.encode(from, to, amount), crypto.keccak256(\"Transfer(address,address,uint256)\"));\n return 1;\n}\n```\n\n## ABI Methods\n\n### ERC20ABI\n\n- `transfer(address,uint256)` - Transfer tokens. Params: to (recipient), amount (token amount). Returns bool success\n- `approve(address,uint256)` - Approve spender. Params: spender (authorized address), amount (max transfer amount). Returns bool success\n- `transferFrom(address,address,uint256)` - Transfer from approved address. Params: from (source), to (destination), amount. Returns bool success\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256 balance\n- `allowance(address,address)` - Query allowance. Params: owner, spender. Returns uint256 remaining allowance\n- `totalSupply()` - Total token supply. Returns uint256\n- `name()` - Token name. Returns string\n- `symbol()` - Token symbol. Returns string\n- `decimals()` - Token decimals. Returns uint8 (typically 18; USDC/USDT use 6)\n\n### Events\n\n- `Transfer(address indexed from, address indexed to, uint256 value)` - Emitted on transfer and transferFrom\n- `Approval(address indexed owner, address indexed spender, uint256 value)` - Emitted on approve\n\n## Notes\n\n- Standard interface (EIP-20) - not a specific deployment, no fixed addresses\n- Every fungible token on every EVM chain implements this interface\n- approve before transferFrom: spender must be approved by the owner first\n- Common pattern: approve max (type(uint256).max) for DeFi interactions, or use exact amounts for security\n- decimals varies: most tokens use 18, USDC/USDT use 6, WBTC uses 8\n- Some tokens (USDT) do not return bool from transfer/approve. The writes above are `@sauce/token`'s, which follow OpenZeppelin SafeERC20: empty returndata is accepted from a contract, a `false` return reverts, and `approve` resets a non-zero allowance to zero first. They return nothing, since a refused write reverts the program\n- `ERC20ABI.at(token).transfer(...)` is the raw ABI call and is NOT SafeERC20: it reverts on a token that returns nothing and accepts one that returns `false`. Use it only when that is what you mean\n- The storageTransfer example demonstrates how to implement ERC-20 logic entirely in SauceScript using storage operations\n",
42
42
  "erc3156": "# ERC-3156\n\nStandard interface for flash loans (EIP-3156). Provides flashLoan, flashFee, and maxFlashLoan functions for any conforming lender.\n\n## Category\n\ninfrastructure | Chains: (standard interface, any chain)\n\n## Key Operations\n\n- **flashFee**: Query the fee for a flash loan of a specific token and amount\n- **maxFlashLoan**: Query the maximum flash loan amount for a token\n- **flashLoan**: Execute a flash loan (borrow and repay in same transaction)\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/erc3156\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Query flash loan fee\nimport { FlashLenderABI as IFlashLender } from \"./abis\";\nfunction main(lenderAddress: Address, token: Address, amount: Uint256): Uint256 {\n const lender = IFlashLender.at(lenderAddress);\n return lender.flashFee(token, amount);\n}\n\n// Query max flash loan amount\nimport { FlashLenderABI as IFlashLender } from \"./abis\";\nfunction main(lenderAddress: Address, token: Address): Uint256 {\n const lender = IFlashLender.at(lenderAddress);\n return lender.maxFlashLoan(token);\n}\n```\n\n## ABI Methods\n\n### FlashLenderABI\n\n- `flashLoan(address,address,uint256,bytes)` - Execute flash loan. Params: receiver (contract that receives tokens and implements IERC3156FlashBorrower), token (ERC-20 to borrow), amount (borrow amount), data (arbitrary data passed to receiver callback). Returns bool success\n- `flashFee(address,uint256)` - Query fee for flash loan. Params: token, amount. Returns fee in token units\n- `maxFlashLoan(address)` - Query max borrowable amount. Params: token. Returns max amount (0 if token not supported)\n\n## Notes\n\n- Standard interface (EIP-3156), not a specific deployment - no fixed addresses\n- Aave V3, Maker (sDAI), Uniswap V3, and Balancer all implement ERC-3156\n- Receiver contract must implement `onFlashLoan(address,address,uint256,uint256,bytes)` callback\n- Must approve lender to pull back amount + fee before callback returns\n- Flash loan must be fully repaid within the same transaction (atomic)\n- Fee varies by implementation: Aave charges 0.05-0.09%, Balancer charges 0%, Maker varies\n",
43
43
  "erc4626": "# ERC-4626\n\nStandard interface for tokenized vaults (EIP-4626). Provides deposit, withdraw, mint, redeem and preview functions for any conforming vault.\n\n## Category\n\nyield | Chains: (standard interface, any chain)\n\n## Key Operations\n\n- **deposit**: Deposit underlying assets, receive vault shares\n- **withdraw**: Withdraw by specifying asset amount to receive\n- **redeem**: Withdraw by specifying share amount to burn\n- **mint**: Mint exact number of shares by depositing required assets\n- **previewDeposit**: Preview how many shares a deposit would yield\n- **previewWithdraw**: Preview how many shares would be burned for a withdrawal\n- **convertToShares**: Convert asset amount to equivalent shares\n- **convertToAssets**: Convert share amount to equivalent assets\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/erc4626\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Deposit assets into vault\nimport { VaultABI as IVault } from \"./abis\";\nfunction main(vaultAddress: Address, assets: Uint256, receiver: Address): Uint256 {\n const vault = IVault.at(vaultAddress);\n return vault.deposit(assets, receiver);\n}\n\n// Withdraw assets from vault\nimport { VaultABI as IVault } from \"./abis\";\nfunction main(vaultAddress: Address, assets: Uint256, receiver: Address, owner: Address): Uint256 {\n const vault = IVault.at(vaultAddress);\n return vault.withdraw(assets, receiver, owner);\n}\n\n// Redeem shares for assets\nimport { VaultABI as IVault } from \"./abis\";\nfunction main(vaultAddress: Address, shares: Uint256, receiver: Address, owner: Address): Uint256 {\n const vault = IVault.at(vaultAddress);\n return vault.redeem(shares, receiver, owner);\n}\n\n// Preview deposit\nimport { VaultABI as IVault } from \"./abis\";\nfunction main(vaultAddress: Address, assets: Uint256): Uint256 {\n const vault = IVault.at(vaultAddress);\n return vault.previewDeposit(assets);\n}\n\n// Convert assets to shares\nimport { VaultABI as IVault } from \"./abis\";\nfunction main(vaultAddress: Address, assets: Uint256): Uint256 {\n const vault = IVault.at(vaultAddress);\n return vault.convertToShares(assets);\n}\n```\n\n## ABI Methods\n\n### VaultABI\n\n- `deposit(uint256,address)` - Deposit assets. Params: assets (underlying token amount), receiver (share recipient). Returns shares minted\n- `mint(uint256,address)` - Mint exact shares. Params: shares (exact shares to mint), receiver. Returns assets required\n- `withdraw(uint256,address,address)` - Withdraw by asset amount. Params: assets (amount to withdraw), receiver, owner (share owner). Returns shares burned\n- `redeem(uint256,address,address)` - Redeem shares. Params: shares (to burn), receiver, owner. Returns assets received\n- `totalAssets()` - Total assets managed by vault. Returns uint256\n- `convertToShares(uint256)` - Convert assets to shares (current rate). Params: assets. Returns shares\n- `convertToAssets(uint256)` - Convert shares to assets (current rate). Params: shares. Returns assets\n- `previewDeposit(uint256)` - Preview deposit (includes fees). Params: assets. Returns shares\n- `previewMint(uint256)` - Preview mint cost. Params: shares. Returns assets needed\n- `previewWithdraw(uint256)` - Preview withdrawal. Params: assets. Returns shares to burn\n- `previewRedeem(uint256)` - Preview redemption. Params: shares. Returns assets received\n- `maxDeposit(address)` - Max deposit allowed. Params: receiver. Returns max assets\n- `maxMint(address)` - Max mint allowed. Params: receiver. Returns max shares\n- `maxWithdraw(address)` - Max withdrawal allowed. Params: owner. Returns max assets\n- `maxRedeem(address)` - Max redemption allowed. Params: owner. Returns max shares\n- `asset()` - Underlying asset address. Returns address\n\n## Notes\n\n- Standard interface (EIP-4626) - protocols that implement it: sDAI, sfrxETH, pufETH, Yearn V3, sUSDe, Beefy vaults\n- deposit/withdraw operate in asset terms; mint/redeem operate in share terms\n- preview* functions account for fees; convert* are pure exchange rate calculations\n- Approve the underlying asset to the vault before calling deposit\n- owner parameter in withdraw/redeem allows withdrawal on behalf of another address (requires approval)\n- Share price typically increases over time as yield accrues\n",
44
44
  "ethena": "# Ethena\n\nSynthetic dollar protocol providing USDe, a crypto-native dollar backed by delta-neutral positions. sUSDe offers yield from staking and funding rates.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **stakeUSDe**: Stake USDe to receive sUSDe (yield-bearing)\n- **cooldownAssets**: Start cooldown period to unstake (by asset amount)\n- **cooldownShares**: Start cooldown period to unstake (by share amount)\n- **unstake**: Complete unstake after cooldown period\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/ethena\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Stake USDe for sUSDe\nimport { StakedUSDeABI as IStakedUSDe } from \"./abis\";\nfunction main(susdeAddress: Address, amount: Uint256, receiver: Address): Uint256 {\n const susde = IStakedUSDe.at(susdeAddress);\n return susde.deposit(amount, receiver);\n}\n\n// Start cooldown to unstake\nimport { StakedUSDeABI as IStakedUSDe } from \"./abis\";\nfunction main(susdeAddress: Address, assets: Uint256): Uint256 {\n const susde = IStakedUSDe.at(susdeAddress);\n return susde.cooldownAssets(assets);\n}\n\n// Complete unstake after cooldown\nimport { StakedUSDeABI as IStakedUSDe } from \"./abis\";\nfunction main(susdeAddress: Address, receiver: Address): Uint256 {\n const susde = IStakedUSDe.at(susdeAddress);\n susde.unstake(receiver);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | usde | `0x4c9EDD5852cd905f086C759E8383e09bff1e68b3` |\n| Ethereum | susde | `0x9D39A5DE30e57443BfF2A8307A4256c8797A3497` |\n\n## ABI Methods\n\n### StakedUSDeABI (ERC-4626 + cooldown)\n\n- `deposit(uint256,address)` - Stake USDe, receive sUSDe. Params: assets (USDe amount), receiver. Returns shares (sUSDe minted)\n- `withdraw(uint256,address,address)` - Withdraw by asset amount (after cooldown). Params: assets, receiver, owner. Returns shares burned\n- `redeem(uint256,address,address)` - Redeem by share amount (after cooldown). Params: shares, receiver, owner. Returns assets\n- `cooldownAssets(uint256)` - Start cooldown by specifying USDe amount. Params: assets. Returns shares that will be burned\n- `cooldownShares(uint256)` - Start cooldown by specifying sUSDe amount. Params: shares. Returns assets that will be received\n- `unstake(address)` - Complete unstake after cooldown. Params: receiver (address to receive USDe)\n\n### USDeABI\n\n- `approve(address,uint256)` - Approve USDe spender. Params: spender, amount. Returns bool\n\n## Notes\n\n- TVL: $3B+. Yield comes from ETH staking rewards + perpetual funding rates\n- 7-day cooldown period required before unstaking\n- Flow: deposit (stake) -> cooldownAssets/cooldownShares (start cooldown) -> unstake (after 7 days)\n- Approve USDe to sUSDe contract before depositing\n- sUSDe appreciates against USDe as yield accrues\n",
@@ -47,11 +47,11 @@ export const SKILL_DOCUMENTS = {
47
47
  "fenix": "# Fenix\n\nBlast-native ve(3,3) DEX and liquidity hub. Solidly-fork with concentrated liquidity and gauge voting, leveraging Blast's native yield on ETH and USDB for enhanced LP returns.\n\n## Category\n\ndex | Chains: Blast\n\n## Key Operations\n\n- **swap**: Swap tokens with route-based routing specifying stable or volatile pool type\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/fenix\";\n```\n\n## SauceScript Examples\n\n### swap\n\n```typescript\nimport { FenixRouterABI as IRouter } from \"./abis\";\n\nfunction main(\n routerAddress: Address,\n tokenIn: Address,\n tokenOut: Address,\n stable: bool,\n amountIn: Uint256,\n amountOutMin: Uint256,\n recipient: Address,\n): Uint256[] {\n const router = IRouter.at(routerAddress);\n return router.swapExactTokensForTokens(\n amountIn,\n amountOutMin,\n [{ from: tokenIn, to: tokenOut, stable: stable }],\n recipient,\n 99999999999,\n );\n}\n```\n\n- `routerAddress`: Fenix Router on Blast (`0xbD571125856975DBfC2E9b6d1DE496D614D7BAEE`)\n- `tokenIn` / `tokenOut`: Input and output token addresses\n- `stable`: `true` for stable pools (pegged assets like USDB/USDC), `false` for volatile pools\n- `amountIn`: Exact input amount (in wei)\n- `amountOutMin`: Minimum output for slippage protection\n- `recipient`: Address to receive output tokens\n- Routes support multi-hop: `[{from: A, to: B, stable: false}, {from: B, to: C, stable: true}]`\n- Note: Fenix routes do NOT include a `factory` field (unlike Velodrome/Aerodrome)\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| ----- | -------- | -------------------------------------------- |\n| Blast | Router | `0xbD571125856975DBfC2E9b6d1DE496D614D7BAEE` |\n\n## ABI Methods\n\n### FenixRouterABI\n\n- `swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, tuple[](address from, address to, bool stable) routes, address to, uint256 deadline) -> uint256[] amounts` - Swap with route tuples specifying pool type per hop\n- `addLiquidity(address tokenA, address tokenB, bool stable, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) -> (uint256 amountA, uint256 amountB, uint256 liquidity)` - Add liquidity to a stable or volatile pool\n\n## Notes\n\n- Solidly-fork routes contain `{from, to, stable}` tuples (NO `factory` field)\n- Two pool types: `stable=true` for correlated assets, `stable=false` for uncorrelated\n- ve(3,3) model: FNX token holders vote-lock to direct gauge emissions\n- Blast-only deployment; leverages Blast's native yield on ETH and USDB for additional LP returns\n- For concentrated liquidity swaps on Blast, consider Thruster (Uniswap V3 fork)\n- LP tokens can be staked in gauges for FNX emissions\n- Input token must be ERC20-approved to the Router\n",
48
48
  "fluid": "# Fluid\n\nLiquidity layer that unifies lending and DEX liquidity. Deposited assets simultaneously serve as lending collateral and DEX liquidity.\n\n## Category\n\nlending | Chains: Ethereum (1), Arbitrum (42161)\n\n## SauceScript Functions\n\nThe Liquidity layer (`liquidity` below) has no user-level deposit or borrow function: its only entry point, `operate`, is restricted to Fluid's own protocols. Users lend through **fTokens** (ERC-4626 vaults such as fUSDC) and borrow through **Vaults**.\n\n### deposit\n\nDeposit the fToken's underlying asset and receive fToken shares.\n\n```typescript\nimport { FluidLendingABI as IFToken } from \"./abis\";\n\nfunction main(fTokenAddress: Address, amount: Uint256, to: Address): Uint256 {\n const fToken = IFToken.at(fTokenAddress);\n return fToken.deposit(amount, to);\n}\n```\n\n- `fTokenAddress`: the fToken for the asset (e.g. fUSDC `0x9Fb7b4477576Fe5B32be4C1843aFB1e55F251B33` on Ethereum)\n- `to`: Address that receives the shares\n- Returns the shares minted\n- Requires ERC-20 approval of the underlying to the fToken\n\n### withdraw\n\nWithdraw an amount of the underlying asset, burning the owner's shares.\n\n```typescript\nimport { FluidLendingABI as IFToken } from \"./abis\";\n\nfunction main(fTokenAddress: Address, amount: Uint256, to: Address, owner: Address): Uint256 {\n const fToken = IFToken.at(fTokenAddress);\n return fToken.withdraw(amount, to, owner);\n}\n```\n\n- Returns the shares burned\n\n### borrow\n\nBorrow from a Vault position: `operate` with a positive debt delta.\n\n```typescript\nimport { FluidVaultABI as IFluidVault } from \"./abis\";\n\nfunction main(vaultAddress: Address, nftId: Uint256, amount: Uint256, to: Address): Uint256 {\n const vault = IFluidVault.at(vaultAddress);\n vault.operate(nftId, 0, amount, to);\n return 1;\n}\n```\n\n- `nftId`: the position NFT (0 opens a new position)\n- The position must hold enough collateral (a positive `newCol` delta deposits it)\n\n### repay\n\nRepay a Vault position's debt: `operate` with a negative debt delta.\n\n```typescript\nimport { FluidVaultABI as IFluidVault } from \"./abis\";\n\nfunction main(\n vaultAddress: Address,\n nftId: Uint256,\n negativeDebtDelta: Uint256,\n to: Address,\n): Uint256 {\n const vault = IFluidVault.at(vaultAddress);\n vault.operate(nftId, 0, negativeDebtDelta, to);\n return 1;\n}\n```\n\n- `negativeDebtDelta`: the repayment as a negative `int256`, passed in two's complement (`2**256 - amount`)\n- Requires ERC-20 approval of the debt token to the Vault\n\n## Deployed Addresses\n\n| Chain | Contract | Address |\n| -------- | --------- | -------------------------------------------- |\n| Ethereum | liquidity | `0x52Aa899454998Be5b000Ad077a46Bbe360F4e497` |\n| Arbitrum | liquidity | `0x52Aa899454998Be5b000Ad077a46Bbe360F4e497` |\n\n## ABI Reference\n\n### FluidLendingABI (an fToken)\n\n- `deposit(uint256 assets, address receiver) returns (uint256 shares)` - ERC-4626 deposit\n- `withdraw(uint256 assets, address receiver, address owner) returns (uint256 shares)` - ERC-4626 withdraw\n- `redeem(uint256 shares, address receiver, address owner) returns (uint256 assets)` - ERC-4626 redeem\n- `asset() returns (address)` - The underlying token\n\n### FluidVaultABI (a Vault)\n\n- `operate(uint256 nftId, int256 newCol, int256 newDebt, address to) returns (uint256, int256, int256)` [payable] - Change a position's collateral and debt in one call; positive deltas deposit/borrow, negative ones withdraw/repay\n\n## Notes\n\n- Novel architecture: deposited assets serve dual purpose as lending collateral AND DEX liquidity\n- No interest rate mode selection -- all rates are algorithmically determined\n- Same contract address on Ethereum and Arbitrum\n- Built by the Instadapp team\n- All deposit/repay operations require ERC-20 approval (to the fToken or Vault)\n- TVL: $1B+. Audited\n",
49
49
  "frax-ether": "# Frax Ether\n\nFrax Finance's liquid staking derivative. Two tokens: frxETH (pegged 1:1 to ETH) and sfrxETH (yield-bearing ERC-4626 vault token that accrues staking yield).\n\n## Category\n\nliquid-staking | Chains: Ethereum\n\n## Key Operations\n\n- **submitAndDeposit**: Stake ETH and deposit directly into sfrxETH vault (one-step)\n- **deposit**: Deposit frxETH into sfrxETH vault\n- **redeem**: Redeem sfrxETH shares for frxETH\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/frax-ether\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Stake ETH and get sfrxETH directly\nimport { FrxETHMinterABI as IFrxETHMinter } from \"./abis\";\nfunction main(minterAddress: Address, recipient: Address): Uint256 {\n const minter = IFrxETHMinter.at(minterAddress);\n return minter.submitAndDeposit(recipient);\n}\n\n// Deposit frxETH into sfrxETH vault\nimport { SfrxETHABI as ISfrxETH } from \"./abis\";\nfunction main(sfrxethAddress: Address, assets: Uint256, receiver: Address): Uint256 {\n const sfrxeth = ISfrxETH.at(sfrxethAddress);\n return sfrxeth.deposit(assets, receiver);\n}\n\n// Redeem sfrxETH for frxETH\nimport { SfrxETHABI as ISfrxETH } from \"./abis\";\nfunction main(\n sfrxethAddress: Address,\n shares: Uint256,\n receiver: Address,\n owner: Address,\n): Uint256 {\n const sfrxeth = ISfrxETH.at(sfrxethAddress);\n return sfrxeth.redeem(shares, receiver, owner);\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | ------------ | -------------------------------------------- |\n| Ethereum | sfrxETH | `0xac3E018457B222d93114458476f3E3416Abbe38F` |\n| Ethereum | frxETHMinter | `0xbAFA44EFE7901E04E39Dad13167D089C559c1138` |\n\n## ABI Methods\n\n### FrxETHMinterABI\n\n- `submitAndDeposit(address)` - Stake ETH (payable) and auto-deposit into sfrxETH vault. Returns sfrxETH shares\n\n### SfrxETHABI (ERC-4626)\n\n- `deposit(uint256,address)` - Deposit frxETH, receive sfrxETH shares\n- `redeem(uint256,address,address)` - Redeem sfrxETH shares for frxETH. Params: shares, receiver, owner\n- `convertToShares(uint256)` - Preview shares for frxETH amount\n- `convertToAssets(uint256)` - Preview frxETH for sfrxETH amount\n- `balanceOf(address)` - Query sfrxETH balance\n\n## Notes\n\n- TVL: $700M+. sfrxETH is ERC-4626 compliant (same interface as Yearn V3, pufETH)\n- submitAndDeposit() is payable - send ETH as msg.value. One-step ETH -> sfrxETH\n- frxETH does not earn yield on its own - must be deposited into sfrxETH vault\n- Approve frxETH to sfrxETH contract before calling deposit()\n",
50
- "frax": "# Frax Finance\n\nFractional-algorithmic stablecoin protocol with FRAX stablecoin, sFRAX staking, and FXS governance.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **transfer**: Transfer FRAX tokens\n- **approve**: Approve FRAX token spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/frax\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer FRAX\nimport { FraxERC20ABI as IFRAX } from \"./abis\";\nfunction main(fraxAddress: Address, to: Address, amount: Uint256): Uint256 {\n const frax = IFRAX.at(fraxAddress);\n return frax.transfer(to, amount);\n}\n\n// Approve FRAX spender\nimport { FraxERC20ABI as IFRAX } from \"./abis\";\nfunction main(fraxAddress: Address, spender: Address, amount: Uint256): Uint256 {\n const frax = IFRAX.at(fraxAddress);\n return frax.approve(spender, amount);\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | frax | `0x853d955aCEf822Db058eb8505911ED77F175b99e` |\n\n## ABI Methods\n\n### FraxERC20ABI\n\n- `transfer(address,uint256)` - Transfer FRAX. Params: to, amount. Returns bool\n- `approve(address,uint256)` - Approve spender. Params: spender, amount. Returns bool\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256\n\n## Notes\n\n- TVL: $1B+. V1 was fractional-algorithmic, V2 (Frax v3) is fully collateralized\n- FXS is the governance/value accrual token\n- sFRAX is the staked FRAX vault (ERC-4626) for earning yield - see frax-ether module for sfrxETH\n- Frax ecosystem includes: FRAX (stablecoin), frxETH/sfrxETH (liquid staking), FPI (inflation-pegged)\n",
50
+ "frax": "# Frax Finance\n\nFractional-algorithmic stablecoin protocol with FRAX stablecoin, sFRAX staking, and FXS governance.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **transfer**: Transfer FRAX tokens\n- **approve**: Approve FRAX token spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/frax\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer FRAX\nimport { transfer } from \"@sauce/token\";\nfunction main(fraxAddress: Address, to: Address, amount: Uint256): Uint256 {\n transfer(fraxAddress, to, amount);\n return 1;\n}\n\n// Approve FRAX spender\nimport { approve } from \"@sauce/token\";\nfunction main(fraxAddress: Address, spender: Address, amount: Uint256): Uint256 {\n approve(fraxAddress, spender, amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | frax | `0x853d955aCEf822Db058eb8505911ED77F175b99e` |\n\n## ABI Methods\n\n### FraxERC20ABI\n\n- `transfer(address,uint256)` - Transfer FRAX. Params: to, amount. Returns bool\n- `approve(address,uint256)` - Approve spender. Params: spender, amount. Returns bool\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256\n\n## Notes\n\n- TVL: $1B+. V1 was fractional-algorithmic, V2 (Frax v3) is fully collateralized\n- FXS is the governance/value accrual token\n- sFRAX is the staked FRAX vault (ERC-4626) for earning yield - see frax-ether module for sfrxETH\n- Frax ecosystem includes: FRAX (stablecoin), frxETH/sfrxETH (liquid staking), FPI (inflation-pegged)\n",
51
51
  "gains-network": "# Gains Network\n\nDecentralized leveraged trading platform (gTrade) supporting crypto, forex, and stocks with synthetic leverage up to 1000x on forex pairs. Diamond proxy architecture.\n\n## Category\n\nperpetuals | Chains: Arbitrum, Polygon\n\n## Key Operations\n\n- **closeTradeMarket**: Close an open trade at market price\n- **updateStopLoss**: Update stop loss on an open trade\n- **updateTakeProfit**: Update take profit on an open trade\n\n## SDK Usage\n\n```typescript\nimport {\n protocolInfo,\n deployments,\n sauceFunctions,\n} from \"@eco-incorp/sauce/protocols/gains-network\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Close trade at market price\nimport { DiamondABI as IDiamond } from \"./abis\";\nfunction main(diamondAddress: Address, index: Uint256, expectedPrice: Uint256): Uint256 {\n const diamond = IDiamond.at(diamondAddress);\n diamond.closeTradeMarket(index, expectedPrice);\n return 1;\n}\n\n// Update stop loss\nimport { DiamondABI as IDiamond } from \"./abis\";\nfunction main(diamondAddress: Address, index: Uint256, newSl: Uint256): Uint256 {\n const diamond = IDiamond.at(diamondAddress);\n diamond.updateSl(index, newSl);\n return 1;\n}\n\n// Update take profit\nimport { DiamondABI as IDiamond } from \"./abis\";\nfunction main(diamondAddress: Address, index: Uint256, newTp: Uint256): Uint256 {\n const diamond = IDiamond.at(diamondAddress);\n diamond.updateTp(index, newTp);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Arbitrum | diamond | `0xFF162c694eAA571f685030649814282eA457f169` |\n| Arbitrum | gns | `0x18c11FD286C5EC11c3b683Caa813B77f5163A122` |\n| Polygon | diamond | `0x209A9A01980377916851af2cA075C2b170452018` |\n\n## ABI Methods\n\n### DiamondABI\n\nThese are the multi-collateral Diamond's (v8+) signatures. A trade is addressed by the trader's own `index` (their Nth trade), not by `(pairIndex, index)`.\n\n- `openTrade(Trade trade, uint16 maxSlippageP, address referrer)` - Open a trade. Trade: `(address user, uint32 index, uint16 pairIndex, uint24 leverage, bool long, bool isOpen, uint8 collateralIndex, uint8 tradeType, uint120 collateralAmount, uint64 openPrice, uint64 tp, uint64 sl, bool isCounterTrade, uint160 positionSizeToken, uint24 __placeholder)`\n- `closeTradeMarket(uint32 index, uint64 expectedPrice)` - Close at market\n- `updateSl(uint32 index, uint64 newSl)` - Update stop loss\n- `updateTp(uint32 index, uint64 newTp)` - Update take profit\n\n## Notes\n\n- TVL: $50M+. Up to 1000x leverage on forex, 250x on crypto\n- Diamond proxy pattern - single contract for all operations\n- pairIndex identifies the trading pair (0=BTC/USD, 1=ETH/USD, etc.)\n- index identifies one of the trader's trades (a trader can have several)\n- openTrade uses complex tuple param for trade parameters\n",
52
52
  "gamma": "# Gamma Strategies\n\nActive concentrated liquidity management protocol. Manages Uniswap V3, Algebra, and other CL DEX positions with automated rebalancing via Hypervisor vaults.\n\n## Category\n\nyield | Chains: Ethereum, Polygon\n\n## Key Operations\n\n- **deposit**: Deposit token pair into Gamma Hypervisor via UniProxy\n- **withdraw**: Withdraw liquidity from Hypervisor\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/gamma\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Deposit into Gamma Hypervisor\nimport { UniProxyABI as IUniProxy } from \"./abis\";\nfunction main(\n uniProxyAddress: Address,\n deposit0: Uint256,\n deposit1: Uint256,\n to: Address,\n pos: Address,\n): Uint256 {\n const proxy = IUniProxy.at(uniProxyAddress);\n return proxy.deposit(deposit0, deposit1, to, pos, [0, 0, 0, 0]);\n}\n\n// Withdraw from Hypervisor\nimport { HypervisorABI as IHypervisor } from \"./abis\";\nfunction main(hypervisorAddress: Address, shares: Uint256, to: Address, from: Address): Uint256 {\n const hv = IHypervisor.at(hypervisorAddress);\n hv.withdraw(shares, to, from, [0, 0, 0, 0]);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | ------------------------------------------------------------------------------------------ |\n| Ethereum | uniProxy | `0xf5bfa20f4a77933fee0c7bb7f39e7642a070d599` (an earlier release: use `UniProxyLegacyABI`) |\n| Polygon | uniProxy | `0xA42d55074869491D60Ac05490376B74cF19B00e6` |\n\n## ABI Methods\n\n### UniProxyABI\n\n- `deposit(uint256,uint256,address,address,uint256[4])` - Deposit tokens. Params: deposit0, deposit1, to (receiver), pos (hypervisor address), minIn[4] (slippage, use [0,0,0,0])\n- `getDepositAmount(address,address,uint256)` - Preview required token1 amount for given token0 deposit. Params: pos, token, deposit amount. Returns (amountStart, amountEnd)\n\n### UniProxyLegacyABI\n\nThe Ethereum UniProxy is an earlier release; its `deposit` has no `minIn` (the `depositLegacy` template calls it).\n\n- `deposit(uint256,uint256,address,address)` - Deposit tokens. Params: deposit0, deposit1, to (receiver), pos (hypervisor address). Returns shares\n- `getDepositAmount(address,address,uint256)` - As on the current UniProxy\n\n### HypervisorABI\n\n- `withdraw(uint256,address,address,uint256[4])` - Withdraw by burning shares. Params: shares, to, from, minAmounts[4]. Returns (amount0, amount1)\n- `balanceOf(address)` - Query Hypervisor share balance\n- `totalSupply()` - Total shares outstanding\n- `getTotalAmounts()` - Total (amount0, amount1) managed\n\n## Notes\n\n- TVL: $200M+. Each Hypervisor is a vault for a specific token pair\n- Deposit via UniProxy (not directly on Hypervisor) - UniProxy handles deposit ratio enforcement\n- Use getDepositAmount() to find correct token1 amount for your token0 deposit\n- The uint256[4] minIn/minAmounts array provides slippage protection (use zeros for no limit)\n- Approve both token0 and token1 to UniProxy before depositing\n",
53
53
  "gelato": "# Gelato Network\n\nWeb3 automation network for scheduling and executing smart contract functions and off-chain computations.\n\n## Category\n\ninfrastructure | Chains: Ethereum, Arbitrum, Optimism, Polygon, BSC, Avalanche, Base\n\n## Key Operations\n\n- **createTask**: Create an automated task for scheduled execution\n- **cancelTask**: Cancel an existing automated task\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/gelato\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Create automated task\nimport { GelatoAutomateABI as IAutomate } from \"./abis\";\nfunction main(\n automateAddress: Address,\n execAddress: Address,\n execData: bytes,\n moduleData: { modules: Uint256[]; args: bytes[] },\n feeToken: Address,\n): Uint256 {\n const automate = IAutomate.at(automateAddress);\n return automate.createTask(execAddress, execData, moduleData, feeToken);\n}\n\n// Cancel task\nimport { GelatoAutomateABI as IAutomate } from \"./abis\";\nfunction main(automateAddress: Address, taskId: Uint256): Uint256 {\n const automate = IAutomate.at(automateAddress);\n automate.cancelTask(taskId);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| ------------ | -------- | -------------------------------------------- |\n| All 7 chains | automate | `0x2A6C106ae13B558BB9E2Ec64Bd2f1f7BEFF3A5E0` |\n\n## ABI Methods\n\n### GelatoAutomateABI\n\n- `createTask(address,bytes,tuple,address)` - Create automated task. Params: execAddress (contract to call), execDataOrSelector (function calldata or selector), moduleData (tuple: {modules uint8[], args bytes[]} - configures trigger conditions), feeToken (payment token, address(0) for ETH). Returns taskId\n - Module types: 0=Resolver (custom check), 1=Time (interval), 2=Proxy, 3=SingleExec\n - args[] contains encoded config for each module\n- `cancelTask(bytes32)` - Cancel a task. Params: taskId (returned from createTask)\n\n## Notes\n\n- Same address across all 7 chains\n- Supports time-based triggers, event-based triggers, and custom resolver conditions\n- Gelato bots execute tasks when conditions are met, paid via prepaid balance or task fee\n- moduleData configures when the task should execute (time interval, resolver function, etc.)\n- feeToken: use address(0) for ETH, or an ERC-20 address for token payment\n",
54
- "gho": "# GHO\n\nAave-native decentralized stablecoin minted against Aave V3 collateral. Multi-collateral, transparent, and governed by Aave DAO.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **transfer**: Transfer GHO tokens\n- **approve**: Approve GHO token spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/gho\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer GHO\nimport { GhoTokenABI as IGhoToken } from \"./abis\";\nfunction main(ghoAddress: Address, to: Address, amount: Uint256): Uint256 {\n const gho = IGhoToken.at(ghoAddress);\n gho.transfer(to, amount);\n return 1;\n}\n\n// Approve GHO spender\nimport { GhoTokenABI as IGhoToken } from \"./abis\";\nfunction main(ghoAddress: Address, spender: Address, amount: Uint256): Uint256 {\n const gho = IGhoToken.at(ghoAddress);\n gho.approve(spender, amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | gho | `0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f` |\n\n## ABI Methods\n\n### GhoTokenABI\n\n- `transfer(address,uint256)` - Transfer GHO. Params: to, amount. Returns bool\n- `approve(address,uint256)` - Approve spender. Params: spender, amount. Returns bool\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256\n\n## Notes\n\n- Minted via Aave V3 borrow (use Aave V3 Pool.borrow with GHO token address)\n- Facilitator-based model - Aave V3 Pool is the primary facilitator\n- GHO has a variable borrow rate set by Aave governance\n- stkAAVE holders get a discount on GHO borrow rate\n- To mint GHO: supply collateral to Aave V3, then borrow GHO\n- To repay: use Aave V3 Pool.repay with GHO token address\n",
54
+ "gho": "# GHO\n\nAave-native decentralized stablecoin minted against Aave V3 collateral. Multi-collateral, transparent, and governed by Aave DAO.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **transfer**: Transfer GHO tokens\n- **approve**: Approve GHO token spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/gho\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer GHO\nimport { transfer } from \"@sauce/token\";\nfunction main(ghoAddress: Address, to: Address, amount: Uint256): Uint256 {\n transfer(ghoAddress, to, amount);\n return 1;\n}\n\n// Approve GHO spender\nimport { approve } from \"@sauce/token\";\nfunction main(ghoAddress: Address, spender: Address, amount: Uint256): Uint256 {\n approve(ghoAddress, spender, amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | gho | `0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f` |\n\n## ABI Methods\n\n### GhoTokenABI\n\n- `transfer(address,uint256)` - Transfer GHO. Params: to, amount. Returns bool\n- `approve(address,uint256)` - Approve spender. Params: spender, amount. Returns bool\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256\n\n## Notes\n\n- Minted via Aave V3 borrow (use Aave V3 Pool.borrow with GHO token address)\n- Facilitator-based model - Aave V3 Pool is the primary facilitator\n- GHO has a variable borrow rate set by Aave governance\n- stkAAVE holders get a discount on GHO borrow rate\n- To mint GHO: supply collateral to Aave V3, then borrow GHO\n- To repay: use Aave V3 Pool.repay with GHO token address\n",
55
55
  "gmx-v1": "# GMX V1\n\nDecentralized perpetual exchange with multi-asset liquidity pool (GLP). Supports leverage trading up to 50x with low swap fees and zero price impact trades.\n\n## Category\n\nperpetuals | Chains: Arbitrum, Avalanche\n\n## Key Operations\n\n- **openPosition**: Open/increase leveraged position via PositionRouter\n- **closePosition**: Close/decrease leveraged position\n- **swap**: Swap tokens via GMX Router\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/gmx-v1\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Open leveraged long position\nimport { PositionRouterABI as IPositionRouter } from \"./abis\";\nfunction main(\n positionRouterAddress: Address,\n path: Address[],\n indexToken: Address,\n amountIn: Uint256,\n sizeDelta: Uint256,\n isLong: bool,\n): Uint256 {\n const positionRouter = IPositionRouter.at(positionRouterAddress);\n positionRouter.createIncreasePosition(\n path,\n indexToken,\n amountIn,\n 0,\n sizeDelta,\n isLong,\n 0,\n 200000000000000,\n 0x0000000000000000000000000000000000000000000000000000000000000000,\n 0x0000000000000000000000000000000000000000,\n );\n return 1;\n}\n\n// Close position\nimport { PositionRouterABI as IPositionRouter } from \"./abis\";\nfunction main(\n positionRouterAddress: Address,\n path: Address[],\n indexToken: Address,\n collateralDelta: Uint256,\n sizeDelta: Uint256,\n isLong: bool,\n receiver: Address,\n): Uint256 {\n const positionRouter = IPositionRouter.at(positionRouterAddress);\n positionRouter.createDecreasePosition(\n path,\n indexToken,\n collateralDelta,\n sizeDelta,\n isLong,\n receiver,\n 0,\n 0,\n 200000000000000,\n false,\n 0x0000000000000000000000000000000000000000,\n );\n return 1;\n}\n\n// Swap tokens\nimport { RouterABI as IRouter } from \"./abis\";\nfunction main(\n routerAddress: Address,\n path: Address[],\n amountIn: Uint256,\n minOut: Uint256,\n receiver: Address,\n): Uint256 {\n const router = IRouter.at(routerAddress);\n router.swap(path, amountIn, minOut, receiver);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| --------- | -------------- | -------------------------------------------- |\n| Arbitrum | vault | `0x489ee077994B6658eAfA855C308275EAd8097C4A` |\n| Arbitrum | router | `0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064` |\n| Arbitrum | positionRouter | `0xb87a436B93fFE9D75c5cFA7bAcFff96430b09868` |\n| Arbitrum | glp | `0x4277f8F2c384827B5273592FF7CeBd9f2C1ac258` |\n| Avalanche | vault | `0x9ab2De34A33fB459b538c43f251eB825645e8595` |\n| Avalanche | router | `0x5F719c2F1095F7B9fc68a68e35B51194f4b6abe8` |\n\n## ABI Methods\n\n### PositionRouterABI\n\n- `createIncreasePosition(address[],address,uint256,uint256,uint256,bool,uint256,uint256,bytes32,address) returns (bytes32)` - Open/increase. Params: path, indexToken, amountIn, minOut, sizeDelta, isLong, acceptablePrice, executionFee, referralCode, callbackTarget (zero for none). Returns the request key\n- `createDecreasePosition(address[],address,uint256,uint256,bool,address,uint256,uint256,uint256,bool,address) returns (bytes32)` - Close/decrease. Params: path, indexToken, collateralDelta, sizeDelta, isLong, receiver, acceptablePrice, minOut, executionFee, withdrawETH, callbackTarget (zero for none). Returns the request key\n\n### RouterABI\n\n- `approvePlugin(address)` - Approve PositionRouter as plugin (required once)\n- `swap(address[],uint256,uint256,address)` - Swap tokens. Params: path, amountIn, minOut, receiver\n\n### VaultABI\n\n- `swap(address,address,address)` - Direct swap (internal)\n- `increasePosition(address,address,address,uint256,bool)` - Direct increase (internal)\n- `decreasePosition(address,address,address,uint256,uint256,bool,address)` - Direct decrease (internal)\n\n## Notes\n\n- TVL: $500M+. Execution fee of 200000000000000 wei (0.0002 ETH) required for position requests\n- Must call router.approvePlugin(positionRouter) once before using position operations\n- Path: for longs, path=[collateral]. For shorts, path=[stablecoin]. For swaps, path=[tokenIn, tokenOut]\n- sizeDelta is position size in USD with 30 decimals\n- acceptablePrice: 0 for market price\n",
56
56
  "gmx-v2": "# GMX V2\n\nNext generation of GMX perpetual exchange with isolated markets, improved risk management, and GM liquidity tokens replacing GLP.\n\n## Category\n\nperpetuals | Chains: Arbitrum\n\n## Key Operations\n\n- **createOrder**: Create a market/limit order for trading (complex tuple params)\n- **sendTokens**: Send tokens via ExchangeRouter (used before creating orders)\n- **cancelOrder**: Cancel a pending order by key\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/gmx-v2\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Create a market order\nimport { ExchangeRouterABI as IExchangeRouter } from \"./abis\";\nfunction main(\n exchangeRouterAddress: Address,\n receiver: Address,\n cancellationReceiver: Address,\n market: Address,\n initialCollateralToken: Address,\n sizeDeltaUsd: Uint256,\n initialCollateralDeltaAmount: Uint256,\n triggerPrice: Uint256,\n acceptablePrice: Uint256,\n executionFee: Uint256,\n minOutputAmount: Uint256,\n orderType: Uint256,\n isLong: bool,\n): Uint256 {\n const router = IExchangeRouter.at(exchangeRouterAddress);\n const result = router.createOrder({\n addresses: {\n receiver: receiver,\n cancellationReceiver: cancellationReceiver,\n callbackContract: 0x0000000000000000000000000000000000000000,\n uiFeeReceiver: 0x0000000000000000000000000000000000000000,\n market: market,\n initialCollateralToken: initialCollateralToken,\n swapPath: [],\n },\n numbers: {\n sizeDeltaUsd: sizeDeltaUsd,\n initialCollateralDeltaAmount: initialCollateralDeltaAmount,\n triggerPrice: triggerPrice,\n acceptablePrice: acceptablePrice,\n executionFee: executionFee,\n callbackGasLimit: 0,\n minOutputAmount: minOutputAmount,\n validFromTime: 0,\n },\n orderType: orderType,\n decreasePositionSwapType: 0,\n isLong: isLong,\n shouldUnwrapNativeToken: false,\n autoCancel: false,\n referralCode: 0x0000000000000000000000000000000000000000000000000000000000000000,\n dataList: [],\n });\n return result;\n}\n\n// Send tokens to market vault before creating order\nimport { ExchangeRouterABI as IExchangeRouter } from \"./abis\";\nfunction main(\n exchangeRouterAddress: Address,\n token: Address,\n receiver: Address,\n amount: Uint256,\n): Uint256 {\n const router = IExchangeRouter.at(exchangeRouterAddress);\n router.sendTokens(token, receiver, amount);\n return 1;\n}\n\n// Cancel pending order\nimport { ExchangeRouterABI as IExchangeRouter } from \"./abis\";\nfunction main(exchangeRouterAddress: Address, orderKey: Uint256): Uint256 {\n const router = IExchangeRouter.at(exchangeRouterAddress);\n router.cancelOrder(orderKey);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------------- | -------------------------------------------- |\n| Arbitrum | exchangeRouter | `0x7dE39FF2e232A2203196788d37e234cF8F1b83f1` |\n| Arbitrum | dataStore | `0xFD70de6b91282D8017aA4E741e9Ae325CAb992d8` |\n\n## ABI Methods\n\n### ExchangeRouterABI\n\n- `createOrder(tuple)` - Create order. Returns order key (bytes32). See CreateOrderParams struct below\n- `cancelOrder(bytes32)` - Cancel pending order by key\n- `sendTokens(address,address,uint256)` - Send tokens to vault. Must be called before createOrder\n\n### CreateOrderParams Struct\n\n- **addresses**: { receiver, cancellationReceiver, callbackContract, uiFeeReceiver, market, initialCollateralToken, swapPath[] }\n- **numbers**: { sizeDeltaUsd, initialCollateralDeltaAmount, triggerPrice, acceptablePrice, executionFee, callbackGasLimit, minOutputAmount, validFromTime }\n- **flags and extras**: orderType, decreasePositionSwapType, isLong, shouldUnwrapNativeToken, autoCancel, referralCode (bytes32), dataList (bytes32[])\n- **orderType**: 0=MarketSwap, 1=LimitSwap, 2=MarketIncrease, 3=LimitIncrease, 4=MarketDecrease, 5=LimitDecrease, 6=StopLossDecrease\n- **isLong**: true for long, false for short\n\n## Notes\n\n- TVL: $800M+. Isolated markets with separate GM tokens per trading pair\n- Order flow: 1) sendTokens to market vault, 2) createOrder with execution fee\n- GMX redeploys the ExchangeRouter on upgrades and revokes the old one; the listed router is the one RoleStore currently grants CONTROLLER\n- executionFee is paid in ETH (msg.value) to cover keeper gas costs\n- sizeDeltaUsd is in USD with 30 decimals (1 USD = 1e30)\n- Each market has its own address - query dataStore for market info\n",
57
57
  "harvest": "# Harvest Finance\n\nYield farming protocol that automatically compounds rewards across DeFi strategies via Vault+Strategy pattern.\n\n## Category\n\nyield | Chains: Ethereum\n\n## Key Operations\n\n- **deposit**: Deposit tokens into Harvest vault\n- **withdraw**: Withdraw tokens from vault\n- **getPricePerFullShare**: Query vault share price\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/harvest\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Deposit into Harvest vault\nimport { VaultABI as IVault } from \"./abis\";\n\nfunction main(vaultAddress: Address, amount: Uint256): Uint256 {\n const vault = IVault.at(vaultAddress);\n vault.deposit(amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | ---------- | -------------------------------------------- |\n| Ethereum | controller | `0x222412af183BCeAdEFd72e4Cb1b71f1889953b1C` |\n| Ethereum | farm | `0xa0246c9032bC3A600820415aE600c6388619A14D` |\n\n## ABI Methods\n\n- `deposit(uint256) returns (uint256)` - Deposit tokens\n- `withdraw(uint256) returns (uint256)` - Withdraw shares\n- `getPricePerFullShare()` - Get share price\n\n## Notes\n\n- FARM token for governance. Auto-compounds strategy rewards.\n",
@@ -71,7 +71,7 @@ export const SKILL_DOCUMENTS = {
71
71
  "lifi": "# LI.FI\n\nMulti-chain bridge and DEX aggregator via LiFiDiamond proxy. Routes through optimal bridges and DEXes for cross-chain swaps.\n\n## Category\n\nbridge aggregator | Direction: any-to-any | Chains: Ethereum (1), Arbitrum (42161), Optimism (10), Base (8453), Polygon (137), BSC (56), Avalanche (43114), Gnosis (100), Fantom (250), Arc (5042)\n\n## SauceScript Functions\n\n### bridge\n\nBridge tokens cross-chain via the LI.FI Diamond's Across V4 facet. Each underlying bridge has its own facet entry point (`startBridgeTokensVia<Bridge>`), with a bridge-specific second struct.\n\n```typescript\nimport { LiFiDiamondABI as ILiFiDiamond } from \"./abis\";\n\nfunction main(\n diamondAddress: Address,\n transactionId: Uint256,\n sendingAsset: Address,\n receivingAsset: Address,\n receiver: Address,\n amount: Uint256,\n outputAmount: Uint256,\n destinationChainId: Uint256,\n quoteTimestamp: Uint256,\n fillDeadline: Uint256,\n): Uint256 {\n const lifi = ILiFiDiamond.at(diamondAddress);\n lifi.startBridgeTokensViaAcrossV4(\n {\n transactionId: transactionId,\n bridge: \"acrossV4\",\n integrator: \"sauce\",\n referrer: 0x0000000000000000000000000000000000000000,\n sendingAssetId: sendingAsset,\n receiver: receiver,\n minAmount: amount,\n destinationChainId: destinationChainId,\n hasSourceSwaps: false,\n hasDestinationCall: false,\n },\n {\n receiverAddress: receiver,\n refundAddress: receiver,\n sendingAssetId: sendingAsset,\n receivingAssetId: receivingAsset,\n outputAmount: outputAmount,\n outputAmountMultiplier: 0,\n exclusiveRelayer: 0,\n quoteTimestamp: quoteTimestamp,\n fillDeadline: fillDeadline,\n exclusivityParameter: 0,\n message: \"\",\n },\n );\n return 1;\n}\n```\n\n- `transactionId`: Unique identifier for tracking this bridge transaction (bytes32)\n- `integrator`: Integration partner identifier (e.g. \"sauce\")\n- `referrer`: Referral address for fee sharing. `address(0)` for none\n- `sendingAssetId` / `receivingAssetId`: token sent, and token received on the destination\n- `minAmount` / `outputAmount`: amount bridged, and the amount the Across relayer delivers\n- `quoteTimestamp` / `fillDeadline`: the Across quote's timestamp and fill deadline\n- `hasSourceSwaps` / `hasDestinationCall`: `true` only with pre-bridge swaps or a destination call\n- Requires ERC-20 approval to the LiFi Diamond\n\n## Deployed Addresses\n\n| Chain | Contract | Address |\n| --------- | ----------- | -------------------------------------------- |\n| Ethereum | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n| Arbitrum | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n| Optimism | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n| Base | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n| Polygon | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n| BSC | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n| Avalanche | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n| Gnosis | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n| Fantom | lifiDiamond | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` |\n\n## ABI Reference\n\n### LiFiDiamondABI\n\n- `startBridgeTokensViaAcrossV4(BridgeData _bridgeData, AcrossV4Data _acrossData)` [payable] - Bridge through Across V4 (AcrossFacetV4)\n- `extractBridgeData(bytes data) returns (BridgeData bridgeData)` - Decode bridge data from raw calldata (pure)\n\nAcrossV4Data tuple: `(bytes32 receiverAddress, bytes32 refundAddress, bytes32 sendingAssetId, bytes32 receivingAssetId, uint256 outputAmount, uint128 outputAmountMultiplier, bytes32 exclusiveRelayer, uint32 quoteTimestamp, uint32 fillDeadline, uint32 exclusivityParameter, bytes message)`\n\nBridgeData tuple: `(bytes32 transactionId, string bridge, string integrator, address referrer, address sendingAssetId, address receiver, uint256 minAmount, uint256 destinationChainId, bool hasSourceSwaps, bool hasDestinationCall)`\n\n## Notes\n\n- LI.FI is a bridge **aggregator** -- it routes through underlying bridges (Across, Stargate, Hop, cBridge, etc.)\n- Uses a Diamond proxy pattern (EIP-2535) so the contract address is the same across all chains\n- Same Diamond address (`0x1231DEB6...`) deployed on all supported chains\n- Uses standard EVM chain IDs for `destinationChainId`\n- The `bridge` field in BridgeData specifies which underlying bridge to use\n- `hasSourceSwaps` / `hasDestinationCall` enable swap-then-bridge or bridge-then-execute patterns\n- Requires ERC-20 approval to the LiFi Diamond address\n- Finality depends on the underlying bridge selected (varies from 1 minute to 20 minutes)\n- TVL: $300M+. Audited\n",
72
72
  "linea-bridge": "# Linea Native Bridge\n\nOfficial Linea zkEVM bridge via L1MessageService. Deposits ETH from Ethereum to Linea with ZK proof-based finality.\n\n## Category\n\nnative L2 bridge | Direction: L1 to L2 (Ethereum to Linea) | Chains: Ethereum (1), Linea (59144)\n\n## SauceScript Functions\n\n### bridgeETH\n\nBridge ETH from Ethereum L1 to Linea L2 via the message service.\n\n```typescript\nimport { LineaL1MessageServiceABI as IL1MessageService } from \"./abis\";\n\nfunction main(messageServiceAddress: Address, recipient: Address, fee: Uint256): Uint256 {\n const service = IL1MessageService.at(messageServiceAddress);\n service.sendMessage(recipient, fee, 0x00);\n return 1;\n}\n```\n\n- `recipient`: Address to receive ETH on Linea L2\n- `fee`: Fee for the postman (relayer) to deliver the message on L2. Set to 0 for self-claim\n- `_calldata`: Optional calldata to execute on L2 when message is delivered. `0x00` for simple ETH transfers\n- ETH amount (minus fee) is sent as `msg.value`\n\n## Deployed Addresses\n\n| Chain | Contract | Address |\n| -------- | ---------------- | -------------------------------------------- |\n| Ethereum | l1MessageService | `0xd19d4B5d358258f05D7B411E21A1460D11B0876F` |\n| Linea | l2MessageService | `0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec` |\n\n## ABI Reference\n\n### LineaL1MessageServiceABI\n\n- `sendMessage(address _to, uint256 _fee, bytes _calldata)` [payable] - Send ETH and/or message from L1 to L2. `msg.value` = amount to bridge + fee\n- `claimMessage(address _from, address _to, uint256 _fee, uint256 _value, address _feeRecipient, bytes _calldata, uint256 _nonce)` - Claim a message on the destination side (called by relayers or self-claim)\n\n## Notes\n\n- **L1 to L2** via L1MessageService. L2 to L1 via L2MessageService on Linea\n- `sendMessage` is a general-purpose L1-to-L2 message sender -- ETH transfer is implicit via msg.value\n- The `_fee` parameter incentivizes postmen (relayers) to deliver the message. Set to 0 and self-claim via `claimMessage`\n- `_calldata` enables arbitrary contract execution on L2 when the message is delivered\n- zkEVM architecture -- finality with ZK proofs (typically 8-32 hours for proof generation)\n- L2 to L1 withdrawals also require ZK proof finalization\n- For ERC-20 bridging, use the Linea Token Bridge contracts (separate from the message service)\n- Canonical bridge -- no third-party risk, secured by Linea's ZK proving system (Consensys)\n- Audited\n",
73
73
  "liquity-v1": "# Liquity V1\n\nDecentralized borrowing protocol offering interest-free loans against ETH collateral. Issues LUSD stablecoin with minimum 110% collateral ratio.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **closeTrove**: Close an existing Trove (repay all debt and withdraw collateral)\n- **repayLUSD**: Repay LUSD debt on an open Trove\n- **openTrove**: Open a new Trove by depositing ETH and borrowing LUSD\n- **adjustTrove**: Adjust Trove collateral and/or debt\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/liquity-v1\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Close Trove\nimport { BorrowerOperationsABI as IBorrowerOperations } from \"./abis\";\nfunction main(borrowerOpsAddress: Address): Uint256 {\n const borrowerOps = IBorrowerOperations.at(borrowerOpsAddress);\n borrowerOps.closeTrove();\n return 1;\n}\n\n// Repay LUSD debt\nimport { BorrowerOperationsABI as IBorrowerOperations } from \"./abis\";\nfunction main(borrowerOpsAddress: Address, amount: Uint256): Uint256 {\n const borrowerOps = IBorrowerOperations.at(borrowerOpsAddress);\n borrowerOps.repayLUSD(\n amount,\n 0x0000000000000000000000000000000000000000,\n 0x0000000000000000000000000000000000000000,\n );\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | ------------------ | -------------------------------------------- |\n| Ethereum | lusd | `0x5f98805A4E8be255a32880FDeC7F6728C6568bA0` |\n| Ethereum | borrowerOperations | `0x24179CD81c9e782A4096035f7eC97fB8B783e007` |\n\n## ABI Methods\n\n### BorrowerOperationsABI\n\n- `openTrove(uint256,uint256,address,address)` - Open new Trove. Payable (send ETH as collateral). Params: maxFeePercentage (max borrowing fee, e.g. 5e16 = 5%), LUSDAmount (LUSD to borrow), upperHint (sorted troves hint), lowerHint (sorted troves hint)\n- `closeTrove()` - Close Trove. Must repay all LUSD debt first. Returns all ETH collateral\n- `adjustTrove(uint256,uint256,uint256,bool,address,address)` - Adjust Trove. Payable (send ETH to add collateral). Params: maxFeePercentage, collWithdrawal (ETH to withdraw), LUSDChange (LUSD amount to change), isDebtIncrease (true=borrow more, false=repay), upperHint, lowerHint\n- `repayLUSD(uint256,address,address)` - Repay LUSD debt. Params: LUSDAmount, upperHint, lowerHint (sorted-trove hints; the zero address is valid)\n\n## Notes\n\n- TVL: $500M+. Interest-free loans with one-time borrowing fee (0.5% - 5%)\n- Minimum collateral ratio: 110%. Below this, Trove can be liquidated\n- Hint addresses (upperHint, lowerHint) optimize sorted trove list insertion - use Liquity frontend SDK to compute\n- Minimum debt: 2000 LUSD (including 200 LUSD gas reserve)\n- closeTrove requires repaying all debt including the 200 LUSD gas reserve\n",
74
- "liquity-v2": "# Liquity V2\n\nNext generation of Liquity protocol with user-set interest rates, multi-collateral support, and the BOLD stablecoin.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **transfer**: Transfer BOLD tokens\n- **approve**: Approve BOLD token spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/liquity-v2\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer BOLD\nimport { BoldTokenABI as IBoldToken } from \"./abis\";\nfunction main(boldAddress: Address, to: Address, amount: Uint256): Uint256 {\n const bold = IBoldToken.at(boldAddress);\n bold.transfer(to, amount);\n return 1;\n}\n\n// Approve BOLD spender\nimport { BoldTokenABI as IBoldToken } from \"./abis\";\nfunction main(boldAddress: Address, spender: Address, amount: Uint256): Uint256 {\n const bold = IBoldToken.at(boldAddress);\n bold.approve(spender, amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | bold | `0x6440f144b7e50D6a8439336510312d2F54beB01D` |\n\n## ABI Methods\n\n### BoldTokenABI\n\n- `transfer(address,uint256)` - Transfer BOLD tokens. Params: to, amount. Returns bool\n- `approve(address,uint256)` - Approve spender. Params: spender, amount. Returns bool\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256\n\n## Notes\n\n- User-set interest rates - borrowers choose their own rate (higher rate = lower liquidation risk)\n- Multi-collateral: ETH, wstETH, rETH, and other LSTs\n- BOLD is fully redeemable 1:1 for collateral at any time\n- Successor to Liquity V1 with improved capital efficiency\n",
74
+ "liquity-v2": "# Liquity V2\n\nNext generation of Liquity protocol with user-set interest rates, multi-collateral support, and the BOLD stablecoin.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **transfer**: Transfer BOLD tokens\n- **approve**: Approve BOLD token spender\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/liquity-v2\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Transfer BOLD\nimport { transfer } from \"@sauce/token\";\nfunction main(boldAddress: Address, to: Address, amount: Uint256): Uint256 {\n transfer(boldAddress, to, amount);\n return 1;\n}\n\n// Approve BOLD spender\nimport { approve } from \"@sauce/token\";\nfunction main(boldAddress: Address, spender: Address, amount: Uint256): Uint256 {\n approve(boldAddress, spender, amount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | bold | `0x6440f144b7e50D6a8439336510312d2F54beB01D` |\n\n## ABI Methods\n\n### BoldTokenABI\n\n- `transfer(address,uint256)` - Transfer BOLD tokens. Params: to, amount. Returns bool\n- `approve(address,uint256)` - Approve spender. Params: spender, amount. Returns bool\n- `balanceOf(address)` - Query balance. Params: account. Returns uint256\n\n## Notes\n\n- User-set interest rates - borrowers choose their own rate (higher rate = lower liquidation risk)\n- Multi-collateral: ETH, wstETH, rETH, and other LSTs\n- BOLD is fully redeemable 1:1 for collateral at any time\n- Successor to Liquity V1 with improved capital efficiency\n",
75
75
  "lynex": "# Lynex\n\nLinea-native ve(3,3) DEX and liquidity marketplace. Solidly-fork with gauge voting and support for both stable and volatile pool types on the Linea zkEVM network.\n\n## Category\n\ndex | Chains: Linea\n\n## Key Operations\n\n- **swap**: Swap tokens with route-based routing specifying stable or volatile pool type\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/lynex\";\n```\n\n## SauceScript Examples\n\n### swap\n\n```typescript\nimport { LynexRouterABI as IRouter } from \"./abis\";\n\nfunction main(\n routerAddress: Address,\n tokenIn: Address,\n tokenOut: Address,\n stable: bool,\n amountIn: Uint256,\n amountOutMin: Uint256,\n recipient: Address,\n): Uint256[] {\n const router = IRouter.at(routerAddress);\n return router.swapExactTokensForTokens(\n amountIn,\n amountOutMin,\n [{ from: tokenIn, to: tokenOut, stable: stable }],\n recipient,\n 99999999999,\n );\n}\n```\n\n- `routerAddress`: Lynex Router on Linea (`0x610D2f07b7EdC67565160F587F37636194C34E74`)\n- `tokenIn` / `tokenOut`: Input and output token addresses\n- `stable`: `true` for stable pools (pegged assets), `false` for volatile pools (uncorrelated)\n- `amountIn`: Exact input amount (in wei)\n- `amountOutMin`: Minimum output for slippage protection\n- `recipient`: Address to receive output tokens\n- Routes support multi-hop: `[{from: A, to: B, stable: false}, {from: B, to: C, stable: true}]`\n- Note: Lynex routes do NOT include a `factory` field (unlike Velodrome/Aerodrome)\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| ----- | -------- | -------------------------------------------- |\n| Linea | Router | `0x610D2f07b7EdC67565160F587F37636194C34E74` |\n\n## ABI Methods\n\n### LynexRouterABI\n\n- `swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, tuple[](address from, address to, bool stable) routes, address to, uint256 deadline) -> uint256[] amounts` - Swap with route tuples specifying pool type per hop\n- `addLiquidity(address tokenA, address tokenB, bool stable, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) -> (uint256 amountA, uint256 amountB, uint256 liquidity)` - Add liquidity to a stable or volatile pool\n\n## Notes\n\n- Solidly-fork routes contain `{from, to, stable}` tuples (NO `factory` field)\n- Two pool types: `stable=true` for correlated assets, `stable=false` for uncorrelated\n- ve(3,3) model: LYNX token holders vote-lock to direct gauge emissions\n- Linea-only deployment; the dominant DEX on Linea zkEVM\n- LP tokens can be staked in gauges for LYNX emissions\n- Input token must be ERC20-approved to the Router\n",
76
76
  "maker": "# Maker\n\nDecentralized credit protocol behind DAI and USDS stablecoins. Users deposit collateral into Vaults to mint/borrow DAI. Includes the DAI Savings Rate (DSR) via sDAI.\n\n## Category\n\ncdp | Chains: Ethereum\n\n## Key Operations\n\n- **depositToSDAI**: Deposit DAI into Savings DAI vault (ERC-4626)\n- **withdrawFromSDAI**: Withdraw DAI from sDAI vault by asset amount\n- **redeemFromSDAI**: Redeem sDAI shares for DAI\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/maker\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Deposit DAI into sDAI\nimport { SavingsDaiABI as ISavingsDai } from \"./abis\";\nfunction main(sDAIAddress: Address, amount: Uint256, receiver: Address): Uint256 {\n const sDAI = ISavingsDai.at(sDAIAddress);\n return sDAI.deposit(amount, receiver);\n}\n\n// Withdraw DAI from sDAI\nimport { SavingsDaiABI as ISavingsDai } from \"./abis\";\nfunction main(sDAIAddress: Address, amount: Uint256, receiver: Address, owner: Address): Uint256 {\n const sDAI = ISavingsDai.at(sDAIAddress);\n return sDAI.withdraw(amount, receiver, owner);\n}\n\n// Redeem sDAI shares for DAI\nimport { SavingsDaiABI as ISavingsDai } from \"./abis\";\nfunction main(sDAIAddress: Address, shares: Uint256, receiver: Address, owner: Address): Uint256 {\n const sDAI = ISavingsDai.at(sDAIAddress);\n return sDAI.redeem(shares, receiver, owner);\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | dai | `0x6B175474E89094C44Da98b954EedeAC495271d0F` |\n| Ethereum | usds | `0xdC035D45d973E3EC169d2276DDab16f1e407384F` |\n| Ethereum | sDAI | `0x83F20F44975D03b1b09e64809B757c47f942BEeA` |\n| Ethereum | vat | `0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B` |\n| Ethereum | pot | `0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7` |\n\n## ABI Methods\n\n### SavingsDaiABI (ERC-4626)\n\n- `deposit(uint256,address)` - Deposit DAI, receive sDAI shares. Params: assets (DAI amount), receiver. Returns shares minted\n- `withdraw(uint256,address,address)` - Withdraw by DAI amount. Params: assets (DAI to withdraw), receiver, owner. Returns shares burned\n- `redeem(uint256,address,address)` - Redeem sDAI shares. Params: shares (sDAI to burn), receiver, owner. Returns assets (DAI received)\n- `convertToShares(uint256)` - Preview DAI to sDAI conversion. Params: assets. Returns shares\n- `convertToAssets(uint256)` - Preview sDAI to DAI conversion. Params: shares. Returns assets\n\n### PotABI (DSR Engine)\n\n- `join(uint256)` - Join DAI savings (internal). Params: wad (DAI amount in 18 decimals)\n- `exit(uint256)` - Exit DAI savings (internal). Params: wad\n- `chi()` - Current accumulated DSR rate (view). Returns rate accumulator (ray, 27 decimals)\n- `dsr()` - Current DSR per-second rate (view). Returns rate (ray)\n\n## Notes\n\n- TVL: $8B+. sDAI is the preferred way to earn DSR yield (ERC-4626 compliant)\n- DSR yield accrues automatically - sDAI appreciates against DAI over time\n- Approve DAI to sDAI contract before depositing\n- Pot is the internal DSR accumulator - chi() tracks the accumulated rate\n- USDS is the rebranded DAI stablecoin\n",
77
77
  "mantle-meth": "# Mantle mETH\n\nMantle's liquid staking token for Ethereum. Stake ETH and receive mETH, a non-rebasing token that accrues staking rewards over time.\n\n## Category\n\nliquid-staking | Chains: Ethereum\n\n## Key Operations\n\n- **stake**: Stake ETH for mETH with minimum output check\n\n## SDK Usage\n\n```typescript\nimport { protocolInfo, deployments, sauceFunctions } from \"@eco-incorp/sauce/protocols/mantle-meth\";\n```\n\n## SauceScript Examples\n\n```typescript\n// Stake ETH for mETH\nimport { METHStakingABI as IStaking } from \"./abis\";\nfunction main(stakingAddress: Address, minMETHAmount: Uint256): Uint256 {\n const staking = IStaking.at(stakingAddress);\n staking.stake(minMETHAmount);\n return 1;\n}\n```\n\n## Key Addresses\n\n| Chain | Contract | Address |\n| -------- | -------- | -------------------------------------------- |\n| Ethereum | mETH | `0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa` |\n| Ethereum | staking | `0xe3cBd06D7dadB3F4e6557bAb7EdD924CD1489E8f` |\n\n## ABI Methods\n\n### METHStakingABI\n\nThe Staking contract (`staking`), not the token.\n\n- `stake(uint256)` - Stake ETH (payable), receive mETH. Param: minMETHAmount for slippage protection\n- `mETHToETH(uint256)` - Convert mETH amount to ETH value\n- `ethToMETH(uint256)` - Convert ETH amount to mETH value\n\n### METHABI\n\nThe mETH token (`mETH`).\n\n- `balanceOf(address)` - Query mETH balance\n- `approve(address,uint256)` - Approve mETH spending\n\n## Notes\n\n- TVL: $1.5B+. Non-rebasing token backed by Mantle treasury\n- stake() is payable - send ETH as msg.value\n- minMETHAmount param provides slippage protection\n- Use mETHToETH()/ethToMETH() to preview exchange rates\n",
@@ -1 +1 @@
1
- {"version":3,"file":"skills.generated.js","sourceRoot":"","sources":["../../src/embedded/skills.generated.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skills.generated.js","sourceRoot":"","sources":["../../src/embedded/skills.generated.ts"],"names":[],"mappings":"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"}
@@ -10,8 +10,8 @@ export const STD_SOURCES = {
10
10
  };
11
11
  /** Each shipped `.sauce.ts` recipe, keyed by path under `src/`, verbatim. */
12
12
  export const RECIPE_SOURCES = {
13
- "recipes/cctp-split.sauce.ts": "/**\n * cctp-split — move a USDC `CCTP_AMOUNT` in CCTP burns capped at `CCTP_CHUNK_SIZE`, ATOMIC in one\n * Sauce cook.\n *\n * The INTERFACE is the whole point: you pass the TOTAL amount and the per-message chunk size, and\n * THE SCRIPT does the splitting — `count = amount / chunkSize` full-cap burns, then one final burn of\n * the leftover `remainder = amount % chunkSize` when it isn't zero. No divisibility assumption, no\n * remainder dropped: `$2.50` at a `$1` cap becomes `$1 + $1 + $0.50`. The TypeScript builder computes\n * nothing; it only hands the script its two numbers.\n *\n * THIS INTENT'S VALUES ARRIVE AS `defines`. The five consts below are declared with placeholder\n * defaults so this file reads (and compiles) standalone; the builder\n * (`recipes/cctp-transfer.ts`) overrides each one per intent, and a define REPLACES the module's own\n * `const` of that name. They are constant-folded, so a given set of values yields fixed bytecode.\n * Standalone asset, not a string assembled in TypeScript. NOT the same shape as `settle.sauce.ts`: that one takes its values as runtime entry\n * arguments precisely so its bytecode stays invariant and hash-pinnable (`settle.sauce.ts:44`), where\n * these fold the values in and change bytecode with them.\n *\n * DSL-NATIVE, ZERO HARDCODING — no ABI, no address:\n * • `USDC` resolves to the source chain's USDC address from the token registry.\n * • `Cctp.TokenMessenger.depositForBurn(...)` resolves interface + address from the CCTP protocol\n * descriptor.\n * • `Cctp.TokenMessenger` (the approve spender) resolves to that same address as a VALUE — the\n * accessor works in value position, not just as a call target (equivalent to the underscore\n * ambient define `Cctp_TokenMessenger`, just the accessor-consistent spelling).\n * One approval for the whole amount, then the burns against it — if any burn reverts (e.g. it\n * exceeds Circle's per-message cap) the entire source-chain tx reverts (all-or-nothing).\n *\n * Declarations are intentionally UNANNOTATED: the DSL accessor rewrite that resolves `USDC`/`Cctp` is\n * acorn-based (plain JS), so a `.sauce.ts` routed through it must carry no TypeScript type syntax.\n */\nconst CCTP_RECIPIENT = 0n;\nconst CCTP_AMOUNT = 0n;\nconst CCTP_CHUNK_SIZE = 1n;\nconst CCTP_DEST_DOMAIN = 0n;\nconst CCTP_MAX_FEE = 0n;\n\nfunction main() {\n USDC.approve(Cctp.TokenMessenger, CCTP_AMOUNT);\n let count = CCTP_AMOUNT / CCTP_CHUNK_SIZE;\n for (let i = 0n; i < count; i++) {\n Cctp.TokenMessenger.depositForBurn(\n CCTP_CHUNK_SIZE,\n CCTP_DEST_DOMAIN,\n CCTP_RECIPIENT,\n USDC,\n 0n,\n CCTP_MAX_FEE,\n 0n,\n );\n }\n let remainder = CCTP_AMOUNT % CCTP_CHUNK_SIZE;\n if (remainder > 0n) {\n Cctp.TokenMessenger.depositForBurn(\n remainder,\n CCTP_DEST_DOMAIN,\n CCTP_RECIPIENT,\n USDC,\n 0n,\n CCTP_MAX_FEE,\n 0n,\n );\n }\n return 0n;\n}\n",
14
- "recipes/plain-transfer.sauce.ts": "/**\n * plain-transfer — the no-bridge baseline: move `XFER_AMOUNT` USDC to `XFER_RECIPIENT` in one call.\n * Same standalone-asset shape as `cctp-split.sauce.ts`: the two consts below carry placeholder\n * defaults and the builder overrides them per intent via `defines`, which are constant-folded into\n * fixed bytecode. `USDC` resolves from the token registry. Declarations unannotated for the same\n * acorn-based-rewrite reason documented there.\n */\nconst XFER_RECIPIENT = 0n;\nconst XFER_AMOUNT = 0n;\n\nfunction main() {\n USDC.transfer(XFER_RECIPIENT, XFER_AMOUNT);\n return 0n;\n}\n",
13
+ "recipes/cctp-split.sauce.ts": "/**\n * cctp-split — move a USDC `CCTP_AMOUNT` in CCTP burns capped at `CCTP_CHUNK_SIZE`, ATOMIC in one\n * Sauce cook.\n *\n * The INTERFACE is the whole point: you pass the TOTAL amount and the per-message chunk size, and\n * THE SCRIPT does the splitting — `count = amount / chunkSize` full-cap burns, then one final burn of\n * the leftover `remainder = amount % chunkSize` when it isn't zero. No divisibility assumption, no\n * remainder dropped: `$2.50` at a `$1` cap becomes `$1 + $1 + $0.50`. The TypeScript builder computes\n * nothing; it only hands the script its two numbers.\n *\n * THIS INTENT'S VALUES ARRIVE AS `defines`. The five consts below are declared with placeholder\n * defaults so this file reads (and compiles) standalone; the builder\n * (`recipes/cctp-transfer.ts`) overrides each one per intent, and a define REPLACES the module's own\n * `const` of that name. They are constant-folded, so a given set of values yields fixed bytecode.\n * Standalone asset, not a string assembled in TypeScript. NOT the same shape as `settle.sauce.ts`: that one takes its values as runtime entry\n * arguments precisely so its bytecode stays invariant and hash-pinnable (`settle.sauce.ts:44`), where\n * these fold the values in and change bytecode with them.\n *\n * DSL-NATIVE, ZERO HARDCODING — no ABI, no address:\n * • `USDC` resolves to the source chain's USDC address from the token registry.\n * • `Cctp.TokenMessenger.depositForBurn(...)` resolves interface + address from the CCTP protocol\n * descriptor.\n * • `Cctp.TokenMessenger` (the approve spender) resolves to that same address as a VALUE — the\n * accessor works in value position, not just as a call target (equivalent to the underscore\n * ambient define `Cctp_TokenMessenger`, just the accessor-consistent spelling).\n * One approval for the whole amount, then the burns against it — if any burn reverts (e.g. it\n * exceeds Circle's per-message cap) the entire source-chain tx reverts (all-or-nothing). The\n * approval is `@sauce/token`'s SafeERC20 `approve`: it resets an allowance a previous cook left\n * standing to zero before setting the new one, and reverts on a `false` return.\n *\n * Declarations are intentionally UNANNOTATED: the DSL accessor rewrite that resolves `USDC`/`Cctp` is\n * acorn-based (plain JS), so a `.sauce.ts` routed through it must carry no TypeScript type syntax.\n */\nconst CCTP_RECIPIENT = 0n;\nconst CCTP_AMOUNT = 0n;\nconst CCTP_CHUNK_SIZE = 1n;\nconst CCTP_DEST_DOMAIN = 0n;\nconst CCTP_MAX_FEE = 0n;\n\nfunction main() {\n USDC.approve(Cctp.TokenMessenger, CCTP_AMOUNT);\n let count = CCTP_AMOUNT / CCTP_CHUNK_SIZE;\n for (let i = 0n; i < count; i++) {\n Cctp.TokenMessenger.depositForBurn(\n CCTP_CHUNK_SIZE,\n CCTP_DEST_DOMAIN,\n CCTP_RECIPIENT,\n USDC,\n 0n,\n CCTP_MAX_FEE,\n 0n,\n );\n }\n let remainder = CCTP_AMOUNT % CCTP_CHUNK_SIZE;\n if (remainder > 0n) {\n Cctp.TokenMessenger.depositForBurn(\n remainder,\n CCTP_DEST_DOMAIN,\n CCTP_RECIPIENT,\n USDC,\n 0n,\n CCTP_MAX_FEE,\n 0n,\n );\n }\n return 0n;\n}\n",
14
+ "recipes/plain-transfer.sauce.ts": "/**\n * plain-transfer — the no-bridge baseline: move `XFER_AMOUNT` USDC to `XFER_RECIPIENT` in one call.\n * Same standalone-asset shape as `cctp-split.sauce.ts`: the two consts below carry placeholder\n * defaults and the builder overrides them per intent via `defines`, which are constant-folded into\n * fixed bytecode. `USDC` resolves from the token registry. Declarations unannotated for the same\n * acorn-based-rewrite reason documented there.\n *\n * `USDC.transfer(...)` is lowered to `@sauce/token`'s `transfer`, which is OpenZeppelin `SafeERC20`:\n * a token that returns nothing is accepted, and one that returns `false` reverts the cook.\n */\nconst XFER_RECIPIENT = 0n;\nconst XFER_AMOUNT = 0n;\n\nfunction main() {\n USDC.transfer(XFER_RECIPIENT, XFER_AMOUNT);\n return 0n;\n}\n",
15
15
  "recipes/settle.sauce.ts": "import { IERC20 } from \"./artifacts/IERC20.json\";\n\n// settle.sauce.ts — a STANDALONE, reusable Sauce program: sweep the Pot's balance of a list of\n// tokens to one recipient, with a minimum-output floor enforced on the first token.\n//\n// It is deliberately NOT tied to any recipe, protocol or product. Nothing in the program below\n// knows what produced the balances it moves — a swap, a bridge, an airdrop, a manual transfer, or\n// nothing at all. Its contract is exactly \"these tokens, this floor, this recipient\", so any caller\n// that needs a Pot emptied to a destination can compile and run it. Keep it that way: no\n// caller-specific names, in the code OR in the revert string (the string is inside the compiled\n// program, so changing it rotates SETTLE_WIRE.PROGRAM_HASH — see sdk/src/verify/wire.ts).\n//\n// COMPOSING IT AFTER ANOTHER PROGRAM (the common case, and the reason the floor exists): a\n// V12Pot.cook runs exactly ONE program, so ONE cook cannot run\n// \"do the thing, then sweep\". That composition is TWO TOP-LEVEL cook() calls joined by an\n// owner-authorised multicall in ONE transaction:\n// multicall → [ Pot.cook(engine, producerProgram) , Pot.cook(engine, sweepProgram) ]\n// The multicall contract MUST be this Pot's `owner` (V12Pot.cook is\n// `msg.sender == owner || msg.sender == address(this)`-gated — there is no third path) and MUST\n// propagate a reverting call's revert data rather than swallow it (a non-reverting/tryAggregate\n// dispatch turns a sweep failure into \"the first cook landed, the recipient got nothing\").\n// SECURITY: the multicall must NEVER be a permissionless batcher. Multicall3's `aggregate3` CALLs\n// from its own address, so if it owned a Pot, any address could call\n// `aggregate3([{target: pot, callData: cook([drainProgram])}])` and drain it — owning the batcher\n// IS owning the Pot. The operator's batcher must be owner-controlled (see OwnerMulticall.sol,\n// this repo's test-only reference fixture for the shape a real deployment needs).\n//\n// COMPILED BY THE ORDINARY COMPILER — see `sdk/src/recipes/index.ts` for the exact snippet and\n// options. This file is the ONE source of truth for the program text; consumers compile it rather\n// than keeping a second copy. `sdk/src/verify/` itself is the DECODING / authenticity surface (turn\n// a payload's bytes back into `(tokens, minOut, recipient)` and prove the program half is this\n// audited template); the program source lives here, outside it, because a reusable program is not a\n// verification concern.\n//\n// THE COMPILED PROGRAM IS A FUNCTION OF THIS FILE AND THE COMPILER PIN ONLY — never of the\n// arguments. `main`'s three parameters below are RUNTIME ENTRY ARGUMENTS: the caller appends them\n// to the compiled program as 32-byte words and the program reads them back through CALLDATA, so no\n// value a caller picks is visible to the compiler at all. One 435-byte blob therefore serves every\n// token list, floor and recipient, which is what lets `sdk/src/verify/` pin a single\n// `keccak256(program)` and still report per-payload params.\n//\n// KEEP THEM PARAMETERS. Moving any of the three to a compile-time `define` would fold it into the\n// program and make the bytes vary per caller — the pinned-hash design would collapse, and the whole\n// `/verify` surface with it.\n//\n// THE SHAPE: `main(tokens, minOut, recipient)` sweeps the Pot's CURRENT balance of EVERY token in\n// `tokens` to `recipient` — a full-balance sweep is the EXPLICIT INTENT (not a hazard to guard\n// against): after this cook lands, the Pot holds none of the listed tokens. `tokens[0]` is the\n// FLOOR TOKEN by POSITION (not a separately-named arg) — chosen over a named field because the\n// decoded shape this way is exactly `(tokens[], minOut, recipient)`, the same three values the\n// caller compiled it with, with no fourth field that could itself drift out of sync with which\n// array slot it names. `minOut` is enforced against tokens[0]'s balance BEFORE any transfer runs,\n// so an unmeetable floor reverts the WHOLE sweep cook (and, when composed with another cook in one\n// transaction, that whole transaction) before anything moves.\n//\n// EVERY TRANSFER IS A SafeERC20 TRANSFER. The loop body's write is exactly what\n// `sdk/src/token/safe.ts#safeTransferStatements` emits — the SDK's one token-write mechanism — and\n// `recipes-assets.test.ts` pins that it stays so. It is OpenZeppelin's `_callOptionalReturn`: the\n// call goes out as raw bytes through `evm.call`, so nothing is decoded on the way back (a `bool`\n// decoded from a no-return token's EMPTY returndata reverts on this engine, which is why a transfer\n// through `IERC20` could not sweep USDT), and then what came back is CHECKED. Empty returndata passes\n// only from an address that has code; anything else must decode as an ABI word equal to `1`, so a\n// token that answers `false` reverts the sweep rather than reading as swept, and so does a return\n// shorter than one word. A transfer that reverts still reverts it.\n// The floor and the balances are read through `IERC20.balanceOf`, whose `uint256` is always there.\n//\n// USEFUL ORDERING PROPERTY: tokens[0] is swept FIRST, and no external call runs between the floor\n// check and that transfer — so for the floor token the amount CHECKED is the amount TRANSFERRED. A\n// hostile token later in the list cannot retroactively affect it, and a duplicate entry is a no-op\n// on its second pass (its balance is already 0).\n//\n// KNOWN PROPERTY, DOCUMENTED AND ACCEPTED — NOT DEFENDED AGAINST: the floor and the sweep both\n// read the Pot's CURRENT balance, not a delta against some baseline. A pre-existing or donated\n// balance of tokens[0] in the Pot therefore counts toward minOut and rides to the recipient along\n// with whatever the caller actually produced. This is a consequence of the sweep model the\n// maintainer chose (the Pot ending clean is the point), not an oversight — do not add baseline/\n// delta machinery to defend against it.\n//\n// A given execution's `tokens`/`minOut`/`recipient` arrive as RUNTIME ENTRY ARGUMENTS, appended after\n// the program as 32-byte words (see `verify/decode.ts` for the layout) — there is no\n// cross-cook or cross-program handoff of any kind (an earlier design used a tagged EIP-1153\n// transient-storage handoff between two cooks; the sweep model made it unnecessary — this program\n// simply carries its own destination and floor, and reads only the Pot's live balances). Running it\n// on its own, against a Pot holding nothing, sweeps whatever is there (0, ordinarily) — there is\n// nothing to \"orphan\" against, since its target and floor are self-contained.\n//\n// THE DECODER (sdk/src/verify/decode.ts's decodeSettleProgram/validateSettleProgram): a payload is\n// `program || args`, and the two regions are disjoint. decodeSettleProgram pins the program's\n// LENGTH and reads the argument tail — three 32-byte head words (a pointer to the token array,\n// `minOut`, `recipient`) followed by the array's count and its elements — so it names the values a\n// payload will run with. validateSettleProgram adds `keccak256` over the WHOLE program against the\n// pinned hash, which is a COMPLETE authenticity answer here: a program carrying different or extra\n// behaviour (say, sweeping to a hardcoded address instead of the supplied `recipient`) is different\n// bytes, so it fails the hash. There is no body/prologue split to reason about any more, and no\n// argument-independent suffix to hash separately — the whole program is argument-independent.\nfunction main(tokens: Address[], minOut: Uint256, recipient: Address): Uint256 {\n const floorToken = IERC20.at(tokens[0]);\n const floorBal: Uint256 = floorToken.balanceOf(ctx.self());\n if (minOut > 0) {\n if (floorBal < minOut) {\n throw \"settle: balance below minOut\";\n }\n }\n for (let i = 0; i < tokens.length; i = i + 1) {\n const t = IERC20.at(tokens[i]);\n const bal: Uint256 = t.balanceOf(ctx.self());\n if (bal > 0) {\n const sweep_tr0 = hex`a9059cbb`;\n const sweep_tr = evm.call(tokens[i], 0n, sweep_tr0.concat(recipient.toBytesBe(32)).concat(bal.toBytesBe(32)));\n if (sweep_tr.length == 0) { require(ctx.codeSize(tokens[i]) > 0); } else { const [sweep_trok] = abi.decode(sweep_tr, [\"Uint256\"]); require(sweep_trok == 1n); }\n }\n }\n return floorBal;\n}\n",
16
16
  };
17
17
  /** Every protocol ABI twin, keyed `<slug>/<Export>.json`, minified. */