diff --git a/.gitignore b/.gitignore index 150a9f21..3a36cc6f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ examples/**/*.sum .idea/ -.vscode/settings.json \ No newline at end of file +.vscode/ \ No newline at end of file diff --git a/paymaster/execute_txn.go b/paymaster/execute_txn.go new file mode 100644 index 00000000..c12d123b --- /dev/null +++ b/paymaster/execute_txn.go @@ -0,0 +1,92 @@ +package paymaster + +import ( + "context" + + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/starknet.go/client/rpcerr" + "github.com/NethermindEth/starknet.go/typedata" +) + +// ExecuteTransaction sends the signed typed data to the paymaster service for execution +// +// Parameters: +// - ctx: The context.Context object for controlling the function call +// - request: The signed typed data of the transaction to be executed by the paymaster service +// +// Returns: +// - *ExecuteTransactionResponse: The hash of the transaction broadcasted by the paymaster and +// the tracking ID corresponding to the user `execute` request +// - error: An error if any error occurs +func (p *Paymaster) ExecuteTransaction( + ctx context.Context, + request *ExecuteTransactionRequest, +) (ExecuteTransactionResponse, error) { + var response ExecuteTransactionResponse + if err := p.c.CallContextWithSliceArgs( + ctx, + &response, + "paymaster_executeTransaction", + request, + ); err != nil { + return response, rpcerr.UnwrapToRPCErr( + err, + ErrInvalidAddress, + ErrClassHashNotSupported, + ErrInvalidDeploymentData, + ErrInvalidSignature, + ErrUnknownError, + ErrMaxAmountTooLow, + ErrTransactionExecutionError, + ) + } + + return response, nil +} + +// ExecuteTransactionRequest is the request to execute a transaction +// via the paymaster (transaction + parameters). +type ExecuteTransactionRequest struct { + // Typed data build by calling paymaster_buildTransaction signed by the + // user to be executed by the paymaster service + Transaction ExecutableUserTransaction `json:"transaction"` + // Execution parameters to be used when executing the transaction + Parameters UserParameters `json:"parameters"` +} + +// ExecutableUserTransaction is a user transaction ready for execution +// (deploy, invoke, or both). +type ExecutableUserTransaction struct { + // The type of the transaction to be executed by the paymaster + Type UserTxnType `json:"type"` + // The deployment data for the transaction, used for `deploy` and + // `deploy_and_invoke` transaction types. + // Should be `nil` for `invoke` transaction types. + Deployment *AccountDeploymentData `json:"deployment,omitempty"` + // Invoke data signed by the user to be executed by the paymaster service, used for`invoke` and + // `deploy_and_invoke` transaction types. + // Should be `nil` for `deploy` transaction types. + Invoke *ExecutableUserInvoke `json:"invoke,omitempty"` +} + +// ExecutableUserInvoke is a signed typed data of an invoke transaction ready +// to be executed by the paymaster service. +type ExecutableUserInvoke struct { + // The address of the user account + UserAddress *felt.Felt `json:"user_address"` + // Typed data returned by the endpoint paymaster_buildTransaction + TypedData *typedata.TypedData `json:"typed_data"` + // Signature of the associated Typed Data + Signature []*felt.Felt `json:"signature"` +} + +// ExecuteTransactionResponse is the response from executing a transaction +// (tracking ID and transaction hash). +type ExecuteTransactionResponse struct { + // A unique identifier used to track an execution request of a user. Its purpose is to track + // possibly different transactions sent by the paymaster and which are associated with a same + // user request. Such cases can happen during congestion, where a fee or tip bump may be needed + // in order for a transaction to enter a block + TrackingID *felt.Felt `json:"tracking_id"` + TransactionHash *felt.Felt `json:"transaction_hash"` +} diff --git a/paymaster/execute_txn_test.go b/paymaster/execute_txn_test.go new file mode 100644 index 00000000..66871c37 --- /dev/null +++ b/paymaster/execute_txn_test.go @@ -0,0 +1,501 @@ +package paymaster + +import ( + "encoding/json" + "testing" + + "github.com/NethermindEth/juno/core/felt" + "github.com/NethermindEth/starknet.go/contracts" + "github.com/NethermindEth/starknet.go/curve" + "github.com/NethermindEth/starknet.go/internal/tests" + internalUtils "github.com/NethermindEth/starknet.go/internal/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func TestExecuteTransaction(t *testing.T) { + t.Parallel() + + t.Run("integration", func(t *testing.T) { + t.Parallel() + tests.RunTestOn(t, tests.IntegrationEnv) + + privKey, pubKey, _, err := curve.GetRandomKeys() + require.NoError(t, err) + + pubKeyFelt := new(felt.Felt).SetBigInt(pubKey) + privKeyFelt := new(felt.Felt).SetBigInt(privKey) + + t.Run("execute deploy transaction", func(t *testing.T) { + t.Parallel() + + pm, spy := SetupPaymaster(t) + t.Log("paymster successfully initialised") + + deployTxn := buildDeployTxn(t, pm, pubKeyFelt) + assert.NotNil(t, deployTxn) + + t.Log("executing the deploy transaction in the paymaster") + + request := ExecuteTransactionRequest{ + Transaction: ExecutableUserTransaction{ + Type: UserTxnDeploy, + Deployment: deployTxn.Deployment, + }, + Parameters: UserParameters{ + Version: UserParamV1, + FeeMode: FeeMode{ + Mode: FeeModeSponsored, + Tip: &TipPriority{ + Priority: TipPriorityNormal, + }, + }, + }, + } + + resp, err := pm.ExecuteTransaction(t.Context(), &request) + require.NoError(t, err) + + t.Log("transaction successfully executed") + t.Logf("Tracking ID: %s", resp.TrackingID) + t.Logf("Transaction Hash: %s", resp.TransactionHash) + + rawResp, err := json.Marshal(resp) + require.NoError(t, err) + assert.JSONEq(t, string(spy.LastResponse()), string(rawResp)) + }) + + t.Run("execute invoke transaction", func(t *testing.T) { + t.Parallel() + + pm, spy := SetupPaymaster(t) + t.Log("paymaster successfully initialised") + + privK, _, accAdd := GetStrkAccountData(t) + t.Log("account data fetched") + + invokeTxn := buildInvokeTxn(t, pm, &accAdd) + assert.NotNil(t, invokeTxn) + + mshHash, err := invokeTxn.TypedData.GetMessageHash(accAdd.String()) + require.NoError(t, err) + t.Log("message hash:", mshHash) + + r, s, err := curve.SignFelts(mshHash, &privK) + require.NoError(t, err) + t.Log("typed data signature:", r, s) + + t.Log("executing the invoke transaction in the paymaster") + + request := ExecuteTransactionRequest{ + Transaction: ExecutableUserTransaction{ + Type: UserTxnInvoke, + Invoke: &ExecutableUserInvoke{ + UserAddress: &accAdd, + TypedData: invokeTxn.TypedData, + Signature: []*felt.Felt{r, s}, + }, + }, + Parameters: UserParameters{ + Version: UserParamV1, + FeeMode: FeeMode{ + Mode: FeeModeSponsored, + Tip: &TipPriority{ + Priority: TipPriorityNormal, + }, + }, + }, + } + + resp, err := pm.ExecuteTransaction(t.Context(), &request) + require.NoError(t, err) + + t.Log("transaction successfully executed") + t.Logf("Tracking ID: %s", resp.TrackingID) + t.Logf("Transaction Hash: %s", resp.TransactionHash) + + rawResp, err := json.Marshal(resp) + require.NoError(t, err) + assert.JSONEq(t, string(spy.LastResponse()), string(rawResp)) + }) + + t.Run("execute deploy_and_invoke transaction", func(t *testing.T) { + t.Parallel() + + pm, spy := SetupPaymaster(t) + t.Log("paymster successfully initialised") + + builtTxn := buildDeployAndInvokeTxn(t, pm, pubKeyFelt) + assert.NotNil(t, builtTxn) + + accAdd := builtTxn.Deployment.Address + mshHash, err := builtTxn.TypedData.GetMessageHash(accAdd.String()) + require.NoError(t, err) + t.Log("message hash:", mshHash) + + r, s, err := curve.SignFelts(mshHash, privKeyFelt) + require.NoError(t, err) + t.Log("typed data signature:", r, s) + + t.Log("executing the deploy_and_invoke transaction in the paymaster") + + request := ExecuteTransactionRequest{ + Transaction: ExecutableUserTransaction{ + Type: UserTxnDeployAndInvoke, + Deployment: builtTxn.Deployment, + Invoke: &ExecutableUserInvoke{ + UserAddress: accAdd, + TypedData: builtTxn.TypedData, + Signature: []*felt.Felt{r, s}, + }, + }, + Parameters: UserParameters{ + Version: UserParamV1, + FeeMode: FeeMode{ + Mode: FeeModeSponsored, + Tip: &TipPriority{ + Priority: TipPriorityFast, + }, + }, + }, + } + + resp, err := pm.ExecuteTransaction(t.Context(), &request) + require.NoError(t, err) + + t.Log("transaction successfully executed") + t.Logf("Tracking ID: %s", resp.TrackingID) + t.Logf("Transaction Hash: %s", resp.TransactionHash) + + rawResp, err := json.Marshal(resp) + require.NoError(t, err) + assert.JSONEq(t, string(spy.LastResponse()), string(rawResp)) + }) + }) + + t.Run("mock", func(t *testing.T) { + t.Parallel() + tests.RunTestOn(t, tests.MockEnv) + + pubKey := internalUtils.TestHexToFelt( + t, + "0x1cf6046c81f47d488c528e52066482f6756029bed10cf5df35608bb8eebac9", + ) + + t.Run("execute deploy transaction", func(t *testing.T) { + t.Parallel() + t.Log("building deploy request") + + deploymentData := createDeploymentData(t, pubKey) + + request := ExecuteTransactionRequest{ + Transaction: ExecutableUserTransaction{ + Type: UserTxnDeploy, + Deployment: deploymentData, + }, + Parameters: UserParameters{ + Version: UserParamV1, + FeeMode: FeeMode{ + Mode: FeeModeSponsored, + }, + }, + } + + t.Log("asserting the request marshalled is equal to the expected request") + expectedReqs := *internalUtils.TestUnmarshalJSONFileToType[[]json.RawMessage](t, "testdata/execute_txn/deploy-request.json", "params") + expectedReq := expectedReqs[0] + + rawReq, err := json.Marshal(request) + require.NoError(t, err) + + assert.JSONEq(t, string(expectedReq), string(rawReq)) + + t.Log("asserting the response marshalled is equal to the expected response") + expectedResp := *internalUtils.TestUnmarshalJSONFileToType[json.RawMessage](t, "testdata/execute_txn/response.json", "result") + + var response ExecuteTransactionResponse + err = json.Unmarshal(expectedResp, &response) + require.NoError(t, err) + + t.Log("setting up mock paymaster and mock call") + pm := SetupMockPaymaster(t) + pm.c.EXPECT().CallContextWithSliceArgs( + t.Context(), + gomock.AssignableToTypeOf(new(ExecuteTransactionResponse)), + "paymaster_executeTransaction", + &request, + ).Return(nil). + SetArg(1, response) + + resp, err := pm.ExecuteTransaction(t.Context(), &request) + require.NoError(t, err) + + rawResp, err := json.Marshal(resp) + require.NoError(t, err) + assert.JSONEq(t, string(expectedResp), string(rawResp)) + }) + + //nolint:dupl // A function just to wrap the test body is overkill + t.Run("execute invoke transaction", func(t *testing.T) { + t.Parallel() + t.Log("building invoke request") + + t.Log("asserting the request marshalled is equal to the expected request") + expectedReqs := *internalUtils.TestUnmarshalJSONFileToType[[]json.RawMessage](t, "testdata/execute_txn/invoke-request.json", "params") + expectedReq := expectedReqs[0] + + // since the invoke request is more complex, let's take it from the file + var request ExecuteTransactionRequest + err := json.Unmarshal(expectedReq, &request) + require.NoError(t, err) + + rawReq, err := json.Marshal(request) + require.NoError(t, err) + + // assert if the MarshalJSON is correct + assert.JSONEq(t, string(expectedReq), string(rawReq)) + + t.Log("asserting the response marshalled is equal to the expected response") + expectedResp := *internalUtils.TestUnmarshalJSONFileToType[json.RawMessage](t, "testdata/execute_txn/response.json", "result") + + var response ExecuteTransactionResponse + err = json.Unmarshal(expectedResp, &response) + require.NoError(t, err) + + t.Log("setting up mock paymaster and mock call") + pm := SetupMockPaymaster(t) + pm.c.EXPECT().CallContextWithSliceArgs( + t.Context(), + gomock.AssignableToTypeOf(new(ExecuteTransactionResponse)), + "paymaster_executeTransaction", + &request, + ).Return(nil). + SetArg(1, response) + + t.Log("executing the invoke transaction in the mock paymaster") + resp, err := pm.ExecuteTransaction(t.Context(), &request) + require.NoError(t, err) + + rawResp, err := json.Marshal(resp) + require.NoError(t, err) + assert.JSONEq(t, string(expectedResp), string(rawResp)) + }) + + //nolint:dupl // A function just to wrap the test body is overkill + t.Run("execute deploy_and_invoke transaction", func(t *testing.T) { + t.Parallel() + t.Log("building deploy_and_invoke request") + + t.Log("asserting the request marshalled is equal to the expected request") + expectedReqs := *internalUtils.TestUnmarshalJSONFileToType[[]json.RawMessage](t, "testdata/execute_txn/deploy_and_invoke-request.json", "params") + expectedReq := expectedReqs[0] + + // since the deploy_and_invoke request is more complex, let's take it from the file + var request ExecuteTransactionRequest + err := json.Unmarshal(expectedReq, &request) + require.NoError(t, err) + + rawReq, err := json.Marshal(request) + require.NoError(t, err) + + // assert if the MarshalJSON is correct + assert.JSONEq(t, string(expectedReq), string(rawReq)) + + t.Log("asserting the response marshalled is equal to the expected response") + expectedResp := *internalUtils.TestUnmarshalJSONFileToType[json.RawMessage](t, "testdata/execute_txn/response.json", "result") + + var response ExecuteTransactionResponse + err = json.Unmarshal(expectedResp, &response) + require.NoError(t, err) + + t.Log("setting up mock paymaster and mock call") + pm := SetupMockPaymaster(t) + pm.c.EXPECT().CallContextWithSliceArgs( + t.Context(), + gomock.AssignableToTypeOf(new(ExecuteTransactionResponse)), + "paymaster_executeTransaction", + &request, + ).Return(nil). + SetArg(1, response) + + t.Log("executing the deploy_and_invoke transaction in the mock paymaster") + resp, err := pm.ExecuteTransaction(t.Context(), &request) + require.NoError(t, err) + + rawResp, err := json.Marshal(resp) + require.NoError(t, err) + assert.JSONEq(t, string(expectedResp), string(rawResp)) + }) + }) +} + +// same as account.PrecomputeAccountAddress, but to avoid circular dependency +func precomputeAccountAddress( + salt, + classHash *felt.Felt, + constructorCalldata []*felt.Felt, +) *felt.Felt { + return contracts.PrecomputeAddress(&felt.Zero, salt, classHash, constructorCalldata) +} + +// createDeploymentData creates the deployment data for a deploy transaction +func createDeploymentData(t *testing.T, pubKey *felt.Felt) *AccountDeploymentData { + t.Helper() + + t.Log("creating deployment data") + + // Argent account class hash that supports outside executions + classHash := internalUtils.TestHexToFelt( + t, + "0x036078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", + ) + constructorCalldata := []*felt.Felt{&felt.Zero, pubKey, new(felt.Felt).SetUint64(1)} + precAddress := precomputeAccountAddress(internalUtils.DeadBeef, classHash, constructorCalldata) + t.Log("precomputed address:", precAddress) + + deploymentData := &AccountDeploymentData{ + Address: precAddress, + ClassHash: classHash, + Salt: internalUtils.DeadBeef, + Calldata: constructorCalldata, + SignatureData: []*felt.Felt{}, + Version: Cairo1, + } + t.Logf("deployment data: %+v", deploymentData) + + return deploymentData +} + +// buildDeployTxn builds a deploy transaction calling the paymaster_buildTransaction method +// +//nolint:dupl // It is similar to buildInvokeTxn, but it has small differences +func buildDeployTxn( + t *testing.T, + pm *Paymaster, + pubKey *felt.Felt, +) *BuildTransactionResponse { + t.Helper() + + t.Log("building deploy transaction") + t.Log("public key:", pubKey) + + deploymentData := createDeploymentData(t, pubKey) + + t.Log("calling paymaster_buildTransaction method") + + resp, err := pm.BuildTransaction(t.Context(), &BuildTransactionRequest{ + Transaction: UserTransaction{ + Type: UserTxnDeploy, + Deployment: deploymentData, + }, + Parameters: UserParameters{ + Version: UserParamV1, + FeeMode: FeeMode{ + Mode: FeeModeSponsored, + }, + }, + }) + require.NoError(t, err) + t.Log("deploy transaction successfully built by the paymaster") + + return &resp +} + +// createInvokeData creates the invoke data for an invoke transaction +func createInvokeData(t *testing.T, accAdd *felt.Felt) *UserInvoke { + t.Helper() + + t.Log("creating invoke data") + + invokeData := &UserInvoke{ + UserAddress: accAdd, + Calls: []Call{ + { + // same ERC20 contract as in examples/simpleInvoke + To: internalUtils.TestHexToFelt( + t, + "0x0669e24364ce0ae7ec2864fb03eedbe60cfbc9d1c74438d10fa4b86552907d54", + ), + Selector: internalUtils.GetSelectorFromNameFelt("mint"), + Calldata: []*felt.Felt{new(felt.Felt).SetUint64(10000), &felt.Zero}, + }, + }, + } + t.Logf("invoke data: %+v", invokeData) + + return invokeData +} + +// buildInvokeTxn builds an invoke transaction calling the paymaster_buildTransaction method +// +//nolint:dupl // It is similar to buildDeployTxn, but it has small differences +func buildInvokeTxn( + t *testing.T, + pm *Paymaster, + accAdd *felt.Felt, +) *BuildTransactionResponse { + t.Helper() + + t.Log("building deploy transaction") + t.Log("account address:", accAdd) + + invokeData := createInvokeData(t, accAdd) + + t.Log("calling paymaster_buildTransaction method") + + resp, err := pm.BuildTransaction(t.Context(), &BuildTransactionRequest{ + Transaction: UserTransaction{ + Type: UserTxnInvoke, + Invoke: invokeData, + }, + Parameters: UserParameters{ + Version: UserParamV1, + FeeMode: FeeMode{ + Mode: FeeModeSponsored, + }, + }, + }) + require.NoError(t, err) + t.Log("invoke transaction successfully built by the paymaster") + + return &resp +} + +// buildDeployAndInvokeTxn builds a deploy and invoke transaction calling the paymaster_buildTransaction method +func buildDeployAndInvokeTxn( + t *testing.T, + pm *Paymaster, + pubKey *felt.Felt, +) *BuildTransactionResponse { + t.Helper() + + t.Log("building deploy_and_invoke transaction") + t.Log("public key:", pubKey) + + deploymentData := createDeploymentData(t, pubKey) + invokeData := createInvokeData(t, deploymentData.Address) + + t.Log("calling paymaster_buildTransaction method") + + resp, err := pm.BuildTransaction(t.Context(), &BuildTransactionRequest{ + Transaction: UserTransaction{ + Type: UserTxnDeployAndInvoke, + Deployment: deploymentData, + Invoke: invokeData, + }, + Parameters: UserParameters{ + Version: UserParamV1, + FeeMode: FeeMode{ + Mode: FeeModeSponsored, + Tip: &TipPriority{ + Priority: TipPriorityFast, + }, + }, + }, + }) + require.NoError(t, err) + t.Log("deploy_and_invoke transaction successfully built by the paymaster") + + return &resp +} diff --git a/paymaster/main_test.go b/paymaster/main_test.go index 9607338d..a6f8167b 100644 --- a/paymaster/main_test.go +++ b/paymaster/main_test.go @@ -6,8 +6,10 @@ import ( "os" "testing" + "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/starknet.go/client" "github.com/NethermindEth/starknet.go/internal/tests" + internalUtils "github.com/NethermindEth/starknet.go/internal/utils" "github.com/NethermindEth/starknet.go/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -59,6 +61,22 @@ func SetupMockPaymaster(t *testing.T) *MockPaymaster { return mpm } +// GetStrkAccountData returns the STRK account data from the environment variables. +// This is used for integration tests, where we need a real testnet account with STRK tokens. +func GetStrkAccountData(t *testing.T) (privKey, pubKey, accountAddress felt.Felt) { + t.Helper() + + strkPrivKey := os.Getenv("STARKNET_PRIVATE_KEY") + strkPubKey := os.Getenv("STARKNET_PUBLIC_KEY") + strkAccountAddress := os.Getenv("STARKNET_ACCOUNT_ADDRESS") + + tempPrivKey := internalUtils.TestHexToFelt(t, strkPrivKey) + tempPubKey := internalUtils.TestHexToFelt(t, strkPubKey) + tempAccountAddress := internalUtils.TestHexToFelt(t, strkAccountAddress) + + return *tempPrivKey, *tempPubKey, *tempAccountAddress +} + // CompareEnumsHelper compares an enum type with the expected value and error expected. func CompareEnumsHelper[T any](t *testing.T, input string, expected T, errorExpected bool) { t.Helper() diff --git a/paymaster/paymaster.go b/paymaster/paymaster.go index 6c56daaf..99dd616f 100644 --- a/paymaster/paymaster.go +++ b/paymaster/paymaster.go @@ -26,6 +26,10 @@ type paymasterInterface interface { ctx context.Context, request *BuildTransactionRequest, ) (BuildTransactionResponse, error) + ExecuteTransaction( + ctx context.Context, + request *ExecuteTransactionRequest, + ) (ExecuteTransactionResponse, error) IsAvailable(ctx context.Context) (bool, error) // More methods coming... } diff --git a/paymaster/testdata/execute_txn/deploy-request.json b/paymaster/testdata/execute_txn/deploy-request.json new file mode 100644 index 00000000..b6988bcf --- /dev/null +++ b/paymaster/testdata/execute_txn/deploy-request.json @@ -0,0 +1,30 @@ +{ + "jsonrpc": "2.0", + "method": "paymaster_executeTransaction", + "params": [ + { + "transaction": { + "type": "deploy", + "deployment": { + "address": "0x71387536587c0e164c75c3ed972fee9aff1b261192749a752ce880c16ac3bf2", + "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", + "salt": "0xdeadbeef", + "calldata": [ + "0x0", + "0x1cf6046c81f47d488c528e52066482f6756029bed10cf5df35608bb8eebac9", + "0x1" + ], + "version": 1 + } + }, + "parameters": { + "version": "0x1", + "fee_mode": { + "mode": "sponsored" + }, + "time_bounds": null + } + } + ], + "id": 1 +} \ No newline at end of file diff --git a/paymaster/testdata/execute_txn/deploy_and_invoke-request.json b/paymaster/testdata/execute_txn/deploy_and_invoke-request.json new file mode 100644 index 00000000..22cb3d4c --- /dev/null +++ b/paymaster/testdata/execute_txn/deploy_and_invoke-request.json @@ -0,0 +1,119 @@ +{ + "jsonrpc": "2.0", + "method": "paymaster_executeTransaction", + "params": [ +{ + "transaction": { + "type": "deploy_and_invoke", + "deployment": { + "address": "0x14de49678059510c1544d1da042be2b5d0c80e03906ddcf6a24151b966e1492", + "class_hash": "0x36078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f", + "salt": "0xdeadbeef", + "calldata": [ + "0x0", + "0x13e195a5933270cd3c3cf637b72c4caaf9983ccb2a0da6181659c1042d7718", + "0x1" + ], + "version": 1 + }, + "invoke": { + "user_address": "0x14de49678059510c1544d1da042be2b5d0c80e03906ddcf6a24151b966e1492", + "typed_data": { + "types": { + "Call": [ + { + "name": "To", + "type": "ContractAddress" + }, + { + "name": "Selector", + "type": "selector" + }, + { + "name": "Calldata", + "type": "felt*" + } + ], + "OutsideExecution": [ + { + "name": "Caller", + "type": "ContractAddress" + }, + { + "name": "Nonce", + "type": "felt" + }, + { + "name": "Execute After", + "type": "u128" + }, + { + "name": "Execute Before", + "type": "u128" + }, + { + "name": "Calls", + "type": "Call*" + } + ], + "StarknetDomain": [ + { + "name": "name", + "type": "shortstring" + }, + { + "name": "version", + "type": "shortstring" + }, + { + "name": "chainId", + "type": "shortstring" + }, + { + "name": "revision", + "type": "shortstring" + } + ] + }, + "primaryType": "OutsideExecution", + "domain": { + "name": "Account.execute_from_outside", + "version": "2", + "chainId": "SN_SEPOLIA", + "revision": "1" + }, + "message": { + "Caller": "0x75a180e18e56da1b1cae181c92a288f586f5fe22c18df21cf97886f1e4b316c", + "Calls": [ + { + "Calldata": [ + "0x2710", + "0x0" + ], + "Selector": "0x2f0b3c5710379609eb5495f1ecd348cb28167711b73609fe565a72734550354", + "To": "0x669e24364ce0ae7ec2864fb03eedbe60cfbc9d1c74438d10fa4b86552907d54" + } + ], + "Execute After": "0x1", + "Execute Before": "0x68d6b242", + "Nonce": "0x9ab1bbf59c0c9b82a74bcf6668c4f6c0" + } + }, + "signature": [ + "0x228fffde4140c340085a1a5db076816eda02876b4a19af7a96e396f24dcab76", + "0x5f44d545352b517ae1186b92de1ee9e12f6d030c5c90c99ef47063783344261" + ] + } + }, + "parameters": { + "version": "0x1", + "fee_mode": { + "mode": "sponsored", + "tip": "fast" + }, + "time_bounds": null + } +} + ], + "id": 1 +} \ No newline at end of file diff --git a/paymaster/testdata/execute_txn/invoke-request.json b/paymaster/testdata/execute_txn/invoke-request.json new file mode 100644 index 00000000..8c8b9e6b --- /dev/null +++ b/paymaster/testdata/execute_txn/invoke-request.json @@ -0,0 +1,108 @@ +{ + "jsonrpc": "2.0", + "method": "paymaster_executeTransaction", + "params": [ + { + "transaction": { + "type": "invoke", + "invoke": { + "user_address": "0x5c74db20fa8f151bfd3a7a462cf2e8d4578a88aa4bd7a1746955201c48d8e5e", + "typed_data": { + "types": { + "Call": [ + { + "name": "To", + "type": "ContractAddress" + }, + { + "name": "Selector", + "type": "selector" + }, + { + "name": "Calldata", + "type": "felt*" + } + ], + "OutsideExecution": [ + { + "name": "Caller", + "type": "ContractAddress" + }, + { + "name": "Nonce", + "type": "felt" + }, + { + "name": "Execute After", + "type": "u128" + }, + { + "name": "Execute Before", + "type": "u128" + }, + { + "name": "Calls", + "type": "Call*" + } + ], + "StarknetDomain": [ + { + "name": "name", + "type": "shortstring" + }, + { + "name": "version", + "type": "shortstring" + }, + { + "name": "chainId", + "type": "shortstring" + }, + { + "name": "revision", + "type": "shortstring" + } + ] + }, + "primaryType": "OutsideExecution", + "domain": { + "name": "Account.execute_from_outside", + "version": "2", + "chainId": "SN_SEPOLIA", + "revision": "1" + }, + "message": { + "Caller": "0x75a180e18e56da1b1cae181c92a288f586f5fe22c18df21cf97886f1e4b316c", + "Calls": [ + { + "Calldata": [ + "0x2710", + "0x0" + ], + "Selector": "0x2f0b3c5710379609eb5495f1ecd348cb28167711b73609fe565a72734550354", + "To": "0x669e24364ce0ae7ec2864fb03eedbe60cfbc9d1c74438d10fa4b86552907d54" + } + ], + "Execute After": "0x1", + "Execute Before": "0x68d6af77", + "Nonce": "0xbfb889ffaccd4489e04743330626a442" + } + }, + "signature": [ + "0x76ad9fcf754cbd7e56931eb3995ae5d8150732cfe8b6c669266084f2255c9c6", + "0x7ed536fdbb7b3479cf8e7ee0b9ca2067141ddb225174f073c28753403ecec21" + ] + } + }, + "parameters": { + "version": "0x1", + "fee_mode": { + "mode": "sponsored", + "tip": "normal" + }, + "time_bounds": null + } + } + ], + "id": 1 +} \ No newline at end of file diff --git a/paymaster/testdata/execute_txn/response.json b/paymaster/testdata/execute_txn/response.json new file mode 100644 index 00000000..a9795459 --- /dev/null +++ b/paymaster/testdata/execute_txn/response.json @@ -0,0 +1,8 @@ +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "transaction_hash": "0x303de97c66775a1e74fdb42df94fdf3cdb9a3225445461564fdcf46ae0ab419", + "tracking_id": "0x0" + } +} \ No newline at end of file