Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,18 @@ APP_SERVICE_RPC_PRIVATEKEY="your_private_key_here"
# APP_SERVICE_RPC_REQUESTTIMEOUT=3s
# APP_SERVICE_RPC_LOGENABLED=false


# Config delegated contract address to enable the AccountAbstract service, otherwise it will be disabled.
# APP_SERVICE_ACCOUNTABSTRACT_DELEGATEDCONTRACT="your_contract_hex40_address_here"


# Config verifying paymaster contract address and whitelist to enable the VerifyingPaymaster service, otherwise it will be disabled.
# APP_SERVICE_VERIFYINGPAYMASTER_ADDRESS="paymaster_contract_hex40_address_here"
# APP_SERVICE_VERIFYINGPAYMASTER_CONTRACTWHITELIST="contract_hex40_address_1,contract_hex40_address_2"
# APP_SERVICE_VERIFYINGPAYMASTER_MAXGASCOST=100000000000000000 # 0.1 CFX
# APP_SERVICE_VERIFYINGPAYMASTER_SIGNATURETIMEOUT="5m"


# Config gas tank paymaster contract address to enable the GasTank service, otherwise it will be disabled.
# APP_SERVICE_GASTANK_ADDRESS="paymaster_contract_hex40_address_here"
# APP_SERVICE_GASTANK_SIGNATURETIMEOUT="5m"
Expand Down
65 changes: 65 additions & 0 deletions api/controller_verifying_paymaster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package api

import (
"github.com/Conflux-Chain/fluent-backend/service"
"github.com/Conflux-Chain/go-conflux-util/api"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/gin-gonic/gin"
)

type VerifyingPaymasterController struct {
services service.Services
}

func NewVerifyingPaymasterController(services service.Services) *VerifyingPaymasterController {
return &VerifyingPaymasterController{services}
}

// Stub returns the stub paymasterData of verifying paymaster for gas estimation.
//
// @ID aaPaymasterStub
// @Summary Returns the stub paymasterData of verifying paymaster for gas estimation
// @Description Returns the stub paymasterData of verifying paymaster for gas estimation.
// @Tags Paymaster
// @Accept json
// @Produce json
// @Success 200 {object} api.BusinessError{data=string} "Paymaster and data (0x-prefixed hex)"
// @Failure 600 {object} api.BusinessError{data=string} "Internal server error"
// @Router /aa/paymaster/stub [get]
func (controller *VerifyingPaymasterController) Stub(c *gin.Context) (any, error) {
stub := controller.services.VerifyingPaymaster.Stub()

return hexutil.Encode(stub), nil
}

