Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8b553da
refactor: remove unused CreateIntentConfigurationWithTimedRefundSapient
ScreamingHawk Jul 27, 2026
195627c
feat: add optional payload gate leaf to CreateIntentTree/CreateIntent…
ScreamingHawk Jul 27, 2026
4b4377d
feat: gate sapient signer leaves behind the payload gate too
ScreamingHawk Jul 29, 2026
100e352
fix: skip the calls gate when there are no call batches
ScreamingHawk Jul 29, 2026
26a73e4
fix: cap payloadGateLeaf to weight 1 in wrapPayloadGate
ScreamingHawk Jul 29, 2026
1f1286b
refactor: merge calls and sapient behind a single payload gate
ScreamingHawk Jul 29, 2026
80db92b
fix: reject WalletConfigTreeNestedLeaf in leafWeight
ScreamingHawk Jul 30, 2026
680c9dd
fix: match full signer identity in BuildIntentConfigurationSignature
ScreamingHawk Jul 30, 2026
ed9047d
feat: thread payloadGateLeafNode through GetIntentConfigurationSignature
ScreamingHawk Jul 30, 2026
df27a83
fix: embed supplied signatures deterministically in intent config sig…
ScreamingHawk Jul 30, 2026
6237ebe
refactor: replace optional leaf params with functional options
ScreamingHawk Jul 30, 2026
9e09633
fix: ignore nil IntentConfigOption values
ScreamingHawk Jul 30, 2026
52d9fc6
fix: reject payload gate signer among gated leaves
ScreamingHawk Jul 31, 2026
5560a42
fix: reject payload-matching leaves as payload gate
ScreamingHawk Jul 31, 2026
4cde50e
fix: reject typed-nil payload gate leaves
ScreamingHawk Jul 31, 2026
18cf2c0
refactor: rename payloadGate to gate
ScreamingHawk Jul 31, 2026
a512cd1
fix: reject gate signer at any depth in gated subtree
ScreamingHawk Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 78 additions & 15 deletions intent_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,20 +186,60 @@ func CreateAnyAddressSubdigestTree(calls []*v3.CallsPayload) ([]v3.WalletConfigT
return leaves, nil
}

