@eco-incorp/sauce 0.99.1 → 0.99.2

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 (185) hide show
  1. package/LICENSE.md +29 -0
  2. package/README.md +69 -249
  3. package/actions/dist/to-sauce.d.ts.map +1 -1
  4. package/actions/dist/to-sauce.js +4 -5
  5. package/dev-tools/README.md +11 -170
  6. package/docs/README.md +39 -0
  7. package/docs/api/README.md +71 -0
  8. package/docs/api/compiler.md +234 -0
  9. package/docs/api/coverage.md +117 -0
  10. package/docs/api/routes.md +110 -0
  11. package/docs/concepts/architecture.md +134 -0
  12. package/docs/concepts/saucescript.md +172 -0
  13. package/docs/examples/README.md +34 -0
  14. package/docs/examples/compile-program.ts +20 -0
  15. package/docs/examples/evm-execution.ts +59 -0
  16. package/docs/examples/first-intent.ts +56 -0
  17. package/docs/examples/protocol-approval.ts +33 -0
  18. package/docs/guides/builders-and-actions.md +108 -0
  19. package/docs/guides/compiling.md +264 -0
  20. package/docs/guides/intents.md +158 -0
  21. package/docs/guides/nested-intents.md +79 -0
  22. package/docs/guides/protocols-and-tokens.md +116 -0
  23. package/docs/guides/quick-start.md +64 -0
  24. package/docs/guides/solana.md +196 -0
  25. package/docs/guides/verification.md +253 -0
  26. package/package.json +9 -4
  27. package/sdk/dist/artifacts/V12Deployments.json +23 -5
  28. package/sdk/dist/artifacts/V12RuntimeBytecode.json +1 -1
  29. package/sdk/dist/artifacts/svm/engine-devnet.so +0 -0
  30. package/sdk/dist/artifacts/svm/engine-mainnet.so +0 -0
  31. package/sdk/dist/artifacts/svm/engine-wire-devnet.json +3 -3
  32. package/sdk/dist/artifacts/svm/engine-wire-mainnet.json +3 -3
  33. package/sdk/dist/deployments/index.d.ts +14 -46
  34. package/sdk/dist/deployments/index.d.ts.map +1 -1
  35. package/sdk/dist/deployments/index.js +22 -47
  36. package/sdk/dist/deployments/index.js.map +1 -1
  37. package/sdk/dist/deployments/v12-addresses.d.ts +9 -5
  38. package/sdk/dist/deployments/v12-addresses.d.ts.map +1 -1
  39. package/sdk/dist/deployments/v12-addresses.js +5 -6
  40. package/sdk/dist/deployments/v12-addresses.js.map +1 -1
  41. package/sdk/dist/deployments/v12.generated.d.ts +3 -3
  42. package/sdk/dist/deployments/v12.generated.d.ts.map +1 -1
  43. package/sdk/dist/deployments/v12.generated.js +3 -3
  44. package/sdk/dist/deployments/v12.generated.js.map +1 -1
  45. package/sdk/dist/deposit/index.d.ts +28 -102
  46. package/sdk/dist/deposit/index.d.ts.map +1 -1
  47. package/sdk/dist/deposit/index.js.map +1 -1
  48. package/sdk/dist/deposit/params.d.ts +1 -1
  49. package/sdk/dist/deposit/params.js +2 -2
  50. package/sdk/dist/deposit/params.js.map +1 -1
  51. package/sdk/dist/deposit/source.d.ts +1 -1
  52. package/sdk/dist/deposit/source.d.ts.map +1 -1
  53. package/sdk/dist/deposit/source.js +18 -33
  54. package/sdk/dist/deposit/source.js.map +1 -1
  55. package/sdk/dist/deposit/types.d.ts +5 -4
  56. package/sdk/dist/deposit/types.d.ts.map +1 -1
  57. package/sdk/dist/evm/engine.d.ts +32 -0
  58. package/sdk/dist/evm/engine.d.ts.map +1 -1
  59. package/sdk/dist/evm/engine.js +2 -0
  60. package/sdk/dist/evm/engine.js.map +1 -1
  61. package/sdk/dist/index.d.ts +1 -0
  62. package/sdk/dist/index.d.ts.map +1 -1
  63. package/sdk/dist/index.js +2 -0
  64. package/sdk/dist/index.js.map +1 -1
  65. package/sdk/dist/plugin/index.d.ts +16 -41
  66. package/sdk/dist/plugin/index.d.ts.map +1 -1
  67. package/sdk/dist/plugin/index.js +2 -6
  68. package/sdk/dist/plugin/index.js.map +1 -1
  69. package/sdk/dist/recipes/index.js +2 -2
  70. package/sdk/dist/recipes/settle.sauce.ts +1 -1
  71. package/sdk/dist/routes/index.d.ts +3 -3
  72. package/sdk/dist/routes/index.js +3 -3
  73. package/sdk/dist/routes/intent-dsl.d.ts +23 -10
  74. package/sdk/dist/routes/intent-dsl.d.ts.map +1 -1
  75. package/sdk/dist/routes/intent-dsl.js +19 -2
  76. package/sdk/dist/routes/intent-dsl.js.map +1 -1
  77. package/sdk/dist/routes/nest.d.ts +23 -0
  78. package/sdk/dist/routes/nest.d.ts.map +1 -1
  79. package/sdk/dist/routes/nest.js +2 -4
  80. package/sdk/dist/routes/nest.js.map +1 -1
  81. package/sdk/dist/routes/protocol-rewrite.d.ts +2 -2
  82. package/sdk/dist/routes/protocol-rewrite.d.ts.map +1 -1
  83. package/sdk/dist/routes/protocol-rewrite.js +19 -35
  84. package/sdk/dist/routes/protocol-rewrite.js.map +1 -1
  85. package/sdk/dist/routes/sauce-calls.d.ts +7 -13
  86. package/sdk/dist/routes/sauce-calls.d.ts.map +1 -1
  87. package/sdk/dist/routes/sauce-calls.js +6 -11
  88. package/sdk/dist/routes/sauce-calls.js.map +1 -1
  89. package/sdk/dist/routes/sauce-route.d.ts +24 -53
  90. package/sdk/dist/routes/sauce-route.d.ts.map +1 -1
  91. package/sdk/dist/routes/sauce-route.js +5 -6
  92. package/sdk/dist/routes/sauce-route.js.map +1 -1
  93. package/sdk/dist/routes/source-globals.generated.d.ts +931 -0
  94. package/sdk/dist/routes/source-globals.generated.d.ts.map +1 -0
  95. package/sdk/dist/routes/source-globals.generated.js +7 -0
  96. package/sdk/dist/routes/source-globals.generated.js.map +1 -0
  97. package/sdk/dist/std/token/token.svm.js +19 -14
  98. package/sdk/dist/svm/cpi-probe.d.ts +1 -1
  99. package/sdk/dist/svm/cpi-probe.d.ts.map +1 -1
  100. package/sdk/dist/svm/cpi-probe.js +5 -2
  101. package/sdk/dist/svm/cpi-probe.js.map +1 -1
  102. package/sdk/dist/svm/intent.d.ts +12 -40
  103. package/sdk/dist/svm/intent.d.ts.map +1 -1
  104. package/sdk/dist/svm/intent.js +22 -66
  105. package/sdk/dist/svm/intent.js.map +1 -1
  106. package/sdk/dist/svm/venues/byreal/index.d.ts.map +1 -1
  107. package/sdk/dist/svm/venues/byreal/index.js +4 -1
  108. package/sdk/dist/svm/venues/byreal/index.js.map +1 -1
  109. package/sdk/dist/svm/venues/carrot/index.d.ts.map +1 -1
  110. package/sdk/dist/svm/venues/carrot/index.js +4 -0
  111. package/sdk/dist/svm/venues/carrot/index.js.map +1 -1
  112. package/sdk/dist/svm/venues/cropper/index.d.ts +1 -1
  113. package/sdk/dist/svm/venues/cropper/index.js +1 -1
  114. package/sdk/dist/svm/venues/huma/index.d.ts.map +1 -1
  115. package/sdk/dist/svm/venues/huma/index.js +4 -1
  116. package/sdk/dist/svm/venues/huma/index.js.map +1 -1
  117. package/sdk/dist/svm/venues/index.d.ts +1 -0
  118. package/sdk/dist/svm/venues/index.d.ts.map +1 -1
  119. package/sdk/dist/svm/venues/index.js +1 -0
  120. package/sdk/dist/svm/venues/index.js.map +1 -1
  121. package/sdk/dist/svm/venues/math.d.ts +4 -1
  122. package/sdk/dist/svm/venues/math.d.ts.map +1 -1
  123. package/sdk/dist/svm/venues/math.js +4 -1
  124. package/sdk/dist/svm/venues/math.js.map +1 -1
  125. package/sdk/dist/svm/venues/meteora-damm-v1-stable/index.d.ts.map +1 -1
  126. package/sdk/dist/svm/venues/meteora-damm-v1-stable/index.js +4 -1
  127. package/sdk/dist/svm/venues/meteora-damm-v1-stable/index.js.map +1 -1
  128. package/sdk/dist/svm/venues/meteora-damm-v2/index.d.ts.map +1 -1
  129. package/sdk/dist/svm/venues/meteora-damm-v2/index.js +5 -2
  130. package/sdk/dist/svm/venues/meteora-damm-v2/index.js.map +1 -1
  131. package/sdk/dist/svm/venues/obric-v2/index.d.ts.map +1 -1
  132. package/sdk/dist/svm/venues/obric-v2/index.js +16 -5
  133. package/sdk/dist/svm/venues/obric-v2/index.js.map +1 -1
  134. package/sdk/dist/svm/venues/oracle-exponent.d.ts +82 -0
  135. package/sdk/dist/svm/venues/oracle-exponent.d.ts.map +1 -0
  136. package/sdk/dist/svm/venues/oracle-exponent.js +97 -0
  137. package/sdk/dist/svm/venues/oracle-exponent.js.map +1 -0
  138. package/sdk/dist/svm/venues/orca-legacy-token-swap/index.d.ts.map +1 -1
  139. package/sdk/dist/svm/venues/orca-legacy-token-swap/index.js +4 -1
  140. package/sdk/dist/svm/venues/orca-legacy-token-swap/index.js.map +1 -1
  141. package/sdk/dist/svm/venues/pumpswap/index.d.ts.map +1 -1
  142. package/sdk/dist/svm/venues/pumpswap/index.js +19 -13
  143. package/sdk/dist/svm/venues/pumpswap/index.js.map +1 -1
  144. package/sdk/dist/svm/venues/raydium-amm-v4/index.d.ts.map +1 -1
  145. package/sdk/dist/svm/venues/raydium-amm-v4/index.js +4 -1
  146. package/sdk/dist/svm/venues/raydium-amm-v4/index.js.map +1 -1
  147. package/sdk/dist/svm/venues/raydium-clmm/index.d.ts.map +1 -1
  148. package/sdk/dist/svm/venues/raydium-clmm/index.js +4 -1
  149. package/sdk/dist/svm/venues/raydium-clmm/index.js.map +1 -1
  150. package/sdk/dist/svm/venues/raydium-cp-swap/index.d.ts.map +1 -1
  151. package/sdk/dist/svm/venues/raydium-cp-swap/index.js +4 -1
  152. package/sdk/dist/svm/venues/raydium-cp-swap/index.js.map +1 -1
  153. package/sdk/dist/svm/venues/scorch/index.d.ts.map +1 -1
  154. package/sdk/dist/svm/venues/scorch/index.js +4 -1
  155. package/sdk/dist/svm/venues/scorch/index.js.map +1 -1
  156. package/sdk/dist/svm/venues/stabble-common.d.ts +1 -1
  157. package/sdk/dist/svm/venues/stabble-common.js +1 -1
  158. package/sdk/dist/svm/venues/types.d.ts +4 -1
  159. package/sdk/dist/svm/venues/types.d.ts.map +1 -1
  160. package/sdk/dist/svm/venues/woofi/index.d.ts.map +1 -1
  161. package/sdk/dist/svm/venues/woofi/index.js +4 -0
  162. package/sdk/dist/svm/venues/woofi/index.js.map +1 -1
  163. package/sdk/dist/svm/verify.d.ts +27 -133
  164. package/sdk/dist/svm/verify.d.ts.map +1 -1
  165. package/sdk/dist/svm/verify.js +72 -139
  166. package/sdk/dist/svm/verify.js.map +1 -1
  167. package/sdk/dist/swap/index.d.ts +16 -59
  168. package/sdk/dist/swap/index.d.ts.map +1 -1
  169. package/sdk/dist/swap/index.js +16 -59
  170. package/sdk/dist/swap/index.js.map +1 -1
  171. package/sdk/dist/token/source.d.ts.map +1 -1
  172. package/sdk/dist/token/source.js +4 -1
  173. package/sdk/dist/token/source.js.map +1 -1
  174. package/sdk/dist/verify/decode.d.ts +6 -6
  175. package/sdk/dist/verify/decode.js +7 -7
  176. package/sdk/dist/verify/index.js +2 -2
  177. package/sdk/dist/verify/intent.js +2 -2
  178. package/sdk/dist/verify/vectors.d.ts +1 -1
  179. package/sdk/dist/verify/vectors.d.ts.map +1 -1
  180. package/sdk/dist/verify/vectors.js +2 -2
  181. package/sdk/dist/verify/vectors.js.map +1 -1
  182. package/sdk/dist/verify/wire.d.ts +5 -4
  183. package/sdk/dist/verify/wire.d.ts.map +1 -1
  184. package/sdk/dist/verify/wire.js +5 -4
  185. package/sdk/dist/verify/wire.js.map +1 -1
