diff --git a/api/handlers/signing.go b/api/handlers/signing.go index 10aeeed9..ba617f47 100644 --- a/api/handlers/signing.go +++ b/api/handlers/signing.go @@ -34,6 +34,7 @@ type SigningBody struct { Calldata string `json:"calldata"` DepositTxHash string `json:"depositTxHash"` BorrowAmount *BigInt `json:"borrowAmount"` + BorrowToken string `json:"borrowToken"` RepaymentChainId uint64 `json:"repaymentChainId"` Deadline uint64 `json:"deadline"` TokenOut string `json:"tokenOut"` @@ -102,6 +103,7 @@ func (h *SigningHandler) HandleSigning(w http.ResponseWriter, r *http.Request) { Destination: b.ChainId, Deadline: b.Deadline, BorrowAmount: b.BorrowAmount.Int, + BorrowToken: b.BorrowToken, }) } case LighterProtocol: diff --git a/app/app.go b/app/app.go index b77b1a69..55c52410 100644 --- a/app/app.go +++ b/app/app.go @@ -17,14 +17,18 @@ import ( "github.com/ethereum/go-ethereum/common" ethereumCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/libp2p/go-libp2p/core/crypto" + "github.com/lmittmann/w3" "github.com/rs/zerolog/log" "github.com/spf13/viper" "github.com/sprintertech/lifi-solver/pkg/pricing" "github.com/sprintertech/lifi-solver/pkg/protocols" + "github.com/sprintertech/lifi-solver/pkg/protocols/erc4626" "github.com/sprintertech/lifi-solver/pkg/protocols/lifi/validation" "github.com/sprintertech/lifi-solver/pkg/router" "github.com/sprintertech/lifi-solver/pkg/token" + "github.com/sprintertech/lifi-solver/pkg/tokenpricing/aggregator" "github.com/sprintertech/lifi-solver/pkg/tokenpricing/pyth" + "github.com/sprintertech/lifi-solver/pkg/tokenpricing/vault" solverConfig "github.com/sprintertech/solver-config/go/config" "github.com/sprintertech/sprinter-signing/api" "github.com/sprintertech/sprinter-signing/api/handlers" @@ -65,6 +69,10 @@ import ( var Version string +const ( + ETHEREUM uint64 = 1 +) + //nolint:gocognit func Run() error { var err error @@ -167,6 +175,11 @@ func Run() error { mpcAddress = common.HexToAddress(solverConfig.ProtocolsMetadata.Sprinter.MpcAddress) } + usdPricer := pyth.NewClient(ctx) + err = usdPricer.Start(ctx) + panicOnError(err) + multiPricer := aggregator.New(usdPricer) + var hubPoolContract across.TokenMatcher acrossPools := make(map[uint64]common.Address) lifiOutputSettlers := make(map[uint64]common.Address) @@ -271,14 +284,27 @@ func Run() error { } if c.LifiOutputSettler != "" { - usdPricer := pyth.NewClient(ctx) - err = usdPricer.Start(ctx) - panicOnError(err) + + if *c.GeneralChainConfig.Id == ETHEREUM { + w3Client, err := w3.Dial(c.GeneralChainConfig.Endpoint) + panicOnError(err) + + roycoVault := common.HexToAddress(solverConfig.ProtocolsMetadata.Royco.Vault) + roycoVaultContract := erc4626.NewErc4626Contract(w3Client, &roycoVault) + vaultPricer := vault.NewPricer( + usdPricer, + []vault.Vault{ + vault.NewErc4626(roycoVaultContract, "srRoyUSDC", "USDC", 6), + }, + time.Second*30, + ) + multiPricer.Add(vaultPricer, "srRoyUSDC") + } + resolver := token.NewTokenResolver(solverConfig, multiPricer) lifiConfig, err := lifiConfig.GetSolverConfig(solverConfig, protocols.LifiEscrow, lifiConfig.PulsarSolver) panicOnError(err) - resolver := token.NewTokenResolver(solverConfig, usdPricer) orderPricer := pricing.NewStandardPricer(resolver) lifiApi := lifi.NewLifiEventFetcher( client, @@ -295,7 +321,7 @@ func Run() error { communication, keyshareStore, watcher, - tokenStore, + resolver, lifiApi, orderPricer, router.NewRouter(resolver, nil, nil, lifiConfig.Routes), diff --git a/chains/evm/message/lifiEscrow.go b/chains/evm/message/lifiEscrow.go index 498a66f8..1f2f1497 100644 --- a/chains/evm/message/lifiEscrow.go +++ b/chains/evm/message/lifiEscrow.go @@ -16,7 +16,6 @@ import ( "github.com/sprintertech/sprinter-signing/chains/evm/calls/consts" "github.com/sprintertech/sprinter-signing/chains/evm/signature" "github.com/sprintertech/sprinter-signing/comm" - "github.com/sprintertech/sprinter-signing/config" "github.com/sprintertech/sprinter-signing/tss" "github.com/sprintertech/sprinter-signing/tss/ecdsa/signing" "github.com/sygmaprotocol/sygma-core/relayer/message" @@ -25,6 +24,7 @@ import ( "github.com/sprintertech/lifi-solver/pkg/pricing" "github.com/sprintertech/lifi-solver/pkg/protocols/lifi" "github.com/sprintertech/lifi-solver/pkg/router" + "github.com/sprintertech/lifi-solver/pkg/token" ) type OrderFetcher interface { @@ -43,7 +43,7 @@ type LifiEscrowMessageHandler struct { confirmationWatcher ConfirmationWatcher lifiAddresses map[uint64]common.Address - tokenStore config.TokenStore + tokenResolver token.TokenResolver mpcAddress common.Address orderFetcher OrderFetcher @@ -64,7 +64,7 @@ func NewLifiEscrowMessageHandler( comm comm.Communication, fetcher signing.SaveDataFetcher, confirmationWatcher ConfirmationWatcher, - tokenStore config.TokenStore, + tokenResolver token.TokenResolver, orderFetcher OrderFetcher, orderPricer pricing.OrderPricer, router router.OrderRouter, @@ -80,7 +80,7 @@ func NewLifiEscrowMessageHandler( comm: comm, fetcher: fetcher, confirmationWatcher: confirmationWatcher, - tokenStore: tokenStore, + tokenResolver: tokenResolver, orderFetcher: orderFetcher, orderPricer: orderPricer, validator: validator, @@ -109,7 +109,7 @@ func (h *LifiEscrowMessageHandler) HandleMessage(m *message.Message) (*proposal. return nil, err } - err = h.verifyOrder(order, data.BorrowAmount) + err = h.verifyOrder(order) if err != nil { data.ErrChn <- err return nil, err @@ -121,7 +121,11 @@ func (h *LifiEscrowMessageHandler) HandleMessage(m *message.Message) (*proposal. return nil, err } - borrowToken, destChainID, err := h.borrowToken(order) + borrowToken, destChainID, err := h.borrowToken( + data, + order, + orderValue, + ) if err != nil { data.ErrChn <- err return nil, err @@ -195,20 +199,65 @@ func (h *LifiEscrowMessageHandler) HandleMessage(m *message.Message) (*proposal. return nil, nil } -func (h *LifiEscrowMessageHandler) borrowToken(order *lifi.LifiOrder) (common.Address, uint64, error) { +func (h *LifiEscrowMessageHandler) borrowToken( + data *LifiEscrowData, + order *lifi.LifiOrder, + amountInValue float64, +) (common.Address, uint64, error) { destChainID := order.Order.Outputs[0].ChainID - tokenIn := common.BytesToAddress(order.GenericInputs[0].TokenAddress[:]) - symbol, _, err := h.tokenStore.ConfigByAddress(h.chainID, tokenIn) + + tokenIn, err := h.tokenResolver.Token( + order.GenericInputs[0].ChainID, + *order.GenericInputs[0].TokenAddress) if err != nil { return common.Address{}, destChainID, err } - destinationBorrowToken, err := h.tokenStore.ConfigBySymbol(destChainID, symbol) + tokenOut, err := h.tokenResolver.Token( + order.GenericOutputs[0].ChainID, + *order.GenericOutputs[0].TokenAddress) if err != nil { return common.Address{}, destChainID, err } - return destinationBorrowToken.Address, destChainID, err + if data.BorrowToken != tokenIn.Symbol && data.BorrowToken != tokenOut.Symbol { + return common.Address{}, destChainID, fmt.Errorf( + "borrow token %s must be either input %s or output token symbol %s", + data.BorrowToken, + tokenIn.Symbol, + tokenOut.Symbol) + } + + if data.BorrowToken == tokenIn.Symbol { + dstToken, err := h.tokenResolver.TokenFromSymbol( + order.GenericOutputs[0].ChainID, + tokenIn.Symbol) + if err != nil { + return common.Address{}, destChainID, err + } + + scaledInputAmount := chains.ScaleTokenAmount( + order.GenericInputs[0].Amount, + tokenIn.Decimals, + dstToken.Decimals) + if scaledInputAmount.Cmp(data.BorrowAmount) == -1 { + return common.Address{}, destChainID, fmt.Errorf( + "order input is less than requested borrow amount") + } + + return common.BytesToAddress(dstToken.Address[:]), destChainID, nil + } else { + amountOutValue := tokenOut.AmountToUSD(data.BorrowAmount) + if amountInValue < amountOutValue { + return common.Address{}, destChainID, fmt.Errorf( + "order with destination borrow token has lower input amount value %f:%f", + amountInValue, + amountOutValue, + ) + } + + return common.BytesToAddress(tokenOut.Address[:]), destChainID, nil + } } func (h *LifiEscrowMessageHandler) calldata(order *lifi.LifiOrder) ([]byte, error) { @@ -254,7 +303,7 @@ func (h *LifiEscrowMessageHandler) calldata(order *lifi.LifiOrder) ([]byte, erro } // verifyOrder verifies order based on these instructions https://docs.catalyst.exchange/solver/orderflow/#order-validation -func (h *LifiEscrowMessageHandler) verifyOrder(order *lifi.LifiOrder, borrowAmount *big.Int) error { +func (h *LifiEscrowMessageHandler) verifyOrder(order *lifi.LifiOrder) error { if len(order.Order.Inputs) > 1 || len(order.Order.Inputs) == 0 { return fmt.Errorf("orders with multiple inputs not supported") } @@ -263,20 +312,6 @@ func (h *LifiEscrowMessageHandler) verifyOrder(order *lifi.LifiOrder, borrowAmou return fmt.Errorf("orders with multiple outputs not supported") } - tokenIn := common.BytesToAddress(order.GenericInputs[0].TokenAddress[:]) - symbol, srcToken, err := h.tokenStore.ConfigByAddress(h.chainID, tokenIn) - if err != nil { - return err - } - dstToken, err := h.tokenStore.ConfigBySymbol(order.Order.Outputs[0].ChainID, symbol) - if err != nil { - return err - } - scaledInputAmount := chains.ScaleTokenAmount(order.GenericInputs[0].Amount, int64(srcToken.Decimals), int64(dstToken.Decimals)) - if scaledInputAmount.Cmp(borrowAmount) == -1 { - return fmt.Errorf("order input is less than requested borrow amount") - } - augmentedOrder, err := order.AugmentedOrder(h.orderPricer, h.router) if err != nil { return err diff --git a/chains/evm/message/message.go b/chains/evm/message/message.go index 8b037f8a..25114a7f 100644 --- a/chains/evm/message/message.go +++ b/chains/evm/message/message.go @@ -50,6 +50,7 @@ type LifiEscrowData struct { Caller common.Address DepositTxHash string BorrowAmount *big.Int + BorrowToken string Deadline uint64 Nonce *big.Int Source uint64 diff --git a/go.mod b/go.mod index 87198692..d7cabc5c 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/rs/zerolog v1.25.0 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.9.0 - github.com/sprintertech/lifi-solver v1.10.1-0.20260504134943-9bcb16937adf + github.com/sprintertech/lifi-solver v1.12.7-0.20260512144109-aff2bb29e55a github.com/sprintertech/solver-config/go v0.0.0-20260420164134-699e2fffda37 github.com/stretchr/testify v1.11.1 github.com/sygmaprotocol/sygma-core v0.0.0-20250304150334-bd39ac4f7b82 diff --git a/go.sum b/go.sum index f8e0314c..a471ae0d 100644 --- a/go.sum +++ b/go.sum @@ -862,6 +862,10 @@ github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/sprintertech/lifi-solver v1.10.1-0.20260504134943-9bcb16937adf h1:VJ8iwyTEL0jJm9Mz+WUbUP/N5PeDynWUFT/LwvpGRDI= github.com/sprintertech/lifi-solver v1.10.1-0.20260504134943-9bcb16937adf/go.mod h1:fGyHGX9kbFN7AtwCAvmQF+eu75vxr6JFLESfhXHlc70= +github.com/sprintertech/lifi-solver v1.12.6-0.20260511112454-9da73ca80e8e h1:RKtudMi7W6DMBqWhOsIPL8d+zpzzjwfQKD3nFAbDfQ8= +github.com/sprintertech/lifi-solver v1.12.6-0.20260511112454-9da73ca80e8e/go.mod h1:fGyHGX9kbFN7AtwCAvmQF+eu75vxr6JFLESfhXHlc70= +github.com/sprintertech/lifi-solver v1.12.7-0.20260512144109-aff2bb29e55a h1:1mpBylUWbaf2nNJ/EY1aP7D8DTYUHFjDpZTXLPdkScY= +github.com/sprintertech/lifi-solver v1.12.7-0.20260512144109-aff2bb29e55a/go.mod h1:fGyHGX9kbFN7AtwCAvmQF+eu75vxr6JFLESfhXHlc70= github.com/sprintertech/solver-config/go v0.0.0-20260420164134-699e2fffda37 h1:cV00hOEI+SpQnaDyVxAjK199IbDEP5ONjTAmfg+HEb0= github.com/sprintertech/solver-config/go v0.0.0-20260420164134-699e2fffda37/go.mod h1:MrIGW6M815PSYKtWSeOd1Z7eiSeOIk/uA/6E2PhlQVQ= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=