@eco-incorp/sauce 0.99.0 → 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 +82 -0
- 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 +15 -39
- package/sdk/dist/deployments/index.d.ts.map +1 -1
- package/sdk/dist/deployments/index.js +23 -39
- package/sdk/dist/deployments/index.js.map +1 -1
- package/sdk/dist/deployments/v12-addresses.d.ts +85 -0
- package/sdk/dist/deployments/v12-addresses.d.ts.map +1 -0
- package/sdk/dist/deployments/v12-addresses.js +51 -0
- package/sdk/dist/deployments/v12-addresses.js.map +1 -0
- 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 +7 -4
- package/sdk/dist/verify/wire.d.ts.map +1 -1
- package/sdk/dist/verify/wire.js +7 -4
- package/sdk/dist/verify/wire.js.map +1 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Compiling programs
|
|
2
|
+
|
|
3
|
+
[Documentation](../README.md) · [Compiler API](../api/compiler.md) · [SauceScript](../concepts/saucescript.md)
|
|
4
|
+
|
|
5
|
+
Use the SDK route compiler when a program should become an Eco route call with
|
|
6
|
+
chain-scoped token and protocol names. Use the raw compiler when you need a
|
|
7
|
+
reusable parameterized program, an SVM account manifest, or explicit compiler
|
|
8
|
+
options such as compact arguments.
|
|
9
|
+
|
|
10
|
+
## Choose a compilation path
|
|
11
|
+
|
|
12
|
+
| Entry point | Inputs you supply | What the SDK supplies |
|
|
13
|
+
| ------------------------------------------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
14
|
+
| `Base(body, options)` / `routes.openRoute(...)` | Closure or source, intent options, execution addresses | Chain defines, default ambient `@sauce/token`, registry rewrites, compilation, intent construction |
|
|
15
|
+
| `routes.compileSauceRoute(destination, source, execution, options)` | Source, destination, execution settings | Registry rewrites, token defines, compilation, one encoded route call |
|
|
16
|
+
| `compile({ target, resolve, ... })` | Complete source/dependency resolution and compiler settings | The published compiler result, including entry metadata and SVM manifest |
|
|
17
|
+
|
|
18
|
+
Bare `USDC`, `Token(address)`, and protocol-member calls are resolved by SDK route
|
|
19
|
+
rewrites. Raw `compile()` expects compiler-native source, such as
|
|
20
|
+
`ERC20.at(address).approve(...)` backed by an ABI import or registered binding.
|
|
21
|
+
|
|
22
|
+
## Compile an EVM route
|
|
23
|
+
|
|
24
|
+
This function constructs a call; the caller supplies the deployed Pot,
|
|
25
|
+
compatible engine address, and the Kitchen's current execution fee. It submits
|
|
26
|
+
no transaction.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { routes } from "@eco-incorp/sauce";
|
|
30
|
+
import type { Address } from "viem";
|
|
31
|
+
|
|
32
|
+
export function buildApprovalRoute(pot: Address, engine: Address, executionFee: bigint) {
|
|
33
|
+
return routes.compileSauceRoute("base", "USDC.approve(Uniswap.UniversalRouter, 1_000_000n);", {
|
|
34
|
+
pot,
|
|
35
|
+
engine,
|
|
36
|
+
value: executionFee,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The destination determines registry addresses. The result contains one entry in
|
|
42
|
+
`calls` and the compiled program in `compiled.bytecode[0]`. Inspect `source` to
|
|
43
|
+
see the SDK's rewrites. This approval program spends no native token, so its call
|
|
44
|
+
value is only the execution fee. A program spending native tokens also needs that
|
|
45
|
+
amount in its call value. For Portal fulfillment, the Pot must be owned by the
|
|
46
|
+
destination Portal's Executor, and `route.nativeAmount` must cover the call values.
|
|
47
|
+
Use [the intent guide](intents.md) to read the fee, derive the correctly owned Pot,
|
|
48
|
+
and add source-chain funding, a reward, and Portal publication calldata.
|
|
49
|
+
|
|
50
|
+
`compileSauceRoute` accepts a bare body or a complete `function main(...)`
|
|
51
|
+
program. Keep route source in the JavaScript syntax accepted by its rewrite
|
|
52
|
+
passes; use raw `compile` for programs that require SauceScript type annotations.
|
|
53
|
+
The raw compiler accepts typed source directly.
|
|
54
|
+
|
|
55
|
+
## Compile a reusable program
|
|
56
|
+
|
|
57
|
+
This Node example compiles a program that sums runtime inputs and checks a floor.
|
|
58
|
+
All modules are in memory, so it needs no filesystem resolver.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { compile } from "@eco-incorp/sauce/compiler";
|
|
62
|
+
|
|
63
|
+
const source = `
|
|
64
|
+
function main(amounts: Uint256[], minimum: Uint256): Uint256 {
|
|
65
|
+
let total = 0n;
|
|
66
|
+
for (let i = 0; i < amounts.length; i = i + 1) {
|
|
67
|
+
total = total + amounts[i];
|
|
68
|
+
}
|
|
69
|
+
require(total >= minimum);
|
|
70
|
+
return total;
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const entry = new TextEncoder().encode(source);
|
|
75
|
+
const artifact = compile({
|
|
76
|
+
target: "evm",
|
|
77
|
+
entry: "main.ts",
|
|
78
|
+
resolve: (path) => (path === "main.ts" ? entry : undefined),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
console.log(artifact.isaRevision); // "stack-compact-v2"
|
|
82
|
+
console.log(artifact.entryEncoding); // "abi-v1"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The amount list and minimum have not been supplied yet. Changing those execution
|
|
86
|
+
values does not require recompilation.
|
|
87
|
+
|
|
88
|
+
### ABI-v1 entry arguments
|
|
89
|
+
|
|
90
|
+
Encode the `main` arguments in declaration order and append them to the compiled
|
|
91
|
+
program. Entry arguments have no function selector.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { encodeAbiParameters, hexToBytes, bytesToHex } from "viem";
|
|
95
|
+
import { v12Pot } from "@eco-incorp/sauce/evm/engine";
|
|
96
|
+
|
|
97
|
+
const args = hexToBytes(
|
|
98
|
+
encodeAbiParameters([{ type: "uint256[]" }, { type: "uint256" }], [[25n, 75n], 100n]),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const payload = new Uint8Array(artifact.bytecode.length + args.length);
|
|
102
|
+
payload.set(artifact.bytecode);
|
|
103
|
+
payload.set(args, artifact.bytecode.length);
|
|
104
|
+
|
|
105
|
+
// Given a compatible engine address:
|
|
106
|
+
// const data = v12Pot.encodeCook(engine, bytesToHex(payload));
|
|
107
|
+
// Send `data` from the Pot owner with value = executionFee + programNativeValue.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The same byte payload is the `program` argument to `Pot.cook(engine, program)`;
|
|
111
|
+
the outer cook call has its own ordinary contract ABI encoding. Supplying
|
|
112
|
+
argument bytes to the compiler as `defines` would produce a different kind of
|
|
113
|
+
program, specialized to those values.
|
|
114
|
+
|
|
115
|
+
### Compact-v1 entry arguments
|
|
116
|
+
|
|
117
|
+
Compile the same source with `compactArgs: true`, then use the returned schema.
|
|
118
|
+
The schema and bytecode belong to the same artifact.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import {
|
|
122
|
+
compile,
|
|
123
|
+
encodeCompactArguments,
|
|
124
|
+
decodeCompactArguments,
|
|
125
|
+
} from "@eco-incorp/sauce/compiler";
|
|
126
|
+
|
|
127
|
+
const compact = compile({
|
|
128
|
+
target: "evm",
|
|
129
|
+
entry: "main.ts",
|
|
130
|
+
resolve: (path) => (path === "main.ts" ? entry : undefined),
|
|
131
|
+
compactArgs: true,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (compact.entryEncoding !== "compact-v1" || !compact.entrySchema) {
|
|
135
|
+
throw new Error("Expected a compact argument schema");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const compactArgs = encodeCompactArguments(compact.entrySchema, [[25n, 75n], 100n]);
|
|
139
|
+
const inspected = decodeCompactArguments(compact.entrySchema, compactArgs);
|
|
140
|
+
// inspected: [[25n, 75n], 100n]
|
|
141
|
+
|
|
142
|
+
const compactPayload = new Uint8Array(compact.bytecode.length + compactArgs.length);
|
|
143
|
+
compactPayload.set(compact.bytecode);
|
|
144
|
+
compactPayload.set(compactArgs, compact.bytecode.length);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
A compact program requires an engine implementing its ISA revision. It does not
|
|
148
|
+
accept an ABI-v1 tail, and an ABI-v1 program does not accept compact arguments.
|
|
149
|
+
Compact encoding often reduces scalar and collection payload size, but runtime
|
|
150
|
+
heap, compute, and transaction limits still apply. Its format does not change
|
|
151
|
+
contract ABI calls performed inside the program.
|
|
152
|
+
|
|
153
|
+
### Encode EVM return values
|
|
154
|
+
|
|
155
|
+
`entryEncoding` and `compactArgs` describe inputs only. To return data for a host
|
|
156
|
+
ABI decoder, encode it explicitly in SauceScript:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
function main(): bytes {
|
|
160
|
+
return abi.encode([1n, 2n, 3n]);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Decode that result as one `uint256[]` with Viem's `decodeAbiParameters`. It is
|
|
165
|
+
160 bytes: an offset, an element count, and three values. The EVM Pot returns
|
|
166
|
+
these bytes directly; do not decode another ABI `bytes` wrapper around them.
|
|
167
|
+
This applies with both ABI-v1 and compact-v1 inputs. SVM has no `abi.encode`
|
|
168
|
+
builtin.
|
|
169
|
+
|
|
170
|
+
Compiler 2.3.0 changed raw EVM scalar-array literal returns: `return [1n, 2n, 3n]`
|
|
171
|
+
now returns three packed 32-byte values without an ABI offset or count. Compiler
|
|
172
|
+
2.2.1 returned heap descriptors for that source. Raw tuple returns still contain
|
|
173
|
+
heap descriptors. Recompilation can therefore change observable return bytes;
|
|
174
|
+
use explicit `abi.encode` when a caller relies on ABI output.
|
|
175
|
+
|
|
176
|
+
## Resolve source modules and ABIs
|
|
177
|
+
|
|
178
|
+
A resolver is synchronous and returns bytes. Missing target-arm candidates must
|
|
179
|
+
return `undefined`; unreadable existing files should fail compilation.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import { readFileSync } from "node:fs";
|
|
183
|
+
import { resolve as resolvePath } from "node:path";
|
|
184
|
+
import { compile } from "@eco-incorp/sauce/compiler";
|
|
185
|
+
|
|
186
|
+
const root = resolvePath("./programs");
|
|
187
|
+
|
|
188
|
+
const artifact = compile({
|
|
189
|
+
target: "evm",
|
|
190
|
+
entry: "main.ts",
|
|
191
|
+
resolve(path) {
|
|
192
|
+
try {
|
|
193
|
+
return readFileSync(resolvePath(root, path));
|
|
194
|
+
} catch (error) {
|
|
195
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
196
|
+
if (code === "ENOENT" || code === "ENOTDIR") return undefined;
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This resolver reads files; it is not an isolation boundary for untrusted source.
|
|
204
|
+
Use an allowlisted virtual module map when applications accept user-supplied
|
|
205
|
+
programs. Fetch any remote modules before the synchronous compile step.
|
|
206
|
+
|
|
207
|
+
For an ABI already held in memory, use raw compiler
|
|
208
|
+
`inlineContracts: [["ERC20", JSON.stringify(abi)]]`. The route API instead accepts
|
|
209
|
+
`contracts: { ERC20: { abi } }` with a parsed ABI and adapts it internally.
|
|
210
|
+
Do not pass the route shape to raw `compile()`.
|
|
211
|
+
|
|
212
|
+
Raw compiler resolution does not automatically locate the SDK's `@sauce/token`
|
|
213
|
+
copy. SDK route compilation resolves that package itself. With raw compilation,
|
|
214
|
+
provide the package's source and neutral entry mapping through your resolver.
|
|
215
|
+
|
|
216
|
+
## Compile for SVM
|
|
217
|
+
|
|
218
|
+
Selecting `target: "svm"` produces an account manifest. For example:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import { compile } from "@eco-incorp/sauce/compiler";
|
|
222
|
+
|
|
223
|
+
const source = new TextEncoder().encode(`
|
|
224
|
+
function main(vault: Account, amount: Uint256): Uint256 {
|
|
225
|
+
return amount;
|
|
226
|
+
}
|
|
227
|
+
`);
|
|
228
|
+
|
|
229
|
+
const artifact = compile({
|
|
230
|
+
target: "svm",
|
|
231
|
+
resolve: (path) => (path === "main.js" ? source : undefined),
|
|
232
|
+
compactArgs: true,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
console.log(artifact.manifest?.accounts);
|
|
236
|
+
// [{ kind: "slot", index: 0, name: "vault", requires: "readonly" }]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The compact root has one field, `amount`; `vault` is attached at account slot 0.
|
|
240
|
+
Resolve and attach accounts in manifest order using the SVM helpers. The manifest
|
|
241
|
+
does not discover an account's address, balance, owner, or liveness.
|
|
242
|
+
|
|
243
|
+
For staged execution, stage the bytes through the Kitchen, finalize the code
|
|
244
|
+
account, then use the Kitchen client or instruction builders. The SVM
|
|
245
|
+
`routes.compileSauceRoute` path drops the manifest and emits a legacy instruction
|
|
246
|
+
incompatible with the released engine. Use raw compilation for account plans
|
|
247
|
+
and follow the [Solana execution guide](solana.md).
|
|
248
|
+
|
|
249
|
+
## Keep compilation reproducible
|
|
250
|
+
|
|
251
|
+
Record the compiler version, target, ISA revision, source/dependency bytes,
|
|
252
|
+
contract ABIs, defines, ambient modules, optimization settings, and argument
|
|
253
|
+
format with an artifact. Keep request arguments separate from the code cache;
|
|
254
|
+
hash the final code-plus-arguments payload when committing to a particular
|
|
255
|
+
execution.
|
|
256
|
+
|
|
257
|
+
The compiler's function cache is enabled by default. It is an optimization of
|
|
258
|
+
lowering, not an application-level bytecode cache or a dependency resolver.
|
|
259
|
+
`cache: false` forces fresh lowering. If an application mutates cached resolver
|
|
260
|
+
inputs, refresh those inputs and use the compiler package's cache controls as
|
|
261
|
+
needed.
|
|
262
|
+
|
|
263
|
+
For canonical settle programs, use the shipped recipe source and the pinned
|
|
264
|
+
verification surface described in [settlement verification](verification.md).
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Create and fund an Eco intent
|
|
2
|
+
|
|
3
|
+
[Documentation](../README.md) · [Routes API](../api/routes.md) · [Nested intents](nested-intents.md)
|
|
4
|
+
|
|
5
|
+
`Base(body, options).reward(reward)` compiles a program for Base and packages it as an Eco intent. `Base.route(...)` has the same behavior. The destination chain selects the compiler target, token addresses, and protocol deployments.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import "@eco-incorp/sauce";
|
|
9
|
+
import type { routes } from "@eco-incorp/sauce";
|
|
10
|
+
|
|
11
|
+
export function buildApprovalIntent(routeOptions: routes.IntentOptions, reward: routes.RewardArg) {
|
|
12
|
+
return Base(() => {
|
|
13
|
+
USDC.approve(Uniswap.UniversalRouter, 1_000_000n);
|
|
14
|
+
}, routeOptions).reward(reward);
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The SDK constructs the intent and transaction requests. Your application supplies deployments, funds the source reward, submits transactions, and arranges fulfillment by a solver.
|
|
19
|
+
|
|
20
|
+
## Source, destination, and funding
|
|
21
|
+
|
|
22
|
+
An intent has two sides:
|
|
23
|
+
|
|
24
|
+
| Field | Meaning |
|
|
25
|
+
| ---------------------------------------- | ----------------------------------------------------------------- |
|
|
26
|
+
| `Base(...)` | Destination chain where the Sauce program executes |
|
|
27
|
+
| `options.portal` | Destination Portal recorded in the route |
|
|
28
|
+
| `options.pot`, `options.engine` | Destination Pot and interpreter used by `cook` |
|
|
29
|
+
| `options.tokens`, `options.nativeAmount` | Destination assets delivered by the solver to the Portal Executor |
|
|
30
|
+
| `reward.source` | Chain where the reward is funded |
|
|
31
|
+
| `options.sourcePortal` | Source Portal used by submission and read helpers |
|
|
32
|
+
| `reward.tokens`, `reward.nativeAmount` | Source assets offered as the reward |
|
|
33
|
+
| `reward.creator`, `reward.prover` | Creator and prover recorded in the reward |
|
|
34
|
+
|
|
35
|
+
Destination route assets initially arrive in the **Executor**. The Sauce program executes in the **Pot**. For an ERC-20 program that spends solver-delivered assets, prepend a token transfer from the Executor to the Pot. Native value reaches the Pot through `execution.value`; declaring `nativeAmount` alone does not forward it into `cook`.
|
|
36
|
+
|
|
37
|
+
Every EVM `cook` must receive the Kitchen's current `executionFee()` in native wei, including ERC-20-only programs. Set `execution.value` to **fee + native assets the program needs**, and budget `nativeAmount` for the route's calls. The Pot remits the fee before executing; its existing balance cannot replace this payment. Nested transfer programs add another cook and fee. Read the fee before building; an increase before fulfillment can make the route revert.
|
|
38
|
+
|
|
39
|
+
Deadlines are absolute Unix timestamps in seconds. `route.deadline` limits destination fulfillment. `reward.deadline` allows source refunds when no valid proof exists; leave time for fulfillment and proof delivery before it.
|
|
40
|
+
|
|
41
|
+
## Prepare the destination Pot
|
|
42
|
+
|
|
43
|
+
For direct Portal routes, the Pot's owner must be the destination **`Portal.executor()`**. A user-owned or solver-owned Pot rejects the Executor's call with `NotOwner()`.
|
|
44
|
+
|
|
45
|
+
Use [evm-execution.ts](../examples/evm-execution.ts) on the destination chain to read the Executor, resolve the current Kitchen and engine, predict the Pot, and quote the execution fee. If the Pot is absent, the helper returns a deployment request. Send it, wait for a successful receipt, and run the helper again. Only use a result with `ready: true`, which confirms code is present and checks the Pot's `owner()` and `kitchen()`.
|
|
46
|
+
|
|
47
|
+
Deployment helpers return release addresses; the historical chain roster does not attest current release liveness.
|
|
48
|
+
|
|
49
|
+
An Executor-owned Pot can be invoked by other valid Portal routes. Deliver assets, use them, and return leftovers **within the same fulfillment**; do not leave user funds or standing allowances in it. Source reward escrow belongs in the intent vault. These caller and custody rules follow [Eco Routes' Executor calls](https://github.com/eco/eco-routes/blob/49de5bb10a5eb00509b21702df6cf54de448ee11/contracts/Executor.sol#L50-L74) and [destination fulfillment](https://github.com/eco/eco-routes/blob/49de5bb10a5eb00509b21702df6cf54de448ee11/contracts/Inbox.sol#L225-L271).
|
|
50
|
+
|
|
51
|
+
## Build a Base USDC transfer intent
|
|
52
|
+
|
|
53
|
+
This function takes deployment and reward configuration as inputs. It delivers one USDC on Base to `recipient` and offers the configured source-token reward. It only constructs data.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { token, type routes } from "@eco-incorp/sauce";
|
|
57
|
+
|
|
58
|
+
interface TransferIntentConfig {
|
|
59
|
+
pot: routes.AddressInput;
|
|
60
|
+
engine: routes.AddressInput;
|
|
61
|
+
executionFee: bigint;
|
|
62
|
+
destinationPortal: routes.AddressInput;
|
|
63
|
+
baseUsdc: routes.AddressInput;
|
|
64
|
+
recipient: routes.AddressInput;
|
|
65
|
+
source: routes.ChainRef;
|
|
66
|
+
sourcePortal: routes.AddressInput;
|
|
67
|
+
rewardToken: routes.AddressInput;
|
|
68
|
+
rewardAmount: bigint;
|
|
69
|
+
creator: routes.AddressInput;
|
|
70
|
+
prover: routes.AddressInput;
|
|
71
|
+
routeDeadline: bigint;
|
|
72
|
+
rewardDeadline: bigint;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function buildBaseTransfer(config: TransferIntentConfig) {
|
|
76
|
+
const amount = 1_000_000n;
|
|
77
|
+
const delivery = token.Token.transfer({
|
|
78
|
+
token: config.baseUsdc,
|
|
79
|
+
to: config.pot,
|
|
80
|
+
amount,
|
|
81
|
+
}).toCall();
|
|
82
|
+
|
|
83
|
+
return Base("USDC.transfer(RECIPIENT, AMOUNT);", {
|
|
84
|
+
execution: { pot: config.pot, engine: config.engine, value: config.executionFee },
|
|
85
|
+
nativeAmount: config.executionFee,
|
|
86
|
+
portal: config.destinationPortal,
|
|
87
|
+
sourcePortal: config.sourcePortal,
|
|
88
|
+
deadline: config.routeDeadline,
|
|
89
|
+
tokenDefines: { USDC: config.baseUsdc },
|
|
90
|
+
defines: { RECIPIENT: BigInt(config.recipient), AMOUNT: amount },
|
|
91
|
+
tokens: [{ token: config.baseUsdc, amount }],
|
|
92
|
+
precalls: [delivery],
|
|
93
|
+
}).reward({
|
|
94
|
+
source: config.source,
|
|
95
|
+
creator: config.creator,
|
|
96
|
+
prover: config.prover,
|
|
97
|
+
deadline: config.rewardDeadline,
|
|
98
|
+
tokens: [{ token: config.rewardToken, amount: config.rewardAmount }],
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use an EVM source when using the Portal submission helpers below; they generate EVM calldata and reject an SVM source. The route model can represent SVM destinations, but the current SVM route emitter is incompatible with the released gated engine.
|
|
104
|
+
|
|
105
|
+
`executionFee` is the destination Kitchen quote obtained above. The source reward must separately compensate the solver for delivery, fees, and gas; the SDK does not price it.
|
|
106
|
+
|
|
107
|
+
The explicit `tokenDefines` ensures that the token transferred into the Pot matches the token used by the program. To use the SDK's default Base USDC, resolve it with `routes.registryTokenAddress(Base.chain, "USDC")` and check the result before building. That lookup returns a `bigint`; convert it to a 20-byte hex address with Viem's `toHex(value, { size: 20 })` for intent token fields, as in the [complete example](../examples/first-intent.ts).
|
|
108
|
+
|
|
109
|
+
## Callbacks are compiled source
|
|
110
|
+
|
|
111
|
+
A callback is read with `Function.prototype.toString()`, parsed, and compiled as SauceScript. It is never invoked as a JavaScript callback and captures no outer variables. An `amount` in surrounding JavaScript does not become a value inside the program.
|
|
112
|
+
|
|
113
|
+
Use literals for self-contained callbacks, or source strings plus `defines` for application inputs, as above. Defines accept scalar `bigint`, `number`, or `boolean` values; use `bigint` for addresses and token quantities. `compile.defines` overrides `options.defines`, which overrides chain defaults. Local declarations in the program take precedence over ambient names.
|
|
114
|
+
|
|
115
|
+
Callbacks must be synchronous and take no parameters. Bound/native functions, generators, and async functions are rejected. If a bundler transforms function source, ship an explicit SauceScript string or source asset. A TypeScript type declaration does not create a captured value.
|
|
116
|
+
|
|
117
|
+
`Base(...)` adds the SDK's default ambient token module, enabling function forms such as `approve(tokenAddress, spender, amount)`. Direct `compileSauceRoute(...)` does not automatically add that ambient module. The token and protocol member syntax described in [protocols and tokens](protocols-and-tokens.md) is a separate rewrite.
|
|
118
|
+
|
|
119
|
+
## Submit the source reward
|
|
120
|
+
|
|
121
|
+
For a built intent, prepare the source-chain requests:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import type { routes } from "@eco-incorp/sauce";
|
|
125
|
+
|
|
126
|
+
export function sourceTransactions(built: routes.BuiltIntent) {
|
|
127
|
+
return [...built.approvals(), built.publishAndFundCalldata(false)];
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Each request is `{ to, data, value }`. Send them from the intended funder on `built.source`, in order, waiting for successful approvals before funding. ERC-20 approvals authorize the **source Portal**, which pulls tokens into the intent vault. Native rewards are included as transaction `value`. `.approvals()` covers only the root reward, even when the intent has children.
|
|
132
|
+
|
|
133
|
+
`false` selects full funding; use partial funding only when the application deliberately supports it. `publishCalldata()` publishes without funding, and `fundCalldata(allowPartial)` funds separately. These methods do no RPC and send no transactions.
|
|
134
|
+
|
|
135
|
+
Set `sourcePortal` while building, or pass `{ portal: sourcePortal }` to each helper. The destination `portal` is never silently reused as the submission target.
|
|
136
|
+
|
|
137
|
+
## Inspect hashes, vaults, and status
|
|
138
|
+
|
|
139
|
+
`built.hash()` returns `intentHash`, `routeHash`, and `rewardHash`. `built.intent` or `.root()` returns the normalized intent. `.intents()` returns the root and descendants in preorder; it does not imply they execute atomically across chains.
|
|
140
|
+
|
|
141
|
+
Read helpers return a request plus its decoder:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import type { routes } from "@eco-incorp/sauce";
|
|
145
|
+
|
|
146
|
+
export async function readVault(
|
|
147
|
+
built: routes.BuiltIntent,
|
|
148
|
+
ethCall: (request: Pick<routes.PortalReadCall<routes.Hex>, "to" | "data">) => Promise<routes.Hex>,
|
|
149
|
+
) {
|
|
150
|
+
const request = built.vaultCall();
|
|
151
|
+
const result = await ethCall({ to: request.to, data: request.data });
|
|
152
|
+
return request.decode(result);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Execute this read on the source chain. `hashCall()` cross-checks hashes against the Portal; `fundedCall()` checks funding. `predictVault({ portal, vaultInitCodeHash })` provides an offline CREATE2 prediction when you have verified the deployed vault configuration.
|
|
157
|
+
|
|
158
|
+
The current SVM route emitter produces an older direct-engine instruction and cannot target the released gated engine. Code staging alone does not make those routes executable. See the [Solana guide](solana.md) for supported Kitchen execution and route-helper limitations.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Nested intents
|
|
2
|
+
|
|
3
|
+
[Documentation](../README.md) · [Create an intent](intents.md) · [Routes API](../api/routes.md)
|
|
4
|
+
|
|
5
|
+
`.nest(...)` adds a child intent whose source is the parent's destination. When the parent fulfills, its route funds the child's reward. A solver can fulfill that child in a later transaction on the child's destination chain. Each leg is a separate intent; a failure on a later chain does not roll back a completed parent.
|
|
6
|
+
|
|
7
|
+
Use the thunk form to receive the parent's destination as the child's source:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import "@eco-incorp/sauce";
|
|
11
|
+
import type { routes } from "@eco-incorp/sauce";
|
|
12
|
+
|
|
13
|
+
export function buildCascade(
|
|
14
|
+
parentOptions: routes.IntentOptions,
|
|
15
|
+
childOptions: routes.IntentOptions,
|
|
16
|
+
parentReward: routes.SauceRewardSpec,
|
|
17
|
+
childReward: Omit<routes.SauceRewardSpec, "source">,
|
|
18
|
+
executionFee: bigint,
|
|
19
|
+
) {
|
|
20
|
+
return Base("return 1n;", parentOptions)
|
|
21
|
+
.nest(
|
|
22
|
+
(on) =>
|
|
23
|
+
Arbitrum("return 1n;", childOptions).reward({
|
|
24
|
+
...childReward,
|
|
25
|
+
source: on,
|
|
26
|
+
}),
|
|
27
|
+
{ requireCap: true, executionFee },
|
|
28
|
+
)
|
|
29
|
+
.reward(parentReward);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The example isolates composition: replace the no-op bodies with your programs. Each options object supplies its destination execution, Portal, deadline, and body cook fee. `executionFee` is the Base Kitchen quote for the additional transfer cook; include it in `parentOptions.nativeAmount` alongside the parent body and any native reward forwarding. Configure `childOptions.sourcePortal` as the Portal on Base. Deliver or earn the child's reward assets in the parent fulfillment before the funding step; see [the Executor-to-Pot example](intents.md#build-a-base-usdc-transfer-intent).
|
|
34
|
+
|
|
35
|
+
You can also pass an already built child. The SDK checks that its source matches the parent's destination. Both forms produce static children: reward amounts and other hashed fields are fixed before execution. A runtime balance can determine how much is transferred to a child's vault; it does not rewrite the child's reward or hash.
|
|
36
|
+
|
|
37
|
+
## Default: transfer to the child vault
|
|
38
|
+
|
|
39
|
+
The fluent API defaults to `via: "transfer"` and `position: "after"`. After the parent program, another Sauce program reads the Pot's balance and transfers assets to the child vault. By default each transfer is capped at the declared child reward amount. Deliver or earn those assets in the same parent fulfillment; do not leave user funds in the Executor-owned Pot between transactions.
|
|
40
|
+
|
|
41
|
+
| Option | Default | Effect |
|
|
42
|
+
| --------------------- | ----------- | ------------------------------------------------------------------------------------ |
|
|
43
|
+
| `clamp` | `true` | Transfer at most the declared child reward amount |
|
|
44
|
+
| `requireCap` | `false` | If enabled, revert the parent fulfillment when the Pot cannot cover the cap |
|
|
45
|
+
| `native` | `"balance"` | Include the native leg; `"skip"` omits it |
|
|
46
|
+
| `fundNativeFromRoute` | `false` | Forward the child's declared native reward from route value into the transfer `cook` |
|
|
47
|
+
| `executionFee` | `0n` | Additional wei for the merged transfer cook's Kitchen fee |
|
|
48
|
+
| `position` | `"after"` | Run after the parent body; `"before"` runs before it |
|
|
49
|
+
|
|
50
|
+
With default `requireCap: false`, a short Pot balance can leave the child underfunded while the parent succeeds. Select `requireCap: true` when full child funding must be a condition of parent fulfillment. With multiple children, transfers consume balances sequentially.
|
|
51
|
+
|
|
52
|
+
Read the current Kitchen fee and supply `executionFee` for every transfer child. Children merged into one position share one fee and must specify the same value; separate `"before"` and `"after"` cooks each pay. The generated call adds this fee to any `fundNativeFromRoute` reward amounts, so the fee does not reduce the child's funding. Zero works only on a fee-free Kitchen. When a nonzero fee is supplied, the builder checks that `nativeAmount` covers the calls' combined value, excluding publication funding explicitly marked `fundedByBody`.
|
|
53
|
+
|
|
54
|
+
The default vault resolution calls the Portal from the generated program. Alternatively, pass `vault` after verifying it with `child.vaultCall()`, or pass `vaultConfig` containing the verified `vaultInitCodeHash` for an offline prediction. Explicit `vault` takes precedence over `vaultConfig`.
|
|
55
|
+
|
|
56
|
+
Transfer funding does not emit a child `IntentPublished` event. Share the built child with your solver or intent-distribution system; an indexer relying only on publication events cannot discover it from such an event.
|
|
57
|
+
|
|
58
|
+
## Opt in to publication and Portal funding
|
|
59
|
+
|
|
60
|
+
Use `{ via: "publishAndFund" }` to insert ERC-20 approvals followed by a Portal `publishAndFund` call in the parent route. This mode adds no Sauce cook or Kitchen fee. Those calls run as the Executor, so child reward assets must remain in the Executor or return there from the parent program before publication. `allowPartial` defaults to `false`. Budget the parent's `nativeAmount` for its cook fee, native program spending, and the child's native reward.
|
|
61
|
+
|
|
62
|
+
The builder checks that declared parent route assets cover child rewards for this mode. Set `fundedByBody: true` only when the parent program actually earns or supplies those assets and makes them available to the Executor. That option skips a static coverage check; it does not move assets.
|
|
63
|
+
|
|
64
|
+
`allowPartial` and `fundedByBody` belong to publication mode. `vault`, `vaultConfig`, `clamp`, `requireCap`, `native`, `fundNativeFromRoute`, `executionFee`, `pot`, `engine`, and `compile` belong to transfer mode. Mixing mode-specific options throws.
|
|
65
|
+
|
|
66
|
+
## Ordering and constraints
|
|
67
|
+
|
|
68
|
+
The current call order is:
|
|
69
|
+
|
|
70
|
+
1. Parent `precalls`.
|
|
71
|
+
2. Children positioned `"before"`.
|
|
72
|
+
3. The parent's compiled program.
|
|
73
|
+
4. Children positioned `"after"`.
|
|
74
|
+
|
|
75
|
+
Within either position group, publication-mode calls precede the merged transfer program. Transfer children execute in their `.nest()` order inside that program. Do not depend on an interleaving of different funding modes. Transfer children merged into one position must use the same Pot, engine, and execution fee.
|
|
76
|
+
|
|
77
|
+
The builder enforces an EVM parent destination, matching parent-destination/child-source chains, and child route and reward deadlines at least as late as the parent route deadline. The codec accepts SVM children, but the [built-in SVM Sauce route is incompatible with the released engine](solana.md#svm-destinations-in-eco-intents). Duplicate direct-child intent hashes are rejected; use distinct salts for otherwise identical children. The default salt is zero, so separate construction alone does not create a distinct intent.
|
|
78
|
+
|
|
79
|
+
`.intents()` lists the root and descendants in preorder, while `.children` lists direct children. Store the whole cascade if your application must distribute every leg. The root's approval helpers fund only the root; child funding is part of parent fulfillment.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Protocols and tokens
|
|
2
|
+
|
|
3
|
+
[Documentation](../README.md) · [Quick start](quick-start.md) · [Coverage](../api/coverage.md)
|
|
4
|
+
|
|
5
|
+
Within an EVM route, token symbols and protocol names resolve against the destination chain:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
USDC.approve(Uniswap.UniversalRouter, 1_000_000n);
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`USDC` resolves to the chain's registered token address. `Uniswap.UniversalRouter` resolves to the registered contract address when used as an argument, and exposes its ABI methods when used as a call target. Amounts are integers in the token's smallest units; the SDK does not infer decimal conversions.
|
|
12
|
+
|
|
13
|
+
## Compile a global call
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { routes } from "@eco-incorp/sauce";
|
|
17
|
+
|
|
18
|
+
export function compileApproval(pot: routes.AddressInput, engine: routes.AddressInput) {
|
|
19
|
+
return routes.compileSauceRoute("base", "USDC.approve(Uniswap.UniversalRouter, 1_000_000n);", {
|
|
20
|
+
pot,
|
|
21
|
+
engine,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The returned `calls` encode an EVM Pot `cook`; this function does not submit a transaction. The same body can be a callback to `Base(() => { ... }, options).reward(...)`; see [creating intents](intents.md).
|
|
27
|
+
|
|
28
|
+
The supported Universal Router spelling is `UniswapV4.UniversalRouter`, or the unambiguous family alias `Uniswap.UniversalRouter`. `UniswapV3.UniversalRouter` is not registered. The router's `execute` method takes the arguments declared by its ABI; the namespace does not construct swap commands or select a route for you.
|
|
29
|
+
|
|
30
|
+
## Which names exist?
|
|
31
|
+
|
|
32
|
+
The protocol catalog, contract descriptors, token registry, and SVM venue registry provide different coverage. A catalog entry does not automatically provide a callable contract namespace. [Coverage](../api/coverage.md) lists what is registered.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { contracts, getProtocol, routes } from "@eco-incorp/sauce";
|
|
36
|
+
|
|
37
|
+
const protocol = getProtocol("uniswap-v4");
|
|
38
|
+
const descriptors = contracts.listDescriptors();
|
|
39
|
+
const router = contracts.on("base").UniswapV4.UniversalRouter;
|
|
40
|
+
const baseTokens = routes.knownTokenSymbols(routes.chain("base").chain);
|
|
41
|
+
|
|
42
|
+
console.log(protocol, descriptors.length, router.available, baseTokens);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Contract accessors expose `address`, `available`, `chains`, `abi`, `methods`, and `coverage`. A namespace's presence does not establish deployment on every chain. A call to a registered contract with no deployment on the selected chain fails during construction or compilation.
|
|
46
|
+
|
|
47
|
+
Versioned names are explicit: `UniswapV2.Factory` and `UniswapV3.Factory` select different descriptors. A family alias only resolves a contract name when it identifies one descriptor; `Uniswap.Factory` is ambiguous and fails. A protocol with a single default contract also permits the shorter `Protocol.method(...)` form.
|
|
48
|
+
|
|
49
|
+
## Token methods and balances
|
|
50
|
+
|
|
51
|
+
These are SauceScript route-body fragments:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
USDC.approve(Uniswap.UniversalRouter, 1_000_000n);
|
|
55
|
+
let available = USDC.balanceOf(self);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For a token absent from the registry, use `Token(address)`:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
Token(0x1111111111111111111111111111111111111111n).approve(Uniswap.UniversalRouter, 1_000_000n);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Replace that illustrative address with your ERC-20. The wrapper accepts a literal, a local variable, or a compile-time define. The token member syntax supports `transfer`, `approve`, and `balanceOf`. Use an explicit ABI binding for additional ERC-20 methods. The shorthand `self.USDC` reads the executing contract's balance; `USDC.balanceOf(self)` makes the same intent explicit. `self` refers to the executing contract, which is the Pot in the EVM route path.
|
|
65
|
+
|
|
66
|
+
Use `tokenDefines` on an intent to add or override symbols, or `tokens` on `compileSauceRoute`'s compile options. Overrides merge per symbol with the registry. These maps contain **addresses**; `IntentOptions.tokens` instead declares the **token amounts** a solver must deliver for the route.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { routes } from "@eco-incorp/sauce";
|
|
70
|
+
|
|
71
|
+
export function compileCustomTokenApproval(
|
|
72
|
+
pot: routes.AddressInput,
|
|
73
|
+
engine: routes.AddressInput,
|
|
74
|
+
asset: routes.AddressInput,
|
|
75
|
+
) {
|
|
76
|
+
return routes.compileSauceRoute(
|
|
77
|
+
"base",
|
|
78
|
+
"ASSET.approve(Uniswap.UniversalRouter, 100n);",
|
|
79
|
+
{ pot, engine },
|
|
80
|
+
{ tokens: { ASSET: asset } },
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Custom symbol names work in source strings. The package's generated TypeScript declarations cover registered names; applications authoring callbacks with additional names must supply their own declarations. A declaration provides a type, while the token map provides the address used by the compiler.
|
|
86
|
+
|
|
87
|
+
## Route source and host JavaScript
|
|
88
|
+
|
|
89
|
+
Importing the package installs real chain globals such as `Base` and `Solana`, plus the host `Token` builder. The package also supplies TypeScript declarations for bare route names such as `USDC` and `Uniswap`. Those bare names are **route syntax**: there is no host JavaScript `USDC` or `Uniswap` object to execute.
|
|
90
|
+
|
|
91
|
+
Use a side-effect import, `import "@eco-incorp/sauce"`, when relying on installed globals. A type-only import does not install them, and TypeScript can erase a named import used only for types.
|
|
92
|
+
|
|
93
|
+
Outside a route, `Base.UniswapV4.UniversalRouter` or `contracts.on("base").UniswapV4.UniversalRouter` is a real host accessor. Its methods return inert `ContractCall` objects. A call can provide encoded `data`, `.toCall()` for `{ target, data, value }`, or `.toSauceScript()` for source and import paths. Host accessor methods currently have the static type `unknown`; use an explicit signature when invoking one in TypeScript:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { contracts } from "@eco-incorp/sauce";
|
|
97
|
+
|
|
98
|
+
const router = contracts.on("base").UniswapV4.UniversalRouter;
|
|
99
|
+
const execute = router.execute as (
|
|
100
|
+
commands: `0x${string}`,
|
|
101
|
+
inputs: readonly `0x${string}`[],
|
|
102
|
+
deadline: bigint,
|
|
103
|
+
) => contracts.ContractCall;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
That declaration does not build or send a swap. Supply valid Universal Router commands before calling it. For ordinary ERC-20 operations from host code, the typed `token.Token.transfer({ ... })` and `.approve({ ... })` builders are usually more convenient; see [builders and actions](builders-and-actions.md).
|
|
107
|
+
|
|
108
|
+
## Chain selection and ABI bindings
|
|
109
|
+
|
|
110
|
+
A bare protocol reference uses the enclosing destination. A qualified reference such as `Ethereum.UniswapV4.UniversalRouter` selects an address from Ethereum's registry. It does **not** move execution to Ethereum: the resulting call still executes on the enclosing route's chain. Use separate intents and [nesting](nested-intents.md) for cross-chain execution. Set `compile.accessors.chainScope` to restrict explicitly qualified chain names when needed.
|
|
111
|
+
|
|
112
|
+
Route rewriting rejects missing methods, ambiguous aliases, missing deployments, and descriptors marked with widened ABI types. A widened ABI can produce a selector different from the deployed contract. Inspect `coverage` and use the exact ABI for that deployment; opting past the check does not repair the ABI.
|
|
113
|
+
|
|
114
|
+
To call a contract outside the descriptor registry, supply an ABI binding through `compile.contracts` or a resolved ABI import, then call `Binding.at(address).method(...)` in source. See [compiler integration](../api/compiler.md). Host `.at(address)` rebinding and SauceScript ABI `.at(address)` are separate APIs; the global protocol rewrite does not provide arbitrary `.at(...)` chains.
|
|
115
|
+
|
|
116
|
+
The token and EVM protocol rewrites apply to EVM routes. Solana uses account parameters and venue adapters, covered in the [Solana guide](solana.md).
|