From cccc033d9d4d936847c55fda8e98ac976bf89794 Mon Sep 17 00:00:00 2001 From: Alvin Reyes Date: Mon, 14 Oct 2024 13:27:57 -0400 Subject: [PATCH] fix: lower default pricing and check LP balance (#403) * patch: use latest version of nitro node when running local chain * chore: add alvin to clabot * fix: lower default instruction price and check LP balance * use faucet address for unit test * added no lp balance check * add return on token contract call --- pkg/solver/controller.go | 14 ++++++++- pkg/web3/sdk.go | 34 ++++++++++++++++++++ pkg/web3/sdk_test.go | 67 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) diff --git a/pkg/solver/controller.go b/pkg/solver/controller.go index 30447eef..baf53d27 100644 --- a/pkg/solver/controller.go +++ b/pkg/solver/controller.go @@ -57,6 +57,7 @@ type SolverController struct { // reacts to events in the system - this 10 second background // loop is just for in case we miss any events const CONTROL_LOOP_INTERVAL = 10 * time.Second +const REQUIRED_BALANCE_IN_WEI = 0.0006 func NewSolverController( web3SDK *web3.Web3SDK, @@ -332,12 +333,23 @@ func (controller *SolverController) addResourceOffer(resourceOffer data.Resource return nil, fmt.Errorf("failed to retrieve ETH balance for resource provider: %v", err) } // Convert InstructionPrice from ETH to Wei - requiredBalanceWei := web3.EtherToWei(float64(resourceOffer.DefaultPricing.InstructionPrice)) + requiredBalanceWei := web3.EtherToWei(REQUIRED_BALANCE_IN_WEI) // 0.0006 based on the required balance for a job + // If the balance is less than the required balance, don't add the resource offer if balance.Cmp(requiredBalanceWei) < 0 { return nil, fmt.Errorf("address %s doesn't have enough funds. required balance is %s but expected balance is %s", resourceOffer.ResourceProvider, requiredBalanceWei, balance) } + // required LP balance + requiredBalanceLp := web3.EtherToWei(float64(resourceOffer.DefaultPricing.InstructionPrice)) // based on the required LP balance for a job + balanceLp, err := controller.web3SDK.GetLPBalance(resourceOffer.ResourceProvider) + if err != nil { + return nil, fmt.Errorf("failed to retrieve LP balance for resource provider: %v", err) + } + if balanceLp.Cmp(requiredBalanceLp) < 0 { + return nil, fmt.Errorf("address %s doesn't have enough LP balance. required balance is %s but expected balance is %s", resourceOffer.ResourceProvider, requiredBalanceLp, balanceLp) + } + controller.log.Info("add resource offer", resourceOffer) metricsDashboard.TrackNodeInfo(resourceOffer) diff --git a/pkg/web3/sdk.go b/pkg/web3/sdk.go index 706730d9..77ea2de8 100644 --- a/pkg/web3/sdk.go +++ b/pkg/web3/sdk.go @@ -5,6 +5,7 @@ import ( "crypto/ecdsa" "errors" "fmt" + "github.com/ethereum/go-ethereum/accounts/abi" "math/big" "net/url" "strconv" @@ -29,6 +30,9 @@ import ( "go.opentelemetry.io/otel/trace" ) +// LP token ABI +const erc20ABI = `[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256","internalType": "uint256"}],"type":"function"}]` + // these are the go-binding wrappers for the various deployed contracts type Contracts struct { Token *token.Token @@ -308,3 +312,33 @@ func (sdk *Web3SDK) GetBalance(address string) (*big.Int, error) { } return balance, nil } + +func (sdk *Web3SDK) GetLPBalance(address string) (*big.Int, error) { + // Convert the string address to common.Address + client := sdk.Client + ethAddress := common.HexToAddress(address) + lpToken := common.HexToAddress(sdk.Options.TokenAddress) // LP Token Address + + // Get the balance using the converted address + erc20ABIObj, err := abi.JSON(strings.NewReader(erc20ABI)) + if err != nil { + log.Error().Msgf("error parsing ABI: %s", err) + return nil, err + } + tokenContract := bind.NewBoundContract(lpToken, erc20ABIObj, client, client, client) + + var out []interface{} + err = tokenContract.Call(nil, &out, "balanceOf", ethAddress) + if err != nil { + log.Error().Msgf("error calling balanceOf: %s", err) + return nil, err + } + + lpBalance := *abi.ConvertType(out[0], new(big.Int)).(*big.Int) + + if err != nil { + log.Error().Msgf("error for GetBalance: %s", err.Error()) + return nil, err + } + return &lpBalance, nil +} diff --git a/pkg/web3/sdk_test.go b/pkg/web3/sdk_test.go index c577a755..6ac848a1 100644 --- a/pkg/web3/sdk_test.go +++ b/pkg/web3/sdk_test.go @@ -2,8 +2,13 @@ package web3_test import ( "context" + "crypto/ecdsa" "errors" + "fmt" + "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/crypto/sha3" "log" + "math/big" "os" "testing" @@ -48,6 +53,8 @@ func CreateTestWeb3SDK() (*web3.Web3SDK, error) { JobCreatorAddress: config.Web3.JobCreatorAddress, } + fmt.Println("options: ", options) + noopTracer := noop.NewTracerProvider().Tracer(system.GetOTelServiceName(system.DefaultService)) sdk, err := web3.NewContractSDK(context.Background(), options, noopTracer) if err != nil { @@ -70,6 +77,42 @@ func TestGetBalance(t *testing.T) { t.Logf("Balance: %d\n", balance) } +func TestGetLPBalance(t *testing.T) { + sdk, err := CreateTestWeb3SDK() + if err != nil { + t.Fatalf("Failed to create Web3SDK: %v", err) + } + balance, err := sdk.GetLPBalance("0x9162B48910E12079c089477DeF4384312f0a6E00") // faucet address + if err != nil { + t.Fatalf("Failed to get LP balance: %v", err) + } + t.Logf("LP Balance: %d\n", balance) +} + +func TestGetLPBalanceNoBalance(t *testing.T) { + sdk, err := CreateTestWeb3SDK() + noBalanceInt := big.NewInt(0) + if err != nil { + t.Fatalf("Failed to create Web3SDK: %v", err) + } + + // generate new address with no balance + newAddress := generateNewAddressWoLp() + + t.Logf("newAddress to check no LP balance: %s\n", newAddress) + + balance, err := sdk.GetLPBalance(newAddress) // randomly generated address (100% no LP balance) + if err != nil { + t.Fatalf("Failed to get LP balance: %v", err) + } + + if balance.Cmp(noBalanceInt) > 0 { + t.Fatalf("Balance should be nil") + } + + t.Logf("LP Balance: %d\n", balance) +} + func TestGetBlockNumber(t *testing.T) { sdk, err := CreateTestWeb3SDK() if err != nil { @@ -83,3 +126,27 @@ func TestGetBlockNumber(t *testing.T) { t.Logf("Block number: %s\n", blockNumberHex) } + +func generateNewAddressWoLp() string { + privateKey, err := crypto.GenerateKey() + if err != nil { + log.Fatal(err) + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + log.Fatal("error casting public key to ECDSA") + } + + publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA) + + address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + fmt.Println(address) + + hash := sha3.NewLegacyKeccak256() + hash.Write(publicKeyBytes[1:]) + + return address + +}