Skip to content

Commit 9c63a47

Browse files
committed
qsolc
1 parent 41cca82 commit 9c63a47

File tree

13 files changed

+695
-7
lines changed

13 files changed

+695
-7
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,6 @@ dmypy.json
122122

123123
# Pyre type checker
124124
.pyre/
125+
126+
# node modules
127+
node_modules/

README.md

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Quick solc
2-
The qsolc.py Python script creates a standard-JSON compiler input based on the provided input flags (see `python qsolc.py --help`). Then, it runs solc on the created compiler input. You can save the output to a file by using the `--output` flag. If no output flag is given, the result will be printed to the standard output. You can observe the generated compiler_input.json by setting the `--debug` flag. It will generate a compiler_input.json.
2+
The qsolc Python script creates a standard-JSON compiler input based on the provided input flags provided as key=value pairs. Then it runs solc on the created compiler input. qsolc uses the locally installed solc version (see `solc --version`). You can save the output to a file by using the `--output` flag. If no output flag is given, the result will be printed to the standard output. You can observe the generated compiler_input.json by setting the `--debug` flag. It will generate a compiler_input.json.
33

4-
You provide compiler input flags as key-value parameters. Use "\\." to escape "." (dot). For instance the following key-value pair is used to define the source contract: `sources.example/BeerBar\.sol.urls[0]='example/BeerBar.sol'`.
4+
You provide compiler input flags as key-value parameters. Use "\\." to escape "." (dot). For instance the following key=value pair is used to define the source contract: `sources.example/BeerBar\.sol.urls[0]='example/BeerBar.sol'`.
55
Which will be translated to:
66
```json
77
{
@@ -14,22 +14,60 @@ Which will be translated to:
1414
```
1515
Follow this scheme to set any arbritrary parameter for the compiler input. Refer to the solidity documentation for further information about [Input Description](https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description).
1616

17-
Note: This implementation of qsolc is slightly modified. It already sets the required input variables to create a valid output.json needed to run the solidity_address_mapper, such as useLiteralContent and outputSelection.
1817

1918
## Complete Example
2019
After you installed the library contracts for the example BeerBar.sol using `npm i @openzeppelin/contracts` <ins>in the example folder</ins>, you can run qsolc.py from the "workdir/quick-solc" folder as follows:
2120

2221
```bash
23-
python .\qsolc.py
24-
settings.remappings[0]='./=example/'
25-
settings.remappings[1]='@openzeppelin/=example/node_modules/@openzeppelin/'
22+
.\qsolc
23+
language='Solidity'
24+
settings.metadata.useLiteralContent=true
25+
settings.outputSelection.*.*[0]='evm.deployedBytecode.sourceMap'
26+
settings.outputSelection.*.*[1]='evm.deployedBytecode.object'
27+
settings.outputSelection.*.*[2]='evm.deployedBytecode.opcodes'
28+
settings.outputSelection.*.*[3]='metadata'
29+
settings.remappings[0]='./=example/'
30+
settings.remappings[1]='@openzeppelin/=example/node_modules/@openzeppelin/'
2631
sources.example/BeerBar\.sol.urls[0]='example/BeerBar.sol'
2732
```
33+
The command will run solc with the following compiler input.json:
34+
```json
35+
{
36+
"language": "Solidity",
37+
"settings": {
38+
"metadata": {
39+
"useLiteralContent": true
40+
},
41+
"outputSelection": {
42+
"*": {
43+
"*": [
44+
"evm.deployedBytecode.sourceMap",
45+
"evm.deployedBytecode.object",
46+
"evm.deployedBytecode.opcodes",
47+
"metadata"
48+
]
49+
}
50+
},
51+
"remappings": [
52+
"./=example/",
53+
"@openzeppelin/=example/node_modules/@openzeppelin/"
54+
]
55+
},
56+
"sources": {
57+
"example/BeerBar.sol": {
58+
"urls": [
59+
"example/BeerBar.sol"
60+
]
61+
}
62+
}
63+
}
64+
```
2865

2966

