@eco-incorp/sauce/verify validates an EVM settlement payload against the pinned settle program (SETTLE_WIRE: compiler 2.3.0, 356 bytes) and decodes its runtime argument tail. It depends on viem only, so it runs in browsers and edge runtimes without loading the compiler.
This page covers sdk/dist/verify/decode.d.ts.
Generated from the type declarations shipped in
@eco-incorp/sauce 0.99.4. Each entry shows the package authors’ JSDoc and declaration for this SDK version. For runtime compatibility and deployed addresses, see architecture and deployments.decodeSettleProgram
Function · sdk/dist/verify/decode.d.ts
Decode a settle payload back into (tokens, minOut, recipient). STRICT: rejects a non-canonical
tokens pointer, a tail longer or shorter than the token count implies, an address word with
dirty upper bytes, and a zero recipient - see §4 for why. Throws SettleDecodeError (carrying a
stable .code) on any of the failures in SettleFailureCode.
This is a STRUCTURAL decode: it proves the payload is 356 bytes || a canonical argument tail.
It does NOT prove those 356 bytes are the settle program you audited - any 356-byte prefix
passes. Use validateSettleProgram when that matters, which is nearly always.
validateSettleProgram
Function · sdk/dist/verify/decode.d.ts
decodeSettleProgram plus the authenticity pin: the payload’s 356 program bytes must hash to
SETTLE_WIRE.PROGRAM_HASH. Because the arguments no longer live inside the program, a match
means the payload runs the audited recipes/settle.sauce.ts and nothing else - the whole
question, not the structural half.
Throws SettleDecodeError with code PROGRAM_HASH when the program is a different 356 bytes,
and whatever decodeSettleProgram would throw otherwise.
accepted overrides the pin. The dependency is a caret range and SETTLE_WIRE documents that a
codegen change is answered by RE-PINNING - at which point every intent already on chain carries the
OLD program, and a single hard-coded pin makes all of them unverifiable. Pass the pins you accept
(the current one plus any historical ones) to keep those payloads readable across a re-pin.
A PIN IS A HASH AND A LENGTH, which is why a bare hash is not enough. The payload is split into
program and argument tail at a byte offset, before any hash is computed - so if a re-pin changes the
program’s length (the likely outcome of a codegen change), splitting a historical payload at the NEW
length yields a wrong program and a truncated tail, and it dies as ARGS_TRUNCATED without any hash
ever being compared. Each entry may be { hash, bytes }; a bare Hex means the CURRENT
SETTLE_WIRE.PROGRAM_BYTES and is only safe for a same-length re-pin. Candidates are tried in order
and the first whose program hash matches wins. Defaults to the current pin.
encodeSettleProgram
Function · sdk/dist/verify/decode.d.ts
§5 of the wire spec - the cheaper, EQUIVALENT alternative to scan-and-check: when the caller
already knows the intended (tokens, minOut, recipient) (the normal case - they asked for the
swap), encode them directly and memcmp/hash-compare the whole payload rather than decoding.
Given §4 this produces the UNIQUE canonical encoding, so encodeSettleProgram(...) equals
payload exactly when validateSettleProgram(payload) succeeds with those values - in one
comparison, with no parser exposed to a hostile input at all. This is the recommended
non-TypeScript (Solidity/Go/Python) implementation, where a payload is bytes and the comparison is
a plain memcmp.
THE EQUIVALENCE IS OVER BYTES, NOT OVER HEX SPELLINGS. validateSettleProgram accepts any spelling
of the same bytes - uppercase, a 0X prefix, no prefix at all - while this returns canonical
lowercase 0x. So validateSettleProgram(p.toUpperCase()) succeeds where
encodeSettleProgram(...) === p.toUpperCase() is false. A string === is only equivalent once
both sides are in canonical spelling; normalize first, or compare decoded bytes.
programHex is supplied by the caller - pass the program you compiled yourself. There is
deliberately no default: the bytes you compare against should be ones you derived, not ones
shipped alongside the claim they support. SETTLE_WIRE.PROGRAM_HASH is what to check them
against once you have them.
parseSettleProgram
Function · sdk/dist/verify/decode.d.ts
Best-effort single left-to-right pass - never throws. This is the shared engine both
decodeSettleProgram (throws on parse.fatal) and bestEffortDecode (keeps whatever DID
parse even when a later stage failed) run on top of.
bestEffortDecode
Function · sdk/dist/verify/decode.d.ts
Decode whenever the shape is well-formed enough to name a full (tokens, minOut, recipient) - even when a check that does not block reading (a trailing-slack tail, a dirty address word, a
zero recipient) would make decodeSettleProgram throw - so a rejected payload’s decoded intent is
still visible to a caller debugging the rejection.
DECLINES wherever the report would not match execution - the whole point being that on a hostile
payload, reporting nothing beats reporting a wrong answer confidently. Two cases:
- a non-canonical tokens pointer: the engine follows it, this reads at the canonical offset, so the two name different TOKENS.
- a dirty RECIPIENT word:
recipientis masked here, but the engine does not mask that word (§4), so execution addresses a different 256-bit key than the masked answer would suggest.
SettleDecodeError
Class · sdk/dist/verify/decode.d.ts
DecodedSettleProgram
Interface · sdk/dist/verify/decode.d.ts
ProgramPin
Interface · sdk/dist/verify/decode.d.ts
An accepted program: its keccak256 AND the byte length the payload splits at. Both are needed - the split happens before any hash is computed, so a historical pin whose program was a different
length cannot be matched by hash alone.
SettleFailureCode
Type · sdk/dist/verify/decode.d.ts
Stable failure codes - safe to switch on. Every one is a REAL rejection this decoder makes.
BREAKING vs the v1 grammar: eight codes are GONE, because the defects they named cannot occur on a
fixed-width wire. NON_MINIMAL_PUSH, TRUNCATED_PUSH, TRUNCATED_MINOUT, TRUNCATED_RECIPIENT
and OVERSIZE_ADDRESS all described a variable-length PUSH prologue that no longer exists;
ARITY_MISMATCH and NOT_SETTLE_SHAPED described shape inference the whole-program hash now
settles outright; BODY_LENGTH described a body slice that is no longer a separate region. Their
uniqueness role is taken by §4’s canonicality rules and PROGRAM_HASH. Only EMPTY and
ZERO_RECIPIENT carry over. A consumer with an exhaustive switch on the old union will compile
and silently stop matching, which is why this ships as a breaking change.
SettleParse
Interface · sdk/dist/verify/decode.d.ts
Internal parse result - used by both the throwing decodeSettleProgram and the non-throwing
bestEffortDecode, which keeps partial state past a failure.
Address20
Type · sdk/dist/verify/decode.d.ts
A 20-byte address rendered as lowercase 0x-hex - deliberately NOT viem’s Address branded
type: callers that want an EIP-55 checksum should getAddress() it themselves.
