Skip to content

Commit

Permalink
add bep2p tokens for faucet (ethereum#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
unclezoro authored Jun 30, 2020
1 parent 5e4f578 commit 511bbf5
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 28 deletions.
117 changes: 95 additions & 22 deletions cmd/faucet/faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import (
"sync"
"time"

"github.com/ethereum/go-ethereum/accounts/abi"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -83,11 +85,15 @@ var (
noauthFlag = flag.Bool("noauth", false, "Enables funding requests without authentication")
logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")

fixGasPrice = flag.Int64("faucet.fixedprice", 0, "Will use fixed gas price if specified")
bep2eContracts = flag.String("bep2eContracts", "", "the list of bep2p contracts")
bep2eSymbols = flag.String("bep2eSymbols", "", "the symbol of bep2p tokens")
bep2eAmounts = flag.String("bep2eAmounts", "", "the amount of bep2p tokens")
fixGasPrice = flag.Int64("faucet.fixedprice", 0, "Will use fixed gas price if specified")
)

var (
ether = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)
ether = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)
bep2eAbiJson = `[ { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }, { "inputs": [], "name": "totalSupply", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "decimals", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getOwner", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "account", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "_owner", "type": "address" }, { "internalType": "address", "name": "spender", "type": "address" } ], "name": "allowance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "approve", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" } ]`
)

var (
Expand All @@ -110,18 +116,51 @@ func main() {
amounts[i] = strings.TrimSuffix(amounts[i], "s")
}
}
bep2eNumAmounts := make([]string, 0)
if bep2eAmounts != nil && len(*bep2eAmounts) > 0 {
bep2eNumAmounts = strings.Split(*bep2eAmounts, ",")
}

symbols := make([]string, 0)
if bep2eSymbols != nil && len(*bep2eSymbols) > 0 {
symbols = strings.Split(*bep2eSymbols, ",")
}

contracts := make([]string, 0)
if bep2eContracts != nil && len(*bep2eContracts) > 0 {
contracts = strings.Split(*bep2eContracts, ",")
}

if len(bep2eNumAmounts) != len(symbols) || len(symbols) != len(contracts) {
log.Crit("Length of bep2eContracts, bep2eSymbols, bep2eAmounts mismatch")
}

bep2eInfos := make(map[string]bep2eInfo, 0)
for idx, s := range symbols {
n, ok := big.NewInt(0).SetString(bep2eNumAmounts[idx], 10)
if !ok {
log.Crit("failed to parse bep2eAmounts")
}
amountStr := big.NewFloat(0).Quo(big.NewFloat(0).SetInt(n), big.NewFloat(0).SetInt64(params.Ether)).String()

bep2eInfos[s] = bep2eInfo{
Contract: common.HexToAddress(contracts[idx]),
Amount: *n,
AmountStr: amountStr,
}
}
// Load up and render the faucet website
tmpl, err := Asset("faucet.html")
if err != nil {
log.Crit("Failed to load the faucet template", "err", err)
}
website := new(bytes.Buffer)
err = template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
"Network": *netnameFlag,
"Amounts": amounts,
"Recaptcha": *captchaToken,
"NoAuth": *noauthFlag,
"Network": *netnameFlag,
"Amounts": amounts,
"Recaptcha": *captchaToken,
"NoAuth": *noauthFlag,
"Bep2eInfos": bep2eInfos,
})
if err != nil {
log.Crit("Failed to render the faucet template", "err", err)
Expand Down Expand Up @@ -162,7 +201,7 @@ func main() {
ks.Unlock(acc, pass)

// Assemble and start the faucet light service
faucet, err := newFaucet(genesis, *ethPortFlag, enodes, *netFlag, *statsFlag, ks, website.Bytes())
faucet, err := newFaucet(genesis, *ethPortFlag, enodes, *netFlag, *statsFlag, ks, website.Bytes(), bep2eInfos)
if err != nil {
log.Crit("Failed to start faucet", "err", err)
}
Expand All @@ -181,6 +220,12 @@ type request struct {
Tx *types.Transaction `json:"tx"` // Transaction funding the account
}

type bep2eInfo struct {
Contract common.Address
Amount big.Int
AmountStr string
}

// faucet represents a crypto faucet backed by an Ethereum light client.
type faucet struct {
config *params.ChainConfig // Chain configurations for signing
Expand All @@ -201,9 +246,12 @@ type faucet struct {
update chan struct{} // Channel to signal request updates

lock sync.RWMutex // Lock protecting the faucet's internals

bep2eInfos map[string]bep2eInfo
bep2eAbi abi.ABI
}

func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network uint64, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) {
func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network uint64, stats string, ks *keystore.KeyStore, index []byte, bep2eInfos map[string]bep2eInfo) (*faucet, error) {
// Assemble the raw devp2p protocol stack
stack, err := node.New(&node.Config{
Name: "geth",
Expand All @@ -222,6 +270,10 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
if err != nil {
return nil, err
}
bep2eAbi, err := abi.JSON(strings.NewReader(bep2eAbiJson))
if err != nil {
return nil, err
}
// Assemble the Ethereum light client protocol
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
cfg := eth.DefaultConfig
Expand Down Expand Up @@ -261,14 +313,16 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
client := ethclient.NewClient(api)

return &faucet{
config: genesis.Config,
stack: stack,
client: client,
index: index,
keystore: ks,
account: ks.Accounts()[0],
timeouts: make(map[string]time.Time),
update: make(chan struct{}, 1),
config: genesis.Config,
stack: stack,
client: client,
index: index,
keystore: ks,
account: ks.Accounts()[0],
timeouts: make(map[string]time.Time),
update: make(chan struct{}, 1),
bep2eInfos: bep2eInfos,
bep2eAbi: bep2eAbi,
}, nil
}

Expand Down Expand Up @@ -371,6 +425,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
URL string `json:"url"`
Tier uint `json:"tier"`
Captcha string `json:"captcha"`
Symbol string `json:"symbol"`
}
if err = conn.ReadJSON(&msg); err != nil {
return
Expand Down Expand Up @@ -475,13 +530,31 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
fund bool
timeout time.Time
)
if timeout = f.timeouts[username]; time.Now().After(timeout) {
// User wasn't funded recently, create the funding transaction
amount := new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether)
amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil))
amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil))

tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, 21000, f.price, nil)
if timeout = f.timeouts[username]; time.Now().After(timeout) {
var tx *types.Transaction
if msg.Symbol == "BNB" {
// User wasn't funded recently, create the funding transaction
amount := new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether)
amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil))
amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil))

tx = types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, 21000, f.price, nil)
} else {
tokenInfo, ok := f.bep2eInfos[msg.Symbol]
if !ok {
f.lock.Unlock()
log.Warn("Failed to find symbol", "symbol", msg.Symbol)
continue
}
input, err := f.bep2eAbi.Pack("transfer", address, &tokenInfo.Amount)
if err != nil {
f.lock.Unlock()
log.Warn("Failed to pack transfer transaction", "err", err)
continue
}
tx = types.NewTransaction(f.nonce+uint64(len(f.reqs)), tokenInfo.Contract, nil, 420000, f.price, input)
}
signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainID)
if err != nil {
f.lock.Unlock()
Expand Down
11 changes: 9 additions & 2 deletions cmd/faucet/faucet.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,13 @@ <h1 style="text-align: center;"><i class="fa fa-bath" aria-hidden="true"></i> {{
<span class="input-group-btn">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Give me BNB <i class="fa fa-caret-down" aria-hidden="true"></i></button>
<ul class="dropdown-menu dropdown-menu-right">{{range $idx, $amount := .Amounts}}
<li><a style="text-align: center;" onclick="tier={{$idx}}; {{if $.Recaptcha}}grecaptcha.execute(){{else}}submit({{$idx}}){{end}}">{{$amount}}</a></li>{{end}}
<li><a style="text-align: center;" onclick="tier={{$idx}};symbol='BNB'; {{if $.Recaptcha}}grecaptcha.execute(){{else}}submit({{$idx}}){{end}}">{{$amount}}</a></li>{{end}}
</ul>
</span>
<span class="input-group-btn">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Peggy tokens<i class="fa fa-caret-down" aria-hidden="true"></i></button>
<ul class="dropdown-menu dropdown-menu-right"> {{range $symbol, $bep2eInfo := .Bep2eInfos}}
<li><a style="text-align: center;" onclick="symbol={{$symbol}}; {{if $.Recaptcha}}grecaptcha.execute(){{else}}submitBep2e({{$symbol}}){{end}}">{{$bep2eInfo.AmountStr}} {{$symbol}}</a></li>{{end}}
</ul>
</span>
</div>{{if .Recaptcha}}
Expand Down Expand Up @@ -85,6 +91,7 @@ <h1 style="text-align: center;"><i class="fa fa-bath" aria-hidden="true"></i> {{
var attempt = 0;
var server;
var tier = 0;
var symbol="";
var requests = [];

// Define a function that creates closures to drop old requests
Expand All @@ -100,7 +107,7 @@ <h1 style="text-align: center;"><i class="fa fa-bath" aria-hidden="true"></i> {{
};
// Define the function that submits a gist url to the server
var submit = function({{if .Recaptcha}}captcha{{end}}) {
server.send(JSON.stringify({url: $("#url")[0].value, tier: tier{{if .Recaptcha}}, captcha: captcha{{end}}}));{{if .Recaptcha}}
server.send(JSON.stringify({url: $("#url")[0].value, symbol: symbol, tier: tier{{if .Recaptcha}}, captcha: captcha{{end}}}));{{if .Recaptcha}}
grecaptcha.reset();{{end}}
};
// Define a method to reconnect upon server loss
Expand Down
Loading

0 comments on commit 511bbf5

Please sign in to comment.