3067
Note: The name of the contract source will be changed from "Beerbar.sol" to "example/Beerbar.sol." If you don't want this, you have to move qsolc.py into the "example" folder and run it like this:
3168
```bash
32-
python .\qsolc.py
69+
python .\qsolc.py
70+
...
3371
settings.remappings[0]='@openzeppelin/=node_modules/@openzeppelin/'
3472
sources.BeerBar\.sol.urls[0]='BeerBar.sol'
3573
```

example/Address.sol

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
pragma solidity ^0.8.0;
2+
3+
/**
4+
* @dev Collection of functions related to the address type
5+
*/
6+
library Address {
7+
/**
8+
* @dev Returns true if `account` is a contract.
9+
*
10+
* This test is non-exhaustive, and there may be false-negatives: during the
11+
* execution of a contract's constructor, its address will be reported as
12+
* not containing a contract.
13+
*
14+
* > It is unsafe to assume that an address for which this function returns
15+
* false is an externally-owned account (EOA) and not a contract.
16+
*/
17+
function isContract(address account) internal view returns (bool) {
18+
// This method relies in extcodesize, which returns 0 for contracts in
19+
// construction, since the code is only stored at the end of the
20+
// constructor execution.
21+
22+
uint256 size;
23+
// solhint-disable-next-line no-inline-assembly
24+
assembly { size := extcodesize(account) }
25+
return size > 0;
26+
}
27+
28+
/**
29+
* @dev Converts an `address` into `address payable`. Note that this is
30+
* simply a type cast: the actual underlying value is not changed.
31+
*/
32+
function toPayable(address account) internal pure returns (address payable) {
33+
return payable(account);
34+
}
35+
}

