|  | 
|  | 1 | +const http = require("http"); | 
|  | 2 | +const querystring = require("querystring"); | 
|  | 3 | +const exec = require("child_process").exec; | 
|  | 4 | + | 
|  | 5 | +const FAUCET_WALLET_NAME = process.env.FAUCET_WALLET_NAME || "a"; | 
|  | 6 | +const FAUCET_AMOUNT = process.env.FAUCET_AMOUNT || "1000000000"; | 
|  | 7 | +const DENOM = process.env.DENOM || "uscrt"; | 
|  | 8 | + | 
|  | 9 | +let faucet_address; | 
|  | 10 | + | 
|  | 11 | +/** | 
|  | 12 | + * Execute a shell command and return it as a Promise. | 
|  | 13 | + * @param cmd {string} | 
|  | 14 | + * @return {Promise<string>} | 
|  | 15 | + */ | 
|  | 16 | +function execShellCommand(cmd) { | 
|  | 17 | +  return new Promise((resolve, reject) => { | 
|  | 18 | +    exec(cmd, (error, stdout, stderr) => { | 
|  | 19 | +      if (error) { | 
|  | 20 | +        console.error("error in execShellCommand", error); | 
|  | 21 | +        reject(error); | 
|  | 22 | +      } else if (stderr) { | 
|  | 23 | +        console.error("stderr in execShellCommand", stderr); | 
|  | 24 | +        reject(stderr); | 
|  | 25 | +      } else { | 
|  | 26 | +        resolve(JSON.parse(stdout)); | 
|  | 27 | +      } | 
|  | 28 | +    }); | 
|  | 29 | +  }); | 
|  | 30 | +} | 
|  | 31 | + | 
|  | 32 | +/** | 
|  | 33 | + * Command to send coins. | 
|  | 34 | + * @param src_key_name source account key name, default 'a' | 
|  | 35 | + * @param src_address  source account's secret address | 
|  | 36 | + * @param dest_address destination address | 
|  | 37 | + * @param amount amount to send | 
|  | 38 | + * @returns result of executing the command. | 
|  | 39 | + */ | 
|  | 40 | +async function send_command(src_key_name, src_address, dest_address, amount) { | 
|  | 41 | +  const send_message = `secretd tx bank send ${src_address} ${dest_address} ${amount}${DENOM} --from ${src_key_name} --gas-prices 0.25uscrt -y`; | 
|  | 42 | +  console.log(`send_message: \n ${send_message}`); | 
|  | 43 | + | 
|  | 44 | +  const result = await execShellCommand(send_message); | 
|  | 45 | +  console.log(`Sent tokens with txhash: ${result.txhash}`); | 
|  | 46 | +  return result.txhash; | 
|  | 47 | +} | 
|  | 48 | + | 
|  | 49 | +/** | 
|  | 50 | + * Returns the address for the requested account key. | 
|  | 51 | + * @param key_name faucet account key to use, default 'a' | 
|  | 52 | + * @returns address | 
|  | 53 | + */ | 
|  | 54 | +async function get_address(key_name) { | 
|  | 55 | +  // Already looked up, won't change while running | 
|  | 56 | +  if (faucet_address !== undefined) { | 
|  | 57 | +    return faucet_address; | 
|  | 58 | +  } | 
|  | 59 | + | 
|  | 60 | +  const list_keys = "secretd keys list"; | 
|  | 61 | +  const result = await execShellCommand(list_keys); | 
|  | 62 | + | 
|  | 63 | +  for (index in result) { | 
|  | 64 | +    const key = result[index]; | 
|  | 65 | +    if (key["name"] == key_name) { | 
|  | 66 | +      console.log(`Found key with address: ${key["address"]}`); | 
|  | 67 | +      faucet_address = key["address"]; | 
|  | 68 | +      break; | 
|  | 69 | +    } | 
|  | 70 | +  } | 
|  | 71 | + | 
|  | 72 | +  return faucet_address; | 
|  | 73 | +} | 
|  | 74 | + | 
|  | 75 | +// Start the http server | 
|  | 76 | +const server = http.createServer(); | 
|  | 77 | +server.on("request", async (req, res) => { | 
|  | 78 | +  try { | 
|  | 79 | +    // for root or status, return the configured faucet address and amount sent | 
|  | 80 | +    if (req.url === "/" || req.url === "/status") { | 
|  | 81 | +      const faucet_address = await get_address(FAUCET_WALLET_NAME); | 
|  | 82 | + | 
|  | 83 | +      if (faucet_address === undefined) { | 
|  | 84 | +        console.error( | 
|  | 85 | +          `No key account with required name: ${FAUCET_WALLET_NAME}` | 
|  | 86 | +        ); | 
|  | 87 | + | 
|  | 88 | +        res.writeHead(500, { "Content-Type": "application/json" }); | 
|  | 89 | +        res.write( | 
|  | 90 | +          JSON.stringify({ | 
|  | 91 | +            error: `No key account with required name: ${FAUCET_WALLET_NAME}`, | 
|  | 92 | +          }) | 
|  | 93 | +        ); | 
|  | 94 | +        res.end(); | 
|  | 95 | +        return; | 
|  | 96 | +      } else { | 
|  | 97 | +        res.writeHead(200, { "Content-Type": "application/json" }); | 
|  | 98 | +        res.write( | 
|  | 99 | +          JSON.stringify({ | 
|  | 100 | +            faucet_address: faucet_address, | 
|  | 101 | +            amount: FAUCET_AMOUNT, | 
|  | 102 | +          }) | 
|  | 103 | +        ); | 
|  | 104 | +        res.end(); | 
|  | 105 | +      } | 
|  | 106 | +    } else if (req.url.startsWith("/faucet")) { | 
|  | 107 | +      // ensure address is present, not necessarily valid checksum | 
|  | 108 | +      if (!req.url.startsWith("/faucet?address=")) { | 
|  | 109 | +        res.writeHead(400, { "Content-Type": "application/json" }); | 
|  | 110 | +        res.write(JSON.stringify({ error: "address is required" })); | 
|  | 111 | +        res.end(); | 
|  | 112 | +        return; | 
|  | 113 | +      } | 
|  | 114 | + | 
|  | 115 | +      const address = querystring.parse(req.url)["/faucet?address"]; | 
|  | 116 | +      const faucet_address = await get_address(FAUCET_WALLET_NAME); | 
|  | 117 | + | 
|  | 118 | +      if (faucet_address === undefined) { | 
|  | 119 | +        console.error( | 
|  | 120 | +          `No key account with required name: ${FAUCET_WALLET_NAME}` | 
|  | 121 | +        ); | 
|  | 122 | + | 
|  | 123 | +        res.writeHead(500, { "Content-Type": "application/json" }); | 
|  | 124 | +        res.write( | 
|  | 125 | +          JSON.stringify({ | 
|  | 126 | +            error: `No key account with required name: ${FAUCET_WALLET_NAME}`, | 
|  | 127 | +          }) | 
|  | 128 | +        ); | 
|  | 129 | +        res.end(); | 
|  | 130 | +        return; | 
|  | 131 | +      } else { | 
|  | 132 | +        const txhash = await send_command( | 
|  | 133 | +          FAUCET_WALLET_NAME, | 
|  | 134 | +          faucet_address, | 
|  | 135 | +          address, | 
|  | 136 | +          FAUCET_AMOUNT | 
|  | 137 | +        ); | 
|  | 138 | + | 
|  | 139 | +        res.writeHead(200, { "Content-Type": "application/json" }); | 
|  | 140 | +        res.write(JSON.stringify({ txhash: txhash })); | 
|  | 141 | +        res.end(); | 
|  | 142 | +      } | 
|  | 143 | +    } else { | 
|  | 144 | +      res.end("Invalid Request!"); | 
|  | 145 | +    } | 
|  | 146 | +  } catch (err) { | 
|  | 147 | +    res.writeHead(500, { "Content-Type": "application/json" }); | 
|  | 148 | +    res.write(JSON.stringify({ error: `${err.message}` })); | 
|  | 149 | +    res.end(); | 
|  | 150 | +  } | 
|  | 151 | +}); | 
|  | 152 | + | 
|  | 153 | +server.listen(5000); | 
|  | 154 | + | 
|  | 155 | +console.log("Secret Faucet is running on port 5000 ..."); | 
0 commit comments