{"id":46147028,"url":"https://github.com/omgnetwork/boba-faucet","last_synced_at":"2026-03-02T07:34:53.418Z","repository":{"id":43521703,"uuid":"457904781","full_name":"omgnetwork/boba-faucet","owner":"omgnetwork","description":"Boba faucet using Turing","archived":false,"fork":false,"pushed_at":"2022-02-27T03:39:20.000Z","size":1196,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-01T04:48:28.277Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/omgnetwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-10T18:43:19.000Z","updated_at":"2022-02-16T17:38:51.000Z","dependencies_parsed_at":"2022-08-23T12:51:03.654Z","dependency_job_id":null,"html_url":"https://github.com/omgnetwork/boba-faucet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/omgnetwork/boba-faucet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omgnetwork%2Fboba-faucet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omgnetwork%2Fboba-faucet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omgnetwork%2Fboba-faucet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omgnetwork%2Fboba-faucet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/omgnetwork","download_url":"https://codeload.github.com/omgnetwork/boba-faucet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omgnetwork%2Fboba-faucet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29995087,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T01:47:34.672Z","status":"online","status_checked_at":"2026-03-02T02:00:07.342Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-02T07:34:52.406Z","updated_at":"2026-03-02T07:34:53.410Z","avatar_url":"https://github.com/omgnetwork.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Boba Faucet\n\n- [Overview](#Overview)\n- [Directory Structure](#Directory-Structure)\n- [Specification](#Specification)\n- [Impementation](#Implementaion)\n  * [Step 1: Creating API endpoints](#Step1--Creating-API-endpoints)\n  * [Step2: Creating Boba Faucet Contract](#Step2--Creating-Boba-Faucet-Contract)\n  * [Step3: Funding Turing Helper Contract](#Step3--Funding-Turing-Helper-Contract)\n\n## Overview\n\nBoba Faucet is a system for getting Boba Rinkeby ETH and Boba Rinkeby Boba Token. It's implemented using the Turing. Turing is a system for interacting with the outside world from within solidity smart contracts.\n\nBefore claiming the token, users have to answer the CAPTCHA. The answer is hashed and compared off-chain via Turing. Once the answer is verified, the smart contract releases the funds.\n\n## Directory Structure\n\n* [`packages`](./packages): Contains all the typescript packages and contracts\n  * [`contracts`](./packages/contracts): Solidity smart contracts implementing the Boba Faucet\n  * [`gateway`](./packages/gate): The Boba Web faucet\n  * [`deployment`](./packages/deployment): Boba faucet contract addresses\n  * [`api`](./packages/api): Boba faucet backend API\n\n## Specification\n\nThis procedure takes place in five steps:\n\n1. User gets the CAPTCHA image and UUID of the image\n\n   The API GET request is sent to `https://api-turing.boba.network/get.catcha` on the frontend. The returned payload is \n\n   ```js\n   {\n     \"UUID\": \"BYTES32\",\n     \"imageBase64\": \"CAPTCHA IMAGE\"\n   }\n   ```\n\n   \u003e The UUID and hashed CAPTCHA answer are stored in the AWS Redis.\n\n2. User sends the transaction to the `Boba Faucet` contract with UUID and CAPTCHA answer\n\n   ```javascript\n   const BobaFaucet = new ethers.Contract(\n     BOBA_FAUCET_CONTRACE_ADDRESS,\n     BobaFaucetJson.abi,\n     this.provider.getSigner()\n   )\n   const tx = await BobaFaucet.getBobaFaucet(uuid, answer)\n   await tx.wait()\n   ```\n\n3. Geth sends the request to the backend and retrieves the result\n\n   \u003e  The answer is hashed in the `Boba Faucet` contract first before sending it to backend API.\n   \u003e\n   \u003e ```\n   \u003e bytes32 hashedKey = keccak256(abi.encodePacked(_key));\n   \u003e bytes memory encRequest = abi.encodePacked(_uuid, hashedKey);\n   \u003e bytes memory encResponse = turing.TuringTx(turingUrl, encRequest);\n   \u003e ```\n\n   The POST request is sent to `https://api-turing.boba.network/verify.captcha` . It decodes the input and verifies the UUID with the hashed answer.\n\n   ```python\n   paramsHexString = body['params'][0]\n   # Select uuid and answer\n   # paramsHexString[0: 66] is the length of input data\n   uuid = '0x' + paramsHexString[66: 130]\n   answer = '0x' + paramsHexString[130:]\n   # Get answer from Redis\n   keyInRedis = db.get(uuid)\n   # Return the payload\n   return returnPayload(keyInRedis.decode('utf-8') == key)\n   \n   # Payload is built based on the result\n   def returnPayload(result):\n       # We return 0 or 1 using uint256\n       payload = '0x' + '{0:0{1}x}'.format(int(32), 64)\n       if result:\n           # Add UINT256 1 if the result is correct\n           payload += '{0:0{1}x}'.format(int(1), 64)\n       else:\n           # Add UINT256 0 if the result is wrong\n           payload += '{0:0{1}x}'.format(int(0), 64)\n   \n       returnPayload = {\n           'statusCode': 200,\n           'body': json.dumps({ 'result': payload })\n       }\n   \n       return returnPayload\n   ```\n\n4. Geth atomically revises the calldata\n\n   On the contract level, we only need to decode the result from the Turing request and release the funds if the answer is correct.\n\n   ```solidity\n   // Decode the response from outside API\n   bytes memory encResponse = turing.TuringTx(turingUrl, encRequest);\n   uint256 result = abi.decode(encResponse,(uint256));\n   // Release the funds if it is correct\n   require(result == 1, 'Invalid key and UUID');\n   IERC20(BobaAddress).safeTransfer(msg.sender, BobaFaucetAmount);\n   ```\n\n5. User gets the funds if the answer is correct or the error message\n   \u003cimg width=\"873\" alt=\"image\" src=\"https://user-images.githubusercontent.com/46272347/153475813-f4ffd103-3b95-4df7-a951-a321b84ff34a.png\"\u003e\n\n## Implementation\n\n### Step1: Creating API endpoints\n\nTwo simple API endpoints are created.\n\n#### get.captcha\n\n```python\nimage = ImageCaptcha(width=280, height=90)\n\n# For image\nuuid1 = uuid.uuid4()\nimageStr = str(uuid1).split('-')[0]\nimageStrBytes = Web3.solidityKeccak(['string'], [str(imageStr)]).hex()\n\n# For key\nuuid2 = uuid.uuid4()\nkeyBytes = Web3.solidityKeccak(['string'], [str(uuid2)]).hex()\n\n# Use the first random string\nimageData = image.generate(imageStr)\n\n# Get base64\nimageBase64 = base64.b64encode(imageData.getvalue())\n\n# Read YML\nwith open(\"env.yml\", 'r') as ymlfile:\nconfig = yaml.load(ymlfile)\n\n# Connect to Redis Elasticache\ndb = redis.StrictRedis(host=config.get('REDIS_URL'),\nport=config.get('REDIS_PORT'))\n\n# Store and set the expire time as 10 minutes\nprint('keyBytes: ', keyBytes, 'imageStrBytes: ', imageStrBytes, 'imageStr: ', imageStr)\ndb.set(keyBytes, imageStrBytes, ex=600)\n\npayload = {'uuid': keyBytes, 'imageBase64': imageBase64.decode('utf-8') }\n```\n\n#### verify.captcha\n\n```python\nparamsHexString = body['params'][0]\n\nuuid = '0x' + paramsHexString[66: 130]\nkey = '0x' + paramsHexString[130:]\n\n# Read YML\nwith open(\"env.yml\", 'r') as ymlfile:\n  config = yaml.load(ymlfile)\n\n  # Connect to Redis Elasticache\n  db = redis.StrictRedis(host=config.get('REDIS_URL'),\n                         port=config.get('REDIS_PORT'))\n\n  # Store and set the expire time as 10 minutes\n  keyInRedis = db.get(uuid)\n\n  if keyInRedis:\n    print('keyInRedis: ', keyInRedis.decode('utf-8'), 'key: ', key)\n    return returnPayload(keyInRedis.decode('utf-8') == key)\n  else:\n    return returnPayload(False)\n```\n\n### Step2: Creating Boba Faucet Contract\n\nThe smart contract imports [Turing Helper Contract](https://github.com/omgnetwork/optimism-v2/blob/develop/packages/boba/turing/contracts/TuringHelper.sol), so it can interact with outside API endpoints.\n\n```solidity\nimport './TuringHelper.sol';\n\ncontract BobaFaucet is Ownable {\n    // Turing\n    address public turingHelperAddress;\n    string public turingUrl;\n    TuringHelper public turing;\n    \n    constructor(\n        address _turingHelperAddress,\n        string memory _turingUrl\n    ) {\n        turingHelperAddress = _turingHelperAddress;\n        turing = TuringHelper(_turingHelperAddress);\n        turingUrl = _turingUrl;\n    }\n    \n    function getBobaFaucet(\n        bytes32 _uuid,\n        string memory _key\n    ) external {\n        require(BobaClaimRecords[msg.sender] + waitingPeriod \u003c block.timestamp, 'Invalid request');\n\t\t\t\t\n        // The key is hashed\n        bytes32 hashedKey = keccak256(abi.encodePacked(_key));\n        uint256 result = _verifyKey(_uuid, hashedKey);\n\n        require(result == 1, 'Invalid key and UUID');\n\n        BobaClaimRecords[msg.sender] = block.timestamp;\n        IERC20(BobaAddress).safeTransfer(msg.sender, BobaFaucetAmount);\n\n        emit GetBobaFaucet(_uuid, hashedKey, msg.sender, BobaFaucetAmount, block.timestamp);\n    }\n    \n    // Call Turing contract to get the result from the outside API endpoint\n    function _verifyKey(bytes32 _uuid, bytes32 _key) private returns (uint256) {\n        bytes memory encRequest = abi.encodePacked(_uuid, _key);\n        bytes memory encResponse = turing.TuringTx(turingUrl, encRequest);\n\n        uint256 result = abi.decode(encResponse,(uint256));\n\n        emit VerifyKey(turingUrl, _uuid, _key, result);\n\n        return result;\n    }\n}\n```\n\n### Step3: Funding Turing Helper Contract\n\nWe charge 0.01 BOBA for each Turing request and it's based on the Turing Helper Contract.\n\n```js\n// Deploy Turing Helper Contract\nconst Factory__TuringHelperr = new ContractFactory(\n  TuringHelperJson.abi,\n  TuringHelperJson.bytecode,\n  (hre as any).deployConfig.deployer_l2\n)\n\nconst TuringHelper = await Factory__TuringHelperr.deploy()\nawait TuringHelper.deployTransaction.wait()\n\nconsole.log(`TuringHelper was deployed at: ${TuringHelper.address}`)\n\n// Approve Boba Token to add funds to Turing Credit Contract\nconst BobaTuringCredit = new Contract(\n  (hre as any).deployConfig.BobaTuringCreditAddress,\n  BobaTuringCreditJson.abi,\n  (hre as any).deployConfig.deployer_l2\n)\nconst approveTx = await L2BobaToken.approve(\n  BobaTuringCredit.address,\n  utils.parseEther('100')\n)\nawait approveTx.wait()\n\nconst addCreditTx = await BobaTuringCredit.addBalanceTo(\n  utils.parseEther('100'),\n  TuringHelper.address\n)\nawait addCreditTx.wait()\n\n// Add permission for Boba Faucet contract\nconst addPermissionTx = await TuringHelper.addPermittedCaller(\n  BobaFaucet.address\n)\nawait addPermissionTx.wait()\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fomgnetwork%2Fboba-faucet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fomgnetwork%2Fboba-faucet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fomgnetwork%2Fboba-faucet/lists"}