example/BeerBar.sol

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import "./IBeerBar.sol";
5+
import "./BeerToken.sol";
6+
import "./IERC223Recipient.sol";
7+
8+
import "@openzeppelin/contracts/access/AccessControl.sol";
9+
10+
11+
//contract BeerBar is IBeerBar, <Token-Recipient>
12+
contract BeerBar is IBeerBar, AccessControl {
13+
BeerToken public beerTokenContract;
14+
bytes32 public constant OWNER = keccak256("OWNER");
15+
bytes32 public constant BARKEEPER = keccak256("BARKEEPER");
16+
bool public _barIsOpen;
17+
uint public _beerPrice;
18+
19+
mapping(address => uint) orders;
20+
21+
22+
constructor () {
23+
_grantRole(OWNER, msg.sender);
24+
_grantRole(0x0000000000000000000000000000000000000000000000000000000000000000, msg.sender);
25+
}
26+
27+
// ROLES
28+
// * Only owners can add new owners
29+
// * Owners can only renounce themselves again
30+
function isOwner(address account) external view returns (bool) {
31+
return hasRole(OWNER, account);
32+
}
33+
34+
function addOwner(address account) external {
35+
require(hasRole(OWNER, msg.sender), "Only owners can add new owners");
36+
_grantRole(OWNER, account);
37+
emit OwnerAdded(account);
38+
}
39+
40+
function renounceOwner() external {
41+
require(hasRole(OWNER, msg.sender), "Owners can only renounce themselves again");
42+
_revokeRole(OWNER, msg.sender);
43+
emit OwnerRemoved(msg.sender);
44+
}
45+
46+
// * Barkeepers have to be set by owners
47+
// * Barkeepers can be revoked by owners
48+
// * Barkeepers can renounce themselves
49+
function isBarkeeper(address account) external view returns (bool) {
50+
return hasRole(BARKEEPER, account);
51+
}
52+
53+
function addBarkeeper(address account) external {
54+
require(hasRole(OWNER, msg.sender), "Barkeepers have to be set by owners");
55+
_grantRole(BARKEEPER, account);
56+
emit BarkeeperAdded(account);
57+
}
58+
59+
function revokeBarkeeper(address account) external {
60+
require(hasRole(OWNER, msg.sender), "Barkeepers can be revoked by owners");
61+
_revokeRole(BARKEEPER, msg.sender);
62+
emit BarkeeperRemoved(account);
63+
}
64+
65+
function renounceBarkeeper() external {
66+
require(hasRole(BARKEEPER, msg.sender), "Barkeepers can renounce themselves");
67+
_revokeRole(BARKEEPER, msg.sender);
68+
emit BarkeeperRemoved(msg.sender);
69+
}
70+
71+
// * The bar uses its own tokens as local currency.
72+
// Connect the bar contract with the token contract.
73+
// * This can only be done by owners
74+
function setBeerTokenContractAddress(address addr) external {
75+
require(hasRole(OWNER, msg.sender), "This can only be done by owners");
76+
beerTokenContract = BeerToken(addr);
77+
}
78+
// * Show which beer is served
79+
function beerTokenContractAddress() external view returns(address) {
80+
return address(beerTokenContract);
81+
}
82+
83+
// * The bar has opening hours during which beer can be ordered and served
84+
// * The bar is opened and closed by barkeepers
85+
function openBar() external {
86+
require(hasRole(BARKEEPER, msg.sender), " The bar is opened and closed by barkeepers");
87+
_barIsOpen = true;
88+
emit BarOpened();
89+
}
90+
91+
function closeBar() external virtual {
92+
require(hasRole(BARKEEPER, msg.sender), " The bar is opened and closed by barkeepers");
93+
_barIsOpen = false;
94+
emit BarClosed();
95+
}
96+
97+
function barIsOpen() external view returns (bool) {
98+
return _barIsOpen;
99+
}
100+
101+
102+
103+
// Both, supply and order, have to be implemented in a token fallback function
104+
function tokenReceived(address _from, uint _value, bytes memory _data) public virtual {
105+
require(msg.sender == address(beerTokenContract),"Wrong token");
106+
if(keccak256(_data) == hex"b308cfbb7d2d38db3a215f9728501ac69445a6afbee328cdeae4e23db54b850a") {
107+
require(hasRole(OWNER, tx.origin) ,"Only the token owner can supply");
108+
emit BeerSupplied(_from, _value);
109+
} else if(keccak256(_data) == hex"e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c") {
110+
require(!hasRole(BARKEEPER, msg.sender) && !hasRole(OWNER, msg.sender), "Only customers can order");
111+
require(_barIsOpen, "Bar is closed");
112+
orders[_from] += _value;
113+
emit BeerOrdered(_from, _value);
114+
}
115+
}
116+
117+
// * Beer that has been ordered will be served by barkeepers
118+
// * Beer can only be served while the bar is open
119+
// * Served beer has to be burned by the token contract
120+
function serveBeer(address customer, uint amount) external {
121+
require(_barIsOpen, " Beer can only be served while the bar is open");
122+
require(hasRole(BARKEEPER, msg.sender), " Only barkeepers can serve beer");
123+
require(orders[customer] >= amount, " The customer did not order that much!");
124+
orders[customer] -= amount;
125+
beerTokenContract.burn(amount);
126+
}
127+
128+
// * Orders that haven't yet been processed may be canceled by the
129+
// customer, who will get back the tokens
130+
// * This triggers the event BeerCanceled
131+
// * Orders can be canceled at any time
132+
function cancelOrder(uint amount) external {
133+
require(orders[msg.sender] >= amount);
134+
require(beerTokenContract.transfer(msg.sender, amount),"Failed to transfer tokens");
135+
orders[msg.sender] -= amount;
136+
emit BeerCanceled(msg.sender, amount);
137+
138+
}
139+
140+
141+
// * Get pending orders for a customer
142+
function pendingBeer(address customer) external view returns (uint256) {
143+
return orders[customer];
144+
}
145+
146+
// * Beer price can only be changed by owners when the bar is closed
147+
function setBeerPrice(uint256 price) external {
148+
require(!_barIsOpen, " Beer price can only be changed when the bar is closed");
149+
require(hasRole(OWNER, msg.sender), " Beer price can only be changed by owners ");
150+
_beerPrice = price;
151+
}
152+
153+
function getBeerPrice() external view returns(uint256) {
154+
return _beerPrice;
155+
}
156+
157+
// * Customers may buy tokens for Ether
158+
// * If the supplied Ether is not divisible by the beer price
159+
// the rest is kept as a tip. The caller (like the web interface)
160+
// can check that the value is a multiple of the beer price.
161+
function buyToken() external payable {
162+
require(_beerPrice > 0, " Beer price has not been set");
163+
require(beerTokenContract.transfer(msg.sender, (msg.value-(msg.value % _beerPrice))/_beerPrice),"Failed to transfer");
164+
//require(beerTokenContract.mint(msg.sender, (msg.value-(msg.value % _beerPrice))/_beerPrice),"Failed to mint");
165+
}
166+
167+
// * `amount` (in Wei) of the Ether stored in the contract is transferred to
168+
// `receiver`, provided `amount` does not exceed the balance of the contract
169+
// * Only owners are allowed to do this
170+
function payout(address payable receiver, uint256 amount) external {
171+
require(hasRole(OWNER, msg.sender), " Only owners are allowed to do this");
172+
(bool success, ) = receiver.call{value: amount}("");
173+
require(success, "Failed to send Ether");
174+
}
175+
176+
177+
}

