66 "math/big"
77
88 "github.com/0xsequence/ethkit/ethcoder"
9+ "github.com/0xsequence/ethkit/ethrpc"
910 "github.com/0xsequence/ethkit/go-ethereum/accounts/abi"
1011 "github.com/0xsequence/ethkit/go-ethereum/common"
1112 "github.com/0xsequence/go-sequence/core"
@@ -24,6 +25,14 @@ type DestinationToken struct {
2425 Amount * big.Int `abi:"amount"`
2526}
2627
28+ // MalleableSapientConfig contains the configuration for a MalleableSapient signer for a single call payload.
29+ // Each call payload can have its own sapient address, signature, and provider (for different chains).
30+ type MalleableSapientConfig struct {
31+ Address common.Address // The MalleableSapient contract address
32+ Signature []byte // Signature describing which parts are static vs malleable
33+ Provider * ethrpc.Provider // Provider for the chain this call is on
34+ }
35+
2736// IntentParams is a new version of intent parameters that uses CallsPayload for destination calls.
2837type IntentParams struct {
2938 UserAddress common.Address
@@ -188,10 +197,15 @@ func CreateAnyAddressSubdigestTree(calls []*v3.CallsPayload) ([]v3.WalletConfigT
188197
189198// `CreateIntentTree` creates a tree from a list of intent operations and a main signer address.
190199func CreateIntentTree (mainSigner common.Address , calls []* v3.CallsPayload , sapientSignerLeafNode v3.WalletConfigTree ) (* v3.WalletConfigTree , error ) {
191- // Create the subdigest leaves from the batched transactions.
192- leaves , err := CreateAnyAddressSubdigestTree (calls )
193- if err != nil {
194- return nil , err
200+ var leaves []v3.WalletConfigTree
201+
202+ if len (calls ) > 0 {
203+ // Create the subdigest leaves from the batched transactions.
204+ subdigestLeaves , err := CreateAnyAddressSubdigestTree (calls )
205+ if err != nil {
206+ return nil , err
207+ }
208+ leaves = append (leaves , subdigestLeaves ... )
195209 }
196210
197211 // If the sapient signer leaf is not nil, add it to the leaves.
@@ -205,6 +219,10 @@ func CreateIntentTree(mainSigner common.Address, calls []*v3.CallsPayload, sapie
205219 Address : mainSigner ,
206220 }
207221
222+ if len (leaves ) == 0 {
223+ return nil , fmt .Errorf ("no leaves to create tree from" )
224+ }
225+
208226 // If the length of the leaves is 1
209227 if len (leaves ) == 1 {
210228 tree := v3 .WalletConfigTreeNodes (mainSignerLeaf , leaves [0 ])
@@ -238,6 +256,61 @@ func CreateIntentConfiguration(mainSigner common.Address, calls []*v3.CallsPaylo
238256 return config , nil
239257}
240258
259+ // `CreateIntentConfigurationWithMalleableSapient` creates a wallet configuration with optional MalleableSapient configuration
260+ func CreateIntentConfigurationWithMalleableSapient (
261+ ctx context.Context ,
262+ mainSigner common.Address ,
263+ calls []* v3.CallsPayload ,
264+ configs []MalleableSapientConfig ,
265+ ) (* v3.WalletConfig , error ) {
266+ if len (calls ) == 0 {
267+ return nil , fmt .Errorf ("calls cannot be empty" )
268+ }
269+
270+ if len (configs ) != len (calls ) {
271+ return nil , fmt .Errorf ("configs length (%d) must match calls length (%d)" , len (configs ), len (calls ))
272+ }
273+
274+ var callsWithoutMalleableSapient []* v3.CallsPayload
275+ var sapientSignerLeaves []v3.WalletConfigTree
276+ for i , callPayload := range calls {
277+ config := configs [i ]
278+
279+ // Skip if address is zero address. Will be handled by CreateIntentConfiguration.
280+ if config .Address == (common.Address {}) {
281+ callsWithoutMalleableSapient = append (callsWithoutMalleableSapient , callPayload )
282+ continue
283+ }
284+
285+ if config .Provider == nil {
286+ return nil , fmt .Errorf ("provider is required for configs[%d]" , i )
287+ }
288+
289+ // Recover the image hash for the payload and configuration
290+ imageHash , err := v3 .RecoverSapientSignature (ctx , config .Address , * callPayload , config .Signature , config .Provider )
291+ if err != nil {
292+ return nil , fmt .Errorf ("failed to recover sapient signature for configs[%d]: %w" , i , err )
293+ }
294+
295+ // Create a SapientSignerLeaf for this payload
296+ sapientSignerLeaf := & v3.WalletConfigTreeSapientSignerLeaf {
297+ Weight : 1 ,
298+ Address : config .Address ,
299+ ImageHash_ : imageHash ,
300+ }
301+ sapientSignerLeaves = append (sapientSignerLeaves , sapientSignerLeaf )
302+ }
303+
304+ // Create a tree from all the SapientSignerLeaves
305+ var sapientSignerTree v3.WalletConfigTree
306+ if len (sapientSignerLeaves ) > 0 {
307+ sapientSignerTree = v3 .WalletConfigTreeNodes (sapientSignerLeaves ... )
308+ }
309+
310+ // Remaining calls go to CreateIntentConfiguration
311+ return CreateIntentConfiguration (mainSigner , callsWithoutMalleableSapient , sapientSignerTree )
312+ }
313+
241314// `GetIntentConfigurationSignature` creates a signature for the intent configuration that can be used to bypass chain ID validation.
242315// The signature is based on the transaction bundle digests only.
243316func GetIntentConfigurationSignature (
0 commit comments