func createIntentTree(mainSigner common.Address, calls []*v3.CallsPayload, additionalLeaves ...v3.WalletConfigTree) (*v3.WalletConfigTree, error) {
// wrapPayloadGate requires both payloadGateLeaf and protectedLeaves' own threshold to be
// satisfied (weight 1 each, gate threshold 2), so withholding payloadGateLeaf's signature
// blocks protectedLeaves regardless of their own weight. payloadGateLeaf must carry weight
// 1. Safe to call more than once with the same payloadGateLeaf: one signature satisfies
// every occurrence.
func wrapPayloadGate(payloadGateLeaf v3.WalletConfigTree, protectedLeaves ...v3.WalletConfigTree) v3.WalletConfigTree {
inner := &v3.WalletConfigTreeNestedLeaf{
Weight: 1,
Threshold: 1,
Tree: v3.WalletConfigTreeNodes(protectedLeaves...),
}
gate := v3.WalletConfigTreeNodes(payloadGateLeaf, inner)
return &v3.WalletConfigTreeNestedLeaf{
Weight: 1,
Threshold: 2,
Comment thread
ScreamingHawk marked this conversation as resolved.
Outdated
Tree: gate,
}
}

func createIntentTree(
mainSigner common.Address,
calls []*v3.CallsPayload,
payloadGateLeafNode v3.WalletConfigTree,
sapientSignerLeafNode v3.WalletConfigTree,
) (*v3.WalletConfigTree, error) {
// Create the subdigest leaves from the batched transactions.
leaves, err := CreateAnyAddressSubdigestTree(calls)
subdigestLeaves, err := CreateAnyAddressSubdigestTree(calls)
if err != nil {
return nil, err
}

for _, leaf := range additionalLeaves {
if leaf != nil {
leaves = append(leaves, leaf)
var leaves []v3.WalletConfigTree

if payloadGateLeafNode != nil {
// calls && payloadGateLeafNode must match together.
leaves = append(leaves, wrapPayloadGate(payloadGateLeafNode, subdigestLeaves...))
Comment thread
ScreamingHawk marked this conversation as resolved.
Outdated
} else {
// No gate: preserve the exact flat structure of the original (pre-gating) tree so
// already-derived counterfactual addresses do not change.
leaves = append(leaves, subdigestLeaves...)
}

if sapientSignerLeafNode != nil {
if payloadGateLeafNode != nil {
// Gated the same way as the calls leaves. mainSignerLeaf below is the only
// leaf left unaffected by the gate.
leaves = append(leaves, wrapPayloadGate(payloadGateLeafNode, sapientSignerLeafNode))
} else {
leaves = append(leaves, sapientSignerLeafNode)
}
}

// Create the main signer leaf (with weight 1).
// Create the main signer leaf (with weight 1). Never gated: the owner must remain able
// to act (e.g. recover funds) regardless of the gate's paused state.
mainSignerLeaf := &v3.WalletConfigTreeAddressLeaf{
Weight: 1,
Address: mainSigner,
Expand All @@ -220,13 +260,28 @@ func createIntentTree(mainSigner common.Address, calls []*v3.CallsPayload, addit
return &fullTree, nil
}

// `CreateIntentTree` creates a tree from a list of intent operations and a main signer address.
func CreateIntentTree(mainSigner common.Address, calls []*v3.CallsPayload, sapientSignerLeafNode v3.WalletConfigTree) (*v3.WalletConfigTree, error) {
return createIntentTree(mainSigner, calls, sapientSignerLeafNode)
// `CreateIntentTree` creates a tree from a list of intent operations and a main signer
// address. When payloadGateLeafNode is set, the calls leaves and sapientSignerLeafNode (if
// provided) are each gated behind it in their own 2-of-2 subtree (see wrapPayloadGate).
// mainSigner is never gated. Passing nil for payloadGateLeafNode preserves the legacy tree
// shape.
func CreateIntentTree(
mainSigner common.Address,
calls []*v3.CallsPayload,
payloadGateLeafNode v3.WalletConfigTree,
Comment thread
ScreamingHawk marked this conversation as resolved.
Outdated
sapientSignerLeafNode v3.WalletConfigTree,
) (*v3.WalletConfigTree, error) {
return createIntentTree(mainSigner, calls, payloadGateLeafNode, sapientSignerLeafNode)
}

func createIntentConfiguration(mainSigner common.Address, calls []*v3.CallsPayload, checkpoint uint64, additionalLeaves ...v3.WalletConfigTree) (*v3.WalletConfig, error) {
tree, err := createIntentTree(mainSigner, calls, additionalLeaves...)
func createIntentConfiguration(
mainSigner common.Address,
calls []*v3.CallsPayload,
checkpoint uint64,
payloadGateLeafNode v3.WalletConfigTree,
sapientSignerLeafNode v3.WalletConfigTree,
) (*v3.WalletConfig, error) {
tree, err := createIntentTree(mainSigner, calls, payloadGateLeafNode, sapientSignerLeafNode)
if err != nil {
return nil, err
}
Expand All @@ -238,9 +293,17 @@ func createIntentConfiguration(mainSigner common.Address, calls []*v3.CallsPaylo
}, nil
}

// `CreateIntentConfiguration` creates a wallet configuration where the intent's transaction batches are grouped into the initial subdigest.
func CreateIntentConfiguration(mainSigner common.Address, calls []*v3.CallsPayload, checkpoint uint64, sapientSignerLeafNode v3.WalletConfigTree) (*v3.WalletConfig, error) {
return createIntentConfiguration(mainSigner, calls, checkpoint, sapientSignerLeafNode)
// `CreateIntentConfiguration` creates a wallet configuration where the intent's transaction
// batches are grouped into the initial subdigest. See CreateIntentTree for
// payloadGateLeafNode and sapientSignerLeafNode semantics.
func CreateIntentConfiguration(
mainSigner common.Address,
calls []*v3.CallsPayload,
checkpoint uint64,
payloadGateLeafNode v3.WalletConfigTree,
sapientSignerLeafNode v3.WalletConfigTree,
) (*v3.WalletConfig, error) {
return createIntentConfiguration(mainSigner, calls, checkpoint, payloadGateLeafNode, sapientSignerLeafNode)
}

// `BuildIntentConfigurationSignature` creates a signature for an already-built intent configuration
Expand Down Expand Up @@ -287,7 +350,7 @@ func GetIntentConfigurationSignature(
sapientSignerLeafNode v3.WalletConfigTree,
signerSignatures []*core.SignerSignature,
) ([]byte, error) {
config, err := createIntentConfiguration(mainSigner, calls, checkpoint, sapientSignerLeafNode)
config, err := createIntentConfiguration(mainSigner, calls, checkpoint, nil, sapientSignerLeafNode)
Comment thread
ScreamingHawk marked this conversation as resolved.
Outdated
if err != nil {
return nil, err
}
Expand Down
Loading
Loading