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
5 changes: 2 additions & 3 deletions cmd/facilitator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,9 @@ func run() {
}
log.Logger = zerolog.New(os.Stdout).With().Timestamp().Caller().Logger()

facilitator, err := facilitator.NewFacilitator(config.Scheme, config.Url, config.PrivateKey)
facilitator, err := facilitator.NewFacilitator(config.Scheme, config.Network, config.Url, config.PrivateKey)
if err != nil {
log.Fatal().Err(err).
Msg("Failed to create facilitator, shutting down...")
log.Fatal().Err(err).Msg("Failed to create facilitator, shutting down...")
}

api := api.NewServer(facilitator)
Expand Down
25 changes: 14 additions & 11 deletions facilitator/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,29 @@ type EVMFacilitator struct {
address common.Address
}

func NewEVMFacilitator(networkOrRpcUrl string, privateKeyHex string) (*EVMFacilitator, error) {
var url string
if chainInfo := evm.GetChainInfo(networkOrRpcUrl); chainInfo != nil {
url = chainInfo.DefaultUrl // if networkName is provided, use default URL
} else {
url = networkOrRpcUrl // if it's not a known network name, treat it as a URL
func NewEVMFacilitator(network string, url string, privateKeyHex string) (*EVMFacilitator, error) {
if network == "" && url == "" {
return nil, fmt.Errorf("network or rpc url must be provided")
} else if url == "" {
// if url is not provided, use default URL
if chainInfo := evm.GetChainInfo(network); chainInfo == nil {
return nil, fmt.Errorf("unsupported network name: %s", network)
} else {
url = chainInfo.DefaultUrl
}
}

client, err := ethclient.Dial(url)
if err != nil {
return nil, fmt.Errorf("failed to connect to Ethereum client: %w", err)
}

networkId, err := client.NetworkID(context.Background())
if err != nil {
return nil, fmt.Errorf("failed to get network ID: %w", err)
}
network := evm.GetChainName(networkId)
if network == "" {
return nil, fmt.Errorf("unsupported network ID: %s", networkId.String())
chainName := evm.GetChainName(networkId)
if chainName == "" || chainName != network {
return nil, fmt.Errorf("unsupported network: %s", network)
}

privateKey, err := hex.DecodeString(privateKeyHex)
Expand Down Expand Up @@ -143,7 +146,7 @@ func (t *EVMFacilitator) Verify(ctx context.Context, payload *types.PaymentPaylo
if err != nil {
return nil, err
}
if valid := evm.VerifySignature(pubkey, digest, sig); !valid {
if valid := evm.VerifySignature(pubkey, digest, sig[:64]); !valid {
return &types.PaymentVerifyResponse{
IsValid: false,
InvalidReason: types.ErrInvalidSignature.Error(),
Expand Down
44 changes: 33 additions & 11 deletions facilitator/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,45 @@ import (
)

const (
PrivateKey = ""
X402Version = 1
Network = "base-sepolia"
Token = "USDC"
PrivateKey = ""
Network = "base-sepolia"
Token = "USDC"
)

func TestEVMVerify(t *testing.T) {
facilitator, err := NewEVMFacilitator(Network, PrivateKey)
facilitator, err := NewEVMFacilitator(Network, "", PrivateKey)
require.NoError(t, err)

_ = facilitator
privKey, err := hex.DecodeString("")
require.NoError(t, err)
evmPayload, err := evm.NewEVMPayload(Network, Token,
"", "", big.NewInt(10000), evm.NewRawPrivateSigner(privKey))
require.NoError(t, err)

evmPayloadJson, err := json.Marshal(evmPayload)
require.NoError(t, err)

payload := &types.PaymentPayload{
X402Version: int(types.X402VersionV1),
Scheme: string(types.EVM),
Network: Network,
Payload: evmPayloadJson,
}
req := &types.PaymentRequirements{
Scheme: string(types.EVM),
Network: Network,
Asset: Token,
}

res, err := facilitator.Verify(t.Context(), payload, req)
require.NoError(t, err)
jsonRes, err := json.MarshalIndent(res, "", "\t")
require.NoError(t, err)
fmt.Println(string(jsonRes))
}

func TestEVMSettle(t *testing.T) {
facilitator, err := NewEVMFacilitator(Network, PrivateKey)
facilitator, err := NewEVMFacilitator(Network, "", PrivateKey)
require.NoError(t, err)

privKey, err := hex.DecodeString("")
Expand All @@ -38,10 +62,8 @@ func TestEVMSettle(t *testing.T) {
evmPayloadJson, err := json.Marshal(evmPayload)
require.NoError(t, err)

domainConfig := evm.GetDomainConfig(Network, Token)

payload := &types.PaymentPayload{
X402Version: X402Version,
X402Version: int(types.X402VersionV1),
Scheme: string(types.EVM),
Network: Network,
Payload: evmPayloadJson,
Expand All @@ -50,7 +72,7 @@ func TestEVMSettle(t *testing.T) {
req := &types.PaymentRequirements{
Scheme: string(types.EVM),
Network: Network,
Asset: domainConfig.VerifyingContract.String(),
Asset: Token,
}

res, err := facilitator.Settle(t.Context(), payload, req)
Expand Down
10 changes: 5 additions & 5 deletions facilitator/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ type Facilitator interface {
Supported() []*types.SupportedKind
}

func NewFacilitator(scheme types.Scheme, networkOrRpcUrl string, privateKeyHex string) (Facilitator, error) {
func NewFacilitator(scheme types.Scheme, network, rpcUrl string, privateKeyHex string) (Facilitator, error) {
switch scheme {
case types.EVM:
return NewEVMFacilitator(networkOrRpcUrl, privateKeyHex)
return NewEVMFacilitator(network, rpcUrl, privateKeyHex)
case types.Solana:
return NewSolanaFacilitator(networkOrRpcUrl, privateKeyHex)
return NewSolanaFacilitator(network, rpcUrl, privateKeyHex)
case types.Sui:
return NewSuiFacilitator(networkOrRpcUrl, privateKeyHex)
return NewSuiFacilitator(network, rpcUrl, privateKeyHex)
case types.Tron:
return NewTronFacilitator(networkOrRpcUrl, privateKeyHex)
return NewTronFacilitator(network, rpcUrl, privateKeyHex)
default:
return nil, fmt.Errorf("unsupporsed scheme: %s", scheme)
}
Expand Down
2 changes: 1 addition & 1 deletion facilitator/solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type SolanaFacilitator struct {
feePayer solTypes.Account
}

func NewSolanaFacilitator(url string, privateKeyHex string) (*SolanaFacilitator, error) {
func NewSolanaFacilitator(network string, url string, privateKeyHex string) (*SolanaFacilitator, error) {
client := client.NewClient(url)

privKey, err := hex.DecodeString(privateKeyHex)
Expand Down
2 changes: 1 addition & 1 deletion facilitator/sui.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
type SuiFacilitator struct {
}

func NewSuiFacilitator(url string, privateKeyHex string) (*SuiFacilitator, error) {
func NewSuiFacilitator(network string, url string, privateKeyHex string) (*SuiFacilitator, error) {
return &SuiFacilitator{}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion facilitator/tron.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
type TronFacilitator struct {
}

func NewTronFacilitator(url string, privateKeyHex string) (*TronFacilitator, error) {
func NewTronFacilitator(network string, url string, privateKeyHex string) (*TronFacilitator, error) {
return &TronFacilitator{}, nil
}

Expand Down
5 changes: 4 additions & 1 deletion scheme/evm/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ func sigToPub(hash, sig []byte) (*secp256k1.PublicKey, error) {
}
// Convert to secp256k1 input format with 'recovery id' v at the beginning.
btcsig := make([]byte, SignatureLength)
btcsig[0] = sig[RecoveryIDOffset] + 27
btcsig[0] = sig[RecoveryIDOffset]
if btcsig[0] < 27 {
btcsig[0] += 27
}
copy(btcsig[1:], sig)

pub, _, err := decred_ecdsa.RecoverCompact(btcsig, hash)
Expand Down
6 changes: 6 additions & 0 deletions types/scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ const (
Sui Scheme = "sui"
Tron Scheme = "tron"
)

type X402Version int

const (
X402VersionV1 X402Version = 1
)