diff --git a/.env b/.env index 980f1e6..b78c6df 100644 --- a/.env +++ b/.env @@ -3,4 +3,4 @@ PRIVATE_KEY= KEYSTORE= ERC20_TOKEN_ADDRESS= HCAPTCHA_SITEKEY= -HCAPTCHA_SECRET= \ No newline at end of file +HCAPTCHA_SECRET= diff --git a/.github/workflows/fork-sync.yml b/.github/workflows/fork-sync.yml new file mode 100644 index 0000000..9e56b79 --- /dev/null +++ b/.github/workflows/fork-sync.yml @@ -0,0 +1,20 @@ +name: Sync Fork + +on: + schedule: + - cron: "0 0 * * 1" # once a week on Monday + workflow_dispatch: # on button click + +jobs: + sync: + runs-on: ubuntu-latest + + steps: + - uses: tgymnich/fork-sync@v2.0.10 + with: + owner: chainflag + repo: eth-faucet + head: main + base: main + pr_title: Sync forked repo + pr_message: Merge latest changes from upstream repo diff --git a/Dockerfile b/Dockerfile index 51f153d..ca740fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:lts-alpine as frontend +FROM node:lts-alpine AS frontend WORKDIR /frontend-build @@ -8,7 +8,7 @@ RUN yarn install COPY web ./ RUN yarn build -FROM golang:1.22-alpine as backend +FROM golang:1.22-alpine AS backend RUN apk add --no-cache gcc musl-dev linux-headers diff --git a/Makefile b/Makefile index a13f5e2..fd8bf2c 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ generate-binding: generate-abi test: # Runs tests @echo "Test packages" go test -race -shuffle=on -coverprofile=coverage.out -cover $(PKGS) - + lint: # Runs golangci-lint on the repo @go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest golangci-lint run @@ -66,4 +66,4 @@ docker-stop: .PHONY: help help: # Show help for each of the Makefile recipes - @grep -E '^[a-zA-Z0-9 -]+:.*#' Makefile | sort | while read -r l; do printf "$(GREEN)$$(echo $$l | cut -f 1 -d':')$(COLOR_END):$$(echo $$l | cut -f 2- -d'#')\n"; done \ No newline at end of file + @grep -E '^[a-zA-Z0-9 -]+:.*#' Makefile | sort | while read -r l; do printf "$(GREEN)$$(echo $$l | cut -f 1 -d':')$(COLOR_END):$$(echo $$l | cut -f 2- -d'#')\n"; done diff --git a/README.md b/README.md index 39d5d21..ef21c6b 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ make generate-binding ERC20_CONTRACT_FILE_PATH= **NOTE**: Please make sure to generate the Go binding corresponding to your contract before building the project. -4. Build Go project +4. Build Go project ```bash make build-backend ``` @@ -102,6 +102,7 @@ The following are the available command-line flags(excluding above wallet flags) | -httpport | Listener port to serve HTTP connection | 8080 | | -proxycount | Count of reverse proxies in front of the server | 0 | | -token.address | Token contract address | 0x8a21CF9Ba08Ae709D64Cb25AfAA951183EC9FF6D | +| -token.decimals | Token decimals | 18 | | -faucet.amount | Number of ERC20 tokens to transfer per user request | 1 | | -faucet.minutes | Number of minutes to wait between funding rounds | 10080 (1 week) | | -faucet.name | Network name to display on the frontend | sepolia | diff --git a/cmd/server.go b/cmd/server.go index a461d1d..7c61cdb 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -20,13 +20,14 @@ var ( appVersion = "v1.1.0" chainIDMap = map[string]int{"lisk_sepolia": 4202} - tokenAddress = flag.String("token.address", os.Getenv("ERC20_TOKEN_ADDRESS"), "Contract address of ERC20 token") + tokenAddress = flag.String("token.address", os.Getenv("ERC20_TOKEN_ADDRESS"), "Contract address of ERC20 token") + tokenDecimalsFlag = flag.Int("token.decimals", 18, "Token decimals") httpPortFlag = flag.Int("httpport", 8080, "Listener port to serve HTTP connection") proxyCntFlag = flag.Int("proxycount", 0, "Count of reverse proxies in front of the server") versionFlag = flag.Bool("version", false, "Print version number") - payoutFlag = flag.Int("faucet.amount", 1, "Number of ERC20 tokens to transfer per user request") + payoutFlag = flag.Float64("faucet.amount", 0.1, "Number of ERC20 tokens to transfer per user request") intervalFlag = flag.Int("faucet.minutes", 10080, "Number of minutes to wait between funding rounds") netnameFlag = flag.String("faucet.name", "lisk_sepolia", "Network name to display on the frontend") symbolFlag = flag.String("faucet.symbol", "LSK", "Token symbol to display on the frontend") @@ -65,7 +66,7 @@ func Execute() { if err != nil { panic(fmt.Errorf("cannot connect to web3 provider: %w", err)) } - config := server.NewConfig(*netnameFlag, *symbolFlag, *httpPortFlag, *intervalFlag, *payoutFlag, *proxyCntFlag, *hcaptchaSiteKeyFlag, *hcaptchaSecretFlag, *explorerURL, *explorerTxPath) + config := server.NewConfig(*netnameFlag, *symbolFlag, *payoutFlag, *tokenDecimalsFlag, *httpPortFlag, *intervalFlag, *proxyCntFlag, *hcaptchaSiteKeyFlag, *hcaptchaSecretFlag, *explorerURL, *explorerTxPath) go server.NewServer(txBuilder, config).Run() c := make(chan os.Signal, 1) diff --git a/internal/chain/util.go b/internal/chain/util.go index 9f6ca3a..3051d46 100644 --- a/internal/chain/util.go +++ b/internal/chain/util.go @@ -1,16 +1,12 @@ package chain import ( + "math" "math/big" "github.com/ethereum/go-ethereum/common" ) -func EtherToWei(amount int64) *big.Int { - ether := new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) - return new(big.Int).Mul(big.NewInt(amount), ether) -} - func Has0xPrefix(str string) bool { return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') } @@ -22,11 +18,16 @@ func IsValidAddress(address string, checksummed bool) bool { return !checksummed || common.HexToAddress(address).Hex() == address } -func LSKToWei(amount int64) *big.Int { - oneLskInWei := new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) - return new(big.Int).Mul(big.NewInt(amount), oneLskInWei) -} - func addLeftPadding(input []byte) []byte { return common.LeftPadBytes(input, 32) } + +func TokenToWei(amount float64, decimals int) *big.Int { + ethDecimals := 18 + oneEthToWei := math.Pow10(ethDecimals) + + amountInt := int64(amount * oneEthToWei) + + oneTokenInWei := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil) + return new(big.Int).Div(new(big.Int).Mul(big.NewInt(amountInt), oneTokenInWei), big.NewInt(int64(oneEthToWei))) +} diff --git a/internal/chain/util_test.go b/internal/chain/util_test.go index 29d18e3..62fcf6c 100644 --- a/internal/chain/util_test.go +++ b/internal/chain/util_test.go @@ -34,40 +34,24 @@ func TestIsValidAddress(t *testing.T) { } } -func TestEtherToWei(t *testing.T) { +func TestTokenToWei(t *testing.T) { tests := []struct { - name string - amount int64 - want *big.Int + name string + amount float64 + tokenDecimals int + want *big.Int }{ - {name: "1ether", amount: 1, want: new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)}, + {name: "1ether", amount: 1, tokenDecimals: 18, want: new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := EtherToWei(tt.amount); !reflect.DeepEqual(got, tt.want) { + if got := TokenToWei(tt.amount, tt.tokenDecimals); !reflect.DeepEqual(got, tt.want) { t.Errorf("EtherToWei() = %v, want %v", got, tt.want) } }) } } -func Test_LSKToWei(t *testing.T) { - tests := []struct { - name string - amount int64 - want *big.Int - }{ - {name: "should convert 1 LSK to 10^18 Wei", amount: 1, want: big.NewInt(1000000000000000000)}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := LSKToWei(tt.amount); !reflect.DeepEqual(got, tt.want) { - t.Errorf("LSKToWei() = %v, want %v", got, tt.want) - } - }) - } -} - func Test_addLeftPadding(t *testing.T) { tests := []struct { name string diff --git a/internal/server/config.go b/internal/server/config.go index 770237e..ba8e4f3 100644 --- a/internal/server/config.go +++ b/internal/server/config.go @@ -3,9 +3,10 @@ package server type Config struct { network string symbol string + payout float64 + tokenDecimals int httpPort int interval int - payout int proxyCount int hcaptchaSiteKey string hcaptchaSecret string @@ -13,13 +14,14 @@ type Config struct { explorerTxPath string } -func NewConfig(network, symbol string, httpPort, interval, payout, proxyCount int, hcaptchaSiteKey, hcaptchaSecret, explorerURL, explorerTxPath string) *Config { +func NewConfig(network, symbol string, payout float64, tokenDecimals, httpPort, interval, proxyCount int, hcaptchaSiteKey, hcaptchaSecret, explorerURL, explorerTxPath string) *Config { return &Config{ network: network, symbol: symbol, httpPort: httpPort, interval: interval, payout: payout, + tokenDecimals: tokenDecimals, proxyCount: proxyCount, hcaptchaSiteKey: hcaptchaSiteKey, hcaptchaSecret: hcaptchaSecret, diff --git a/internal/server/server.go b/internal/server/server.go index 3962b8d..38fc9b0 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -65,7 +65,7 @@ func (s *Server) handleClaim() http.HandlerFunc { currBalance = big.NewInt(0) } - txHash, err := s.TransferERC20(ctx, address, chain.LSKToWei(int64(s.cfg.payout)), currBalance) + txHash, err := s.TransferERC20(ctx, address, chain.TokenToWei(s.cfg.payout, s.cfg.tokenDecimals), currBalance) if err != nil { log.WithError(err).Error("failed to send transaction") renderJSON(w, claimResponse{Message: err.Error()}, http.StatusInternalServerError) @@ -91,7 +91,7 @@ func (s *Server) handleInfo() http.HandlerFunc { Account: s.Sender().String(), Network: s.cfg.network, Symbol: s.cfg.symbol, - Payout: strconv.Itoa(s.cfg.payout), + Payout: strconv.FormatFloat(s.cfg.payout, 'f', -1, 64), HcaptchaSiteKey: s.cfg.hcaptchaSiteKey, ExplorerURL: s.cfg.explorerURL, ExplorerTxPath: s.cfg.explorerTxPath,