{"id":13794582,"url":"https://github.com/dapphub/chai","last_synced_at":"2025-07-16T21:14:59.955Z","repository":{"id":66537006,"uuid":"223230094","full_name":"dapphub/chai","owner":"dapphub","description":"ERC20 wrapper over the Dai Savings Rate","archived":false,"fork":false,"pushed_at":"2020-04-01T20:41:51.000Z","size":165,"stargazers_count":149,"open_issues_count":4,"forks_count":27,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-03-25T20:33:30.456Z","etag":null,"topics":["chai","defi","stablecoin"],"latest_commit_sha":null,"homepage":"https://chai.money","language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dapphub.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-11-21T17:39:46.000Z","updated_at":"2025-01-06T19:12:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"bf9df655-518c-44fc-805d-b9926cbbe26c","html_url":"https://github.com/dapphub/chai","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapphub%2Fchai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapphub%2Fchai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapphub%2Fchai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapphub%2Fchai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dapphub","download_url":"https://codeload.github.com/dapphub/chai/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501223,"owners_count":21114632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["chai","defi","stablecoin"],"created_at":"2024-08-03T23:00:43.903Z","updated_at":"2025-04-12T00:31:25.382Z","avatar_url":"https://github.com/dapphub.png","language":"Solidity","funding_links":[],"categories":["References","Projects"],"sub_categories":["DappTools"],"readme":"# 🍵\n\n_1 Chai = 1 Dai * Pot.chi_\n\n`chai` is an ERC20 token representing a claim on deposits in the DSR. It can be freely converted to and from `dai`: the amount of `dai` claimed by one `chai` is always increasing, at the Dai Savings Rate. Like any well-behaved token, a user's `chai` _balance_ won't change unexpectedly, while the `chai`'s _value_ grows day by day.\n\n`chai` is a very simple contract. Apart from the standard ERC20 functionality, it also implements the same `permit` off-chain approval as `dai` itself. You can also call `dai(address usr)` to check how many `dai` a given user's `chai` balance claims. The token has no owner or authority. That's all there is to it.\n\n## mainnet deployment\n\n`chai` is live on the mainnet since December 1st at [0x06af07097c9eeb7fd685c692751d5c66db49c215](https://etherscan.io/token/0x06af07097c9eeb7fd685c692751d5c66db49c215)\n\nYou can interact with the Chai contract at [chai.money](https://chai.money). The source for the ui is hosted [here](https://github.com/lucasvo/chui).\n\n## audit\n\nThe deployed `chai` contract underwent a two day audit by Trail Of Bits in the beginning of February, finding no security related issues.\nA summary can be found [here](./ToB_Letter_of_Attestation_Chai.pdf).\n\n## building and testing\n\nThis contract is built using [dapptools](http://dapp.tools/), and follows the standard dapptools procedure for building and testing.\n\nTo compile:\n```sh\n$ make all\n```\n\nTo run the tests:\n```sh\n$ make test\n```\n\n## documentation\n\n### ERC20 functions\n\nChai.sol implements the standard ERC20 functions (balanceOf, allowance, approve, transfer, transferFrom). \n\nSimilar to tokens like `WETH`, `MKR` and `DAI`, an allowance of `uint(-1)` is treated as \"infinity\", so `transferFrom` calls from an address that has been given an allowance of `uint(-1)` will not cause the allowance to decrease.\n\n### Join \n\nThe chai contract is an ERC20 token where minting happens in a function called `join`, which converts a users Dai balance into Chai:\n```sol\n    function join(address dst, uint wad) external {\n        uint chi = (now \u003e pot.rho()) ? pot.drip() : pot.chi();\n        uint pie = rdiv(wad, chi);\n        balanceOf[dst] = add(balanceOf[dst], pie);\n        totalSupply    = add(totalSupply, pie);\n\n        daiToken.transferFrom(msg.sender, address(this), wad);\n        daiJoin.join(address(this), wad);\n        pot.join(pie);\n        emit Transfer(address(0), dst, pie);\n    }\n```\n\nCalling this function transfers `wad` Dai into the `pot` contract from `msg.sender`, granting the `dst` address a Chai balance representing their claim of Dai in the `pot` contract.\n\n### Exit\n\nChai is burned (converted into Dai) through a function called `exit`:\n```sol\n    function exit(address src, uint wad) public {\n        require(balanceOf[src] \u003e= wad, \"chai/insufficient-balance\");\n        if (src != msg.sender \u0026\u0026 allowance[src][msg.sender] != uint(-1)) {\n            require(allowance[src][msg.sender] \u003e= wad, \"chai/insufficient-allowance\");\n            allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);\n        }\n        balanceOf[src] = sub(balanceOf[src], wad);\n        totalSupply    = sub(totalSupply, wad);\n\n        uint chi = (now \u003e pot.rho()) ? pot.drip() : pot.chi();\n        pot.exit(wad);\n        daiJoin.exit(msg.sender, rmul(chi, wad));\n        emit Transfer(src, address(0), wad);\n    }\n```\nA `msg.sender` with sufficient approval from the address `src` can call this function to decrease their Chai balance by `wad` and transfer its underlying Dai value to the `msg.sender`.\n\n### Draw\n\nSince the `wad` argument to the `exit` function above is denominated in chai, the exact Dai transferred will be determined at the time the transaction is included in a block. If you want to ensure that a specific Dai amount must be transfered, you can use the draw function instead, which takes a dai denominated argument:\n\n```sol\n    // wad is denominated in dai\n    function draw(address src, uint wad) external {\n        uint chi = (now \u003e pot.rho()) ? pot.drip() : pot.chi();\n        // rounding up ensures usr gets at least wad dai\n        exit(src, rdivup(wad, chi));\n    }\n```\n\n\n### Move\n\nSimilarly to `draw`, there is a transferFrom function with a dai denominated argument, ensuring that the receiving address will receive Chai worth at least `wad` dai:\n\n```sol\n    // like transferFrom but dai-denominated\n    function move(address src, address dst, uint wad) external returns (bool) {\n        uint chi = (now \u003e pot.rho()) ? pot.drip() : pot.chi();\n        // rounding up ensures dst gets at least wad dai\n        return transferFrom(src, dst, rdivup(wad, chi));\n    }\n```\n\n### Permit\n\nThe permit method lets a user approve an address to spend on their behalf by signing a ERC712 typed message called `permit`.\n\nA `permit` consists of the following components:\n\n- `holder`, the address granting the permission and the signer of the message\n- `spender`, the address to which permission is being granted or denied\n- `nonce`, for replay attack protection\n- `allowed`, whether the `spender`s permission is being granted or revoked\n\nMessages are signed using the procedure described in [ERC712](https://eips.ethereum.org/EIPS/eip-712), using the `PERMIT_TYPEHASH` and `DOMAIN_SEPARATOR` constants:\n```sol\n    bytes32 public constant DOMAIN_SEPARATOR = 0x0b50407de9fa158c2cba01a99633329490dfd22989a150c20e8c7b4c1fb0fcc3;\n    // keccak256(\"Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)\"));\n    bytes32 public constant PERMIT_TYPEHASH  = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;\n```\n\n`permit` are processed by calling the `permit` method, which increments the `holder` nonce and approves the `spender` to spend on the behalf of `holder` if `allowed` is true, and revokes it otherwise:\n\n```sol\n    // --- Approve by signature ---\n    function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) external\n    {\n        bytes32 digest = keccak256(abi.encodePacked(\n            \"\\x19\\x01\",\n            DOMAIN_SEPARATOR,\n            keccak256(abi.encode(PERMIT_TYPEHASH,\n                                 holder,\n                                 spender,\n                                 nonce,\n                                 expiry,\n                                 allowed))));\n        require(holder != address(0), \"chai/invalid holder\");\n        require(holder == ecrecover(digest, v, r, s), \"chai/invalid-permit\");\n        require(expiry == 0 || now \u003c= expiry, \"chai/permit-expired\");\n        require(nonce == nonces[holder]++, \"chai/invalid-nonce\");\n\n        uint can = allowed ? uint(-1) : 0;\n        allowance[holder][spender] = can;\n        emit Approval(holder, spender, can);\n    }\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdapphub%2Fchai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdapphub%2Fchai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdapphub%2Fchai/lists"}