@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.
- package/LICENSE.md +29 -0
- package/README.md +69 -249
- package/actions/dist/to-sauce.d.ts.map +1 -1
- package/actions/dist/to-sauce.js +4 -5
- package/dev-tools/README.md +11 -170
- package/docs/README.md +39 -0
- package/docs/api/README.md +71 -0
- package/docs/api/compiler.md +234 -0
- package/docs/api/coverage.md +117 -0
- package/docs/api/routes.md +110 -0
- package/docs/concepts/architecture.md +134 -0
- package/docs/concepts/saucescript.md +172 -0
- package/docs/examples/README.md +34 -0
- package/docs/examples/compile-program.ts +20 -0
- package/docs/examples/evm-execution.ts +59 -0
- package/docs/examples/first-intent.ts +56 -0
- package/docs/examples/protocol-approval.ts +33 -0
- package/docs/guides/builders-and-actions.md +108 -0
- package/docs/guides/compiling.md +264 -0
- package/docs/guides/intents.md +158 -0
- package/docs/guides/nested-intents.md +79 -0
- package/docs/guides/protocols-and-tokens.md +116 -0
- package/docs/guides/quick-start.md +64 -0
- package/docs/guides/solana.md +196 -0
- package/docs/guides/verification.md +253 -0
- package/package.json +9 -4
- package/sdk/dist/artifacts/V12Deployments.json +23 -5
- package/sdk/dist/artifacts/V12RuntimeBytecode.json +1 -1
- package/sdk/dist/artifacts/svm/engine-devnet.so +0 -0
- package/sdk/dist/artifacts/svm/engine-mainnet.so +0 -0
- package/sdk/dist/artifacts/svm/engine-wire-devnet.json +3 -3
- package/sdk/dist/artifacts/svm/engine-wire-mainnet.json +3 -3
- package/sdk/dist/deployments/index.d.ts +14 -46
- package/sdk/dist/deployments/index.d.ts.map +1 -1
- package/sdk/dist/deployments/index.js +22 -47
- package/sdk/dist/deployments/index.js.map +1 -1
- package/sdk/dist/deployments/v12-addresses.d.ts +9 -5
- package/sdk/dist/deployments/v12-addresses.d.ts.map +1 -1
- package/sdk/dist/deployments/v12-addresses.js +5 -6
- package/sdk/dist/deployments/v12-addresses.js.map +1 -1
- package/sdk/dist/deployments/v12.generated.d.ts +3 -3
- package/sdk/dist/deployments/v12.generated.d.ts.map +1 -1
- package/sdk/dist/deployments/v12.generated.js +3 -3
- package/sdk/dist/deployments/v12.generated.js.map +1 -1
- package/sdk/dist/deposit/index.d.ts +28 -102
- package/sdk/dist/deposit/index.d.ts.map +1 -1
- package/sdk/dist/deposit/index.js.map +1 -1
- package/sdk/dist/deposit/params.d.ts +1 -1
- package/sdk/dist/deposit/params.js +2 -2
- package/sdk/dist/deposit/params.js.map +1 -1
- package/sdk/dist/deposit/source.d.ts +1 -1
- package/sdk/dist/deposit/source.d.ts.map +1 -1
- package/sdk/dist/deposit/source.js +18 -33
- package/sdk/dist/deposit/source.js.map +1 -1
- package/sdk/dist/deposit/types.d.ts +5 -4
- package/sdk/dist/deposit/types.d.ts.map +1 -1
- package/sdk/dist/evm/engine.d.ts +32 -0
- package/sdk/dist/evm/engine.d.ts.map +1 -1
- package/sdk/dist/evm/engine.js +2 -0
- package/sdk/dist/evm/engine.js.map +1 -1
- package/sdk/dist/index.d.ts +1 -0
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +2 -0
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/plugin/index.d.ts +16 -41
- package/sdk/dist/plugin/index.d.ts.map +1 -1
- package/sdk/dist/plugin/index.js +2 -6
- package/sdk/dist/plugin/index.js.map +1 -1
- package/sdk/dist/recipes/index.js +2 -2
- package/sdk/dist/recipes/settle.sauce.ts +1 -1
- package/sdk/dist/routes/index.d.ts +3 -3
- package/sdk/dist/routes/index.js +3 -3
- package/sdk/dist/routes/intent-dsl.d.ts +23 -10
- package/sdk/dist/routes/intent-dsl.d.ts.map +1 -1
- package/sdk/dist/routes/intent-dsl.js +19 -2
- package/sdk/dist/routes/intent-dsl.js.map +1 -1
- package/sdk/dist/routes/nest.d.ts +23 -0
- package/sdk/dist/routes/nest.d.ts.map +1 -1
- package/sdk/dist/routes/nest.js +2 -4
- package/sdk/dist/routes/nest.js.map +1 -1
- package/sdk/dist/routes/protocol-rewrite.d.ts +2 -2
- package/sdk/dist/routes/protocol-rewrite.d.ts.map +1 -1
- package/sdk/dist/routes/protocol-rewrite.js +19 -35
- package/sdk/dist/routes/protocol-rewrite.js.map +1 -1
- package/sdk/dist/routes/sauce-calls.d.ts +7 -13
- package/sdk/dist/routes/sauce-calls.d.ts.map +1 -1
- package/sdk/dist/routes/sauce-calls.js +6 -11
- package/sdk/dist/routes/sauce-calls.js.map +1 -1
- package/sdk/dist/routes/sauce-route.d.ts +24 -53
- package/sdk/dist/routes/sauce-route.d.ts.map +1 -1
- package/sdk/dist/routes/sauce-route.js +5 -6
- package/sdk/dist/routes/sauce-route.js.map +1 -1
- package/sdk/dist/routes/source-globals.generated.d.ts +931 -0
- package/sdk/dist/routes/source-globals.generated.d.ts.map +1 -0
- package/sdk/dist/routes/source-globals.generated.js +7 -0
- package/sdk/dist/routes/source-globals.generated.js.map +1 -0
- package/sdk/dist/std/token/token.svm.js +19 -14
- package/sdk/dist/svm/cpi-probe.d.ts +1 -1
- package/sdk/dist/svm/cpi-probe.d.ts.map +1 -1
- package/sdk/dist/svm/cpi-probe.js +5 -2
- package/sdk/dist/svm/cpi-probe.js.map +1 -1
- package/sdk/dist/svm/intent.d.ts +12 -40
- package/sdk/dist/svm/intent.d.ts.map +1 -1
- package/sdk/dist/svm/intent.js +22 -66
- package/sdk/dist/svm/intent.js.map +1 -1
- package/sdk/dist/svm/venues/byreal/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/byreal/index.js +4 -1
- package/sdk/dist/svm/venues/byreal/index.js.map +1 -1
- package/sdk/dist/svm/venues/carrot/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/carrot/index.js +4 -0
- package/sdk/dist/svm/venues/carrot/index.js.map +1 -1
- package/sdk/dist/svm/venues/cropper/index.d.ts +1 -1
- package/sdk/dist/svm/venues/cropper/index.js +1 -1
- package/sdk/dist/svm/venues/huma/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/huma/index.js +4 -1
- package/sdk/dist/svm/venues/huma/index.js.map +1 -1
- package/sdk/dist/svm/venues/index.d.ts +1 -0
- package/sdk/dist/svm/venues/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/index.js +1 -0
- package/sdk/dist/svm/venues/index.js.map +1 -1
- package/sdk/dist/svm/venues/math.d.ts +4 -1
- package/sdk/dist/svm/venues/math.d.ts.map +1 -1
- package/sdk/dist/svm/venues/math.js +4 -1
- package/sdk/dist/svm/venues/math.js.map +1 -1
- package/sdk/dist/svm/venues/meteora-damm-v1-stable/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/meteora-damm-v1-stable/index.js +4 -1
- package/sdk/dist/svm/venues/meteora-damm-v1-stable/index.js.map +1 -1
- package/sdk/dist/svm/venues/meteora-damm-v2/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/meteora-damm-v2/index.js +5 -2
- package/sdk/dist/svm/venues/meteora-damm-v2/index.js.map +1 -1
- package/sdk/dist/svm/venues/obric-v2/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/obric-v2/index.js +16 -5
- package/sdk/dist/svm/venues/obric-v2/index.js.map +1 -1
- package/sdk/dist/svm/venues/oracle-exponent.d.ts +82 -0
- package/sdk/dist/svm/venues/oracle-exponent.d.ts.map +1 -0
- package/sdk/dist/svm/venues/oracle-exponent.js +97 -0
- package/sdk/dist/svm/venues/oracle-exponent.js.map +1 -0
- package/sdk/dist/svm/venues/orca-legacy-token-swap/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/orca-legacy-token-swap/index.js +4 -1
- package/sdk/dist/svm/venues/orca-legacy-token-swap/index.js.map +1 -1
- package/sdk/dist/svm/venues/pumpswap/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/pumpswap/index.js +19 -13
- package/sdk/dist/svm/venues/pumpswap/index.js.map +1 -1
- package/sdk/dist/svm/venues/raydium-amm-v4/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/raydium-amm-v4/index.js +4 -1
- package/sdk/dist/svm/venues/raydium-amm-v4/index.js.map +1 -1
- package/sdk/dist/svm/venues/raydium-clmm/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/raydium-clmm/index.js +4 -1
- package/sdk/dist/svm/venues/raydium-clmm/index.js.map +1 -1
- package/sdk/dist/svm/venues/raydium-cp-swap/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/raydium-cp-swap/index.js +4 -1
- package/sdk/dist/svm/venues/raydium-cp-swap/index.js.map +1 -1
- package/sdk/dist/svm/venues/scorch/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/scorch/index.js +4 -1
- package/sdk/dist/svm/venues/scorch/index.js.map +1 -1
- package/sdk/dist/svm/venues/stabble-common.d.ts +1 -1
- package/sdk/dist/svm/venues/stabble-common.js +1 -1
- package/sdk/dist/svm/venues/types.d.ts +4 -1
- package/sdk/dist/svm/venues/types.d.ts.map +1 -1
- package/sdk/dist/svm/venues/woofi/index.d.ts.map +1 -1
- package/sdk/dist/svm/venues/woofi/index.js +4 -0
- package/sdk/dist/svm/venues/woofi/index.js.map +1 -1
- package/sdk/dist/svm/verify.d.ts +27 -133
- package/sdk/dist/svm/verify.d.ts.map +1 -1
- package/sdk/dist/svm/verify.js +72 -139
- package/sdk/dist/svm/verify.js.map +1 -1
- package/sdk/dist/swap/index.d.ts +16 -59
- package/sdk/dist/swap/index.d.ts.map +1 -1
- package/sdk/dist/swap/index.js +16 -59
- package/sdk/dist/swap/index.js.map +1 -1
- package/sdk/dist/token/source.d.ts.map +1 -1
- package/sdk/dist/token/source.js +4 -1
- package/sdk/dist/token/source.js.map +1 -1
- package/sdk/dist/verify/decode.d.ts +6 -6
- package/sdk/dist/verify/decode.js +7 -7
- package/sdk/dist/verify/index.js +2 -2
- package/sdk/dist/verify/intent.js +2 -2
- package/sdk/dist/verify/vectors.d.ts +1 -1
- package/sdk/dist/verify/vectors.d.ts.map +1 -1
- package/sdk/dist/verify/vectors.js +2 -2
- package/sdk/dist/verify/vectors.js.map +1 -1
- package/sdk/dist/verify/wire.d.ts +5 -4
- package/sdk/dist/verify/wire.d.ts.map +1 -1
- package/sdk/dist/verify/wire.js +5 -4
- 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).
|