kopia lustrzana https://github.com/bugout-dev/dao
ERC20 implementation
rodzic
fe3a1e8634
commit
3ac420c409
|
@ -0,0 +1,22 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Authors: Moonstream Engineering (engineering@moonstream.to)
|
||||
* GitHub: https://github.com/bugout-dev/dao
|
||||
*
|
||||
* This is an implementation of the ERC20 governance token for the Moonstream DAO.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./ERC20WithCommonStorage.sol";
|
||||
import "./LibERC20.sol";
|
||||
|
||||
contract ERC20Facet is ERC20WithCommonStorage {
|
||||
constructor() ERC20WithCommonStorage("Moonstream", "MNSTR") {}
|
||||
|
||||
function mint(address account, uint256 amount) external {
|
||||
LibERC20.enforceIsController();
|
||||
_mint(account, amount);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,414 @@
|
|||
# Code generated by moonworm : https://github.com/bugout-dev/moonworm
|
||||
# Moonworm version : 0.1.8
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from brownie import Contract, network, project
|
||||
from brownie.network.contract import ContractContainer
|
||||
from eth_typing.evm import ChecksumAddress
|
||||
|
||||
|
||||
PROJECT_DIRECTORY = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
BUILD_DIRECTORY = os.path.join(PROJECT_DIRECTORY, "build", "contracts")
|
||||
|
||||
|
||||
def boolean_argument_type(raw_value: str) -> bool:
|
||||
TRUE_VALUES = ["1", "t", "y", "true", "yes"]
|
||||
FALSE_VALUES = ["0", "f", "n", "false", "no"]
|
||||
|
||||
if raw_value.lower() in TRUE_VALUES:
|
||||
return True
|
||||
elif raw_value.lower() in FALSE_VALUES:
|
||||
return False
|
||||
|
||||
raise ValueError(
|
||||
f"Invalid boolean argument: {raw_value}. Value must be one of: {','.join(TRUE_VALUES + FALSE_VALUES)}"
|
||||
)
|
||||
|
||||
|
||||
def bytes_argument_type(raw_value: str) -> bytes:
|
||||
return raw_value.encode()
|
||||
|
||||
|
||||
def get_abi_json(abi_name: str) -> List[Dict[str, Any]]:
|
||||
abi_full_path = os.path.join(BUILD_DIRECTORY, f"{abi_name}.json")
|
||||
if not os.path.isfile(abi_full_path):
|
||||
raise IOError(
|
||||
f"File does not exist: {abi_full_path}. Maybe you have to compile the smart contracts?"
|
||||
)
|
||||
|
||||
with open(abi_full_path, "r") as ifp:
|
||||
build = json.load(ifp)
|
||||
|
||||
abi_json = build.get("abi")
|
||||
if abi_json is None:
|
||||
raise ValueError(f"Could not find ABI definition in: {abi_full_path}")
|
||||
|
||||
return abi_json
|
||||
|
||||
|
||||
def contract_from_build(abi_name: str) -> ContractContainer:
|
||||
# This is workaround because brownie currently doesn't support loading the same project multiple
|
||||
# times. This causes problems when using multiple contracts from the same project in the same
|
||||
# python project.
|
||||
PROJECT = project.main.Project("moonworm", Path(PROJECT_DIRECTORY))
|
||||
|
||||
abi_full_path = os.path.join(BUILD_DIRECTORY, f"{abi_name}.json")
|
||||
if not os.path.isfile(abi_full_path):
|
||||
raise IOError(
|
||||
f"File does not exist: {abi_full_path}. Maybe you have to compile the smart contracts?"
|
||||
)
|
||||
|
||||
with open(abi_full_path, "r") as ifp:
|
||||
build = json.load(ifp)
|
||||
|
||||
return ContractContainer(PROJECT, build)
|
||||
|
||||
|
||||
class ERC20Facet:
|
||||
def __init__(self, contract_address: Optional[ChecksumAddress]):
|
||||
self.contract_name = "ERC20Facet"
|
||||
self.address = contract_address
|
||||
self.contract = None
|
||||
self.abi = get_abi_json("ERC20Facet")
|
||||
if self.address is not None:
|
||||
self.contract: Optional[Contract] = Contract.from_abi(
|
||||
self.contract_name, self.address, self.abi
|
||||
)
|
||||
|
||||
def deploy(self, transaction_config):
|
||||
contract_class = contract_from_build(self.contract_name)
|
||||
deployed_contract = contract_class.deploy(transaction_config)
|
||||
self.address = deployed_contract.address
|
||||
self.contract = deployed_contract
|
||||
|
||||
def assert_contract_is_instantiated(self) -> None:
|
||||
if self.contract is None:
|
||||
raise Exception("contract has not been instantiated")
|
||||
|
||||
def allowance(self, owner: ChecksumAddress, spender: ChecksumAddress) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.allowance.call(owner, spender)
|
||||
|
||||
def approve(self, spender: ChecksumAddress, amount: int, transaction_config) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.approve(spender, amount, transaction_config)
|
||||
|
||||
def balance_of(self, account: ChecksumAddress) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.balanceOf.call(account)
|
||||
|
||||
def decimals(self) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.decimals.call()
|
||||
|
||||
def decrease_allowance(
|
||||
self, spender: ChecksumAddress, subtracted_value: int, transaction_config
|
||||
) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.decreaseAllowance(
|
||||
spender, subtracted_value, transaction_config
|
||||
)
|
||||
|
||||
def increase_allowance(
|
||||
self, spender: ChecksumAddress, added_value: int, transaction_config
|
||||
) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.increaseAllowance(spender, added_value, transaction_config)
|
||||
|
||||
def mint(self, account: ChecksumAddress, amount: int, transaction_config) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.mint(account, amount, transaction_config)
|
||||
|
||||
def name(self) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.name.call()
|
||||
|
||||
def symbol(self) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.symbol.call()
|
||||
|
||||
def total_supply(self) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.totalSupply.call()
|
||||
|
||||
def transfer(
|
||||
self, recipient: ChecksumAddress, amount: int, transaction_config
|
||||
) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.transfer(recipient, amount, transaction_config)
|
||||
|
||||
def transfer_from(
|
||||
self,
|
||||
sender: ChecksumAddress,
|
||||
recipient: ChecksumAddress,
|
||||
amount: int,
|
||||
transaction_config,
|
||||
) -> Any:
|
||||
self.assert_contract_is_instantiated()
|
||||
return self.contract.transferFrom(sender, recipient, amount, transaction_config)
|
||||
|
||||
|
||||
def get_transaction_config(args: argparse.Namespace) -> Dict[str, Any]:
|
||||
signer = network.accounts.load(args.sender, args.password)
|
||||
transaction_config: Dict[str, Any] = {"from": signer}
|
||||
if args.gas_price is not None:
|
||||
transaction_config["gas_price"] = args.gas_price
|
||||
if args.confirmations is not None:
|
||||
transaction_config["required_confs"] = args.confirmations
|
||||
return transaction_config
|
||||
|
||||
|
||||
def add_default_arguments(parser: argparse.ArgumentParser, transact: bool) -> None:
|
||||
parser.add_argument(
|
||||
"--network", required=True, help="Name of brownie network to connect to"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--address", required=False, help="Address of deployed contract to connect to"
|
||||
)
|
||||
if not transact:
|
||||
return
|
||||
parser.add_argument(
|
||||
"--sender", required=True, help="Path to keystore file for transaction sender"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--password",
|
||||
required=False,
|
||||
help="Password to keystore file (if you do not provide it, you will be prompted for it)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--gas-price", default=None, help="Gas price at which to submit transaction"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--confirmations",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Number of confirmations to await before considering a transaction completed",
|
||||
)
|
||||
|
||||
|
||||
def handle_deploy(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
transaction_config = get_transaction_config(args)
|
||||
contract = ERC20Facet(None)
|
||||
result = contract.deploy(transaction_config=transaction_config)
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_allowance(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
result = contract.allowance(owner=args.owner, spender=args.spender)
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_approve(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
transaction_config = get_transaction_config(args)
|
||||
result = contract.approve(
|
||||
spender=args.spender, amount=args.amount, transaction_config=transaction_config
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_balance_of(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
result = contract.balance_of(account=args.account)
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_decimals(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
result = contract.decimals()
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_decrease_allowance(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
transaction_config = get_transaction_config(args)
|
||||
result = contract.decrease_allowance(
|
||||
spender=args.spender,
|
||||
subtracted_value=args.subtracted_value,
|
||||
transaction_config=transaction_config,
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_increase_allowance(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
transaction_config = get_transaction_config(args)
|
||||
result = contract.increase_allowance(
|
||||
spender=args.spender,
|
||||
added_value=args.added_value,
|
||||
transaction_config=transaction_config,
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_mint(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
transaction_config = get_transaction_config(args)
|
||||
result = contract.mint(
|
||||
account=args.account, amount=args.amount, transaction_config=transaction_config
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_name(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
result = contract.name()
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_symbol(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
result = contract.symbol()
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_total_supply(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
result = contract.total_supply()
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_transfer(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
transaction_config = get_transaction_config(args)
|
||||
result = contract.transfer(
|
||||
recipient=args.recipient,
|
||||
amount=args.amount,
|
||||
transaction_config=transaction_config,
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
def handle_transfer_from(args: argparse.Namespace) -> None:
|
||||
network.connect(args.network)
|
||||
contract = ERC20Facet(args.address)
|
||||
transaction_config = get_transaction_config(args)
|
||||
result = contract.transfer_from(
|
||||
sender=args.sender_arg,
|
||||
recipient=args.recipient,
|
||||
amount=args.amount,
|
||||
transaction_config=transaction_config,
|
||||
)
|
||||
print(result)
|
||||
|
||||
|
||||
def generate_cli() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description="CLI for ERC20Facet")
|
||||
parser.set_defaults(func=lambda _: parser.print_help())
|
||||
subcommands = parser.add_subparsers()
|
||||
|
||||
deploy_parser = subcommands.add_parser("deploy")
|
||||
add_default_arguments(deploy_parser, True)
|
||||
deploy_parser.set_defaults(func=handle_deploy)
|
||||
|
||||
allowance_parser = subcommands.add_parser("allowance")
|
||||
add_default_arguments(allowance_parser, False)
|
||||
allowance_parser.add_argument("--owner", required=True, help="Type: address")
|
||||
allowance_parser.add_argument("--spender", required=True, help="Type: address")
|
||||
allowance_parser.set_defaults(func=handle_allowance)
|
||||
|
||||
approve_parser = subcommands.add_parser("approve")
|
||||
add_default_arguments(approve_parser, True)
|
||||
approve_parser.add_argument("--spender", required=True, help="Type: address")
|
||||
approve_parser.add_argument(
|
||||
"--amount", required=True, help="Type: uint256", type=int
|
||||
)
|
||||
approve_parser.set_defaults(func=handle_approve)
|
||||
|
||||
balance_of_parser = subcommands.add_parser("balance-of")
|
||||
add_default_arguments(balance_of_parser, False)
|
||||
balance_of_parser.add_argument("--account", required=True, help="Type: address")
|
||||
balance_of_parser.set_defaults(func=handle_balance_of)
|
||||
|
||||
decimals_parser = subcommands.add_parser("decimals")
|
||||
add_default_arguments(decimals_parser, False)
|
||||
decimals_parser.set_defaults(func=handle_decimals)
|
||||
|
||||
decrease_allowance_parser = subcommands.add_parser("decrease-allowance")
|
||||
add_default_arguments(decrease_allowance_parser, True)
|
||||
decrease_allowance_parser.add_argument(
|
||||
"--spender", required=True, help="Type: address"
|
||||
)
|
||||
decrease_allowance_parser.add_argument(
|
||||
"--subtracted-value", required=True, help="Type: uint256", type=int
|
||||
)
|
||||
decrease_allowance_parser.set_defaults(func=handle_decrease_allowance)
|
||||
|
||||
increase_allowance_parser = subcommands.add_parser("increase-allowance")
|
||||
add_default_arguments(increase_allowance_parser, True)
|
||||
increase_allowance_parser.add_argument(
|
||||
"--spender", required=True, help="Type: address"
|
||||
)
|
||||
increase_allowance_parser.add_argument(
|
||||
"--added-value", required=True, help="Type: uint256", type=int
|
||||
)
|
||||
increase_allowance_parser.set_defaults(func=handle_increase_allowance)
|
||||
|
||||
mint_parser = subcommands.add_parser("mint")
|
||||
add_default_arguments(mint_parser, True)
|
||||
mint_parser.add_argument("--account", required=True, help="Type: address")
|
||||
mint_parser.add_argument("--amount", required=True, help="Type: uint256", type=int)
|
||||
mint_parser.set_defaults(func=handle_mint)
|
||||
|
||||
name_parser = subcommands.add_parser("name")
|
||||
add_default_arguments(name_parser, False)
|
||||
name_parser.set_defaults(func=handle_name)
|
||||
|
||||
symbol_parser = subcommands.add_parser("symbol")
|
||||
add_default_arguments(symbol_parser, False)
|
||||
symbol_parser.set_defaults(func=handle_symbol)
|
||||
|
||||
total_supply_parser = subcommands.add_parser("total-supply")
|
||||
add_default_arguments(total_supply_parser, False)
|
||||
total_supply_parser.set_defaults(func=handle_total_supply)
|
||||
|
||||
transfer_parser = subcommands.add_parser("transfer")
|
||||
add_default_arguments(transfer_parser, True)
|
||||
transfer_parser.add_argument("--recipient", required=True, help="Type: address")
|
||||
transfer_parser.add_argument(
|
||||
"--amount", required=True, help="Type: uint256", type=int
|
||||
)
|
||||
transfer_parser.set_defaults(func=handle_transfer)
|
||||
|
||||
transfer_from_parser = subcommands.add_parser("transfer-from")
|
||||
add_default_arguments(transfer_from_parser, True)
|
||||
transfer_from_parser.add_argument(
|
||||
"--sender-arg", required=True, help="Type: address"
|
||||
)
|
||||
transfer_from_parser.add_argument(
|
||||
"--recipient", required=True, help="Type: address"
|
||||
)
|
||||
transfer_from_parser.add_argument(
|
||||
"--amount", required=True, help="Type: uint256", type=int
|
||||
)
|
||||
transfer_from_parser.set_defaults(func=handle_transfer_from)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = generate_cli()
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,6 +1,6 @@
|
|||
import argparse
|
||||
|
||||
from . import diamond
|
||||
from . import core, ERC20Facet
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -10,8 +10,11 @@ def main():
|
|||
parser.set_defaults(func=lambda _: parser.print_help())
|
||||
dao_subparsers = parser.add_subparsers()
|
||||
|
||||
diamond_parser = diamond.generate_cli()
|
||||
dao_subparsers.add_parser("diamond", parents=[diamond_parser], add_help=False)
|
||||
core_parser = core.generate_cli()
|
||||
dao_subparsers.add_parser("core", parents=[core_parser], add_help=False)
|
||||
|
||||
moonstream_parser = ERC20Facet.generate_cli()
|
||||
dao_subparsers.add_parser("moonstream", parents=[moonstream_parser], add_help=False)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
|
|
@ -4,7 +4,7 @@ Generic diamond functionality for Moonstream contracts.
|
|||
|
||||
import argparse
|
||||
import os
|
||||
from typing import Any, Dict, List, Set
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
|
||||
from brownie import network
|
||||
|
||||
|
@ -13,12 +13,14 @@ from . import (
|
|||
Diamond,
|
||||
DiamondCutFacet,
|
||||
DiamondLoupeFacet,
|
||||
ERC20Facet,
|
||||
OwnershipFacet,
|
||||
)
|
||||
|
||||
FACETS: Dict[str, Any] = {
|
||||
"DiamondCutFacet": DiamondCutFacet,
|
||||
"DiamondLoupeFacet": DiamondLoupeFacet,
|
||||
"ERC20Facet": ERC20Facet,
|
||||
"OwnershipFacet": OwnershipFacet,
|
||||
}
|
||||
|
||||
|
@ -26,6 +28,7 @@ FACET_PRECEDENCE: List[str] = [
|
|||
"DiamondCutFacet",
|
||||
"OwnershipFacet",
|
||||
"DiamondLoupeFacet",
|
||||
"ERC20Facet",
|
||||
]
|
||||
|
||||
FACET_ACTIONS: Dict[str, int] = {"add": 0, "replace": 1, "remove": 2}
|
||||
|
@ -39,6 +42,8 @@ def facet_cut(
|
|||
facet_address: str,
|
||||
action: str,
|
||||
transaction_config: Dict[str, Any],
|
||||
ignore_methods: Optional[List[str]] = None,
|
||||
ignore_selectors: Optional[List[str]] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Cuts the given facet onto the given Diamond contract.
|
||||
|
@ -53,6 +58,11 @@ def facet_cut(
|
|||
action in FACET_ACTIONS
|
||||
), f"Invalid cut action: {action}. Choices: {','.join(FACET_ACTIONS)}."
|
||||
|
||||
if ignore_methods is None:
|
||||
ignore_methods = []
|
||||
if ignore_selectors is None:
|
||||
ignore_selectors = []
|
||||
|
||||
project_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
abis = abi.project_abis(project_dir)
|
||||
|
||||
|
@ -70,9 +80,13 @@ def facet_cut(
|
|||
facet_abi = abis.get(facet_name, [])
|
||||
for item in facet_abi:
|
||||
if item["type"] == "function":
|
||||
function_selector = abi.encode_function_signature(item)
|
||||
if function_selector not in reserved_selectors:
|
||||
facet_function_selectors.append(function_selector)
|
||||
if item["name"] not in ignore_methods:
|
||||
function_selector = abi.encode_function_signature(item)
|
||||
if (
|
||||
function_selector not in reserved_selectors
|
||||
and function_selector not in ignore_selectors
|
||||
):
|
||||
facet_function_selectors.append(function_selector)
|
||||
|
||||
target_address = facet_address
|
||||
if FACET_ACTIONS[action] == 2:
|
||||
|
@ -98,7 +112,15 @@ def handle_facet_cut(args: argparse.Namespace) -> None:
|
|||
facet_name = args.facet_name
|
||||
facet_address = args.facet_address
|
||||
transaction_config = Diamond.get_transaction_config(args)
|
||||
facet_cut(diamond_address, facet_name, facet_address, action, transaction_config)
|
||||
facet_cut(
|
||||
diamond_address,
|
||||
facet_name,
|
||||
facet_address,
|
||||
action,
|
||||
transaction_config,
|
||||
ignore_methods=args.ignore_methods,
|
||||
ignore_selectors=args.ignore_selectors,
|
||||
)
|
||||
|
||||
|
||||
def generate_cli() -> argparse.ArgumentParser:
|
||||
|
@ -131,6 +153,16 @@ def generate_cli() -> argparse.ArgumentParser:
|
|||
choices=FACET_ACTIONS,
|
||||
help="Diamond cut action to take on entire facet",
|
||||
)
|
||||
facet_cut_parser.add_argument(
|
||||
"--ignore-methods",
|
||||
nargs="+",
|
||||
help="Names of methods to ignore when cutting a facet onto or off of the diamond",
|
||||
)
|
||||
facet_cut_parser.add_argument(
|
||||
"--ignore-selectors",
|
||||
nargs="+",
|
||||
help="Method selectors to ignore when cutting a facet onto or off of the diamond",
|
||||
)
|
||||
facet_cut_parser.set_defaults(func=handle_facet_cut)
|
||||
|
||||
DiamondCutFacet_parser = DiamondCutFacet.generate_cli()
|
Ładowanie…
Reference in New Issue