Skip to content

Commit c832e12

Browse files
committed
Add TrailsUtils for MalleableSigner support for intents
1 parent eedaece commit c832e12

File tree

10 files changed

+2120
-20
lines changed

10 files changed

+2120
-20
lines changed

contracts/artifacts/trails-contracts/TrailsUtils.sol/TrailsUtils.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

contracts/contracts.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
seqsale721v0 "github.com/0xsequence/go-sequence/contracts/gen/seq_sale/erc721v0"
1919
"github.com/0xsequence/go-sequence/contracts/gen/supply"
2020
"github.com/0xsequence/go-sequence/contracts/gen/tokens"
21+
trailsutils "github.com/0xsequence/go-sequence/contracts/gen/trailsutils"
2122
v1Factory "github.com/0xsequence/go-sequence/contracts/gen/v1/walletfactory"
2223
v1Estimator "github.com/0xsequence/go-sequence/contracts/gen/v1/walletgasestimator"
2324
v1Guest "github.com/0xsequence/go-sequence/contracts/gen/v1/walletguest"
@@ -43,6 +44,7 @@ var GasEstimator,
4344
IERC1271,
4445
ISapient,
4546
ISapientCompact,
47+
TrailsUtils,
4648
ERC20Mock,
4749
IERC20,
4850
IERC721,
@@ -131,6 +133,7 @@ func init() {
131133
IERC1271 = artifact("IERC1271", ierc1271.IERC1271ABI, "")
132134
ISapient = artifact("ISapient", isapient.ISapientABI, "")
133135
ISapientCompact = artifact("ISapientCompact", isapient.ISapientCompactABI, "")
136+
TrailsUtils = artifact("TRAILS_UTILS", trailsutils.TrailsUtilsABI, trailsutils.TrailsUtilsBin)
134137

135138
IERC20 = artifact("IERC20", tokens.IERC20ABI, "")
136139
IERC721 = artifact("IERC721", tokens.IERC721ABI, "")

contracts/gen/gen.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@
7676
//go:generate go run github.com/0xsequence/ethkit/cmd/ethkit abigen --pkg=isapient --type=ISapient --outFile=./isapient/isapient.gen.go --artifactsFile=../artifacts/wallet-contracts-v3/ISapient.sol/ISapient.json
7777
//go:generate go run github.com/0xsequence/ethkit/cmd/ethkit abigen --pkg=isapient --type=ISapientCompact --outFile=./isapient/isapientcompact.gen.go --artifactsFile=../artifacts/wallet-contracts-v3/ISapient.sol/ISapientCompact.json
7878

79+
//
80+
// trails
81+
//
82+
//go:generate go run github.com/0xsequence/ethkit/cmd/ethkit abigen --pkg=trailsutils --type=TrailsUtils --outFile=./trailsutils/trails_utils.gen.go --artifactsFile=../artifacts/trails-contracts/TrailsUtils.sol/TrailsUtils.json
83+
7984
//
8085
// sequence marketplace
8186
//

contracts/gen/trailsutils/trails_utils.gen.go

Lines changed: 1340 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.work.sum

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

intent_config.go

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
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.
2837
type 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.
190199
func 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.
243316
func GetIntentConfigurationSignature(

0 commit comments

Comments
 (0)