From 394cd6cff52ee36aab9fff5d40ed5867fc0883f2 Mon Sep 17 00:00:00 2001 From: Chibuotu Amadi Date: Thu, 20 Feb 2025 19:15:39 +0100 Subject: [PATCH] refactor: update AA service integration and test URLs - Update Golang base image to 1.23.0 in Dockerfiles - Remove outdated migration file - Modify userop utility functions to focus on Biconomy AA service - Update test cases with new Biconomy bundler and paymaster URLs - Simplify AA service detection and endpoint handling - Remove Stackup-specific code paths --- Dockerfile | 2 +- Dockerfile.prod | 2 +- ...2054044_add_bundler_and_paymaster_urls.sql | 28 -------- .../migrations/20250220014823_aa_to_db.sql | 2 + ent/migrate/migrations/atlas.sum | 3 +- utils/userop.go | 64 +++---------------- utils/userop_test.go | 56 ++++++---------- 7 files changed, 37 insertions(+), 120 deletions(-) delete mode 100644 ent/migrate/migrations/20250212054044_add_bundler_and_paymaster_urls.sql create mode 100644 ent/migrate/migrations/20250220014823_aa_to_db.sql diff --git a/Dockerfile b/Dockerfile index ff466145..16b62f85 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Start from golang base image -FROM golang:1.22.11-bullseye +FROM golang:1.23.0-bullseye # Install the air binary RUN curl -sSfL https://raw.githubusercontent.com/cosmtrek/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin diff --git a/Dockerfile.prod b/Dockerfile.prod index 38ba40e5..1d445730 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,5 +1,5 @@ # Start from golang base image -FROM golang:1.22.11-alpine3.21 as builder +FROM golang:1.23-alpine3.21 as builder # Install git. RUN apk update && apk add --no-cache git diff --git a/ent/migrate/migrations/20250212054044_add_bundler_and_paymaster_urls.sql b/ent/migrate/migrations/20250212054044_add_bundler_and_paymaster_urls.sql deleted file mode 100644 index c0c0eed3..00000000 --- a/ent/migrate/migrations/20250212054044_add_bundler_and_paymaster_urls.sql +++ /dev/null @@ -1,28 +0,0 @@ --- Modify "institutions" table -ALTER TABLE "institutions" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1; --- Modify "linked_addresses" table -ALTER TABLE "linked_addresses" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Rename an index from "lockpaymentorder_gateway_id_ra_65d1cd4f9b7a0ff4525b6f2bc506afdc" to "lockpaymentorder_gateway_id_rate_tx_hash_block_number_instituti" -ALTER INDEX "lockpaymentorder_gateway_id_ra_65d1cd4f9b7a0ff4525b6f2bc506afdc" RENAME TO "lockpaymentorder_gateway_id_rate_tx_hash_block_number_instituti"; --- Modify "networks" table -ALTER TABLE "networks" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART, ADD COLUMN "bundler_url" character varying NULL, ADD COLUMN "paymaster_url" character varying NULL; --- Modify "payment_order_recipients" table -ALTER TABLE "payment_order_recipients" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Modify "provider_order_tokens" table -ALTER TABLE "provider_order_tokens" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Modify "provider_ratings" table -ALTER TABLE "provider_ratings" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Modify "provision_buckets" table -ALTER TABLE "provision_buckets" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Modify "receive_addresses" table -ALTER TABLE "receive_addresses" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Modify "sender_order_tokens" table -ALTER TABLE "sender_order_tokens" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Rename an index from "senderordertoken_sender_profil_c0e12093989225f7a56a29b8ff69c3bf" to "senderordertoken_sender_profile_order_tokens_token_sender_setti" -ALTER INDEX "senderordertoken_sender_profil_c0e12093989225f7a56a29b8ff69c3bf" RENAME TO "senderordertoken_sender_profile_order_tokens_token_sender_setti"; --- Modify "tokens" table -ALTER TABLE "tokens" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Modify "webhook_retry_attempts" table -ALTER TABLE "webhook_retry_attempts" ALTER COLUMN "id" SET GENERATED BY DEFAULT SET START WITH 1 SET INCREMENT BY 1 RESTART; --- Drop "ent_types" table -DROP TABLE "ent_types"; diff --git a/ent/migrate/migrations/20250220014823_aa_to_db.sql b/ent/migrate/migrations/20250220014823_aa_to_db.sql new file mode 100644 index 00000000..ea999e94 --- /dev/null +++ b/ent/migrate/migrations/20250220014823_aa_to_db.sql @@ -0,0 +1,2 @@ +-- Modify "networks" table +ALTER TABLE "networks" ADD COLUMN "bundler_url" character varying NULL, ADD COLUMN "paymaster_url" character varying NULL; diff --git a/ent/migrate/migrations/atlas.sum b/ent/migrate/migrations/atlas.sum index 9f8d7d1a..080a01a3 100644 --- a/ent/migrate/migrations/atlas.sum +++ b/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:0TorEmKfcV3nUsITm2cbp5Yv14Y/YkjEuBOXQsvpcH4= +h1:dR+GjJoPfeuDFy3ZE8clPJfTvnRV/Q1LPkeT1nxVxJ4= 20240118234246_initial.sql h1:dYuYBqns33WT+3p8VQvbKUP62k3k6w6h8S+FqNqgSvU= 20240130122324_order_from_address.sql h1:mMVI2iBUd1roIYLUqu0d2jZ7+B6exppRN8qqn+aIHx4= 20240202010744_fees_on_order.sql h1:P7ngxZKqDKefBM5vk6M3kbWeMPVwbZ4MZVcLBjEfS34= @@ -43,3 +43,4 @@ h1:0TorEmKfcV3nUsITm2cbp5Yv14Y/YkjEuBOXQsvpcH4= 20241011010524_linked_addresses.sql h1:JvCXlyrRTwXqYPwz5YoGoh+/KEaGiswxmRp//wyxyhU= 20241226183354_add_reference.sql h1:oS3oVJcfD6q7EvDQrTWudCEf3HjBm/yI8XTlPvGqLC8= 20250205002723_lof_optional_txid.sql h1:Ew1wBRx/K1cBIA//BAOjReVJW11idWCIkLnuaF8FdXY= +20250220014823_aa_to_db.sql h1:pqbdJZe7CXgXI7ak+ggHUhjncM7l5Kiw02S4N/hqCXU= diff --git a/utils/userop.go b/utils/userop.go index cb3b2815..ee65265d 100644 --- a/utils/userop.go +++ b/utils/userop.go @@ -103,8 +103,7 @@ func InitializeUserOperation(ctx context.Context, client types.RPCClient, rpcUrl return userOperation, nil } -// SponsorUserOperation sponsors the user operation from stackup -// ref: https://docs.stackup.sh/docs/paymaster-api-rpc-methods#pm_sponsoruseroperation +// SponsorUserOperation sponsors the user operation with different AA services func SponsorUserOperation(userOp *userop.UserOperation, mode string, token string, chainId int64) error { _, paymasterUrl, err := getEndpoints(chainId) @@ -126,23 +125,6 @@ func SponsorUserOperation(userOp *userop.UserOperation, mode string, token strin var requestParams []interface{} switch aaService { - case "stackup": - switch mode { - case "sponsored": - payload = map[string]interface{}{ - "type": "payg", - } - case "erc20": - if token == "" { - return fmt.Errorf("token address is required") - } - payload = map[string]interface{}{ - "type": "erc20token", - "token": token, - } - default: - return fmt.Errorf("invalid mode") - } case "biconomy": switch mode { case "sponsored": @@ -201,26 +183,6 @@ func SponsorUserOperation(userOp *userop.UserOperation, mode string, token strin } switch aaService { - case "stackup": - type Response struct { - PaymasterAndData string `json:"paymasterAndData" mapstructure:"paymasterAndData"` - PreVerificationGas string `json:"preVerificationGas" mapstructure:"preVerificationGas"` - VerificationGasLimit string `json:"verificationGasLimit" mapstructure:"verificationGasLimit"` - CallGasLimit string `json:"callGasLimit" mapstructure:"callGasLimit"` - } - - var response Response - err = json.Unmarshal(result, &response) - - if err != nil { - return fmt.Errorf("failed to unmarshal response: %w", err) - } - - userOp.CallGasLimit, _ = new(big.Int).SetString(response.CallGasLimit, 0) - userOp.VerificationGasLimit, _ = new(big.Int).SetString(response.VerificationGasLimit, 0) - userOp.PreVerificationGas, _ = new(big.Int).SetString(response.PreVerificationGas, 0) - userOp.PaymasterAndData = common.FromHex(response.PaymasterAndData) - case "biconomy": var response map[string]interface{} err = json.Unmarshal(result, &response) @@ -272,13 +234,11 @@ func SendUserOperation(userOp *userop.UserOperation, chainId int64) (string, str return "", "", 0, fmt.Errorf("invalid AA service URL pattern: %w", err) } + op, _ := userOp.MarshalJSON() + log.Println("userOp", string(op)) + var requestParams []interface{} switch aaService { - case "stackup": - requestParams = []interface{}{ - userOp, - orderConf.EntryPointContractAddress.Hex(), - } case "biconomy": requestParams = []interface{}{ userOp, @@ -291,6 +251,8 @@ func SendUserOperation(userOp *userop.UserOperation, chainId int64) (string, str return "", "", 0, fmt.Errorf("unsupported AA service: %s", aaService) } + log.Println("requestParams", requestParams) + var result json.RawMessage err = client.Call(&result, "eth_sendUserOperation", requestParams...) if err != nil { @@ -335,7 +297,6 @@ func SendUserOperation(userOp *userop.UserOperation, chainId int64) (string, str // GetUserOperationByReceipt fetches the user operation by hash func GetUserOperationByReceipt(userOpHash string, chainId int64) (map[string]interface{}, error) { - bundlerUrl, _, err := getEndpoints(chainId) if err != nil { return nil, fmt.Errorf("failed to get endpoints for chain ID %d: %w", chainId, err) @@ -427,8 +388,7 @@ func GetUserOperationByReceipt(userOpHash string, chainId int64) (map[string]int }, nil } -// GetPaymasterAccount fetches the paymaster account from stackup -// ref: https://docs.stackup.sh/docs/paymaster-api-rpc-methods#pm_accounts +// GetPaymasterAccount returns the paymaster account for the given chain ID func GetPaymasterAccount(chainId int64) (string, error) { _, paymasterUrl, err := getEndpoints(chainId) @@ -568,23 +528,20 @@ func getEndpoints(chainID int64) (string, string, error) { } // Validate URL patterns - aaService, err := detectAAService(network.BundlerURL) + _, err = detectAAService(network.BundlerURL) if err != nil { return "", "", fmt.Errorf("invalid bundler URL pattern: %w", err) } - log.Printf("Chain ID %d using AA service: %s", chainID, aaService) return network.BundlerURL, network.PaymasterURL, nil } // detectAAService detects the AA service based on the provided URL pattern func detectAAService(url string) (string, error) { switch { - case strings.Contains(url, "api.stackup"): - return "stackup", nil - case strings.Contains(url, "api.biconomy"): + case strings.Contains(url, "biconomy.io"): return "biconomy", nil - case strings.Contains(url, "api.pimlico"): + case strings.Contains(url, "api.pimlico.io"): return "pimlico", nil default: return "", fmt.Errorf("unsupported AA service URL pattern: %s", url) @@ -592,7 +549,6 @@ func detectAAService(url string) (string, error) { } // getNonce returns the nonce for the given sender -// https://docs.stackup.sh/docs/useroperation-nonce func getNonce(client types.RPCClient, sender common.Address) (nonce *big.Int, err error) { entrypoint, err := contracts.NewEntryPoint(orderConf.EntryPointContractAddress, client.(bind.ContractBackend)) if err != nil { diff --git a/utils/userop_test.go b/utils/userop_test.go index a0674545..f1bee8f8 100644 --- a/utils/userop_test.go +++ b/utils/userop_test.go @@ -35,8 +35,8 @@ func TestUserOp(t *testing.T) { SetIdentifier("ethereum"). SetIsTestnet(true). SetRPCEndpoint("https://mock-rpc-url"). - SetBundlerURL("http://api.stackup-bundler-url"). - SetPaymasterURL("http://api.stackup-paymaster-url"). + SetBundlerURL("http://bundler.biconomy.io"). + SetPaymasterURL("http://paymaster.biconomy.io"). SetFee(decimal.NewFromInt(1)). Save(ctx) assert.NoError(t, err) @@ -47,8 +47,8 @@ func TestUserOp(t *testing.T) { t.Run("test getEndpoints with mock data", func(t *testing.T) { bundlerURL, paymasterURL, err := getEndpoints(1) assert.NoError(t, err) - assert.Equal(t, "http://api.stackup-bundler-url", bundlerURL) - assert.Equal(t, "http://api.stackup-paymaster-url", paymasterURL) + assert.Equal(t, "http://bundler.biconomy.io", bundlerURL) + assert.Equal(t, "http://paymaster.biconomy.io", paymasterURL) }) t.Run("when chainID is supported getEndpoints", func(t *testing.T) { bundlerID, paymaster, err := getEndpoints(1) @@ -86,7 +86,7 @@ func TestUserOp(t *testing.T) { } // register mock response - httpmock.RegisterResponder("POST", "http://api.stackup-bundler-url", + httpmock.RegisterResponder("POST", "http://bundler.biconomy.io", func(r *http.Request) (*http.Response, error) { bytes, err := io.ReadAll(r.Body) if err != nil { @@ -95,7 +95,7 @@ func TestUserOp(t *testing.T) { if strings.Contains(string(bytes), "eth_sendUserOperation") { - aaService, err := detectAAService("http://api.stackup-bundler-url") + aaService, err := detectAAService("http://bundler.biconomy.io") if err != nil { return nil, err } @@ -186,7 +186,7 @@ func TestUserOp(t *testing.T) { } // register mock response - httpmock.RegisterResponder("POST", "http://api.stackup-paymaster-url", + httpmock.RegisterResponder("POST", "http://paymaster.biconomy.io", func(r *http.Request) (*http.Response, error) { bytes, err := io.ReadAll(r.Body) if err != nil { @@ -194,38 +194,24 @@ func TestUserOp(t *testing.T) { } if strings.Contains(string(bytes), "pm_sponsorUserOperation") { - aaService, err := detectAAService("http://api.stackup-paymaster-url") + aaService, err := detectAAService("http://paymaster.biconomy.io") if err != nil { return nil, err } + assert.Equal(t, "biconomy", aaService) + assert.True(t, strings.Contains(string(bytes), "INFINITISM")) - if aaService == "biconomy" { - assert.True(t, strings.Contains(string(bytes), "INFINITISM")) - resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ - "jsonrpc": "2.0", - "id": 1, - "result": map[string]interface{}{ - "paymasterAndData": "0x00000f79b7faf42eebadba19acc07cd08af447890000000000000000000...", - "preVerificationGas": "186034", - "verificationGasLimit": 395693, - "callGasLimit": 55412, - }, - }) - return resp, err - } else { - assert.True(t, strings.Contains(string(bytes), "pm_sponsorUserOperation")) - resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ - "jsonrpc": "2.0", - "id": 1, - "result": map[string]interface{}{ - "paymasterAndData": "0x00000f79b7faf42eebadba19acc07cd08af447890000000000000000000...", - "preVerificationGas": "0x1234", - "verificationGasLimit": "0x1234", - "callGasLimit": "0x1234", - }, - }) - return resp, err - } + resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{ + "jsonrpc": "2.0", + "id": 1, + "result": map[string]interface{}{ + "paymasterAndData": "0x00000f79b7faf42eebadba19acc07cd08af447890000000000000000000...", + "preVerificationGas": "186034", + "verificationGasLimit": 395693, + "callGasLimit": 55412, + }, + }) + return resp, err } return httpmock.NewBytesResponse(200, []byte(`{"jsonrpc": "2.0","id": 1,"result":[]}`)), nil