diff --git a/go.mod b/go.mod index 24e7043..9f7c04c 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,11 @@ toolchain go1.22.0 // replace github.com/0xsequence/ethkit => /Users/peter/Dev/0xsequence/ethkit require ( - github.com/0xsequence/ethkit v1.24.9 + github.com/0xsequence/ethkit v1.24.10 github.com/0xsequence/go-ethauth v0.13.0 github.com/BurntSushi/toml v1.2.1 github.com/davecgh/go-spew v1.1.1 github.com/gibson042/canonicaljson-go v1.0.3 - github.com/goware/breaker v0.1.2 github.com/goware/cachestore v0.8.1 github.com/goware/logger v0.3.0 github.com/shopspring/decimal v1.3.1 @@ -31,6 +30,7 @@ require ( github.com/go-stack/stack v1.8.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect + github.com/goware/breaker v0.1.2 // indirect github.com/goware/calc v0.2.0 // indirect github.com/goware/channel v0.4.1 // indirect github.com/goware/singleflight v0.2.0 // indirect diff --git a/go.sum b/go.sum index d8fc5fe..ed01980 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/0xsequence/ethkit v1.24.9 h1:yOVmMc4fJ5NjSPr9piX6rjveqYFsgqKOMxqKRLUiRW4= -github.com/0xsequence/ethkit v1.24.9/go.mod h1:E3eymNtV0oJabgqM9R92xTQYOsT7rVHxFRju8A2CFJw= +github.com/0xsequence/ethkit v1.24.10 h1:aWAbCmadC2GlzoXn96uQ+8V1UWcBxO01F+DtIxsFqXM= +github.com/0xsequence/ethkit v1.24.10/go.mod h1:E3eymNtV0oJabgqM9R92xTQYOsT7rVHxFRju8A2CFJw= github.com/0xsequence/go-ethauth v0.13.0 h1:ZaqFEEqy574A2b1P7vjpcy5tb4W/izn+A3swwOYi9wA= github.com/0xsequence/go-ethauth v0.13.0/go.mod h1:f3kx39S9F+W+qvZEB6bkKKbpUstmyB7goUntO3wvlhg= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= diff --git a/receipts.go b/receipts.go index eefa6a5..03ec214 100644 --- a/receipts.go +++ b/receipts.go @@ -56,8 +56,13 @@ func (r *Receipt) setNativeReceipt(receipt *types.Receipt) { // This method is duplicated code from: `compressor/contract.go` // can't be used directly, because it would create a circular dependency -func DecompressCalldata(ctx context.Context, provider *ethrpc.Provider, transaction *types.Transaction) (common.Address, []byte, error) { - data := transaction.Data() +func DecompressCalldata( + ctx context.Context, + provider *ethrpc.Provider, + toAddress common.Address, + calldata []byte, +) (common.Address, []byte, error) { + data := calldata if len(data) == 0 { return common.Address{}, nil, fmt.Errorf("empty transaction data") @@ -74,7 +79,7 @@ func DecompressCalldata(ctx context.Context, provider *ethrpc.Provider, transact c2[0] = byte(0x06) res, err := provider.CallContract(ctx, ethereum.CallMsg{ - To: transaction.To(), + To: &toAddress, Data: c2, }, nil) @@ -93,18 +98,26 @@ func DecompressCalldata(ctx context.Context, provider *ethrpc.Provider, transact func TryDecodeCalldata( ctx context.Context, provider *ethrpc.Provider, - transaction *types.Transaction, + toAddress common.Address, + calldata []byte, + decompressCzip bool, ) (common.Address, Transactions, *big.Int, []byte, error) { - decodedTransactions, decodedNonce, decodedSignature, err := DecodeExecdata(transaction.Data()) + decodedTransactions, decodedNonce, decodedSignature, err := DecodeExecdata(calldata) if err == nil { - return *transaction.To(), decodedTransactions, decodedNonce, decodedSignature, nil + return toAddress, decodedTransactions, decodedNonce, decodedSignature, nil } + var addr common.Address + var decompressed []byte + var err2 error + // Try decoding it decompressed - addr, decompressed, err2 := DecompressCalldata(ctx, provider, transaction) - if err2 != nil { - // Don't bubble up the decompression error, as it might not be a decompression error - return common.Address{}, nil, nil, nil, err + if decompressCzip { + addr, decompressed, err2 = DecompressCalldata(ctx, provider, toAddress, calldata) + if err2 != nil { + // Don't bubble up the decompression error, as it might not be a decompression error + return common.Address{}, nil, nil, nil, err + } } decodedTransactions, decodedNonce, decodedSignature, err = DecodeExecdata(decompressed) @@ -115,19 +128,33 @@ func TryDecodeCalldata( return addr, decodedTransactions, decodedNonce, decodedSignature, nil } -func DecodeReceipt(ctx context.Context, receipt *types.Receipt, provider *ethrpc.Provider) ([]*Receipt, []*types.Log, error) { - transaction, _, err := provider.TransactionByHash(ctx, receipt.TxHash) +func DecodeReceipt( + ctx context.Context, + receipt *types.Receipt, + provider *ethrpc.Provider, + calldata []byte, + decompressCzip bool, +) ([]*Receipt, []*types.Log, error) { + if calldata == nil { + transaction, _, err := provider.TransactionByHash(ctx, receipt.TxHash) + if err != nil { + return nil, nil, err + } + calldata = transaction.Data() + } + + chainID, err := provider.ChainID(ctx) if err != nil { return nil, nil, err } - wallet, decodedTransactions, decodedNonce, decodedSignature, err := TryDecodeCalldata(ctx, provider, transaction) + wallet, decodedTransactions, decodedNonce, decodedSignature, err := TryDecodeCalldata(ctx, provider, receipt.To, calldata, decompressCzip) if err != nil { return nil, nil, err } isGuestExecute := decodedNonce != nil && len(decodedSignature) == 0 - logs, receipts, err := decodeReceipt(receipt.Logs, decodedTransactions, decodedNonce, wallet, transaction.ChainId(), isGuestExecute) + logs, receipts, err := decodeReceipt(receipt.Logs, decodedTransactions, decodedNonce, wallet, chainID, isGuestExecute) if err != nil { return nil, nil, err } diff --git a/relayer.go b/relayer.go index 8f9e556..26a8b0d 100644 --- a/relayer.go +++ b/relayer.go @@ -7,7 +7,6 @@ import ( "time" "github.com/0xsequence/ethkit/ethrpc" - "github.com/0xsequence/ethkit/ethtxn" "github.com/0xsequence/ethkit/go-ethereum/common" "github.com/0xsequence/ethkit/go-ethereum/core/types" "github.com/0xsequence/go-sequence/contracts" @@ -68,13 +67,13 @@ type Relayer interface { // Relay will submit the Sequence signed meta transaction to the relayer. The method will block until the relayer // 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) + Relay(ctx context.Context, signedTxs *SignedTransactions, quote ...*RelayerFeeQuote) (MetaTxnID, *types.Transaction, WaitReceipt, error) // FeeOptions(ctx context.Context, signedTxs *SignedTransactions) ([]*RelayerFeeOption, *RelayerFeeQuote, error) // .. - Wait(ctx context.Context, metaTxnID MetaTxnID, optTimeout ...time.Duration) (MetaTxnStatus, *types.Receipt, error) + Wait(ctx context.Context, metaTxnID MetaTxnID, optTimeout ...time.Duration) (MetaTxnStatus, *types.Receipt, *proto.MetaTxnReceipt, error) // .. Client() proto.Relayer @@ -98,6 +97,8 @@ const ( MetaTxnReverted ) +type WaitReceipt func(ctx context.Context) (*types.Receipt, *proto.MetaTxnReceipt, error) + // returns `to` address (either guest or wallet) and `data` of signed-metatx-calldata, aka execdata func EncodeTransactionsForRelaying(relayer Relayer, walletAddress common.Address, walletConfig core.WalletConfig, walletContext WalletContext, txns Transactions, nonce *big.Int, seqSig []byte) (common.Address, []byte, error) { // TODO/NOTE: first version, we assume the wallet is deployed, then we can add bundlecreation after. diff --git a/relayer/local_relayer.go b/relayer/local_relayer.go index 00d9ced..5ba23a3 100644 --- a/relayer/local_relayer.go +++ b/relayer/local_relayer.go @@ -123,7 +123,7 @@ func (r *LocalRelayer) Simulate(ctx context.Context, txs *sequence.SignedTransac panic("implement me") } -func (r *LocalRelayer) Relay(ctx context.Context, signedTxs *sequence.SignedTransactions, quote ...*sequence.RelayerFeeQuote) (sequence.MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error) { +func (r *LocalRelayer) Relay(ctx context.Context, signedTxs *sequence.SignedTransactions, quote ...*sequence.RelayerFeeQuote) (sequence.MetaTxnID, *types.Transaction, sequence.WaitReceipt, error) { // NOTE: this implementation assumes the wallet is deployed and does not do automatic bundle creation (aka prepending / bundling // a wallet creation call) @@ -208,27 +208,33 @@ func (r *LocalRelayer) Relay(ctx context.Context, signedTxs *sequence.SignedTran return metaTxnID, nil, nil, err } - ntx, waitReceipt, err := sender.SendTransaction(ctx, signedTx) + ntx, _, err = sender.SendTransaction(ctx, signedTx) if err != nil { return metaTxnID, nil, nil, err } + waitReceipt := func(ctx context.Context) (*types.Receipt, *proto.MetaTxnReceipt, error) { + // NOTE: to timeout the request, pass a ctx from context.WithTimeout + _, receipt, metaTxnReceipt, err := r.Wait(ctx, sequence.MetaTxnID(metaTxnID)) + return receipt, metaTxnReceipt, err + } + return metaTxnID, ntx, waitReceipt, nil } -func (r *LocalRelayer) Wait(ctx context.Context, metaTxnID sequence.MetaTxnID, optTimeout ...time.Duration) (sequence.MetaTxnStatus, *types.Receipt, error) { +func (r *LocalRelayer) Wait(ctx context.Context, metaTxnID sequence.MetaTxnID, optTimeout ...time.Duration) (sequence.MetaTxnStatus, *types.Receipt, *proto.MetaTxnReceipt, error) { if r.receiptListener == nil { - return 0, nil, fmt.Errorf("relayer: failed to wait for metaTxnID as receiptListener is not set") + return 0, nil, nil, fmt.Errorf("relayer: failed to wait for metaTxnID as receiptListener is not set") } result, receipt, _, err := sequence.FetchMetaTransactionReceipt(ctx, r.receiptListener, metaTxnID, optTimeout...) if err != nil { - return 0, nil, err + return 0, nil, nil, err } var status sequence.MetaTxnStatus if result != nil { status = result.Status } - return status, receipt.Receipt(), nil + return status, receipt.Receipt(), nil, nil // XXXXXX } func (r *LocalRelayer) FeeOptions(ctx context.Context, signedTxs *sequence.SignedTransactions) ([]*sequence.RelayerFeeOption, *sequence.RelayerFeeQuote, error) { diff --git a/relayer/rpc_relayer.go b/relayer/rpc_relayer.go index d7a096f..a024cbf 100644 --- a/relayer/rpc_relayer.go +++ b/relayer/rpc_relayer.go @@ -11,7 +11,6 @@ import ( "github.com/0xsequence/ethkit" "github.com/0xsequence/ethkit/ethreceipts" "github.com/0xsequence/ethkit/ethrpc" - "github.com/0xsequence/ethkit/ethtxn" "github.com/0xsequence/ethkit/go-ethereum/common" "github.com/0xsequence/ethkit/go-ethereum/common/hexutil" "github.com/0xsequence/ethkit/go-ethereum/core/types" @@ -144,7 +143,7 @@ func (r *RpcRelayer) Simulate(ctx context.Context, txs *sequence.SignedTransacti // Relay will submit the Sequence signed meta transaction to the relayer. The method will block until the relayer // 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. -func (r *RpcRelayer) Relay(ctx context.Context, signedTxs *sequence.SignedTransactions, quote ...*sequence.RelayerFeeQuote) (sequence.MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error) { +func (r *RpcRelayer) Relay(ctx context.Context, signedTxs *sequence.SignedTransactions, quote ...*sequence.RelayerFeeQuote) (sequence.MetaTxnID, *types.Transaction, sequence.WaitReceipt, error) { walletAddress := signedTxs.WalletAddress var err error @@ -196,10 +195,10 @@ func (r *RpcRelayer) Relay(ctx context.Context, signedTxs *sequence.SignedTransa return "", nil, nil, proto.Failf("failed to relay meta transaction: server returned empty metaTxnID") } - waitReceipt := func(ctx context.Context) (*types.Receipt, error) { + waitReceipt := func(ctx context.Context) (*types.Receipt, *proto.MetaTxnReceipt, error) { // NOTE: to timeout the request, pass a ctx from context.WithTimeout - _, receipt, err := r.Wait(ctx, sequence.MetaTxnID(metaTxnID)) - return receipt, err + _, receipt, metaTxnReceipt, err := r.Wait(ctx, sequence.MetaTxnID(metaTxnID)) + return receipt, metaTxnReceipt, err } // TODO: 2nd argument will be nil, we may even want to remove it from here... @@ -232,7 +231,7 @@ func (r *RpcRelayer) FeeOptions(ctx context.Context, signedTxs *sequence.SignedT } // .... -func (r *RpcRelayer) Wait(ctx context.Context, metaTxnID sequence.MetaTxnID, optTimeout ...time.Duration) (sequence.MetaTxnStatus, *types.Receipt, error) { +func (r *RpcRelayer) Wait(ctx context.Context, metaTxnID sequence.MetaTxnID, optTimeout ...time.Duration) (sequence.MetaTxnStatus, *types.Receipt, *proto.MetaTxnReceipt, error) { // Fetch the meta transaction receipt from the relayer service if r.receiptListener == nil { return r.waitMetaTxnReceipt(ctx, metaTxnID, optTimeout...) @@ -241,16 +240,18 @@ func (r *RpcRelayer) Wait(ctx context.Context, metaTxnID sequence.MetaTxnID, opt // Fetch the meta transaction receipt from the receipt listener result, receipt, _, err := sequence.FetchMetaTransactionReceipt(ctx, r.receiptListener, metaTxnID, optTimeout...) if err != nil { - return 0, nil, err + return 0, nil, nil, err } var status sequence.MetaTxnStatus if result != nil { status = result.Status } - return status, receipt.Receipt(), nil + // TODO: need to get from receipt listener result to a sequence meta txn receipt .. + // TODO: can copy bunch of code from GetMetaTxnReceipt .. using sequence.DecodeReceipt(receipt) etc.. + return status, receipt.Receipt(), nil, nil // XXXX } -func (r *RpcRelayer) waitMetaTxnReceipt(ctx context.Context, metaTxnID sequence.MetaTxnID, optTimeout ...time.Duration) (sequence.MetaTxnStatus, *types.Receipt, error) { +func (r *RpcRelayer) waitMetaTxnReceipt(ctx context.Context, metaTxnID sequence.MetaTxnID, optTimeout ...time.Duration) (sequence.MetaTxnStatus, *types.Receipt, *proto.MetaTxnReceipt, error) { // TODO: in future GetMetaTxnReceipt() will be renamed to WaitTransactionReceipt() var clear context.CancelFunc @@ -264,9 +265,9 @@ func (r *RpcRelayer) waitMetaTxnReceipt(ctx context.Context, metaTxnID sequence. case <-ctx.Done(): err := ctx.Err() if err != nil { - return 0, nil, err + return 0, nil, nil, err } - return 0, nil, nil + return 0, nil, nil, nil default: } @@ -278,15 +279,15 @@ func (r *RpcRelayer) waitMetaTxnReceipt(ctx context.Context, metaTxnID sequence. continue } if err != nil { - return sequence.MetaTxnStatusUnknown, nil, err + return sequence.MetaTxnStatusUnknown, nil, nil, err } txnReceipt := metaTxnReceipt.TxnReceipt var receipt *types.Receipt err = json.Unmarshal([]byte(txnReceipt), &receipt) if err != nil { - return 0, nil, fmt.Errorf("failed to decode txn receipt data: %w", err) + return 0, nil, nil, fmt.Errorf("failed to decode txn receipt data: %w", err) } - return MetaTxnStatusFromString(metaTxnReceipt.Status), receipt, nil + return MetaTxnStatusFromString(metaTxnReceipt.Status), receipt, metaTxnReceipt, nil } } diff --git a/utils.go b/utils.go index 3369105..47f1a2a 100644 --- a/utils.go +++ b/utils.go @@ -13,11 +13,12 @@ import ( "github.com/0xsequence/go-sequence/core" v1 "github.com/0xsequence/go-sequence/core/v1" v2 "github.com/0xsequence/go-sequence/core/v2" + "github.com/0xsequence/go-sequence/relayer/proto" ) var zeroAddress = common.Address{} -func DeploySequenceWallet(sender *ethwallet.Wallet, walletConfig core.WalletConfig, walletContext WalletContext) (common.Address, *types.Transaction, ethtxn.WaitReceipt, error) { +func DeploySequenceWallet(sender *ethwallet.Wallet, walletConfig core.WalletConfig, walletContext WalletContext) (common.Address, *types.Transaction, WaitReceipt, error) { if sender.GetProvider() == nil { return common.Address{}, nil, nil, ErrProviderNotSet } @@ -48,7 +49,12 @@ func DeploySequenceWallet(sender *ethwallet.Wallet, walletConfig core.WalletConf tx, waitReceipt, err := sender.SendTransaction(context.Background(), signedDeployTx) - return walletAddress, tx, waitReceipt, nil + metaTxnWaitReceipt := func(ctx context.Context) (*types.Receipt, *proto.MetaTxnReceipt, error) { + receipt, err := waitReceipt(ctx) + return receipt, nil, err + } + + return walletAddress, tx, metaTxnWaitReceipt, err } func EncodeWalletDeployment(walletConfig core.WalletConfig, walletContext WalletContext) (common.Address, common.Address, []byte, error) { diff --git a/utils_test.go b/utils_test.go index dc2a85c..7c92cc3 100644 --- a/utils_test.go +++ b/utils_test.go @@ -40,7 +40,7 @@ func TestDeploySequenceWallet(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, tx) - receipt, err := waitReceipt(context.Background()) + receipt, _, err := waitReceipt(context.Background()) assert.NoError(t, err) assert.True(t, receipt.Status == types.ReceiptStatusSuccessful) @@ -77,7 +77,7 @@ func TestDeploySequenceWallet(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, tx) - receipt, err := waitReceipt(context.Background()) + receipt, _, err := waitReceipt(context.Background()) assert.NoError(t, err) assert.True(t, receipt.Status == types.ReceiptStatusSuccessful) diff --git a/wallet.go b/wallet.go index 42d6120..2778863 100644 --- a/wallet.go +++ b/wallet.go @@ -7,7 +7,6 @@ import ( "github.com/0xsequence/ethkit/ethcoder" "github.com/0xsequence/ethkit/ethrpc" - "github.com/0xsequence/ethkit/ethtxn" "github.com/0xsequence/ethkit/go-ethereum/common" "github.com/0xsequence/ethkit/go-ethereum/core/types" "github.com/0xsequence/go-sequence/core" @@ -577,11 +576,11 @@ func (w *Wallet[C]) SignTransactions(ctx context.Context, txns Transactions) (*S }, nil } -func (w *Wallet[C]) SendTransaction(ctx context.Context, signedTxns *SignedTransactions, feeQuote ...*RelayerFeeQuote) (MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error) { +func (w *Wallet[C]) SendTransaction(ctx context.Context, signedTxns *SignedTransactions, feeQuote ...*RelayerFeeQuote) (MetaTxnID, *types.Transaction, WaitReceipt, error) { return w.SendTransactions(ctx, signedTxns, feeQuote...) } -func (w *Wallet[C]) SendTransactions(ctx context.Context, signedTxns *SignedTransactions, feeQuote ...*RelayerFeeQuote) (MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error) { +func (w *Wallet[C]) SendTransactions(ctx context.Context, signedTxns *SignedTransactions, feeQuote ...*RelayerFeeQuote) (MetaTxnID, *types.Transaction, WaitReceipt, error) { if w.relayer == nil { return "", nil, nil, ErrRelayerNotSet } @@ -654,7 +653,7 @@ func (w *Wallet[C]) IsDeployed() (bool, error) { return IsWalletDeployed(w.provider, w.Address()) } -func (w *Wallet[C]) Deploy(ctx context.Context) (MetaTxnID, *types.Transaction, ethtxn.WaitReceipt, error) { +func (w *Wallet[C]) Deploy(ctx context.Context) (MetaTxnID, *types.Transaction, WaitReceipt, error) { if w.relayer == nil { return "", nil, nil, ErrRelayerNotSet }