diff --git a/.github/workflows/manual-deploy-testnet-faucet.yml b/.github/workflows/manual-deploy-testnet-faucet.yml
index 1c19efede2..4e8245a0ff 100644
--- a/.github/workflows/manual-deploy-testnet-faucet.yml
+++ b/.github/workflows/manual-deploy-testnet-faucet.yml
@@ -21,6 +21,7 @@ on:
options:
- 'dev-testnet'
- 'testnet'
+ - 'sepolia-testnet'
workflow_call:
inputs:
@@ -45,12 +46,21 @@ jobs:
run: |
echo "FAUCET_BUILD_TAG=testnetobscuronet.azurecr.io/obscuronet/faucet_testnet:latest" >> $GITHUB_ENV
echo "TESTNET_ADDR=erpc.testnet.obscu.ro" >> $GITHUB_ENV
+ echo "DEFAULT_FAUCET_AMOUNT=100" >> $GITHUB_ENV
- name: 'Sets env vars for dev-testnet'
if: ${{ inputs.testnet_type == 'dev-testnet' }}
run: |
echo "FAUCET_BUILD_TAG=testnetobscuronet.azurecr.io/obscuronet/dev_faucet_testnet:latest" >> $GITHUB_ENV
echo "TESTNET_ADDR=erpc.dev-testnet.obscu.ro" >> $GITHUB_ENV
+ echo "DEFAULT_FAUCET_AMOUNT=100" >> $GITHUB_ENV
+
+ - name: 'Sets env vars for sepolia-testnet'
+ if: ${{ inputs.testnet_type == 'sepolia-testnet' }}
+ run: |
+ echo "FAUCET_BUILD_TAG=testnetobscuronet.azurecr.io/obscuronet/sepolia_faucet_testnet:latest" >> $GITHUB_ENV
+ echo "TESTNET_ADDR=erpc.sepolia-testnet.obscu.ro" >> $GITHUB_ENV
+ echo "DEFAULT_FAUCET_AMOUNT=0.5" >> $GITHUB_ENV
- name: 'Login to Azure docker registry'
uses: azure/docker-login@v1
@@ -79,7 +89,7 @@ jobs:
location: 'uksouth'
restart-policy: 'Never'
environment-variables: PORT=80
- command-line: ./faucet --nodeHost ${{ env.TESTNET_ADDR }} --pk ${{ secrets.FAUCET_PK }} --jwtSecret ${{ secrets.FAUCET_JWT_SECRET }}
+ command-line: ./faucet --nodeHost ${{ env.TESTNET_ADDR }} --pk ${{ secrets.FAUCET_PK }} --jwtSecret ${{ secrets.FAUCET_JWT_SECRET }} --defaultAmount ${{ env.DEFAULT_FAUCET_AMOUNT }}
ports: '80'
cpu: 2
memory: 2
diff --git a/README.md b/README.md
index 181330f568..c1d5c5af9f 100644
--- a/README.md
+++ b/README.md
@@ -363,7 +363,7 @@ it for an allocation to an externally owned addressed e.g. for the account `0x0d
the following curl command can be used;
```bash
-curl --location --request POST 'http://127.0.0.1:8080/fund/obx' \
+curl --location --request POST 'http://127.0.0.1:8080/fund/eth' \
--header 'Content-Type: application/json' \
--data-raw '{ "address":"0x0d2166b7b3A1522186E809e83d925d7b0B6db084" }'
```
diff --git a/docs/_docs/testnet/deploying-a-smart-contract-programmatically.py b/docs/_docs/testnet/deploying-a-smart-contract-programmatically.py
index e2e9004250..5b56bfb62b 100644
--- a/docs/_docs/testnet/deploying-a-smart-contract-programmatically.py
+++ b/docs/_docs/testnet/deploying-a-smart-contract-programmatically.py
@@ -12,7 +12,7 @@
WHOST = '127.0.0.1'
LOWER = 0
UPPER = 100
-FAUCET_URL = 'http://testnet-faucet.uksouth.azurecontainer.io/fund/obx'
+FAUCET_URL = 'http://testnet-faucet.uksouth.azurecontainer.io/fund/eth'
guesser = '''
// SPDX-License-Identifier: MIT
diff --git a/docs/_docs/testnet/faucet.md b/docs/_docs/testnet/faucet.md
index 567301bd31..2eb484a43b 100644
--- a/docs/_docs/testnet/faucet.md
+++ b/docs/_docs/testnet/faucet.md
@@ -30,6 +30,6 @@ the faucet server using the below;
1. Make a note of your wallet address or copy it to your clipboard.
2. Open a command shell and issue the below command, where `
` should be replaced with the value stored in your clipboard (e.g. `0x75Ad715443e1E2EBdaFA33ABB3B08443966019A6`). The faucet server will credit 100,000 OBX by default.
```bash
-curl --location --request POST 'http://testnet-faucet.uksouth.azurecontainer.io/fund/obx' --header 'Content-Type: application/json' --data-raw '{ "address":"" }'
+curl --location --request POST 'http://testnet-faucet.uksouth.azurecontainer.io/fund/eth' --header 'Content-Type: application/json' --data-raw '{ "address":"" }'
```
3. After a short period of time the curl command will return `{"status":"ok"}` confirming OBX have been credited to your wallet.
diff --git a/integration/faucet/faucet_test.go b/integration/faucet/faucet_test.go
index 5d072634d8..0ddbae6964 100644
--- a/integration/faucet/faucet_test.go
+++ b/integration/faucet/faucet_test.go
@@ -45,13 +45,14 @@ func TestFaucet(t *testing.T) {
time.Sleep(2 * time.Second)
faucetConfig := &faucet.Config{
- Port: startPort,
- Host: "localhost",
- HTTPPort: startPort + integration.DefaultHostRPCHTTPOffset,
- PK: "0x" + contractDeployerPrivateKeyHex,
- JWTSecret: "This_is_secret",
- ChainID: big.NewInt(integration.ObscuroChainID),
- ServerPort: integration.StartPortFaucetHTTPUnitTest,
+ Port: startPort,
+ Host: "localhost",
+ HTTPPort: startPort + integration.DefaultHostRPCHTTPOffset,
+ PK: "0x" + contractDeployerPrivateKeyHex,
+ JWTSecret: "This_is_secret",
+ ChainID: big.NewInt(integration.ObscuroChainID),
+ ServerPort: integration.StartPortFaucetHTTPUnitTest,
+ DefaultFundAmount: new(big.Int).Mul(big.NewInt(100), big.NewInt(1e18)),
}
faucetContainer, err := container.NewFaucetContainerFromConfig(faucetConfig)
assert.NoError(t, err)
@@ -98,7 +99,7 @@ func createObscuroNetwork(t *testing.T, startPort int) {
}
func fundWallet(port int, w wallet.Wallet) error {
- url := fmt.Sprintf("http://localhost:%d/fund/obx", port)
+ url := fmt.Sprintf("http://localhost:%d/fund/eth", port)
method := "POST"
payload := strings.NewReader(fmt.Sprintf(`{"address":"%s"}`, w.Address()))
diff --git a/integration/networktest/env/network_setup.go b/integration/networktest/env/network_setup.go
index 8367a9db73..fecffd1d75 100644
--- a/integration/networktest/env/network_setup.go
+++ b/integration/networktest/env/network_setup.go
@@ -9,7 +9,7 @@ func Testnet() networktest.Environment {
connector := NewTestnetConnector(
"http://erpc.testnet.obscu.ro:80",
[]string{"http://erpc.testnet.obscu.ro:80"}, // for now we'll just use sequencer as validator node... todo (@matt)
- "http://testnet-faucet.uksouth.azurecontainer.io/fund/obx",
+ "http://testnet-faucet.uksouth.azurecontainer.io/fund/eth",
"ws://testnet-eth2network.uksouth.cloudapp.azure.com:9000",
)
return &testnetEnv{connector}
@@ -19,7 +19,7 @@ func DevTestnet() networktest.Environment {
connector := NewTestnetConnector(
"http://erpc.dev-testnet.obscu.ro:80",
[]string{"http://erpc.dev-testnet.obscu.ro:80"}, // for now we'll just use sequencer as validator node... todo (@matt)
- "http://dev-testnet-faucet.uksouth.azurecontainer.io/fund/obx",
+ "http://dev-testnet-faucet.uksouth.azurecontainer.io/fund/eth",
"ws://dev-testnet-eth2network.uksouth.cloudapp.azure.com:9000",
)
return &testnetEnv{connector}
diff --git a/tools/faucet/README.md b/tools/faucet/README.md
index 19645acef0..aff38ea1e6 100644
--- a/tools/faucet/README.md
+++ b/tools/faucet/README.md
@@ -31,11 +31,11 @@ on port `80` within the container, but maps port `8080` on the host machine to t
## Allocating OBX to an EOA on a local testnet
-Allocating OBX to an externally owned account is done through a POST command to the `/fund/obx` endpoint, where the
+Allocating OBX to an externally owned account is done through a POST command to the `/fund/eth` endpoint, where the
data in the POST command specifies the address e.g. for the account `0x0d2166b7b3A1522186E809e83d925d7b0B6db084`
```bash
-curl --location --request POST 'http://127.0.0.1:8080/fund/obx' \
+curl --location --request POST 'http://127.0.0.1:8080/fund/eth' \
--header 'Content-Type: application/json' \
--data-raw '{ "address":"0x0d2166b7b3A1522186E809e83d925d7b0B6db084" }'
```
diff --git a/tools/faucet/cmd/cli.go b/tools/faucet/cmd/cli.go
index 78dbdb8646..b395823904 100644
--- a/tools/faucet/cmd/cli.go
+++ b/tools/faucet/cmd/cli.go
@@ -4,6 +4,8 @@ import (
"flag"
"math/big"
+ "github.com/ethereum/go-ethereum/params"
+
"github.com/obscuronet/go-obscuro/tools/faucet/faucet"
)
@@ -32,6 +34,10 @@ const (
serverPortName = "serverPort"
serverPortDefault = 80
serverPortUsage = "Port where the web server binds to"
+
+ defaultAmountName = "defaultAmount"
+ defaultAmountDefault = 100.0
+ defaultAmountUsage = "Default amount of token to fund (in ETH)"
)
func parseCLIArgs() *faucet.Config {
@@ -41,15 +47,25 @@ func parseCLIArgs() *faucet.Config {
faucetPK := flag.String(faucetPKName, faucetPKDefault, faucetPKUsage)
jwtSecret := flag.String(jwtSecretName, jwtSecretDefault, jwtSecretUsage)
serverPort := flag.Int(serverPortName, serverPortDefault, serverPortUsage)
+ defaultAmount := flag.Float64(defaultAmountName, defaultAmountDefault, defaultAmountUsage)
flag.Parse()
return &faucet.Config{
- Port: *faucetPort,
- Host: *nodeHost,
- HTTPPort: *nodeHTTPPort,
- PK: *faucetPK,
- JWTSecret: *jwtSecret,
- ServerPort: *serverPort,
- ChainID: big.NewInt(443), // TODO make this configurable
+ Port: *faucetPort,
+ Host: *nodeHost,
+ HTTPPort: *nodeHTTPPort,
+ PK: *faucetPK,
+ JWTSecret: *jwtSecret,
+ ServerPort: *serverPort,
+ ChainID: big.NewInt(443), // TODO make this configurable
+ DefaultFundAmount: toWei(defaultAmount),
}
}
+
+func toWei(amount *float64) *big.Int {
+ amtFloat := new(big.Float).SetFloat64(*amount)
+ weiFloat := new(big.Float).Mul(amtFloat, big.NewFloat(params.Ether))
+ // don't care about the accuracy here, float should have less than 18 decimal places
+ wei, _ := weiFloat.Int(nil)
+ return wei
+}
diff --git a/tools/faucet/container/faucet_container.go b/tools/faucet/container/faucet_container.go
index befefdc239..78718fcf16 100644
--- a/tools/faucet/container/faucet_container.go
+++ b/tools/faucet/container/faucet_container.go
@@ -21,7 +21,7 @@ func NewFaucetContainerFromConfig(cfg *faucet.Config) (*FaucetContainer, error)
return nil, err
}
bindAddress := fmt.Sprintf(":%d", cfg.ServerPort)
- server := webserver.NewWebServer(f, bindAddress, []byte(cfg.JWTSecret))
+ server := webserver.NewWebServer(f, bindAddress, []byte(cfg.JWTSecret), cfg.DefaultFundAmount)
return NewFaucetContainer(f, server)
}
diff --git a/tools/faucet/faucet/faucet.go b/tools/faucet/faucet/faucet.go
index f83d68f435..ff5d5e6a37 100644
--- a/tools/faucet/faucet/faucet.go
+++ b/tools/faucet/faucet/faucet.go
@@ -13,18 +13,19 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/params"
"github.com/obscuronet/go-obscuro/go/obsclient"
"github.com/obscuronet/go-obscuro/go/rpc"
"github.com/obscuronet/go-obscuro/go/wallet"
)
const (
- _timeout = 60 * time.Second
- OBXNativeToken = "obx"
- WrappedOBX = "wobx"
- WrappedEth = "weth"
- WrappedUSDC = "usdc"
+ _timeout = 60 * time.Second
+ NativeToken = "eth"
+ // DeprecatedNativeToken is left in temporarily for tooling that is getting native funds using `/obx` URL
+ DeprecatedNativeToken = "obx" // todo (@matt) remove this once we have fixed the /obx usages
+ WrappedOBX = "wobx"
+ WrappedEth = "weth"
+ WrappedUSDC = "usdc"
)
type Faucet struct {
@@ -50,11 +51,11 @@ func NewFaucet(rpcURL string, chainID int64, pkString string) (*Faucet, error) {
}, nil
}
-func (f *Faucet) Fund(address *common.Address, token string, amount int64) error {
+func (f *Faucet) Fund(address *common.Address, token string, amount *big.Int) error {
var err error
var signedTx *types.Transaction
- if token == OBXNativeToken {
+ if token == NativeToken || token == DeprecatedNativeToken {
signedTx, err = f.fundNativeToken(address, amount)
} else {
return fmt.Errorf("token not fundable atm")
@@ -105,7 +106,7 @@ func (f *Faucet) validateTx(tx *types.Transaction) error {
return fmt.Errorf("unable to fetch tx receipt after %s", _timeout)
}
-func (f *Faucet) fundNativeToken(address *common.Address, amount int64) (*types.Transaction, error) {
+func (f *Faucet) fundNativeToken(address *common.Address, amount *big.Int) (*types.Transaction, error) {
// only one funding at the time
f.fundMutex.Lock()
defer f.fundMutex.Unlock()
@@ -125,7 +126,7 @@ func (f *Faucet) fundNativeToken(address *common.Address, amount int64) (*types.
GasPrice: big.NewInt(225),
Gas: gas,
To: address,
- Value: new(big.Int).Mul(big.NewInt(amount), big.NewInt(params.Ether)),
+ Value: amount,
}
signedTx, err := f.wallet.SignTransaction(tx)
diff --git a/tools/faucet/faucet/faucet_config.go b/tools/faucet/faucet/faucet_config.go
index 1b82dbdf92..81ccb1db41 100644
--- a/tools/faucet/faucet/faucet_config.go
+++ b/tools/faucet/faucet/faucet_config.go
@@ -3,11 +3,12 @@ package faucet
import "math/big"
type Config struct {
- Port int
- Host string
- HTTPPort int
- PK string
- JWTSecret string
- ChainID *big.Int
- ServerPort int
+ Port int
+ Host string
+ HTTPPort int
+ PK string
+ JWTSecret string
+ ChainID *big.Int
+ ServerPort int
+ DefaultFundAmount *big.Int // how much token to fund by default (in wei)
}
diff --git a/tools/faucet/webserver/web_server.go b/tools/faucet/webserver/web_server.go
index 1d76524bd0..f3bf7967bb 100644
--- a/tools/faucet/webserver/web_server.go
+++ b/tools/faucet/webserver/web_server.go
@@ -3,6 +3,7 @@ package webserver
import (
"context"
"fmt"
+ "math/big"
"net/http"
"strings"
"time"
@@ -24,80 +25,39 @@ type requestAddr struct {
Address string `json:"address" binding:"required"`
}
-func NewWebServer(faucetServer *faucet.Faucet, bindAddress string, jwtSecret []byte) *WebServer {
+func NewWebServer(faucetServer *faucet.Faucet, bindAddress string, jwtSecret []byte, defaultAmount *big.Int) *WebServer {
r := gin.New()
gin.SetMode(gin.ReleaseMode)
- // todo move this declaration out of this scope
- parseFunding := func(c *gin.Context) {
- tokenReq := c.Params.ByName("token")
- var token string
-
- // check the token request type
- switch tokenReq {
- case faucet.OBXNativeToken:
- token = faucet.OBXNativeToken
- case faucet.WrappedOBX:
- token = faucet.WrappedOBX
- case faucet.WrappedEth:
- token = faucet.WrappedEth
- case faucet.WrappedUSDC:
- token = faucet.WrappedUSDC
- default:
- errorHandler(c, fmt.Errorf("token not recognized: %s", tokenReq), faucetServer.Logger)
- return
- }
-
- // make sure there's an address
- var req requestAddr
- if err := c.Bind(&req); err != nil {
- errorHandler(c, fmt.Errorf("unable to parse request: %w", err), faucetServer.Logger)
- return
- }
-
- // make sure the address is valid
- if !common.IsHexAddress(req.Address) {
- errorHandler(c, fmt.Errorf("unexpected address %s", req.Address), faucetServer.Logger)
- return
- }
-
- amount := int64(100)
+ // authed endpoint
+ r.POST("/auth/fund/:token", jwtTokenChecker(jwtSecret, faucetServer.Logger), fundingHandler(faucetServer, defaultAmount))
- // fund the address
- addr := common.HexToAddress(req.Address)
- if err := faucetServer.Fund(&addr, token, amount); err != nil {
- errorHandler(c, fmt.Errorf("unable to fund request %w", err), faucetServer.Logger)
- return
- }
+ // todo (@matt) we need to remove this unsecure endpoint before we provide a fully public sepolia faucet
+ r.POST("/fund/:token", fundingHandler(faucetServer, defaultAmount))
- c.JSON(http.StatusOK, gin.H{"status": "ok"})
+ return &WebServer{
+ engine: r,
+ faucet: faucetServer,
+ bindAddress: bindAddress,
}
+}
- jwtTokenCheck := func(c *gin.Context) {
+func jwtTokenChecker(jwtSecret []byte, logger log.Logger) gin.HandlerFunc {
+ return func(c *gin.Context) {
jwtToken, err := extractBearerToken(c.GetHeader("Authorization"))
if err != nil {
- errorHandler(c, err, faucetServer.Logger)
+ errorHandler(c, err, logger)
return
}
_, err = faucet.ValidateToken(jwtToken, jwtSecret)
if err != nil {
- errorHandler(c, err, faucetServer.Logger)
+ errorHandler(c, err, logger)
return
}
c.Next()
}
- // authed endpoint
- r.POST("/auth/fund/:token", jwtTokenCheck, parseFunding)
-
- r.POST("/fund/:token", parseFunding)
-
- return &WebServer{
- engine: r,
- faucet: faucetServer,
- bindAddress: bindAddress,
- }
}
func (w *WebServer) Start() error {
@@ -146,3 +106,50 @@ func extractBearerToken(header string) (string, error) {
return jwtToken[1], nil
}
+
+func fundingHandler(faucetServer *faucet.Faucet, defaultAmount *big.Int) gin.HandlerFunc {
+ return func(c *gin.Context) {
+ tokenReq := c.Params.ByName("token")
+ var token string
+
+ // check the token request type
+ switch tokenReq {
+ case faucet.NativeToken:
+ token = faucet.NativeToken
+ // we leave this option in temporarily for tools that are still using `/obx` endpoint for native funds
+ case faucet.DeprecatedNativeToken:
+ token = faucet.NativeToken
+ case faucet.WrappedOBX:
+ token = faucet.WrappedOBX
+ case faucet.WrappedEth:
+ token = faucet.WrappedEth
+ case faucet.WrappedUSDC:
+ token = faucet.WrappedUSDC
+ default:
+ errorHandler(c, fmt.Errorf("token not recognized: %s", tokenReq), faucetServer.Logger)
+ return
+ }
+
+ // make sure there's an address
+ var req requestAddr
+ if err := c.Bind(&req); err != nil {
+ errorHandler(c, fmt.Errorf("unable to parse request: %w", err), faucetServer.Logger)
+ return
+ }
+
+ // make sure the address is valid
+ if !common.IsHexAddress(req.Address) {
+ errorHandler(c, fmt.Errorf("unexpected address %s", req.Address), faucetServer.Logger)
+ return
+ }
+
+ // fund the address
+ addr := common.HexToAddress(req.Address)
+ if err := faucetServer.Fund(&addr, token, defaultAmount); err != nil {
+ errorHandler(c, fmt.Errorf("unable to fund request %w", err), faucetServer.Logger)
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{"status": "ok"})
+ }
+}