@@ -0,0 +1,134 @@
1
+ # SDK, compiler, and runtime
2
+
3
+ [Documentation](../README.md) · [Quick start](../guides/quick-start.md)
4
+
5
+ Sauce turns a program into an atomic on-chain execution. The SDK supplies chain and
6
+ protocol knowledge, the compiler translates SauceScript into bytecode, and a
7
+ compatible on-chain engine executes that bytecode. An Eco intent describes the
8
+ destination execution and the reward for fulfilling it.
9
+
10
+ ## The three layers
11
+
12
+ | Layer | Published surface | Responsibility |
13
+ | -------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
14
+ | SDK | `@eco-incorp/sauce` and its subpaths | Protocol and token registries, route syntax, builders, intent encoding, account planning, transaction helpers, and settlement verification |
15
+ | Compiler | `@eco-incorp/sauce-compiler`; exposed in Node through `@eco-incorp/sauce/compiler` | Parse and check SauceScript, resolve modules and ABIs, apply compile-time parameters, and emit EVM or SVM bytecode |
16
+ | Runtime | Deployed Sauce EVM/SVM engines and Kitchen/Pot contracts or programs | Execute compatible bytecode against the chain's accounts and contracts |
17
+
18
+ The compiler is included when you install `@eco-incorp/sauce`. Import
19
+ `@eco-incorp/sauce/compiler` to compile standalone programs, or use the SDK
20
+ route builders to resolve protocol and token names automatically.
21
+
22
+ ```mermaid
23
+ flowchart TD
24
+ A[Route closure or source string] --> B[SDK: destination, tokens, protocols, defines]
25
+ B --> C[Rewritten SauceScript and ABI bindings]
26
+ C --> D[Published Sauce compiler]
27
+ E[Standalone SauceScript and resolver] --> D
28
+ D --> F[EVM bytecode]
29
+ D --> G[SVM bytecode and account manifest]
30
+ F --> H[Pot.cook: engine and program]
31
+ G --> I[Account plan and Kitchen cook instruction]
32
+ H --> J[Direct transaction or Eco route call]
33
+ I --> K[Solana transaction]
34
+ ```
35
+
36
+ Both compiler targets use Sauce bytecode. EVM output is not a Solidity contract's
37
+ deployment bytecode, and SVM output is not a Solana ELF program. The selected
38
+ Sauce engine interprets it.
39
+
40
+ ## From global names to contract calls
41
+
42
+ Inside an EVM route, the SDK lets you write:
43
+
44
+ ```js
45
+ USDC.approve(Uniswap.UniversalRouter, 1_000_000n);
46
+ ```
47
+
48
+ The destination chain selects `USDC` and the router deployment. Before invoking
49
+ the compiler, the SDK rewrites token and protocol access into contract bindings,
50
+ supplies the matching ABIs, and injects scalar addresses. The compiler then sees
51
+ the equivalent `IERC20.at(...).approve(...)` call.
52
+
53
+ These names are SDK syntax, not builtins of the standalone compiler. A direct
54
+ `compile()` call needs explicit ABI imports/bindings and any addresses or defines
55
+ the program uses. See [protocols and tokens](../guides/protocols-and-tokens.md) and
56
+ [the compiler API](../api/compiler.md).
57
+
58
+ There are also separate host JavaScript and SauceScript environments. Importing
59
+ the SDK installs chain helpers such as `Base` in the host. A route callback is
60
+ read as source and compiled; it is never invoked as a JavaScript callback and
61
+ does not capture its surrounding variables. Generated TypeScript declarations
62
+ help you author route bodies, but compilation still resolves each name against
63
+ the selected destination's actual registry.
64
+
65
+ ## From bytecode to execution
66
+
67
+ ### EVM
68
+
69
+ `V12Pot.cook(address engine, bytes program)` runs one program. A Pot has an owner;
70
+ the engine is supplied on each cook. Constructing a call therefore needs both a
71
+ **Pot address** and an **engine address**. The SDK does not infer them from the
72
+ protocol namespace. The released engine accepts only canonical Pots deployed through
73
+ its configured Kitchen. Each cook must send the Kitchen's current `executionFee()`
74
+ in `msg.value`; the Pot remits it before running the program. Add native program
75
+ funding on top of that fee. An existing Pot balance does not replace this payment.
76
+
77
+ A multi-statement route body compiles to one program and one cook. To run separate
78
+ programs, construct separate calls and use an authorized execution mechanism that
79
+ propagates failures if the entire operation must be atomic. The caller must have
80
+ authority to invoke the Pot. For direct Eco route calls, the caller and required
81
+ Pot owner is the destination `Portal.executor()`. Keep funds in that shared Pot
82
+ only during the fulfillment; [intent setup](../guides/intents.md#prepare-the-destination-pot)
83
+ shows deployment, custody, and fee handling.
84
+
85
+ ### SVM
86
+
87
+ The compiler returns an account manifest as well as bytecode. `Account` parameters
88
+ name attached slots; their `Writable` and `Signer` modifiers describe required
89
+ roles. Account identities and instruction data travel through separate channels.
90
+
91
+ The SVM SDK resolves account plans, builds instructions and transactions, and
92
+ supports Kitchen-managed code staging. Staged Kitchen execution uses finalized
93
+ metadata and code accounts. The built-in SVM route helper emits a legacy engine
94
+ instruction and is incompatible with the released engine; use the direct Kitchen
95
+ client described in the [Solana guide](../guides/solana.md).
96
+
97
+ The released engine's `execute_from_account` instruction names a code account;
98
+ its instruction data does not carry a content hash. A program hash checked during
99
+ Kitchen staging or cooking and the identity of the account later executed are
100
+ distinct evidence. See [settlement verification](../guides/verification.md#svm-verify-program-and-accounts).
101
+
102
+ ## Intents connect executions across chains
103
+
104
+ `Base(() => { ... }, options).reward(reward)` creates an Eco intent whose route
105
+ executes on Base. The source chain and funding information come from the intent
106
+ options and reward. The SDK compiles the destination program, constructs the
107
+ route, and provides Portal calldata and hashes.
108
+
109
+ Building an intent does not publish, fund, or fulfill it. Those are transactions
110
+ submitted by the application and fulfillment actors. Nested intents are separate
111
+ executions composed with `.nest(...)`; they do not form one synchronous call
112
+ stack across chains. See [creating intents](../guides/intents.md).
113
+
114
+ ## Compiler and engine compatibility
115
+
116
+ Compiler 2.3.0 identifies its output as `isaRevision: "stack-compact-v2"` and
117
+ reports the selected entry-argument format. These fields describe the artifact;
118
+ they do not inspect an address or negotiate runtime support.
119
+
120
+ Use `v12EngineAddress()`, `v12KitchenAddress()`, `v12SvmEngineProgramId()`, and
121
+ `v12SvmKitchenProgramId()` from `@eco-incorp/sauce/deployments` to obtain released
122
+ engine and Kitchen addresses. The legacy chain roster records an older
123
+ deployment and does not attest liveness of the pinned release. Check deployed
124
+ code, cluster, and instruction support on the target chain. Pair programs with the engine
125
+ release intended for them, and retain the compiler version, ISA revision, target,
126
+ and argument format with cached or staged artifacts.
127
+
128
+ Changing the compiler can change bytecode without changing the source-level API.
129
+ Settlement hash pins, compiled-program caches, and staged programs therefore
130
+ belong in a compiler upgrade's validation. The [verification guide](../guides/verification.md)
131
+ explains the SDK's current settlement pins.
132
+
133
+ Start with [SauceScript concepts](saucescript.md) for the language boundary, or
134
+ [compiling programs](../guides/compiling.md) for direct compiler integration.
@@ -0,0 +1,172 @@
1
+ # Writing SauceScript
2
+
3
+ [Documentation](../README.md) · [Architecture](architecture.md) · [Compiling](../guides/compiling.md)
4
+
5
+ SauceScript is a checked subset of TypeScript for programs that run inside the
6
+ Sauce engine. It has familiar variables, functions, conditions, loops, arrays,
7
+ and records, but it does not execute in a JavaScript VM. A host application builds
8
+ and submits programs; SauceScript performs their on-chain computation.
9
+
10
+ ## Programs and route bodies
11
+
12
+ A standalone program can declare an entry function:
13
+
14
+ ```ts
15
+ function main(amounts: Uint256[], minimum: Uint256): Uint256 {
16
+ let total = 0n;
17
+ for (let i = 0; i < amounts.length; i = i + 1) {
18
+ total = total + amounts[i];
19
+ }
20
+ require(total >= minimum);
21
+ return total;
22
+ }
23
+ ```
24
+
25
+ This is SauceScript source, not a host TypeScript module. `Uint256` and `require`
26
+ are interpreted by the Sauce compiler. Source diagnostics identify the relevant
27
+ location and span; option, resolver, and target-capability errors may have no span.
28
+
29
+ A function declared `: void` can finish without returning a value. Other
30
+ functions must return on every path. Annotate helper functions returning arrays,
31
+ bytes, or records so their callers know the return shape. The raw compiler also
32
+ accepts an entry script without an explicit `main` and synthesizes its entry.
33
+ Entry argument encoding does not determine return encoding. For ABI-decodable
34
+ EVM output, [return `abi.encode(...)`](../guides/compiling.md#encode-evm-return-values).
35
+
36
+ The SDK provides a second authoring form: a route body passed to a chain helper.
37
+
38
+ ```js
39
+ () => {
40
+ USDC.approve(Uniswap.UniversalRouter, 1_000_000n);
41
+ };
42
+ ```
43
+
44
+ Pass this body to `Base(body, options)` or another chain helper as shown in the
45
+ [intent guide](../guides/intents.md). The SDK extracts its text, wraps it in
46
+ `main`, resolves SDK global names, and compiles it. For a body that falls through,
47
+ the compilation adapter adds a `void` entry annotation.
48
+
49
+ The callback must be synchronous, take no parameters, and retain source text the
50
+ SDK can parse. It captures **no host variables**. A host `const amount = ...`
51
+ does not make `amount` available to the compiler; pass it through
52
+ `options.compile.defines`, write it into generated source, or use a standalone
53
+ program with runtime entry arguments. If bundling/minification rewrites callback
54
+ names or bodies, use explicit source strings or source assets.
55
+
56
+ ## Three ways values enter a program
57
+
58
+ | Channel | When resolved | Effect on the compiled program |
59
+ | ----------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------- |
60
+ | Literals and compile-time `defines` | During compilation | Values can be folded into bytecode; changing them can change the program hash |
61
+ | `main` parameters | During execution | Values are encoded after the reusable program; their values are not compiler inputs |
62
+ | SVM `Account` parameters | When instruction accounts are attached | Identities occupy manifest slots, separate from encoded scalar/collection arguments |
63
+
64
+ The EVM settle recipe takes its token list, floor, and recipient as runtime
65
+ parameters, so one program hash covers all values. The SVM settle recipe compiles
66
+ its floor and token-program split through defines and verifies them by
67
+ recompilation. Those are deliberate API choices, not interchangeable encodings.
68
+
69
+ ## Values and types
70
+
71
+ SauceScript uses 256-bit scalar words and heap-backed bytes, arrays, and tuples.
72
+ There is no JavaScript floating-point arithmetic. Prefer `bigint` literals in
73
+ host-authored route callbacks so the host preserves exact integer values.
74
+
75
+ | Type or syntax | Meaning |
76
+ | ----------------------------------------- | ------------------------------------------------------------ |
77
+ | `Uint256`, `number`, `bigint` | A scalar integer word |
78
+ | `Address` | An address-valued scalar |
79
+ | `bool`, `boolean` | A boolean type checked separately from integer values |
80
+ | `bytes`, `string` | Byte strings |
81
+ | `T[]` | An array whose element type is `T` |
82
+ | `{ amount: Uint256, recipient: Address }` | A tuple/record shape |
83
+ | A top-level `interface` | A named record type |
84
+ | `Account`, `Account<Writable, Signer>` | An SVM account slot and required role; address-valued on EVM |
85
+ | `void` | A function with no return value |
86
+
87
+ Annotations are checked. Arbitrary TypeScript types are not accepted, and `as`
88
+ casts cannot be used to bypass a type error. Ordinary arithmetic is checked by
89
+ the compiler/runtime rather than following JavaScript number semantics.
90
+
91
+ Module-level scalar `const` values are compile-time values. Module-level `let`
92
+ state is initialized for each invocation; it is not persistent chain state. Use
93
+ the target-specific storage builtins when persistent storage is required.
94
+
95
+ ## Language support
96
+
97
+ The current compiler supports function declarations, recursion, `if`/`else`,
98
+ `while`, C-style `for`, array `for...of`, short-circuit logical operators,
99
+ conditional expressions, array/record values, indexing, mutation, and supported
100
+ byte operations. Modules can import other SauceScript modules, JSON ABIs, and
101
+ JSON data. Constant expressions and compile-time branches can be folded.
102
+
103
+ Useful boundaries when adapting application code:
104
+
105
+ - `async`/`await`, promises, classes, arbitrary JavaScript libraries, and host APIs
106
+ such as `fetch` or `console` are not on-chain operations.
107
+ - Functions are declarations, not first-class values. The outer route callback
108
+ is an SDK wrapper; it does not add general arrow-function support to the
109
+ language. The special EVM external-call `.catch(() => { ... })` handler is a
110
+ supported exception.
111
+ - `switch`, `for...in`, rest/spread, default parameters, and general object
112
+ destructuring are unsupported. Tuple destructuring in function bodies is
113
+ supported.
114
+ - Type aliases, enums, generic application types, unions, `as`, and `satisfies`
115
+ are outside the subset. Use the supported annotations and named interfaces.
116
+ - A template literal with interpolation is unsupported. The `hex` tagged literal
117
+ produces literal bytes.
118
+
119
+ These examples and boundaries are validated with Sauce compiler 2.3.0. Additional
120
+ language and builtin references are available at [sauce.eco.com](https://sauce.eco.com).
121
+
122
+ ## EVM and SVM operations
123
+
124
+ The compiler shares a frontend across targets and rejects unavailable operations
125
+ for the target selected at compile time.
126
+
127
+ | Capability | EVM | SVM |
128
+ | ---------------------------------------------------- | ---------------------------------------- | ------------------------------------------------- |
129
+ | Arithmetic, functions, arrays, records, control flow | Supported | Supported |
130
+ | ABI-bound `Contract.at(address).method(...)` | Supported | Use CPI/account APIs |
131
+ | `evm.call`, `evm.static`, `evm.delegate` | Supported | Unavailable |
132
+ | `svm.call` with attached account slots | Unavailable | Supported |
133
+ | `abi.encode` / `abi.decode` builtins | Supported | Unavailable |
134
+ | Persistent storage | `evm.sload` / `evm.sstore` | `svm.sload` / `svm.sstore` byte ranges |
135
+ | Transient storage and external-call catch handlers | EVM-specific | Unavailable |
136
+ | SDK `USDC.approve(...)` and protocol-member rewrites | Supported for registered EVM deployments | Use SVM account-aware builders |
137
+ | Runtime entry arguments | ABI-v1 or compact-v1 | ABI-v1 or compact-v1, plus separate Account slots |
138
+
139
+ The absence of SVM `abi.encode`/`abi.decode` builtins does not remove runtime
140
+ entry parameters: entry decoding is its own compiler feature. Context values
141
+ also have target semantics: an EVM address and a Solana public key have different
142
+ widths, and `ctx.blockNumber()` refers to the target's block/slot model.
143
+
144
+ ## Modules, ABI bindings, and global names
145
+
146
+ A direct compiler program can bind an EVM ABI explicitly:
147
+
148
+ ```ts
149
+ import ERC20 from "./erc20.abi.json";
150
+
151
+ function main(token: Address, spender: Address, amount: Uint256): void {
152
+ ERC20.at(token).approve(spender, amount);
153
+ }
154
+ ```
155
+
156
+ A bare JSON import binds a contract ABI. A JSON data import uses
157
+ `with { type: "json" }`. The host resolver must return the named file's bytes in
158
+ either case.
159
+
160
+ The compiler's `ambient` option broadcasts a SauceScript module's exports without
161
+ explicit imports. Its `defines` option injects scalar constants into every
162
+ module. Their precedence differs: ambient exports yield to declarations;
163
+ defines replace same-named module `const` values. Pick specific define names
164
+ because that replacement also reaches imported dependencies.
165
+
166
+ The SDK adds chain selection, token symbols, and protocol member access around
167
+ that compiler API. `openRoute` and chain helpers supply chain defines and the
168
+ default `@sauce/token` ambient module. Direct `compileSauceRoute()` accepts an
169
+ explicit `ambient` list and does not add that default list. Raw `compile()` adds
170
+ neither the SDK registry nor its filesystem resolver. See the
171
+ [compilation guide](../guides/compiling.md#choose-a-compilation-path) for the exact
172
+ boundary.
@@ -0,0 +1,34 @@
1
+ # Executable examples
2
+
3
+ [Documentation](../README.md) · [Quick start](../guides/quick-start.md)
4
+
5
+ These examples use the published package exports. The construction examples run offline: they compile source and construct transaction data without sending transactions. Repeated-digit addresses and illustrative fees are fixtures, not deployment configuration. The RPC setup helper is typechecked but only makes chain reads when your application calls it.
6
+
7
+ | Example | Demonstrates |
8
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
9
+ | [protocol-approval.ts](protocol-approval.ts) | A typed `Base(() => ...)` callback with `USDC.approve(Uniswap.UniversalRouter, ...)` |
10
+ | [first-intent.ts](first-intent.ts) | Ethereum reward, Base USDC delivery, Executor-to-Pot funding, explicit callback defines, source transaction requests |
11
+ | [compile-program.ts](compile-program.ts) | The compiler dependency, compact entry schema, argument encoding and decoding |
12
+ | [evm-execution.ts](evm-execution.ts) | Destination RPC setup helper: current Kitchen, Portal Executor ownership, Pot prediction/deployment request, and execution fee |
13
+
14
+ ## Run an example in your project
15
+
16
+ Use Node.js 24 or newer. Install the SDK and TypeScript in your application:
17
+
18
+ ```sh
19
+ pnpm add @eco-incorp/sauce
20
+ pnpm add -D typescript
21
+ ```
22
+
23
+ Set `"type": "module"` in your project's `package.json`. Copy an example to `example.ts`, then compile and run it:
24
+
25
+ ```sh
26
+ pnpm exec tsc example.ts --target ES2022 --module NodeNext --moduleResolution NodeNext --strict --skipLibCheck --outDir dist
27
+ node dist/example.js
28
+ ```
29
+
30
+ For `first-intent.ts` and `evm-execution.ts`, also run `pnpm add viem`: they use Viem's address formatter and RPC interfaces.
31
+
32
+ The examples are typechecked and loaded automatically in the SDK's CI. Construction examples assert their encoded output, including nonzero cook-fee budgeting; the RPC helper is not a live-chain integration test.
33
+
34
+ For actual EVM execution, first call `prepareDestinationPot` from `evm-execution.ts` with a destination client, Portal, and salt. If it returns `ready: false`, send its deployment request with your signer, wait for a successful receipt, and call it again. When `ready: true`, use its Pot, engine, and observed execution fee instead of the offline fixtures. Supply valid deadlines, source funding, and solver fulfillment. Read [intents](../guides/intents.md) for atomic asset handling and fee budgets, and [Solana](../guides/solana.md) for the separate supported execution paths.
@@ -0,0 +1,20 @@
1
+ import {
2
+ compile,
3
+ decodeCompactArguments,
4
+ encodeCompactArguments,
5
+ } from "@eco-incorp/sauce/compiler";
6
+
7
+ const source = "function main(amount: Uint256): Uint256 { return amount + 1; }";
8
+ const artifact = compile({
9
+ target: "evm",
10
+ compactArgs: true,
11
+ resolve: () => new TextEncoder().encode(source),
12
+ });
13
+ if (artifact.entryEncoding !== "compact-v1" || artifact.entrySchema === undefined) {
14
+ throw new Error("Expected a compact entry schema");
15
+ }
16
+ const args = encodeCompactArguments(artifact.entrySchema, [42n]);
17
+ const program = new Uint8Array([...artifact.bytecode, ...args]);
18
+ const decoded = decodeCompactArguments(artifact.entrySchema, args);
19
+ if (!Array.isArray(decoded) || decoded[0] !== 42n) throw new Error("Argument round trip failed");
20
+ console.log("ISA:", artifact.isaRevision, "program bytes:", program.length);
@@ -0,0 +1,59 @@
1
+ import { routes } from "@eco-incorp/sauce";
2
+ import { v12EngineAddress, v12KitchenAddress } from "@eco-incorp/sauce/deployments";
3
+ import { v12Kitchen, v12Pot } from "@eco-incorp/sauce/evm/engine";
4
+ import { getAddress, parseAbi, type Address, type Hex, type PublicClient } from "viem";
5
+
6
+ const portalAbi = parseAbi(["function executor() view returns (address)"]);
7
+ const feeAbi = parseAbi(["function executionFee() view returns (uint256)"]);
8
+ const potKitchenAbi = parseAbi(["function kitchen() view returns (address)"]);
9
+
10
+ // Call with a client for the destination chain. Importing this example performs no RPC.
11
+ export async function prepareDestinationPot(client: PublicClient, portal: Address, salt: Hex) {
12
+ const kitchen = getAddress(v12KitchenAddress());
13
+ const engine = getAddress(v12EngineAddress());
14
+ const [kitchenCode, engineCode] = await Promise.all([
15
+ client.getCode({ address: kitchen }),
16
+ client.getCode({ address: engine }),
17
+ ]);
18
+ if (!kitchenCode || kitchenCode === "0x" || !engineCode || engineCode === "0x") {
19
+ throw new Error("Current Kitchen and engine must be deployed on the destination chain");
20
+ }
21
+
22
+ const executor = await client.readContract({
23
+ address: portal,
24
+ abi: portalAbi,
25
+ functionName: "executor",
26
+ });
27
+ const pot = await client.readContract({
28
+ address: kitchen,
29
+ abi: v12Kitchen.abi,
30
+ functionName: "predictPot",
31
+ args: [executor, salt],
32
+ });
33
+ const executionFee = await client.readContract({
34
+ address: kitchen,
35
+ abi: feeAbi,
36
+ functionName: "executionFee",
37
+ });
38
+ const context = { kitchen, engine, executor, pot, executionFee };
39
+ const code = await client.getCode({ address: pot });
40
+ if (!code || code === "0x") {
41
+ const deploy = routes.sauceEvmDeployPotCall({ kitchen, owner: executor, salt });
42
+ // Send this destination-chain request with your signer, wait for a successful receipt,
43
+ // then call prepareDestinationPot again. Do not build a route until ready is true.
44
+ return {
45
+ ...context,
46
+ ready: false as const,
47
+ deployment: { to: kitchen, data: deploy.data, value: 0n },
48
+ };
49
+ }
50
+
51
+ const [owner, actualKitchen] = await Promise.all([
52
+ client.readContract({ address: pot, abi: v12Pot.abi, functionName: "owner" }),
53
+ client.readContract({ address: pot, abi: potKitchenAbi, functionName: "kitchen" }),
54
+ ]);
55
+ if (getAddress(owner) !== getAddress(executor) || getAddress(actualKitchen) !== kitchen) {
56
+ throw new Error("Pot must belong to this Portal Executor and the current Kitchen");
57
+ }
58
+ return { ...context, ready: true as const };
59
+ }
@@ -0,0 +1,56 @@
1
+ import { routes, token } from "@eco-incorp/sauce";
2
+ import { toHex } from "viem";
3
+
4
+ // Offline fixtures. Use prepareDestinationPot from evm-execution.ts before submission
5
+ // to verify the Executor-owned Pot and read the current Kitchen fee.
6
+ const pot = "0x1111111111111111111111111111111111111111";
7
+ const executionFee = 1_000n;
8
+ const baseUsdcValue = routes.registryTokenAddress(Base.chain, "USDC");
9
+ const sourceUsdcValue = routes.registryTokenAddress(Ethereum.chain, "USDC");
10
+ if (baseUsdcValue === undefined || sourceUsdcValue === undefined)
11
+ throw new Error("Missing USDC registry");
12
+ const baseUsdc = toHex(baseUsdcValue, { size: 20 });
13
+ const sourceUsdc = toHex(sourceUsdcValue, { size: 20 });
14
+ const recipient = 0x2222222222222222222222222222222222222222n;
15
+ const amount = 1_000_000n;
16
+
17
+ const built = Base(
18
+ () => {
19
+ USDC.transfer(recipient, amount);
20
+ },
21
+ {
22
+ execution: {
23
+ pot,
24
+ engine: "0x3333333333333333333333333333333333333333",
25
+ value: executionFee,
26
+ },
27
+ nativeAmount: executionFee,
28
+ portal: "0x4444444444444444444444444444444444444444",
29
+ sourcePortal: "0x5555555555555555555555555555555555555555",
30
+ deadline: 2_000_000_000n,
31
+ // Callbacks capture nothing: explicitly supply every external scalar.
32
+ defines: { recipient, amount },
33
+ tokens: [{ token: baseUsdc, amount }],
34
+ // Route assets arrive at the Executor; the Sauce program spends from the Pot.
35
+ precalls: [token.Token.transfer({ token: baseUsdc, to: pot, amount }).toCall()],
36
+ },
37
+ ).reward({
38
+ source: Ethereum.chain,
39
+ creator: "0x6666666666666666666666666666666666666666",
40
+ prover: "0x7777777777777777777777777777777777777777",
41
+ deadline: 2_000_003_600n,
42
+ tokens: [{ token: sourceUsdc, amount }],
43
+ });
44
+
45
+ const sourceRequests = [...built.approvals(), built.publishAndFundCalldata(false)];
46
+ if (built.intent.route.calls.length !== 2 || sourceRequests.length !== 2) {
47
+ throw new Error("Expected destination delivery + cook and source approval + funding");
48
+ }
49
+ if (
50
+ built.intent.route.calls[1]?.value !== executionFee ||
51
+ built.intent.route.nativeAmount !== executionFee
52
+ ) {
53
+ throw new Error("Destination funding must include the cook fee");
54
+ }
55
+ console.log("Ethereum → Base intent:", built.hash().intentHash);
56
+ console.log("Prepared source transactions:", sourceRequests.length);
@@ -0,0 +1,33 @@
1
+ import "@eco-incorp/sauce";
2
+ import type { routes } from "@eco-incorp/sauce";
3
+
4
+ // Offline fixtures. Read the destination Kitchen fee and use an Executor-owned Pot before use.
5
+ const executionFee = 1_000n;
6
+ const options: routes.IntentOptions = {
7
+ execution: {
8
+ pot: "0x1111111111111111111111111111111111111111",
9
+ engine: "0x2222222222222222222222222222222222222222",
10
+ value: executionFee,
11
+ },
12
+ nativeAmount: executionFee,
13
+ portal: "0x3333333333333333333333333333333333333333",
14
+ deadline: 2_000_000_000n,
15
+ };
16
+
17
+ const built = Base(() => {
18
+ USDC.approve(Uniswap.UniversalRouter, 1_000_000n);
19
+ }, options).reward({
20
+ source: "ethereum",
21
+ creator: "0x4444444444444444444444444444444444444444",
22
+ prover: "0x5555555555555555555555555555555555555555",
23
+ deadline: 2_000_003_600n,
24
+ nativeAmount: 0n,
25
+ });
26
+
27
+ if (!built.compiled.bytecode[0]?.length || built.intent.route.calls.length !== 1) {
28
+ throw new Error("Expected a compiled approval and one Pot call");
29
+ }
30
+ if (built.intent.route.calls[0]!.value !== executionFee) {
31
+ throw new Error("Expected the destination cook to receive its fee");
32
+ }
33
+ console.log("Base approval intent:", built.hash().intentHash);
@@ -0,0 +1,108 @@
1
+ # Builders and actions
2
+
3
+ [Documentation](../README.md) · [Protocol calls](protocols-and-tokens.md) · [Compiler](../api/compiler.md)
4
+
5
+ The SDK provides host-language builders for applications that construct programs from data. Token, swap, and deposit builders produce SauceScript source. The actions package accepts a sequence of routing actions and can produce either source or compiled EVM bytecode. None of these builders selects a market route or submits a transaction.
6
+
7
+ ## Token operations
8
+
9
+ `token.Token` is a host builder. It takes spec objects, while `Token(address).transfer(...)` is syntax inside a compiled EVM route.
10
+
11
+ ```ts
12
+ import { routes, token } from "@eco-incorp/sauce";
13
+
14
+ export function compileTokenTransfer(
15
+ asset: token.AddressInput,
16
+ recipient: token.AddressInput,
17
+ amount: bigint,
18
+ execution: routes.SauceEvmExecutionInput,
19
+ ) {
20
+ const operation = token.Token.transfer({ token: asset, to: recipient, amount });
21
+ const program = operation.toSauceScript("evm");
22
+ return routes.compileSauceRoute("base", program.source, execution, {
23
+ baseDirs: [...program.baseDirs],
24
+ });
25
+ }
26
+ ```
27
+
28
+ `TokenCall.toCall()` instead returns `{ target, data, value }` for a fixed EVM call. Values that require runtime state, such as `amount: "balance"` or balance owner `"self"`, must be compiled. `.statement(target)` emits a transfer/approval statement; `.expression(target)` emits a balance read. `Token.program(specs, { target })` combines several operations into one program.
29
+
30
+ Use `Token.approve({ token, spender, amount })` for approvals and `Token.balanceOf({ token, owner })` for reads. SVM specs name account references instead of ERC-20 addresses; see [Solana](solana.md).
31
+
32
+ ## Swaps
33
+
34
+ `swap.swapSource(spec)` generates a program calling the Sauce Router's unified swap entry point. `swap.swapSource([legA, legB])` emits multiple swaps in one program. `swap.toSwapParams(spec)` exposes the normalized parameters, and `swap.swapCallStatement(spec)` produces a statement for composition.
35
+
36
+ ```ts
37
+ import { routes, swap } from "@eco-incorp/sauce";
38
+
39
+ export function compileSwap(spec: swap.SwapSourceSpec, execution: routes.SauceEvmExecutionInput) {
40
+ return routes.compileSauceRoute("base", swap.swapSource(spec), execution, {
41
+ baseDirs: [...swap.SWAP_BASE_DIRS],
42
+ });
43
+ }
44
+ ```
45
+
46
+ Supply the correct pool, tokens, amount, and pool-specific parameters. The generated program self-calls the Sauce Router path supported by the Pot; the execution deployment must support it. The unified builder supports `UniV2`, `UniV3`, `UniV4`, `Curve`, `BalancerV2`, `DODOV2`, `TraderJoeLB`, `MaverickV2`, and `WOOFi`. The separate Pancake Infinity CL/Bin entry points are not supported by this builder.
47
+
48
+ The builder normalizes each pool type's amount-sign convention. It does not recover and expose a general `amountOut` value from the swap return. Enforce the intended output with an explicit balance/delta check or a settlement program. A program compiling successfully does not demonstrate correct execution against a specific live pool.
49
+
50
+ ## Deposits, withdrawals, staking, and wrapping
51
+
52
+ `deposit.depositSource(spec)` emits the protocol call and its required approval. Discover supported pairs with `deposit.listDepositTemplates()`; template coverage is separate from the protocol catalog. Templates include Aave/Spark supply and withdrawal, Compound V3 supply, ERC-4626/Euler deposits, Lido operations, and WETH wrapping/unwrapping.
53
+
54
+ ```ts
55
+ import { deposit, routes } from "@eco-incorp/sauce";
56
+
57
+ export function compileSupply(
58
+ pool: deposit.AddressInput,
59
+ asset: deposit.AddressInput,
60
+ amount: bigint,
61
+ execution: routes.SauceEvmExecutionInput,
62
+ ) {
63
+ const source = deposit.depositSource({
64
+ protocol: "aave-v3",
65
+ action: "supply",
66
+ target: pool,
67
+ token: asset,
68
+ amount,
69
+ });
70
+ return routes.compileSauceRoute("base", source, execution, {
71
+ baseDirs: [...deposit.DEPOSIT_BASE_DIRS],
72
+ });
73
+ }
74
+ ```
75
+
76
+ The default approval policy is `"exact"`; `"none"` assumes an existing allowance, and `"max"` grants the maximum allowance. The default beneficiary is the executing contract where the protocol supports it. Native-funded templates require value in the executing Pot and use a call carrying native value.
77
+
78
+ `amount: "balance"` reads the executing Pot's balance: native ETH for native-funded templates, the WETH `target` for unwrapping, or the supplied `token` for other templates. `deposit.swapThenDepositSource(...)` composes swaps and deposits and also supports `amount: "delta"` for the increase in that balance across the swap block. Standalone deposit source cannot use `"delta"` because it lacks the pre-swap snapshot. Use `deposit.COMPOSED_BASE_DIRS` when compiling composed source.
79
+
80
+ For Aave/Spark withdrawals, use a fixed underlying-asset amount or `amount: "max"` to withdraw the full position. `"balance"` and `"delta"` read the underlying token held by the Pot, not its supplied position, and should not size a withdrawal.
81
+
82
+ ## Routing actions
83
+
84
+ Import actions from the published package's `/actions` subpath:
85
+
86
+ ```ts
87
+ import { actionsToSauce, actionsToSauceSource } from "@eco-incorp/sauce/actions";
88
+ import type { RoutingAction } from "@eco-incorp/sauce/actions";
89
+
90
+ export function buildRoutingProgram(actions: readonly RoutingAction[]) {
91
+ return {
92
+ source: actionsToSauceSource(actions),
93
+ bytecode: actionsToSauce(actions),
94
+ };
95
+ }
96
+ ```
97
+
98
+ Action variants cover protocol swaps, bridges, wrapping, staking, lending, transfers, and approvals. Each discriminated action specifies concrete addresses and protocol parameters. Keep all actions in a program compatible with the chain where it executes; a `chainId` field does not turn a program into cross-chain execution.
99
+
100
+ Amount resolution uses this precedence:
101
+
102
+ 1. An explicit amount on the action.
103
+ 2. `amountRef`, naming a previously saved output.
104
+ 3. The immediately preceding action's output, for implicit chaining.
105
+
106
+ Set `saveOutputAs` on an action to name its output for later reuse. Saving an output or implicitly chaining the next action can direct the output to the executing Pot and capture it once. Independent actions retain their configured recipients. Quote helpers such as `actionToQuote` construct protocol quote calls; the application performs and interprets the RPC simulation.
107
+
108
+ `actionsToSauce` returns a single `Uint8Array`. To put already compiled EVM bytecode into a route, convert it to hex (for example, with Viem's `bytesToHex`) and use `routes.buildSauceEvmCall({ pot, engine, program })`; see the [Routes API](../api/routes.md). To produce an Eco cross-chain intent, build the destination program and use the [intent builder](intents.md).