example/BeerToken.sol

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import "./IBeerToken.sol";
5+
import "./Address.sol";
6+
import "./IERC223Recipient.sol";
7+
8+
contract BeerToken is IBeerToken {
9+
10+
address private owner;
11+
uint public total;
12+
mapping(address => uint) public tokens;
13+
14+
constructor() {
15+
owner = msg.sender;
16+
}
17+
18+
// * Default attributes of your token
19+
function name() external view returns (string memory) {
20+
return "Corns";
21+
}
22+
23+
function symbol() external view returns (string memory) {
24+
return "Corns";
25+
}
26+
27+
// * Our BeerToken is not divisible
28+
function decimals() external view returns (uint8) {
29+
return 0;
30+
}
31+
32+
// * Show the total supply of tokens
33+
function totalSupply() external view returns (uint256) {
34+
return total;
35+
}
36+
37+
// * Show the token balance of the address
38+
function balanceOf(address who) external view returns (uint256) {
39+
return tokens[who];
40+
}
41+
42+
// * Basic functionality for transferring tokens to user.
43+
// The token contract keeps track of the token balances.
44+
// * Token must not be lost! Make sure they can only be transferred to addresses,
45+
// who also support the receiving of tokens.
46+
function transfer(address to, uint256 value) external returns (bool success) {
47+
require(tokens[msg.sender] >= value);
48+
bytes memory _empty = hex"00000000";
49+
tokens[msg.sender] = tokens[msg.sender] - value;
50+
tokens[to] = tokens[to] + value;
51+
if(Address.isContract(to)) {
52+
IERC223Recipient(to).tokenReceived(msg.sender, value, _empty);
53+
}
54+
emit Transfer(msg.sender, to, value);
55+
//emit TransferData(_empty);
56+
return true;
57+
}
58+
59+
function transfer(address to, uint256 value, bytes calldata data) external returns (bool success) {
60+
require(tokens[msg.sender] >= value);
61+
tokens[msg.sender] = tokens[msg.sender] - value;
62+
tokens[to] = tokens[to] + value;
63+
if(Address.isContract(to)) {
64+
IERC223Recipient(to).tokenReceived(msg.sender, value, data);
65+
}
66+
emit Transfer(msg.sender, to, value);
67+
//emit TransferData(_data);
68+
return true;
69+
}
70+
71+
// * Tokens can be minted by the owner of the token contract
72+
function mint(address account, uint256 value) external returns (bool success) {
73+
require(owner == msg.sender,"Only owner can mint");
74+
tokens[account] += value;
75+
total += value;
76+
emit Transfer(msg.sender, account, value);
77+
return true;
78+
}
79+
80+
// * Tokens can be burned and therefore "destroyed"
81+
function burn(uint256 value) external {
82+
require(tokens[msg.sender] >= value);
83+
tokens[msg.sender] -= value;
84+
total -= value;
85+
}
86+
87+
}

0 commit comments

Comments
 (0)