Proofs
A Sepolia PortfolioSignal is an observation. Affest does not rebalance from a log, a database row, or an MCP reply.
The event
PortfolioSignalEmitter.emitSignal on Sepolia emits:
event PortfolioSignal( bytes32 indexed signalId, address indexed user, address indexed asset, uint256 amount, uint8 signalType );
The source contract rejects zero assets, zero amounts, and duplicate signal IDs. The worker discovers the log and reads the source receipt before asking Attestcoin for a proof. Sepolia is Attestcoin chain key 1.
Proof lifecycle
- The Sepolia source port finds the log and confirms receipt status
0x1. - The attestation port checks the official attested-height endpoint.
- Once the block is attested,
@gluwa/usc-sdk@0.18.0builds a proof. - TypeScript validates the proof-builder shape:
txBytes, continuity proof, and Merkle proof. - The executor supplies that proof to
AffestAttestationVerifier. - The verifier calls
INativeQueryVerifier.verifyAndEmiton the BlockProver precompile at0x0000000000000000000000000000000000000FD2, decodes the returned transaction, and requires receipt status 1. - It then checks emitter, event signature, log index, user, asset, minimum amount, and signal type.
Only after those checks can AffestStrategyManager record an execution against vault limits.
Replay
The verifier derives a query ID from (chainKey, blockHeight, txIndex) and a processed-event key from (queryId, receiptLogIndex). A second submission of the same source receipt log reverts with EventAlreadyProcessed.
What you will see
Rebalance now on the strategy page does not discretionary-swap. It tells you to wait for a verified proof. Pause and resume are on-chain and do not need a proof. Proof status on the strategy page tracks the source observation, not a fake fill.
Deployed emitter and verifier addresses are on Chains and contracts.