// Sign validates the given user operation, signs the paymasterData and returns the reassembled paymasterData.
//
// @ID aaPaymasterSign
// @Summary Sign paymasterData of given user operation and return reassembled paymasterData
// @Description Validates the given UserOperation, adds paymaster signature, and returns reassembled paymasterData.
// @Description Encoding format (129 bytes): paymaster(20) || paymasterVerificationGasLimit(16) || paymasterPostOpGasLimit(16) || validAfter(6) || validUntil(6) || signature(65).
// @Tags Paymaster
// @Accept json
// @Produce json
// @Param userOp body UserOperationWithAuth true "UserOperation for paymaster signing"
// @Success 200 {object} api.BusinessError{data=string} "Signed and reassembled paymasterData (0x-prefixed hex, 129 bytes)"
// @Failure 600 {object} api.BusinessError{data=string} "Internal server error"
// @Router /aa/paymaster/sign [post]
func (controller *VerifyingPaymasterController) Sign(c *gin.Context) (any, error) {
var input UserOperationWithAuth

if err := c.ShouldBind(&input); err != nil {
return nil, api.ErrValidation(err)
}

userOp := input.ToPackedUserOperation()
delegatedContract := common.HexToAddress(input.DelegatedContract)
Comment thread
boqiu marked this conversation as resolved.

paymasterData, err := controller.services.VerifyingPaymaster.Sign(userOp, delegatedContract)
if err != nil {
return nil, err
}

return hexutil.Encode(paymasterData), nil
}
11 changes: 11 additions & 0 deletions api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type GasTankPrepareRefundRequest struct {
type UserOperation struct {
Sender string `json:"sender" binding:"required,hex,len=42"`
Nonce string `json:"nonce" binding:"required,hex,min=4"`
InitCode string `json:"initCode" binding:"required,hex,min=2"`
CallData string `json:"callData" binding:"required,hex,min=2"`
VerificationGasLimit string `json:"verificationGasLimit" binding:"required,hex,min=4,max=34"`
CallGasLimit string `json:"callGasLimit" binding:"required,hex,min=4,max=34"`
Expand Down Expand Up @@ -112,13 +113,15 @@ func (userOp *UserOperation) ToPackedUserOperation() contract.PackedUserOperatio
hexToBig(userOp.PaymasterVerificationGasLimit).FillBytes(paymasterBuf[20:36])
hexToBig(userOp.PaymasterPostOpGasLimit).FillBytes(paymasterBuf[36:52])

initCode, _ := hexutil.Decode(userOp.InitCode)
callData, _ := hexutil.Decode(userOp.CallData)
paymasterData, _ := hexutil.Decode(userOp.PaymasterData)
signature, _ := hexutil.Decode(userOp.Signature)

return contract.PackedUserOperation{
Sender: common.HexToAddress(userOp.Sender),
Nonce: hexToBig(userOp.Nonce),
InitCode: initCode,
CallData: callData,
AccountGasLimits: accountGasLimits,
PreVerificationGas: hexToBig(userOp.PreVerificationGas),
Expand All @@ -128,6 +131,14 @@ func (userOp *UserOperation) ToPackedUserOperation() contract.PackedUserOperatio
}
}

type UserOperationWithAuth struct {
UserOperation

// DelegatedContract is used when user operation carrying an EIP-7702 auth message to upgrade EOA to a smart account.
// Otherwise, use empty address "0x0000000000000000000000000000000000000000".
DelegatedContract string `json:"delegatedContract" binding:"required,hex,len=42"`
}

type TokenPayConfig struct {
// Tokens is the list of ERC20 token contracts supported for token-pay. Note, the tokens[0] is the default USDT token used for quoting and payment.
Tokens []string `json:"tokens"`
Expand Down
21 changes: 14 additions & 7 deletions api/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,24 @@ func MustServe(config Config, services service.Services) {

// account abstract - auth
if services.AccountAbstract != nil {
aaController := NewAccountAbstractController(services)
api.POST("/aa/auth", rateLimiters.Middleware("setAuth"), middleware.Metrics("api.aa.auth.send"), middleware.Wrap(aaController.SendAuth))
api.GET("/aa/auth/:txHash", middleware.Metrics("api.aa.auth.status"), middleware.Wrap(aaController.GetAuthStatus))
controller := NewAccountAbstractController(services)
api.POST("/aa/auth", rateLimiters.Middleware("setAuth"), middleware.Metrics("api.aa.auth.send"), middleware.Wrap(controller.SendAuth))
api.GET("/aa/auth/:txHash", middleware.Metrics("api.aa.auth.status"), middleware.Wrap(controller.GetAuthStatus))
}

// verifying paymaster
if services.VerifyingPaymaster != nil {
controller := NewVerifyingPaymasterController(services)
api.GET("/aa/paymaster/stub", middleware.Metrics("api.aa.paymaster.stub"), middleware.Wrap(controller.Stub))
api.POST("/aa/paymaster/sign", rateLimiters.Middleware("signUserOp"), middleware.Metrics("api.aa.paymaster.sign"), middleware.Wrap(controller.Sign))
}

// Gas tank
if services.GasTank != nil {
gasTankController := NewGasTankController(services)
api.POST("/aa/gastank/prepare/credit", middleware.Metrics("api.aa.gastank.prepare.credit"), middleware.Wrap(gasTankController.PrepareCredit))
api.POST("/aa/gastank/prepare/refund", middleware.Metrics("api.aa.gastank.prepare.refund"), middleware.Wrap(gasTankController.PrepareRefund))
api.POST("/aa/gastank/sign", rateLimiters.Middleware("signUserOp"), middleware.Metrics("api.aa.gastank.signature"), middleware.Wrap(gasTankController.Sign))
controller := NewGasTankController(services)
api.POST("/aa/gastank/prepare/credit", middleware.Metrics("api.aa.gastank.prepare.credit"), middleware.Wrap(controller.PrepareCredit))
api.POST("/aa/gastank/prepare/refund", middleware.Metrics("api.aa.gastank.prepare.refund"), middleware.Wrap(controller.PrepareRefund))
api.POST("/aa/gastank/sign", rateLimiters.Middleware("signUserOp"), middleware.Metrics("api.aa.gastank.sign"), middleware.Wrap(controller.Sign))
}

// token pay
Expand Down
29 changes: 1 addition & 28 deletions contract/SimpleSmartAccount7702.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,6 @@
"name": "AddressEmptyCode",
"type": "error"
},
{
"inputs": [],
"name": "ECDSAInvalidSignature",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "length",
"type": "uint256"
}
],
"name": "ECDSAInvalidSignatureLength",
"type": "error"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "ECDSAInvalidSignatureS",
"type": "error"
},
{
"inputs": [],
"name": "FailedCall",
Expand Down Expand Up @@ -85,7 +58,7 @@
},
{
"internalType": "bytes",
"name": "data",
"name": "callData",
"type": "bytes"
}
],
Expand Down
Loading
Loading