Skip to content

Commit

Permalink
feat: add RelayV3 method to support V3 wallet transactions and relate…
Browse files Browse the repository at this point in the history
…d utils
  • Loading branch information
shunkakinoki committed Mar 4, 2025
1 parent b2f2c47 commit 40bfd51
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 2 deletions.
21 changes: 21 additions & 0 deletions relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/0xsequence/ethkit/go-ethereum/core/types"
"github.com/0xsequence/go-sequence/contracts"
"github.com/0xsequence/go-sequence/core"
v3 "github.com/0xsequence/go-sequence/core/v3"
"github.com/0xsequence/go-sequence/relayer/proto"
)

Expand Down Expand Up @@ -69,6 +70,7 @@ type Relayer interface {
// responds with the native transaction hash (*types.Transaction), which means the relayer has submitted the transaction
// request to the network. Clients can use WaitReceipt to wait until the metaTxnID has been mined.
Relay(ctx context.Context, signedTxs *SignedTransactions, quote ...*RelayerFeeQuote) (MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error)
RelayV3(ctx context.Context, signedTxs *SignedTransactions, quote ...*RelayerFeeQuote) (MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error)

//
FeeOptions(ctx context.Context, signedTxs *SignedTransactions) ([]*RelayerFeeOption, *RelayerFeeQuote, error)
Expand Down Expand Up @@ -128,3 +130,22 @@ func EncodeTransactionsForRelaying(relayer Relayer, walletAddress common.Address

return walletAddress, execdata, nil
}

func EncodeTransactionsForRelayingV3(relayer Relayer, walletAddress common.Address, chainID *big.Int, walletConfig core.WalletConfig, walletContext WalletContext, txns Transactions, space *big.Int, nonce *big.Int, seqSig []byte) (common.Address, []byte, error) {
if len(txns) == 0 {
return common.Address{}, nil, fmt.Errorf("cannot encode empty transactions")
}

payload := ConvertTransactionsToV3Payload(txns, space, nonce)
encoded, err := v3.Encode(payload, nil)
if err != nil {
return common.Address{}, nil, err
}

execdata, err := contracts.V3.WalletStage1Module.Encode("execute", encoded, seqSig)
if err != nil {
return common.Address{}, nil, err
}

return walletAddress, execdata, nil
}
100 changes: 100 additions & 0 deletions relayer/local_relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,106 @@ func (r *LocalRelayer) Relay(ctx context.Context, signedTxs *sequence.SignedTran
return metaTxnID, ntx, waitReceipt, nil
}

func (r *LocalRelayer) RelayV3(ctx context.Context, signedTxs *sequence.SignedTransactions, quote ...*sequence.RelayerFeeQuote) (sequence.MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error) {
sender := r.Sender

fmt.Println("Relaying V3 Local Relayer")
to, execdata, err := sequence.EncodeTransactionsForRelayingV3(
r,
signedTxs.WalletAddress,
signedTxs.ChainID,
signedTxs.WalletConfig,
signedTxs.WalletContext,
signedTxs.Transactions,
signedTxs.Space,
signedTxs.Nonce,
signedTxs.Signature,
)
if err != nil {
return "", nil, nil, err
}

if r.IsDeployTransaction(signedTxs) {
to = signedTxs.WalletContext.GuestModuleAddress
} else {
isDeployed, err := sequence.IsWalletDeployed(r.GetProvider(), to)
if err != nil {
return "", nil, nil, err
}

if !isDeployed {
_, factoryAddress, deployData, err := sequence.EncodeWalletDeployment(signedTxs.WalletConfig, signedTxs.WalletContext)
if err != nil {
return "", nil, nil, err
}

txns := sequence.Transactions{
{
RevertOnError: true,
To: factoryAddress,
Data: deployData,
},
{
RevertOnError: true,
To: to,
Data: execdata,
},
}

_, execdata, err = sequence.EncodeTransactionsForRelayingV3(
r,
signedTxs.WalletAddress,
signedTxs.ChainID,
signedTxs.WalletConfig,
signedTxs.WalletContext,
txns,
signedTxs.Space,
signedTxs.Nonce,
signedTxs.Signature,
)
if err != nil {
return "", nil, nil, err
}

execdata, err = contracts.V3.WalletStage1Module.Encode("execute", execdata, []byte{})
if err != nil {
return "", nil, nil, err
}

to = signedTxs.WalletContext.GuestModuleAddress
}
}

walletAddress, err := sequence.AddressFromWalletConfig(signedTxs.WalletConfig, signedTxs.WalletContext)
if err != nil {
return "", nil, nil, err
}

metaTxnID, _, err := sequence.ComputeMetaTxnID(signedTxs.ChainID, walletAddress, signedTxs.Transactions, signedTxs.Nonce, sequence.MetaTxnWalletExec)
if err != nil {
return "", nil, nil, err
}

ntx, err := sender.NewTransaction(ctx, &ethtxn.TransactionRequest{
To: &to, Data: execdata,
})
if err != nil {
return metaTxnID, nil, nil, err
}

signedTx, err := sender.SignTx(ntx, signedTxs.ChainID)
if err != nil {
return metaTxnID, nil, nil, err
}

ntx, waitReceipt, err := sender.SendTransaction(ctx, signedTx)
if err != nil {
return metaTxnID, nil, nil, err
}

return metaTxnID, ntx, waitReceipt, nil
}

func (r *LocalRelayer) Wait(ctx context.Context, metaTxnID sequence.MetaTxnID, optTimeout ...time.Duration) (sequence.MetaTxnStatus, *types.Receipt, error) {
if r.receiptListener == nil {
return 0, nil, fmt.Errorf("relayer: failed to wait for metaTxnID as receiptListener is not set")
Expand Down
63 changes: 63 additions & 0 deletions relayer/rpc_relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,69 @@ func (r *RpcRelayer) Relay(ctx context.Context, signedTxs *sequence.SignedTransa
return receipt, err
}

return sequence.MetaTxnID(metaTxnID), nil, waitReceipt, nil
}

func (r *RpcRelayer) RelayV3(ctx context.Context, signedTxs *sequence.SignedTransactions, quote ...*sequence.RelayerFeeQuote) (sequence.MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error) {
walletAddress := signedTxs.WalletAddress
var err error

if walletAddress == (common.Address{}) {
walletAddress, err = sequence.AddressFromWalletConfig(signedTxs.WalletConfig, signedTxs.WalletContext)
if err != nil {
return "", nil, nil, err
}
}

to, execdata, err := sequence.EncodeTransactionsForRelayingV3(
r,
signedTxs.WalletAddress,
signedTxs.ChainID,
signedTxs.WalletConfig,
signedTxs.WalletContext,
signedTxs.Transactions,
signedTxs.Space,
signedTxs.Nonce,
signedTxs.Signature,
)
if err != nil {
return "", nil, nil, err
}

if r.IsDeployTransaction(signedTxs) {
to = signedTxs.WalletContext.GuestModuleAddress
}

call := &proto.MetaTxn{
Contract: to.Hex(),
Input: hexutil.Encode(execdata),
WalletAddress: walletAddress.Hex(),
}

var txQuote *string
if len(quote) > 0 {
txQuote = (*string)(quote[0])
}

// TODO: check contents of Contract and input, if empty, lets not even bother asking the server..

ok, metaTxnID, err := r.Service.SendMetaTxn(ctx, call, txQuote, nil)
if err != nil {
return sequence.MetaTxnID(metaTxnID), nil, nil, err
}
if !ok {
return sequence.MetaTxnID(metaTxnID), nil, nil, fmt.Errorf("failed to relay meta transaction: unknown reason")
}
if metaTxnID == "" {
return "", nil, nil, fmt.Errorf("failed to relay meta transaction: server returned empty metaTxnID")
}

waitReceipt := func(ctx context.Context) (*types.Receipt, error) {
// NOTE: to timeout the request, pass a ctx from context.WithTimeout
_, receipt, err := r.Wait(ctx, sequence.MetaTxnID(metaTxnID))
return receipt, err
}

// TODO: 2nd argument will be nil, we may even want to remove it from here...
return sequence.MetaTxnID(metaTxnID), nil, waitReceipt, nil
}
Expand Down
1 change: 1 addition & 0 deletions transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ type SignedTransactions struct {
WalletContext WalletContext

Transactions Transactions // The meta-transactions
Space *big.Int // Nonce space of the transactions
Nonce *big.Int // Nonce of the transactions
Digest common.Hash // Digest of the transactions
Signature []byte // Signature (encoded as bytes from *Signature) of the txn digest
Expand Down
20 changes: 18 additions & 2 deletions wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,10 @@ func (w *Wallet[C]) SignTransactions(ctx context.Context, txns Transactions) (*S

// If the config is v3, encode the transactions as a v3 transaction
if isV3 {
payload := ConvertTransactionsToV3Payload(txns, big.NewInt(0), big.NewInt(0))
// TODO: Hardcode nonce space to 0 for now
space := big.NewInt(0)

payload := ConvertTransactionsToV3Payload(txns, space, nonce)

digest, err := v3.HashPayload(w.address, w.chainID, payload)
if err != nil {
Expand All @@ -660,6 +663,7 @@ func (w *Wallet[C]) SignTransactions(ctx context.Context, txns Transactions) (*S
WalletConfig: w.config,
WalletContext: w.context,
Transactions: txns,
Space: space,
Nonce: nonce,
Digest: digest,
Signature: sig,
Expand All @@ -678,7 +682,19 @@ func (w *Wallet[C]) SendTransactions(ctx context.Context, signedTxns *SignedTran
return "", nil, nil, ErrRelayerNotSet
}

return w.relayer.Relay(ctx, signedTxns, feeQuote...)
_, isV1 := core.WalletConfig(w.config).(*v1.WalletConfig)
_, isV2 := core.WalletConfig(w.config).(*v2.WalletConfig)
_, isV3 := core.WalletConfig(w.config).(*v3.WalletConfig)

if isV1 || isV2 {
return w.relayer.Relay(ctx, signedTxns, feeQuote...)
}

if isV3 {
return w.relayer.RelayV3(ctx, signedTxns, feeQuote...)
}

return "", nil, nil, fmt.Errorf("unknown wallet config type")
}

func (w *Wallet[C]) FeeOptions(ctx context.Context, txs Transactions) ([]*RelayerFeeOption, *RelayerFeeQuote, error) {
Expand Down

0 comments on commit 40bfd51

Please sign in to comment.