Skip to content

Commit

Permalink
Added documentation for each contract.
Browse files Browse the repository at this point in the history
  • Loading branch information
calvogenerico committed Dec 9, 2024
1 parent 7aa617a commit 1448e68
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 131 deletions.
71 changes: 14 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,23 @@
## Foundry
# Double Zero test contracts

**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**
This repo contains a series of contracts meant to be use to test and showcase the [double zero framework](https://github.com/Moonsong-Labs/double-zero).

Foundry consists of:
In order to deploy the contracts in this repo you are going to need [foundry zksync](https://foundry-book.zksync.io/getting-started/installation).

- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.
## Contracts

## Documentation
The contracts in this repo are variations of well known standards taking adventage of the privacy and access control
provided by double zero. Each contracts has comments in the source code explaining how they work and why they work.

https://book.getfoundry.sh/
## Deploy

## Usage
You can deploy all the contracts by running:

### Build

```shell
$ forge build
```

### Test

```shell
$ forge test
``` bash
export RPC_URL=<your_rpc_url> # i.e.: http://localhost:3050
export VERIFIER_URL=<your_rpc_url> # i.e.: http://localhost:3070/contract_verification
export PRIV_KEY=<your_depoyment_priv_key> # space at the beggining to avoid save pk in shell history.
./bash_scripts/deploy_tokens.sh
```

### Format

```shell
$ forge fmt
```

### Gas Snapshots

```shell
$ forge snapshot
```

### Anvil

```shell
$ anvil
```

### Deploy

```shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
```

### Cast

```shell
$ cast <subcommand>
```

### Help

```shell
$ forge --help
$ anvil --help
$ cast --help
```
Once the contracts are deployed you can use the double zero explorer to check their status and interact with them.
47 changes: 31 additions & 16 deletions bash_scripts/deploy_tokens.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,56 @@
set -e
set -o xtrace

if [[ -z "${PRIV_KEY}" ]]; then
echo "missing PRIV_KEY env var"
exit 1
fi

if [[ -z "${RPC_URL}" ]]; then
echo "missing RPC_URL env var"
exit 1
fi

if [[ -z "${VERIFIER_URL}" ]]; then
echo "missing VERIFIER_URL env var"
exit 1
fi

forge create --zksync \
--rpc-url http://localhost:3050 \
--private-key 0x87ce66d9f787696d21af61750c1c3310099f27fb7e7259a193a8c514293a7c0c \
--rpc-url $RPC_URL \
--private-key $PRIV_KEY \
--verify \
--verifier zksync \
--verifier-url http://localhost:3070/contract_verification \
--verifier-url VERIFIER_URL \
src/PublicScopedBalanceErc20.sol:PublicScopedBalanceErc20 \
--constructor-args "PublicScopedBalanceErc20-posta" "PEB20P";
--constructor-args "PublicScopedBalanceErc20" "PEB20";

forge create --zksync \
--rpc-url http://localhost:3050 \
--private-key 0x87ce66d9f787696d21af61750c1c3310099f27fb7e7259a193a8c514293a7c0c \
--rpc-url $RPC_URL \
--private-key $PRIV_KEY \
--verify \
--verifier zksync \
--verifier-url http://localhost:3070/contract_verification \
--verifier-url VERIFIER_URL \
src/ShareBalanceErc20.sol:ShareBalanceErc20 \
--constructor-args "ShareBalanceErc20-posta" "SB20P";
--constructor-args "ShareBalanceErc20" "SB20";

forge create --zksync \
--rpc-url http://localhost:3050 \
--private-key 0x87ce66d9f787696d21af61750c1c3310099f27fb7e7259a193a8c514293a7c0c \
--rpc-url $RPC_URL \
--private-key $PRIV_KEY \
--verify \
--verifier zksync \
--verifier-url http://localhost:3070/contract_verification \
--verifier-url VERIFIER_URL \
src/ShareScopedBalanceErc20.sol:ShareScopedBalanceErc20 \
--constructor-args "ShareScopedBalanceErc20-posta" "SSBE20P";
--constructor-args "ShareScopedBalanceErc20" "SSBE20";


forge create --zksync \
--rpc-url http://localhost:3050 \
--private-key 0x87ce66d9f787696d21af61750c1c3310099f27fb7e7259a193a8c514293a7c0c \
--rpc-url $RPC_URL \
--private-key $PRIV_KEY \
--verify \
--verifier zksync \
--verifier-url http://localhost:3070/contract_verification \
--verifier-url VERIFIER_URL \
src/PublicSelectionErc721.sol:PublicSelectionErc721 \
--constructor-args "PublicSelectionErc721-posta" "PC721P";
--constructor-args "PublicSelectionErc721" "PC721";


19 changes: 0 additions & 19 deletions script/Counter.s.sol

This file was deleted.

24 changes: 0 additions & 24 deletions src/Counter.sol

This file was deleted.

22 changes: 22 additions & 0 deletions src/PublicScopedBalanceErc20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ pragma solidity 0.8.24;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";


/**
* @dev Implementation of the ERC20 meant to be used in the context of double zero.
*
* Inside double zero the balance of each account is private and can only be checked by the user.
* This contract allows a user to publish that the own up to certain amount of the token. The amount
* is set by each user but can be queries by anyone.
*/
contract PublicScopedBalanceErc20 is ERC20 {
uint8 private _decimals;
mapping(address => uint256) private puclicThresholds;
Expand All @@ -13,20 +21,34 @@ contract PublicScopedBalanceErc20 is ERC20 {
_decimals = 4;
}

/**
* Mint. Meant to be used only by admins. Validated at double zero level.
*/
function mint(address to, uint256 amount) public returns (bool) {
_mint(to, amount);
return true;
}

/**
* Public
*/
function decimals() public view override returns (uint8) {
return _decimals;
}

/**
* Anyone can query this. It returns if a user has or not more than an amunt set by
* that user.
*/
function publicThreshold(address target) public view returns (uint256, bool) {
uint256 threshold = puclicThresholds[target];
return (threshold, balanceOf(target) >= threshold);
}

/**
* The double zero layer validates that this method can only be called with the users's
* own address. This allows the user to explose how much balance they allow to query.
*/
function changePublicThreshold(uint256 newThreshold) external {
puclicThresholds[msg.sender] = newThreshold;
}
Expand Down
43 changes: 35 additions & 8 deletions src/PublicSelectionErc721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ pragma solidity 0.8.24;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

/**
* @dev Implementation of the ERC20 meant to be used in the context of double zero.
*
* Inside double zero the iventory of each account is private and can only be checked by the user.
* This contract allows a user to publish take a subset of their inventory and make it public.
* This works because reading directly from contract's storage is not allowed in double zero. Also
* the double zero layer is used to prevent users to read from other users private data.
*/
contract PublicSelectionErc721 is ERC721 {
uint256 private _lastId;
mapping(address owner => uint256[]) private publicInventories;
Expand All @@ -13,6 +21,9 @@ contract PublicSelectionErc721 is ERC721 {
_lastId = 0;
}

/**
* Only for admins
*/
function mintNext(address target) public returns (uint256) {
_lastId += 1;

Expand All @@ -22,10 +33,17 @@ contract PublicSelectionErc721 is ERC721 {
return newItemId;
}

/**
* Global access method.
*/
function publicInventory(address owner) public view returns (uint256[] memory) {
return publicInventories[owner];
}

/**
* Global access method. This cannot be used to filter private user data because on calls,
* double zero ensures that msg.sender is the logged in user.
*/
function expose(uint256 tokenId) public {
if (ownerOf(tokenId) != msg.sender) {
revert();
Expand All @@ -42,19 +60,17 @@ contract PublicSelectionErc721 is ERC721 {
publicInventories[msg.sender].push(tokenId);
}

/**
* Public method.
*/
function hide(uint256 tokenId) public {
return _hideFor(tokenId, msg.sender);
}


function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
if (auth != address(0)) {
_hideFor(tokenId, auth);
}

return super._update(to, tokenId, auth);
}

/**
* Internal method.
*/
function _hideFor(uint256 tokenId, address user) internal {
uint256[] storage exposed = publicInventories[user];
for (uint i = 0; i < exposed.length; i++) {
Expand All @@ -65,4 +81,15 @@ contract PublicSelectionErc721 is ERC721 {
}
}
}

/**
* Internal method.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
if (auth != address(0)) {
_hideFor(tokenId, auth);
}

return super._update(to, tokenId, auth);
}
}
28 changes: 28 additions & 0 deletions src/ShareBalanceErc20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ pragma solidity 0.8.24;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/**
* @dev Implementation of the ERC20 meant to be used in the context of double zero.
*
* Inside double zero the balance of each account is private and can only be checked by the user.
* This contracts allows users to share their balance only with specific users.
* This works because the double zero layer prevents other users to access data in a non intended way.
*/
contract ShareBalanceErc20 is ERC20 {
uint8 private _decimals;
mapping(address => uint256) private puclicThresholds;
Expand All @@ -14,15 +21,30 @@ contract ShareBalanceErc20 is ERC20 {
_decimals = 4;
}

/**
* Unrestricted method
*/
function decimals() public view override returns (uint8) {
return _decimals;
}

/**
* Restricted only to admins
*/
function mint(address _to, uint256 _amount) public returns (bool) {
_mint(_to, _amount);
return true;
}

/**
* Unrestricted method. We control at the smart contract level that the
* sender has permission to read the balance. This works fine because the
* regular "balanceOf" method is restricted in the double zero layer to allow only
* each user to check their balance.
*
* This validation is not on the balanceOf method to avoid creating issues with existing smart
* contracts.
*/
function authorizedBalanceOf(address account) public view returns (uint256) {
if (account != msg.sender && !permissions[account][msg.sender]) {
revert();
Expand All @@ -31,10 +53,16 @@ contract ShareBalanceErc20 is ERC20 {
}


/**
* Unrestricted method. This doesn't leak any private info.
*/
function shareBalanceWith(address reader) public {
permissions[msg.sender][reader] = true;
}

/**
* Unrestricted method. This doesn't leak any private info.
*/
function hideBalanceFrom(address reader) public {
permissions[msg.sender][reader] = false;
}
Expand Down
Loading

0 comments on commit 1448e68

Please sign in to comment.