Merge pull request #9 from bugout-dev/terminus

The Terminus decentralized authorization contract
pull/10/head
Neeraj Kashyap 2021-12-22 12:42:17 -08:00 zatwierdzone przez GitHub
commit c963d77430
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
24 zmienionych plików z 3196 dodań i 23 usunięć

Wyświetl plik

@ -1,6 +1,6 @@
# Deploy the Moonstream governance token
# Deploy the Moonstream platform token
The Moonstream DAO governance token is deployed as an EIP2535 Diamond proxy contract with an ERC20
The Moonstream DAO platform token is deployed as an EIP2535 Diamond proxy contract with an ERC20
facet attached to it.
This checklist describes how to deploy the token.

Wyświetl plik

@ -0,0 +1,86 @@
# Update ERC20Facet on the Moonstream platform token
The Moonstream DAO platform token is deployed as an EIP2535 Diamond proxy contract with an ERC20
facet attached to it.
This checklist describes how to deploy the token.
## Deployed addresses
You will modify this section as you go through the checklist
### `ERC20Facet` address
```
export ERC20FACET_ADDRESS="0x973359bC17de4B2A84Fe3151B5B857f12cf423CB"
```
## Environment variables
- [x] `export DAO_NETWORK=<desired brownie network>`
- [x] `export DAO_OWNER=<path to keystore file for owner account>`
- [x] `export DAO_OWNER_ADDRESS=$(jq -r .address $DAO_OWNER)`
- [x] `export GAS_PRICE="<N> gwei"`
- [x] `export CONFIRMATIONS=<M>`
- [x] `export MOONSTREAM_ADDRESSES=<path to JSON file in which to store diamond addresses>`
- [x] `export MOONSTREAM_DIAMOND="$(jq -r .Diamond $MOONSTREAM_ADDRESSES)"`
## Deploy `ERC20Facet`
- [x] Deploy `ERC20Facet` contract
```bash
dao moonstream deploy \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS
```
- [x] Export address of deployed contract as `export ERC20FACET_ADDRESS=<address>`
- [x] Store address of deployed contract under `Deployed addresses / ERC20Facet address` above
- [x] Remove old `ERC20Facet` from diamond.
This may require you to check out a different commit and compile at that commit.
```bash
dao core facet-cut \
--address $MOONSTREAM_DIAMOND \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS \
--facet-name ERC20Facet \
--facet-address $ERC20FACET_ADDRESS \
--action remove
```
- [x] Attach `ERC20Facet` to diamond.
Check out correct commit and do a `brownie compile`.
```bash
dao core facet-cut \
--address $MOONSTREAM_DIAMOND \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS \
--facet-name ERC20Facet \
--facet-address $ERC20FACET_ADDRESS \
--action add
```
- [x] Check the ERC20 name of the diamond contract: `dao moonstream name --network $DAO_NETWORK --address $MOONSTREAM_DIAMOND`
- [x] Name is `Moonstream DAO`
- [x] Check the ERC20 symbol of the diamond contract: `dao moonstream symbol --network $DAO_NETWORK --address $MOONSTREAM_DIAMOND`
- [x] Symbol is `MNSTR`
- [x] Check the controller of the diamond contract: `dao moonstream moonstream-controller --network $DAO_NETWORK --address $MOONSTREAM_DIAMOND`
- [x] Controller is `$DAO_OWNER_ADDRESS`

Wyświetl plik

@ -1,6 +1,6 @@
# Deploy the Moonstream governance token
# Deploy the Moonstream platform token
The Moonstream DAO governance token is deployed as an EIP2535 Diamond proxy contract with an ERC20
The Moonstream DAO platform token is deployed as an EIP2535 Diamond proxy contract with an ERC20
facet attached to it.
This checklist describes how to deploy the token.

Wyświetl plik

@ -0,0 +1,102 @@
# Update ERC20Facet on the Moonstream platform token
The Moonstream DAO platform token is deployed as an EIP2535 Diamond proxy contract with an ERC20
facet attached to it.
This checklist describes how to deploy the token.
## Deployed addresses
You will modify this section as you go through the checklist
### `ERC20Initializer` address
```
export ERC20INITIALIZER_ADDRESS=""
```
### `ERC20Facet` address
```
export ERC20FACET_ADDRESS=""
```
## Environment variables
- [ ] `export DAO_NETWORK=<desired brownie network>`
- [ ] `export DAO_OWNER=<path to keystore file for owner account>`
- [ ] `export DAO_OWNER_ADDRESS=$(jq -r .address $DAO_OWNER)`
- [ ] `export GAS_PRICE="<N> gwei"`
- [ ] `export CONFIRMATIONS=<M>`
- [ ] `export MOONSTREAM_ADDRESSES=<path to JSON file in which to store diamond addresses>`
- [ ] `export MOONSTREAM_TOTAL_SUPPLY=<number of tokens to mint>`
- [ ] `export MOONSTREAM_DIAMOND="<diamond address>"`
## (Optional) Deploy `ERC20Initializer`
- [ ] Deploy `ERC20Initializer` contract
```bash
dao moonstream-initializer deploy \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS
```
- [ ] Export address of deployed contract as `export ERC20INITIALIZER_ADDRESS=<address>`
- [ ] Store address of deployed contract under `Deployed addresses / ERC20Initializer address` above
## Deploy `ERC20Facet`
- [ ] Deploy `ERC20Facet` contract
```bash
dao moonstream deploy \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS
```
- [ ] Export address of deployed contract as `export ERC20FACET_ADDRESS=<address>`
- [ ] Store address of deployed contract under `Deployed addresses / ERC20Facet address` above
- [ ] Attach `ERC20Facet` to diamond:
If initializer was deployed:
```bash
dao core facet-cut \
--address $MOONSTREAM_DIAMOND \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS \
--facet-name ERC20Facet \
--facet-address $ERC20FACET_ADDRESS \
--action add \
--initializer-address $ERC20INITIALIZER_ADDRESS
```
Otherwise:
```bash
dao core facet-cut \
--address $MOONSTREAM_DIAMOND \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS \
--facet-name ERC20Facet \
--facet-address $ERC20FACET_ADDRESS \
--action add
```
- [ ] Check the ERC20 name of the diamond contract: `dao moonstream name --network $DAO_NETWORK --address $MOONSTREAM_DIAMOND`
- [ ] Name is `Moonstream DAO`
- [ ] Check the ERC20 symbol of the diamond contract: `dao moonstream symbol --network $DAO_NETWORK --address $MOONSTREAM_DIAMOND`
- [ ] Symbol is `MNSTR`

Wyświetl plik

@ -0,0 +1,111 @@
# Deploy the Terminus contract
The Terminus contract is deployed as an EIP2535 Diamond proxy contract with a Terminus facet attached to it.
This checklist describes how to deploy the contract.
## Deployed addresses
You will modify this section as you go through the checklist
### Diamond addresses
```json
```
### `TerminusInitializer` address
```
export TERMINUS_INITIALIZER_ADDRESS=""
```
### `TerminusFacet` address
```
export TERMINUS_FACET_ADDRESS=""
```
## Environment variables
- [ ] `export DAO_NETWORK=<desired brownie network>`
- [ ] `export DAO_OWNER=<path to keystore file for owner account>`
- [ ] `export DAO_OWNER_ADDRESS=$(jq -r .address $DAO_OWNER)`
- [ ] `export GAS_PRICE="<N> gwei"`
- [ ] `export CONFIRMATIONS=<M>`
- [ ] `export TERMINUS_ADDRESSES=<path to JSON file in which to store diamond addresses>`
## Deploy diamond proxy
- [ ] Deploy diamond with all core facets
```bash
dao core gogogo \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS \
--owner $DAO_OWNER_ADDRESS \
--outfile $TERMINUS_ADDRESSES
```
- [ ] Store JSON output under `Deployed addresses / Diamond addresses` above.
- [ ] Export diamond proxy address: `export TERMINUS_DIAMOND="$(jq -r .Diamond $TERMINUS_ADDRESSES)"`
## Deploy `TerminusInitializer`
- [ ] Deploy `TerminusInitializer` contract
```bash
dao moonstream-initializer deploy \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS
```
- [ ] Export address of deployed contract as `export TERMINUS_INITIALIZER_ADDRESS=<address>`
- [ ] Store address of deployed contract under `Deployed addresses / TerminusInitializer address` above
## Deploy `TerminusFacet`
- [ ] Deploy `TerminusFacet` contract
```bash
dao terminus deploy \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS
```
- [ ] Export address of deployed contract as `export TERMINUS_FACET_ADDRESS=<address>`
- [ ] Store address of deployed contract under `Deployed addresses / TerminusFacet address` above
- [ ] Attach `TerminusFacet` to diamond:
```bash
dao core facet-cut \
--address $TERMINUS_DIAMOND \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS \
--facet-name TerminusFacet \
--facet-address $TERMINUS_FACET_ADDRESS \
--action add \
--initializer-address $TERMINUS_INITIALIZER_ADDRESS
```
- [ ] Check the number of pools on the Terminus contract: `dao terminus total-pools --network $DAO_NETWORK --address $TERMINUS_DIAMOND`
- [ ] Number of pools is `0`
- [ ] Check the Terminus controller: `dao terminus terminus-controller --network $DAO_NETWORK --address $TERMINUS_DIAMOND`
- [ ] Controller should be the same as `$DAO_OWNER_ADDRESS`

Wyświetl plik

@ -0,0 +1,121 @@
# Deploy the Terminus contract
The Terminus contract is deployed as an EIP2535 Diamond proxy contract with a Terminus facet attached to it.
This checklist describes how to deploy the contract.
## Deployed addresses
You will modify this section as you go through the checklist
### Diamond addresses
```json
{
"DiamondCutFacet": "0xda30781C3c8d4c81804E6Bf5c02D5E7898180dd7",
"Diamond": "0x040Cf7Ee9752936d8d280062a447eB53808EBc08",
"DiamondLoupeFacet": "0xEC5d886Bc5A7Fc31C76A5aB144c65C75AFa73Aea",
"OwnershipFacet": "0x2725E9FE8f5C97400d324C529e9ACBAd213E68b9",
"attached": [
"DiamondLoupeFacet",
"OwnershipFacet"
]
}
```
### `TerminusInitializer` address
```
export TERMINUS_INITIALIZER_ADDRESS="0xba71CB745C499D4A4f42Fd7aA40044b3b27Da6D4"
```
### `TerminusFacet` address
```
export TERMINUS_FACET_ADDRESS="0x9784e26967779e62450Eb204077EF70B4c7A3612"
```
## Environment variables
- [x] `export DAO_NETWORK=polygon-test`
- [x] `export DAO_OWNER=.secrets/dao-dev.json`
- [x] `export DAO_OWNER_ADDRESS=$(jq -r .address $DAO_OWNER)`
- [x] `export GAS_PRICE="35 gwei"`
- [x] `export CONFIRMATIONS=2`
- [x] `export TERMINUS_ADDRESSES=.secrets/terminus-mumbai-diamond.json`
## Deploy diamond proxy
- [x] Deploy diamond with all core facets
```bash
dao core gogogo \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS \
--owner $DAO_OWNER_ADDRESS \
--outfile $TERMINUS_ADDRESSES
```
- [x] Store JSON output under `Deployed addresses / Diamond addresses` above.
- [x] Export diamond proxy address: `export TERMINUS_DIAMOND="$(jq -r .Diamond $TERMINUS_ADDRESSES)"`
## Deploy `TerminusInitializer`
- [x] Deploy `TerminusInitializer` contract
```bash
dao terminus-initializer deploy \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS
```
- [x] Export address of deployed contract as `export TERMINUS_INITIALIZER_ADDRESS=0xba71CB745C499D4A4f42Fd7aA40044b3b27Da6D4`
- [x] Store address of deployed contract under `Deployed addresses / TerminusInitializer address` above
## Deploy `TerminusFacet`
- [x] Deploy `TerminusFacet` contract
```bash
dao terminus deploy \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS
```
- [x] Export address of deployed contract as `export TERMINUS_FACET_ADDRESS=0x9784e26967779e62450Eb204077EF70B4c7A3612`
- [x] Store address of deployed contract under `Deployed addresses / TerminusFacet address` above
- [x] Attach `TerminusFacet` to diamond:
```bash
dao core facet-cut \
--address $TERMINUS_DIAMOND \
--network $DAO_NETWORK \
--sender $DAO_OWNER \
--gas-price "$GAS_PRICE" \
--confirmations $CONFIRMATIONS \
--facet-name TerminusFacet \
--facet-address $TERMINUS_FACET_ADDRESS \
--action add \
--initializer-address $TERMINUS_INITIALIZER_ADDRESS
```
- [x] Check the number of pools on the Terminus contract: `dao terminus total-pools --network $DAO_NETWORK --address $TERMINUS_DIAMOND`
- [x] Number of pools is `0`
- [x] Check the Terminus controller: `dao terminus terminus-controller --network $DAO_NETWORK --address $TERMINUS_DIAMOND`
- [x] Controller should be the same as `$DAO_OWNER_ADDRESS`

Wyświetl plik

@ -4,7 +4,7 @@
* 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.
* This is an implementation of the ERC20 platform token for the Moonstream DAO.
*/
pragma solidity ^0.8.0;
@ -16,6 +16,10 @@ import "../diamond/libraries/LibDiamond.sol";
contract ERC20Facet is ERC20WithCommonStorage {
constructor() {}
function moonstreamController() external view returns (address) {
return LibERC20.erc20Storage().controller;
}
function mint(address account, uint256 amount) external {
LibERC20.enforceIsController();
_mint(account, amount);

Wyświetl plik

@ -4,7 +4,7 @@
* Authors: Moonstream Engineering (engineering@moonstream.to)
* GitHub: https://github.com/bugout-dev/dao
*
* Initializer for Moonstream DAO governance token. Used when mounting a new ERC20Facet onto its
* Initializer for Moonstream DAO platform token. Used when mounting a new ERC20Facet onto its
* diamond proxy.
*/

Wyświetl plik

@ -1,4 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Authors: Moonstream Engineering (engineering@moonstream.to)
* GitHub: https://github.com/bugout-dev/dao
*
* Common storage structure and internal methods for Moonstream DAO ERC20 tokens.
*/
pragma solidity ^0.8.0;
library LibERC20 {
@ -33,14 +41,6 @@ library LibERC20 {
emit ControlTransferred(previousController, newController);
}
function getController()
internal
view
returns (address contractController)
{
contractController = erc20Storage().controller;
}
function enforceIsController() internal view {
ERC20Storage storage es = erc20Storage();
require(msg.sender == es.controller, "LibERC20: Must be controller");

Wyświetl plik

@ -0,0 +1,631 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Authors: Moonstream Engineering (engineering@moonstream.to)
* GitHub: https://github.com/bugout-dev/dao
*
* An ERC1155 implementation which uses the Moonstream DAO common storage structure for proxies.
* EIP1155: https://eips.ethereum.org/EIPS/eip-1155
*
* The Moonstream contract is used to delegate calls from an EIP2535 Diamond proxy.
*
* This implementation is adapted from the OpenZeppelin ERC1155 implementation:
* https://github.com/OpenZeppelin/openzeppelin-contracts/tree/6bd6b76d1156e20e45d1016f355d154141c7e5b9/contracts/token/ERC1155
*/
pragma solidity ^0.8.9;
import "@openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin-contracts/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin-contracts/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
import "@openzeppelin-contracts/contracts/utils/Address.sol";
import "@openzeppelin-contracts/contracts/utils/Context.sol";
import "@openzeppelin-contracts/contracts/utils/introspection/ERC165.sol";
import "./LibTerminus.sol";
contract ERC1155WithTerminusStorage is
Context,
ERC165,
IERC1155,
IERC1155MetadataURI
{
using Address for address;
constructor() {}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC165, IERC165)
returns (bool)
{
return
interfaceId == type(IERC1155).interfaceId ||
interfaceId == type(IERC1155MetadataURI).interfaceId ||
super.supportsInterface(interfaceId);
}
function uri(uint256 poolID)
public
view
virtual
override
returns (string memory)
{
return LibTerminus.terminusStorage().poolURI[poolID];
}
/**
* @dev See {IERC1155-balanceOf}.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id)
public
view
virtual
override
returns (uint256)
{
require(
account != address(0),
"ERC1155WithTerminusStorage: balance query for the zero address"
);
return LibTerminus.terminusStorage().poolBalances[id][account];
}
/**
* @dev See {IERC1155-balanceOfBatch}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
public
view
virtual
override
returns (uint256[] memory)
{
require(
accounts.length == ids.length,
"ERC1155WithTerminusStorage: accounts and ids length mismatch"
);
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts[i], ids[i]);
}
return batchBalances;
}
/**
* @dev See {IERC1155-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved)
public
virtual
override
{
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC1155-isApprovedForAll}.
*/
function isApprovedForAll(address account, address operator)
public
view
virtual
override
returns (bool)
{
return
LibTerminus.terminusStorage().globalOperatorApprovals[account][
operator
];
}
function isApprovedForPool(uint256 poolID, address operator)
public
view
returns (bool)
{
return LibTerminus._isApprovedForPool(poolID, operator);
}
function approveForPool(uint256 poolID, address operator) external {
LibTerminus.enforcePoolIsController(poolID, _msgSender());
LibTerminus._approveForPool(poolID, operator);
}
/**
* @dev See {IERC1155-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public virtual override {
require(
from == _msgSender() ||
isApprovedForAll(from, _msgSender()) ||
isApprovedForPool(id, _msgSender()),
"ERC1155WithTerminusStorage: caller is not owner nor approved"
);
_safeTransferFrom(from, to, id, amount, data);
}
/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual override {
require(
from == _msgSender() || isApprovedForAll(from, _msgSender()),
"ERC1155WithTerminusStorage: transfer caller is not owner nor approved"
);
_safeBatchTransferFrom(from, to, ids, amounts, data);
}
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
require(
to != address(0),
"ERC1155WithTerminusStorage: transfer to the zero address"
);
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
require(
!ts.poolNotTransferable[id],
"ERC1155WithTerminusStorage: _safeTransferFrom -- pool is not transferable"
);
address operator = _msgSender();
_beforeTokenTransfer(
operator,
from,
to,
_asSingletonArray(id),
_asSingletonArray(amount),
data
);
uint256 fromBalance = ts.poolBalances[id][from];
require(
fromBalance >= amount,
"ERC1155WithTerminusStorage: insufficient balance for transfer"
);
unchecked {
ts.poolBalances[id][from] = fromBalance - amount;
}
ts.poolBalances[id][to] += amount;
emit TransferSingle(operator, from, to, id, amount);
_doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
require(
ids.length == amounts.length,
"ERC1155WithTerminusStorage: ids and amounts length mismatch"
);
require(
to != address(0),
"ERC1155WithTerminusStorage: transfer to the zero address"
);
address operator = _msgSender();
_beforeTokenTransfer(operator, from, to, ids, amounts, data);
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 fromBalance = ts.poolBalances[id][from];
require(
fromBalance >= amount,
"ERC1155WithTerminusStorage: insufficient balance for transfer"
);
unchecked {
ts.poolBalances[id][from] = fromBalance - amount;
}
ts.poolBalances[id][to] += amount;
}
emit TransferBatch(operator, from, to, ids, amounts);
_doSafeBatchTransferAcceptanceCheck(
operator,
from,
to,
ids,
amounts,
data
);
}
/**
* @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
require(
to != address(0),
"ERC1155WithTerminusStorage: mint to the zero address"
);
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
require(
ts.poolSupply[id] + amount <= ts.poolCapacity[id],
"ERC1155WithTerminusStorage: _mint -- Minted tokens would exceed pool capacity"
);
address operator = _msgSender();
_beforeTokenTransfer(
operator,
address(0),
to,
_asSingletonArray(id),
_asSingletonArray(amount),
data
);
ts.poolSupply[id] += amount;
ts.poolBalances[id][to] += amount;
emit TransferSingle(operator, address(0), to, id, amount);
_doSafeTransferAcceptanceCheck(
operator,
address(0),
to,
id,
amount,
data
);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _mintBatch(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
require(
to != address(0),
"ERC1155WithTerminusStorage: mint to the zero address"
);
require(
ids.length == amounts.length,
"ERC1155WithTerminusStorage: ids and amounts length mismatch"
);
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
for (uint256 i = 0; i < ids.length; i++) {
require(
ts.poolSupply[ids[i]] + amounts[i] <= ts.poolCapacity[ids[i]],
"ERC1155WithTerminusStorage: _mintBatch -- Minted tokens would exceed pool capacity"
);
}
address operator = _msgSender();
_beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
for (uint256 i = 0; i < ids.length; i++) {
ts.poolSupply[ids[i]] += amounts[i];
ts.poolBalances[ids[i]][to] += amounts[i];
}
emit TransferBatch(operator, address(0), to, ids, amounts);
_doSafeBatchTransferAcceptanceCheck(
operator,
address(0),
to,
ids,
amounts,
data
);
}
/**
* @dev Destroys `amount` tokens of token type `id` from `from`
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `amount` tokens of token type `id`.
*/
function _burn(
address from,
uint256 id,
uint256 amount
) internal virtual {
require(
from != address(0),
"ERC1155WithTerminusStorage: burn from the zero address"
);
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
require(
ts.poolBurnable[id],
"ERC1155WithTerminusStorage: _burn -- pool is not burnable"
);
address operator = _msgSender();
_beforeTokenTransfer(
operator,
from,
address(0),
_asSingletonArray(id),
_asSingletonArray(amount),
""
);
uint256 fromBalance = ts.poolBalances[id][from];
require(
fromBalance >= amount,
"ERC1155WithTerminusStorage: burn amount exceeds balance"
);
unchecked {
ts.poolBalances[id][from] = fromBalance - amount;
ts.poolSupply[id] -= amount;
}
emit TransferSingle(operator, from, address(0), id, amount);
}
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
*/
function _burnBatch(
address from,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual {
require(
from != address(0),
"ERC1155WithTerminusStorage: burn from the zero address"
);
require(
ids.length == amounts.length,
"ERC1155WithTerminusStorage: ids and amounts length mismatch"
);
address operator = _msgSender();
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
for (uint256 i = 0; i < ids.length; i++) {
require(
ts.poolBurnable[ids[i]],
"ERC1155WithTerminusStorage: _burnBatch -- pool is not burnable"
);
}
_beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
for (uint256 i = 0; i < ids.length; i++) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 fromBalance = ts.poolBalances[id][from];
require(
fromBalance >= amount,
"ERC1155WithTerminusStorage: burn amount exceeds balance"
);
unchecked {
ts.poolBalances[id][from] = fromBalance - amount;
ts.poolSupply[id] -= amount;
}
}
emit TransferBatch(operator, from, address(0), ids, amounts);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits a {ApprovalForAll} event.
*/
function _setApprovalForAll(
address owner,
address operator,
bool approved
) internal virtual {
require(
owner != operator,
"ERC1155WithTerminusStorage: setting approval status for self"
);
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
ts.globalOperatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning, as well as batched variants.
*
* The same hook is called on both single and batched variants. For single
* transfers, the length of the `id` and `amount` arrays will be 1.
*
* Calling conditions (for each `id` and `amount` pair):
*
* - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* of token type `id` will be transferred to `to`.
* - When `from` is zero, `amount` tokens of token type `id` will be minted
* for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
* will be burned.
* - `from` and `to` are never both zero.
* - `ids` and `amounts` have the same, non-zero length.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {}
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) private {
if (to.isContract()) {
try
IERC1155Receiver(to).onERC1155Received(
operator,
from,
id,
amount,
data
)
returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
revert(
"ERC1155WithTerminusStorage: ERC1155Receiver rejected tokens"
);
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert(
"ERC1155WithTerminusStorage: transfer to non ERC1155Receiver implementer"
);
}
}
}
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) private {
if (to.isContract()) {
try
IERC1155Receiver(to).onERC1155BatchReceived(
operator,
from,
ids,
amounts,
data
)
returns (bytes4 response) {
if (
response != IERC1155Receiver.onERC1155BatchReceived.selector
) {
revert(
"ERC1155WithTerminusStorage: ERC1155Receiver rejected tokens"
);
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert(
"ERC1155WithTerminusStorage: transfer to non ERC1155Receiver implementer"
);
}
}
}
function _asSingletonArray(uint256 element)
private
pure
returns (uint256[] memory)
{
uint256[] memory array = new uint256[](1);
array[0] = element;
return array;
}
}

Wyświetl plik

@ -0,0 +1,124 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Authors: Moonstream Engineering (engineering@moonstream.to)
* GitHub: https://github.com/bugout-dev/dao
*
* Common storage structure and internal methods for Moonstream DAO Terminus contracts.
* As Terminus is an extension of ERC1155, this library can also be used to implement bare ERC1155 contracts
* using the common storage pattern (e.g. for use in diamond proxies).
*/
// TODO(zomglings): Should we support EIP1761 in addition to ERC1155 or roll our own scopes and feature flags?
// https://eips.ethereum.org/EIPS/eip-1761
pragma solidity ^0.8.9;
library LibTerminus {
bytes32 constant TERMINUS_STORAGE_POSITION =
keccak256("moonstreamdao.eth.storage.terminus");
struct TerminusStorage {
// Terminus administration
address controller;
bool isTerminusActive;
uint256 currentPoolID;
address paymentToken;
uint256 poolBasePrice;
// Terminus pools
mapping(uint256 => address) poolController;
mapping(uint256 => string) poolURI;
mapping(uint256 => uint256) poolCapacity;
mapping(uint256 => uint256) poolSupply;
mapping(uint256 => mapping(address => uint256)) poolBalances;
mapping(uint256 => bool) poolNotTransferable;
mapping(uint256 => bool) poolBurnable;
mapping(address => mapping(address => bool)) globalOperatorApprovals;
mapping(uint256 => mapping(address => bool)) globalPoolOperatorApprovals;
}
function terminusStorage()
internal
pure
returns (TerminusStorage storage es)
{
bytes32 position = TERMINUS_STORAGE_POSITION;
assembly {
es.slot := position
}
}
event ControlTransferred(
address indexed previousController,
address indexed newController
);
event PoolControlTransferred(
uint256 indexed poolID,
address indexed previousController,
address indexed newController
);
function setController(address newController) internal {
TerminusStorage storage ts = terminusStorage();
address previousController = ts.controller;
ts.controller = newController;
emit ControlTransferred(previousController, newController);
}
function enforceIsController() internal view {
TerminusStorage storage ts = terminusStorage();
require(msg.sender == ts.controller, "LibTerminus: Must be controller");
}
function setTerminusActive(bool active) internal {
TerminusStorage storage ts = terminusStorage();
ts.isTerminusActive = active;
}
function setPoolController(uint256 poolID, address newController) internal {
TerminusStorage storage ts = terminusStorage();
address previousController = ts.poolController[poolID];
ts.poolController[poolID] = newController;
emit PoolControlTransferred(poolID, previousController, newController);
}
function createSimplePool(uint256 _capacity) internal returns (uint256) {
TerminusStorage storage ts = terminusStorage();
uint256 poolID = ts.currentPoolID + 1;
setPoolController(poolID, msg.sender);
ts.poolCapacity[poolID] = _capacity;
ts.currentPoolID++;
return poolID;
}
function enforcePoolIsController(uint256 poolID, address maybeController)
internal
view
{
TerminusStorage storage ts = terminusStorage();
require(
ts.poolController[poolID] == maybeController,
"LibTerminus: Must be pool controller"
);
}
function _isApprovedForPool(uint256 poolID, address operator)
internal
view
returns (bool)
{
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
if (operator == ts.poolController[poolID]) {
return true;
} else if (ts.globalPoolOperatorApprovals[poolID][operator]) {
return true;
}
return false;
}
function _approveForPool(uint256 poolID, address operator) internal {
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
ts.globalPoolOperatorApprovals[poolID][operator] = true;
}
}

Wyświetl plik

@ -0,0 +1,236 @@
// 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 Terminus decentralized authorization contract.
*
* Terminus users can create authorization pools. Each authorization pool has the following properties:
* 1. Controller: The address that controls the pool. Initially set to be the address of the pool creator.
* 2. Pool URI: Metadata URI for the authorization pool.
* 3. Pool capacity: The total number of tokens that can be minted in that authorization pool.
* 4. Pool supply: The number of tokens that have actually been minted in that authorization pool.
* 5. Transferable: A boolean value which denotes whether or not tokens from that pool can be transfered
* between addresses. (Note: Implemented by TerminusStorage.poolNotTransferable since we expect most
* pools to be transferable. This negation is better for storage + gas since false is default value
* in map to bool.)
* 6. Burnable: A boolean value which denotes whether or not tokens from that pool can be burned.
*/
pragma solidity ^0.8.0;
import "@openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "./ERC1155WithTerminusStorage.sol";
import "./LibTerminus.sol";
import "../diamond/libraries/LibDiamond.sol";
contract TerminusFacet is ERC1155WithTerminusStorage {
constructor() {}
event PoolMintBatch(
uint256 indexed id,
address indexed operator,
address from,
address[] toAddresses,
uint256[] amounts
);
function poolMintBatch(
uint256 id,
address[] memory toAddresses,
uint256[] memory amounts
) public {
address operator = _msgSender();
LibTerminus.enforcePoolIsController(id, operator);
require(
toAddresses.length == amounts.length,
"TerminusFacet: _poolMintBatch -- toAddresses and amounts length mismatch"
);
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
uint256 i = 0;
uint256 totalAmount = 0;
for (i = 0; i < toAddresses.length; i++) {
address to = toAddresses[i];
uint256 amount = amounts[i];
require(
to != address(0),
"TerminusFacet: _poolMintBatch -- cannot mint to zero address"
);
totalAmount += amount;
ts.poolBalances[id][to] += amount;
emit TransferSingle(operator, address(0), to, id, amount);
}
require(
ts.poolSupply[id] + totalAmount <= ts.poolCapacity[id],
"TerminusFacet: _poolMintBatch -- Minted tokens would exceed pool capacity"
);
ts.poolSupply[id] += totalAmount;
emit PoolMintBatch(id, operator, address(0), toAddresses, amounts);
}
function terminusController() external view returns (address) {
return LibTerminus.terminusStorage().controller;
}
function paymentToken() external view returns (address) {
return LibTerminus.terminusStorage().paymentToken;
}
function setPaymentToken(address newPaymentToken) external {
LibTerminus.enforceIsController();
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
ts.paymentToken = newPaymentToken;
}
function poolBasePrice() external view returns (uint256) {
return LibTerminus.terminusStorage().poolBasePrice;
}
function setPoolBasePrice(uint256 newBasePrice) external {
LibTerminus.enforceIsController();
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
ts.poolBasePrice = newBasePrice;
}
function _paymentTokenContract() internal view returns (IERC20) {
address paymentTokenAddress = LibTerminus
.terminusStorage()
.paymentToken;
require(
paymentTokenAddress != address(0),
"TerminusFacet: Payment token has not been set"
);
return IERC20(paymentTokenAddress);
}
function withdrawPayments(address toAddress, uint256 amount) external {
LibTerminus.enforceIsController();
require(
_msgSender() == toAddress,
"TerminusFacet: withdrawPayments -- Controller can only withdraw to self"
);
IERC20 paymentTokenContract = _paymentTokenContract();
paymentTokenContract.transfer(toAddress, amount);
}
function setURI(uint256 poolID, string memory poolURI) external {
LibTerminus.enforcePoolIsController(poolID, _msgSender());
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
ts.poolURI[poolID] = poolURI;
}
function totalPools() external view returns (uint256) {
return LibTerminus.terminusStorage().currentPoolID;
}
function terminusPoolController(uint256 poolID)
external
view
returns (address)
{
return LibTerminus.terminusStorage().poolController[poolID];
}
function terminusPoolCapacity(uint256 poolID)
external
view
returns (uint256)
{
return LibTerminus.terminusStorage().poolCapacity[poolID];
}
function terminusPoolSupply(uint256 poolID)
external
view
returns (uint256)
{
return LibTerminus.terminusStorage().poolSupply[poolID];
}
function createSimplePool(uint256 _capacity) external returns (uint256) {
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
uint256 requiredPayment = ts.poolBasePrice;
IERC20 paymentTokenContract = _paymentTokenContract();
require(
paymentTokenContract.allowance(_msgSender(), address(this)) >=
requiredPayment,
"TerminusFacet: createSimplePool -- Insufficient allowance on payment token"
);
paymentTokenContract.transferFrom(
msg.sender,
address(this),
requiredPayment
);
return LibTerminus.createSimplePool(_capacity);
}
function createPoolV1(
uint256 _capacity,
bool _transferable,
bool _burnable
) external returns (uint256) {
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
// TODO(zomglings): Implement requiredPayment update based on pool features.
uint256 requiredPayment = ts.poolBasePrice;
IERC20 paymentTokenContract = _paymentTokenContract();
require(
paymentTokenContract.allowance(_msgSender(), address(this)) >=
requiredPayment,
"TerminusFacet: createPoolV1 -- Insufficient allowance on payment token"
);
paymentTokenContract.transferFrom(
msg.sender,
address(this),
requiredPayment
);
uint256 poolID = LibTerminus.createSimplePool(_capacity);
if (!_transferable) {
ts.poolNotTransferable[poolID] = true;
}
if (_burnable) {
ts.poolBurnable[poolID] = true;
}
return poolID;
}
function mint(
address to,
uint256 poolID,
uint256 amount,
bytes memory data
) external {
LibTerminus.enforcePoolIsController(poolID, msg.sender);
_mint(to, poolID, amount, data);
}
function mintBatch(
address to,
uint256[] memory poolIDs,
uint256[] memory amounts,
bytes memory data
) external {
for (uint256 i = 0; i < poolIDs.length; i++) {
LibTerminus.enforcePoolIsController(poolIDs[i], _msgSender());
}
_mintBatch(to, poolIDs, amounts, data);
}
function burn(
address from,
uint256 poolID,
uint256 amount
) external {
address operator = _msgSender();
require(
operator == from || isApprovedForPool(poolID, operator),
"TerminusFacet: burn -- caller is neither owner nor approved"
);
_burn(from, poolID, amount);
}
}

Wyświetl plik

@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Authors: Moonstream Engineering (engineering@moonstream.to)
* GitHub: https://github.com/bugout-dev/dao
*
* Initializer for Terminus contract. Used when mounting a new TerminusFacet onto its diamond proxy.
*/
pragma solidity ^0.8.9;
import "@openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin-contracts/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
import "../diamond/libraries/LibDiamond.sol";
import "./LibTerminus.sol";
contract TerminusInitializer {
function init() external {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
ds.supportedInterfaces[type(IERC1155).interfaceId] = true;
ds.supportedInterfaces[type(IERC1155MetadataURI).interfaceId] = true;
LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
ts.controller = msg.sender;
}
}

Wyświetl plik

@ -124,6 +124,10 @@ class ERC20Facet:
self.assert_contract_is_instantiated()
return self.contract.mint(account, amount, transaction_config)
def moonstream_controller(self) -> Any:
self.assert_contract_is_instantiated()
return self.contract.moonstreamController.call()
def name(self) -> Any:
self.assert_contract_is_instantiated()
return self.contract.name.call()
@ -268,6 +272,13 @@ def handle_mint(args: argparse.Namespace) -> None:
print(result)
def handle_moonstream_controller(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = ERC20Facet(args.address)
result = contract.moonstream_controller()
print(result)
def handle_name(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = ERC20Facet(args.address)
@ -384,6 +395,10 @@ def generate_cli() -> argparse.ArgumentParser:
mint_parser.add_argument("--amount", required=True, help="Type: uint256", type=int)
mint_parser.set_defaults(func=handle_mint)
moonstream_controller_parser = subcommands.add_parser("moonstream-controller")
add_default_arguments(moonstream_controller_parser, False)
moonstream_controller_parser.set_defaults(func=handle_moonstream_controller)
name_parser = subcommands.add_parser("name")
add_default_arguments(name_parser, False)
name_parser.set_defaults(func=handle_name)

Wyświetl plik

@ -0,0 +1,836 @@
# Code generated by moonworm : https://github.com/bugout-dev/moonworm
# Moonworm version : 0.1.9
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 TerminusFacet:
def __init__(self, contract_address: Optional[ChecksumAddress]):
self.contract_name = "TerminusFacet"
self.address = contract_address
self.contract = None
self.abi = get_abi_json("TerminusFacet")
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 approve_for_pool(
self, pool_id: int, operator: ChecksumAddress, transaction_config
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.approveForPool(pool_id, operator, transaction_config)
def balance_of(self, account: ChecksumAddress, id: int) -> Any:
self.assert_contract_is_instantiated()
return self.contract.balanceOf.call(account, id)
def balance_of_batch(self, accounts: List, ids: List) -> Any:
self.assert_contract_is_instantiated()
return self.contract.balanceOfBatch.call(accounts, ids)
def burn(
self, from_: ChecksumAddress, pool_id: int, amount: int, transaction_config
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.burn(from_, pool_id, amount, transaction_config)
def create_pool_v1(
self, _capacity: int, _transferable: bool, _burnable: bool, transaction_config
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.createPoolV1(
_capacity, _transferable, _burnable, transaction_config
)
def create_simple_pool(self, _capacity: int, transaction_config) -> Any:
self.assert_contract_is_instantiated()
return self.contract.createSimplePool(_capacity, transaction_config)
def is_approved_for_all(
self, account: ChecksumAddress, operator: ChecksumAddress
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.isApprovedForAll.call(account, operator)
def is_approved_for_pool(self, pool_id: int, operator: ChecksumAddress) -> Any:
self.assert_contract_is_instantiated()
return self.contract.isApprovedForPool.call(pool_id, operator)
def mint(
self,
to: ChecksumAddress,
pool_id: int,
amount: int,
data: bytes,
transaction_config,
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.mint(to, pool_id, amount, data, transaction_config)
def mint_batch(
self,
to: ChecksumAddress,
pool_i_ds: List,
amounts: List,
data: bytes,
transaction_config,
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.mintBatch(to, pool_i_ds, amounts, data, transaction_config)
def payment_token(self) -> Any:
self.assert_contract_is_instantiated()
return self.contract.paymentToken.call()
def pool_base_price(self) -> Any:
self.assert_contract_is_instantiated()
return self.contract.poolBasePrice.call()
def pool_mint_batch(
self, id: int, to_addresses: List, amounts: List, transaction_config
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.poolMintBatch(
id, to_addresses, amounts, transaction_config
)
def safe_batch_transfer_from(
self,
from_: ChecksumAddress,
to: ChecksumAddress,
ids: List,
amounts: List,
data: bytes,
transaction_config,
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.safeBatchTransferFrom(
from_, to, ids, amounts, data, transaction_config
)
def safe_transfer_from(
self,
from_: ChecksumAddress,
to: ChecksumAddress,
id: int,
amount: int,
data: bytes,
transaction_config,
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.safeTransferFrom(
from_, to, id, amount, data, transaction_config
)
def set_approval_for_all(
self, operator: ChecksumAddress, approved: bool, transaction_config
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.setApprovalForAll(operator, approved, transaction_config)
def set_payment_token(
self, new_payment_token: ChecksumAddress, transaction_config
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.setPaymentToken(new_payment_token, transaction_config)
def set_pool_base_price(self, new_base_price: int, transaction_config) -> Any:
self.assert_contract_is_instantiated()
return self.contract.setPoolBasePrice(new_base_price, transaction_config)
def set_uri(self, pool_id: int, pool_uri: str, transaction_config) -> Any:
self.assert_contract_is_instantiated()
return self.contract.setURI(pool_id, pool_uri, transaction_config)
def supports_interface(self, interface_id: bytes) -> Any:
self.assert_contract_is_instantiated()
return self.contract.supportsInterface.call(interface_id)
def terminus_controller(self) -> Any:
self.assert_contract_is_instantiated()
return self.contract.terminusController.call()
def terminus_pool_capacity(self, pool_id: int) -> Any:
self.assert_contract_is_instantiated()
return self.contract.terminusPoolCapacity.call(pool_id)
def terminus_pool_controller(self, pool_id: int) -> Any:
self.assert_contract_is_instantiated()
return self.contract.terminusPoolController.call(pool_id)
def terminus_pool_supply(self, pool_id: int) -> Any:
self.assert_contract_is_instantiated()
return self.contract.terminusPoolSupply.call(pool_id)
def total_pools(self) -> Any:
self.assert_contract_is_instantiated()
return self.contract.totalPools.call()
def uri(self, pool_id: int) -> Any:
self.assert_contract_is_instantiated()
return self.contract.uri.call(pool_id)
def withdraw_payments(
self, to_address: ChecksumAddress, amount: int, transaction_config
) -> Any:
self.assert_contract_is_instantiated()
return self.contract.withdrawPayments(to_address, 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
if args.nonce is not None:
transaction_config["nonce"] = args.nonce
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",
)
parser.add_argument(
"--nonce", type=int, default=None, help="Nonce for the transaction (optional)"
)
def handle_deploy(args: argparse.Namespace) -> None:
network.connect(args.network)
transaction_config = get_transaction_config(args)
contract = TerminusFacet(None)
result = contract.deploy(transaction_config=transaction_config)
print(result)
def handle_approve_for_pool(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.approve_for_pool(
pool_id=args.pool_id,
operator=args.operator,
transaction_config=transaction_config,
)
print(result)
def handle_balance_of(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.balance_of(account=args.account, id=args.id)
print(result)
def handle_balance_of_batch(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.balance_of_batch(accounts=args.accounts, ids=args.ids)
print(result)
def handle_burn(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.burn(
from_=args.from_arg,
pool_id=args.pool_id,
amount=args.amount,
transaction_config=transaction_config,
)
print(result)
def handle_create_pool_v1(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.create_pool_v1(
_capacity=args.capacity_arg,
_transferable=args.transferable_arg,
_burnable=args.burnable_arg,
transaction_config=transaction_config,
)
print(result)
def handle_create_simple_pool(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.create_simple_pool(
_capacity=args.capacity_arg, transaction_config=transaction_config
)
print(result)
def handle_is_approved_for_all(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.is_approved_for_all(account=args.account, operator=args.operator)
print(result)
def handle_is_approved_for_pool(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.is_approved_for_pool(pool_id=args.pool_id, operator=args.operator)
print(result)
def handle_mint(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.mint(
to=args.to,
pool_id=args.pool_id,
amount=args.amount,
data=args.data,
transaction_config=transaction_config,
)
print(result)
def handle_mint_batch(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.mint_batch(
to=args.to,
pool_i_ds=args.pool_i_ds,
amounts=args.amounts,
data=args.data,
transaction_config=transaction_config,
)
print(result)
def handle_payment_token(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.payment_token()
print(result)
def handle_pool_base_price(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.pool_base_price()
print(result)
def handle_pool_mint_batch(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.pool_mint_batch(
id=args.id,
to_addresses=args.to_addresses,
amounts=args.amounts,
transaction_config=transaction_config,
)
print(result)
def handle_safe_batch_transfer_from(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.safe_batch_transfer_from(
from_=args.from_arg,
to=args.to,
ids=args.ids,
amounts=args.amounts,
data=args.data,
transaction_config=transaction_config,
)
print(result)
def handle_safe_transfer_from(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.safe_transfer_from(
from_=args.from_arg,
to=args.to,
id=args.id,
amount=args.amount,
data=args.data,
transaction_config=transaction_config,
)
print(result)
def handle_set_approval_for_all(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.set_approval_for_all(
operator=args.operator,
approved=args.approved,
transaction_config=transaction_config,
)
print(result)
def handle_set_payment_token(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.set_payment_token(
new_payment_token=args.new_payment_token, transaction_config=transaction_config
)
print(result)
def handle_set_pool_base_price(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.set_pool_base_price(
new_base_price=args.new_base_price, transaction_config=transaction_config
)
print(result)
def handle_set_uri(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.set_uri(
pool_id=args.pool_id,
pool_uri=args.pool_uri,
transaction_config=transaction_config,
)
print(result)
def handle_supports_interface(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.supports_interface(interface_id=args.interface_id)
print(result)
def handle_terminus_controller(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.terminus_controller()
print(result)
def handle_terminus_pool_capacity(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.terminus_pool_capacity(pool_id=args.pool_id)
print(result)
def handle_terminus_pool_controller(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.terminus_pool_controller(pool_id=args.pool_id)
print(result)
def handle_terminus_pool_supply(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.terminus_pool_supply(pool_id=args.pool_id)
print(result)
def handle_total_pools(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.total_pools()
print(result)
def handle_uri(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
result = contract.uri(pool_id=args.pool_id)
print(result)
def handle_withdraw_payments(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusFacet(args.address)
transaction_config = get_transaction_config(args)
result = contract.withdraw_payments(
to_address=args.to_address,
amount=args.amount,
transaction_config=transaction_config,
)
print(result)
def generate_cli() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="CLI for TerminusFacet")
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)
approve_for_pool_parser = subcommands.add_parser("approve-for-pool")
add_default_arguments(approve_for_pool_parser, True)
approve_for_pool_parser.add_argument(
"--pool-id", required=True, help="Type: uint256", type=int
)
approve_for_pool_parser.add_argument(
"--operator", required=True, help="Type: address"
)
approve_for_pool_parser.set_defaults(func=handle_approve_for_pool)
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.add_argument(
"--id", required=True, help="Type: uint256", type=int
)
balance_of_parser.set_defaults(func=handle_balance_of)
balance_of_batch_parser = subcommands.add_parser("balance-of-batch")
add_default_arguments(balance_of_batch_parser, False)
balance_of_batch_parser.add_argument(
"--accounts", required=True, help="Type: address[]", nargs="+"
)
balance_of_batch_parser.add_argument(
"--ids", required=True, help="Type: uint256[]", nargs="+"
)
balance_of_batch_parser.set_defaults(func=handle_balance_of_batch)
burn_parser = subcommands.add_parser("burn")
add_default_arguments(burn_parser, True)
burn_parser.add_argument("--from-arg", required=True, help="Type: address")
burn_parser.add_argument("--pool-id", required=True, help="Type: uint256", type=int)
burn_parser.add_argument("--amount", required=True, help="Type: uint256", type=int)
burn_parser.set_defaults(func=handle_burn)
create_pool_v1_parser = subcommands.add_parser("create-pool-v1")
add_default_arguments(create_pool_v1_parser, True)
create_pool_v1_parser.add_argument(
"--capacity-arg", required=True, help="Type: uint256", type=int
)
create_pool_v1_parser.add_argument(
"--transferable-arg",
required=True,
help="Type: bool",
type=boolean_argument_type,
)
create_pool_v1_parser.add_argument(
"--burnable-arg", required=True, help="Type: bool", type=boolean_argument_type
)
create_pool_v1_parser.set_defaults(func=handle_create_pool_v1)
create_simple_pool_parser = subcommands.add_parser("create-simple-pool")
add_default_arguments(create_simple_pool_parser, True)
create_simple_pool_parser.add_argument(
"--capacity-arg", required=True, help="Type: uint256", type=int
)
create_simple_pool_parser.set_defaults(func=handle_create_simple_pool)
is_approved_for_all_parser = subcommands.add_parser("is-approved-for-all")
add_default_arguments(is_approved_for_all_parser, False)
is_approved_for_all_parser.add_argument(
"--account", required=True, help="Type: address"
)
is_approved_for_all_parser.add_argument(
"--operator", required=True, help="Type: address"
)
is_approved_for_all_parser.set_defaults(func=handle_is_approved_for_all)
is_approved_for_pool_parser = subcommands.add_parser("is-approved-for-pool")
add_default_arguments(is_approved_for_pool_parser, False)
is_approved_for_pool_parser.add_argument(
"--pool-id", required=True, help="Type: uint256", type=int
)
is_approved_for_pool_parser.add_argument(
"--operator", required=True, help="Type: address"
)
is_approved_for_pool_parser.set_defaults(func=handle_is_approved_for_pool)
mint_parser = subcommands.add_parser("mint")
add_default_arguments(mint_parser, True)
mint_parser.add_argument("--to", required=True, help="Type: address")
mint_parser.add_argument("--pool-id", required=True, help="Type: uint256", type=int)
mint_parser.add_argument("--amount", required=True, help="Type: uint256", type=int)
mint_parser.add_argument(
"--data", required=True, help="Type: bytes", type=bytes_argument_type
)
mint_parser.set_defaults(func=handle_mint)
mint_batch_parser = subcommands.add_parser("mint-batch")
add_default_arguments(mint_batch_parser, True)
mint_batch_parser.add_argument("--to", required=True, help="Type: address")
mint_batch_parser.add_argument(
"--pool-i-ds", required=True, help="Type: uint256[]", nargs="+"
)
mint_batch_parser.add_argument(
"--amounts", required=True, help="Type: uint256[]", nargs="+"
)
mint_batch_parser.add_argument(
"--data", required=True, help="Type: bytes", type=bytes_argument_type
)
mint_batch_parser.set_defaults(func=handle_mint_batch)
payment_token_parser = subcommands.add_parser("payment-token")
add_default_arguments(payment_token_parser, False)
payment_token_parser.set_defaults(func=handle_payment_token)
pool_base_price_parser = subcommands.add_parser("pool-base-price")
add_default_arguments(pool_base_price_parser, False)
pool_base_price_parser.set_defaults(func=handle_pool_base_price)
pool_mint_batch_parser = subcommands.add_parser("pool-mint-batch")
add_default_arguments(pool_mint_batch_parser, True)
pool_mint_batch_parser.add_argument(
"--id", required=True, help="Type: uint256", type=int
)
pool_mint_batch_parser.add_argument(
"--to-addresses", required=True, help="Type: address[]", nargs="+"
)
pool_mint_batch_parser.add_argument(
"--amounts", required=True, help="Type: uint256[]", nargs="+"
)
pool_mint_batch_parser.set_defaults(func=handle_pool_mint_batch)
safe_batch_transfer_from_parser = subcommands.add_parser("safe-batch-transfer-from")
add_default_arguments(safe_batch_transfer_from_parser, True)
safe_batch_transfer_from_parser.add_argument(
"--from-arg", required=True, help="Type: address"
)
safe_batch_transfer_from_parser.add_argument(
"--to", required=True, help="Type: address"
)
safe_batch_transfer_from_parser.add_argument(
"--ids", required=True, help="Type: uint256[]", nargs="+"
)
safe_batch_transfer_from_parser.add_argument(
"--amounts", required=True, help="Type: uint256[]", nargs="+"
)
safe_batch_transfer_from_parser.add_argument(
"--data", required=True, help="Type: bytes", type=bytes_argument_type
)
safe_batch_transfer_from_parser.set_defaults(func=handle_safe_batch_transfer_from)
safe_transfer_from_parser = subcommands.add_parser("safe-transfer-from")
add_default_arguments(safe_transfer_from_parser, True)
safe_transfer_from_parser.add_argument(
"--from-arg", required=True, help="Type: address"
)
safe_transfer_from_parser.add_argument("--to", required=True, help="Type: address")
safe_transfer_from_parser.add_argument(
"--id", required=True, help="Type: uint256", type=int
)
safe_transfer_from_parser.add_argument(
"--amount", required=True, help="Type: uint256", type=int
)
safe_transfer_from_parser.add_argument(
"--data", required=True, help="Type: bytes", type=bytes_argument_type
)
safe_transfer_from_parser.set_defaults(func=handle_safe_transfer_from)
set_approval_for_all_parser = subcommands.add_parser("set-approval-for-all")
add_default_arguments(set_approval_for_all_parser, True)
set_approval_for_all_parser.add_argument(
"--operator", required=True, help="Type: address"
)
set_approval_for_all_parser.add_argument(
"--approved", required=True, help="Type: bool", type=boolean_argument_type
)
set_approval_for_all_parser.set_defaults(func=handle_set_approval_for_all)
set_payment_token_parser = subcommands.add_parser("set-payment-token")
add_default_arguments(set_payment_token_parser, True)
set_payment_token_parser.add_argument(
"--new-payment-token", required=True, help="Type: address"
)
set_payment_token_parser.set_defaults(func=handle_set_payment_token)
set_pool_base_price_parser = subcommands.add_parser("set-pool-base-price")
add_default_arguments(set_pool_base_price_parser, True)
set_pool_base_price_parser.add_argument(
"--new-base-price", required=True, help="Type: uint256", type=int
)
set_pool_base_price_parser.set_defaults(func=handle_set_pool_base_price)
set_uri_parser = subcommands.add_parser("set-uri")
add_default_arguments(set_uri_parser, True)
set_uri_parser.add_argument(
"--pool-id", required=True, help="Type: uint256", type=int
)
set_uri_parser.add_argument(
"--pool-uri", required=True, help="Type: string", type=str
)
set_uri_parser.set_defaults(func=handle_set_uri)
supports_interface_parser = subcommands.add_parser("supports-interface")
add_default_arguments(supports_interface_parser, False)
supports_interface_parser.add_argument(
"--interface-id", required=True, help="Type: bytes4", type=bytes_argument_type
)
supports_interface_parser.set_defaults(func=handle_supports_interface)
terminus_controller_parser = subcommands.add_parser("terminus-controller")
add_default_arguments(terminus_controller_parser, False)
terminus_controller_parser.set_defaults(func=handle_terminus_controller)
terminus_pool_capacity_parser = subcommands.add_parser("terminus-pool-capacity")
add_default_arguments(terminus_pool_capacity_parser, False)
terminus_pool_capacity_parser.add_argument(
"--pool-id", required=True, help="Type: uint256", type=int
)
terminus_pool_capacity_parser.set_defaults(func=handle_terminus_pool_capacity)
terminus_pool_controller_parser = subcommands.add_parser("terminus-pool-controller")
add_default_arguments(terminus_pool_controller_parser, False)
terminus_pool_controller_parser.add_argument(
"--pool-id", required=True, help="Type: uint256", type=int
)
terminus_pool_controller_parser.set_defaults(func=handle_terminus_pool_controller)
terminus_pool_supply_parser = subcommands.add_parser("terminus-pool-supply")
add_default_arguments(terminus_pool_supply_parser, False)
terminus_pool_supply_parser.add_argument(
"--pool-id", required=True, help="Type: uint256", type=int
)
terminus_pool_supply_parser.set_defaults(func=handle_terminus_pool_supply)
total_pools_parser = subcommands.add_parser("total-pools")
add_default_arguments(total_pools_parser, False)
total_pools_parser.set_defaults(func=handle_total_pools)
uri_parser = subcommands.add_parser("uri")
add_default_arguments(uri_parser, False)
uri_parser.add_argument("--pool-id", required=True, help="Type: uint256", type=int)
uri_parser.set_defaults(func=handle_uri)
withdraw_payments_parser = subcommands.add_parser("withdraw-payments")
add_default_arguments(withdraw_payments_parser, True)
withdraw_payments_parser.add_argument(
"--to-address", required=True, help="Type: address"
)
withdraw_payments_parser.add_argument(
"--amount", required=True, help="Type: uint256", type=int
)
withdraw_payments_parser.set_defaults(func=handle_withdraw_payments)
return parser
def main() -> None:
parser = generate_cli()
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()

Wyświetl plik

@ -0,0 +1,175 @@
# 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 TerminusInitializer:
def __init__(self, contract_address: Optional[ChecksumAddress]):
self.contract_name = "TerminusInitializer"
self.address = contract_address
self.contract = None
self.abi = get_abi_json("TerminusInitializer")
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 init(self, transaction_config) -> Any:
self.assert_contract_is_instantiated()
return self.contract.init(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 = TerminusInitializer(None)
result = contract.deploy(transaction_config=transaction_config)
print(result)
def handle_init(args: argparse.Namespace) -> None:
network.connect(args.network)
contract = TerminusInitializer(args.address)
transaction_config = get_transaction_config(args)
result = contract.init(transaction_config=transaction_config)
print(result)
def generate_cli() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="CLI for TerminusInitializer")
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)
init_parser = subcommands.add_parser("init")
add_default_arguments(init_parser, True)
init_parser.set_defaults(func=handle_init)
return parser
def main() -> None:
parser = generate_cli()
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()

Wyświetl plik

@ -1,6 +1,6 @@
import argparse
from . import core, ERC20Facet, ERC20Initializer
from . import core, ERC20Facet, ERC20Initializer, TerminusFacet, TerminusInitializer
def main():
@ -23,6 +23,16 @@ def main():
add_help=False,
)
terminus_parser = TerminusFacet.generate_cli()
dao_subparsers.add_parser("terminus", parents=[terminus_parser], add_help=False)
terminus_initializer_parser = TerminusInitializer.generate_cli()
dao_subparsers.add_parser(
"terminus-initializer",
parents=[terminus_initializer_parser],
add_help=False,
)
args = parser.parse_args()
args.func(args)

Wyświetl plik

@ -18,6 +18,8 @@ from . import (
ERC20Facet,
ERC20Initializer,
OwnershipFacet,
TerminusFacet,
TerminusInitializer,
)
FACETS: Dict[str, Any] = {
@ -25,6 +27,7 @@ FACETS: Dict[str, Any] = {
"DiamondLoupeFacet": DiamondLoupeFacet,
"ERC20Facet": ERC20Facet,
"OwnershipFacet": OwnershipFacet,
"TerminusFacet": TerminusFacet,
}
FACET_PRECEDENCE: List[str] = [
@ -32,6 +35,7 @@ FACET_PRECEDENCE: List[str] = [
"OwnershipFacet",
"DiamondLoupeFacet",
"ERC20Facet",
"TerminusFacet",
]
FACET_ACTIONS: Dict[str, int] = {"add": 0, "replace": 1, "remove": 2}
@ -107,6 +111,12 @@ def facet_cut(
if initializer_address != ZERO_ADDRESS and action != "remove":
erc20_initializer = ERC20Initializer.ERC20Initializer(initializer_address)
calldata = erc20_initializer.contract.init.encode_input()
elif facet_name == "TerminusFacet":
if initializer_address != ZERO_ADDRESS and action != "remove":
terminus_initializer = TerminusInitializer.TerminusInitializer(
initializer_address
)
calldata = terminus_initializer.contract.init.encode_input()
diamond = DiamondCutFacet.DiamondCutFacet(diamond_address)
transaction = diamond.diamond_cut(

Wyświetl plik

@ -5,9 +5,11 @@ from brownie import accounts, network
from .core import facet_cut, gogogo
from .ERC20Facet import ERC20Facet
from .ERC20Initializer import ERC20Initializer
from . import TerminusFacet
from . import TerminusInitializer
class MoonstreamDAOTestCase(unittest.TestCase):
class MoonstreamDAOSingleContractTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
try:
@ -17,7 +19,7 @@ class MoonstreamDAOTestCase(unittest.TestCase):
cls.contracts = gogogo(accounts[0], {"from": accounts[0]})
class MoonstreamDAOFullTestCase(MoonstreamDAOTestCase):
class MoonstreamTokenTestCase(MoonstreamDAOSingleContractTestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
@ -43,7 +45,35 @@ class MoonstreamDAOFullTestCase(MoonstreamDAOTestCase):
cls.erc20_facet = erc20_facet.address
class TestCoreDeployment(MoonstreamDAOTestCase):
class TerminusTestCase(MoonstreamTokenTestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.terminus_contracts = gogogo(accounts[0], {"from": accounts[0]})
# Deploy Terminus
initializer = TerminusInitializer.TerminusInitializer(None)
initializer.deploy({"from": accounts[0]})
terminus_facet = TerminusFacet.TerminusFacet(None)
terminus_facet.deploy({"from": accounts[0]})
diamond_address = cls.terminus_contracts["Diamond"]
facet_cut(
diamond_address,
"TerminusFacet",
terminus_facet.address,
"add",
{"from": accounts[0]},
initializer.address,
)
cls.terminus_initializer = initializer.address
cls.terminus_facet = terminus_facet.address
class TestCoreDeployment(MoonstreamDAOSingleContractTestCase):
def test_gogogo(self):
self.assertIn("DiamondCutFacet", self.contracts)
self.assertIn("Diamond", self.contracts)

Wyświetl plik

@ -5,10 +5,10 @@ import brownie
from . import ERC20Facet, ERC20Initializer
from .core import ZERO_ADDRESS, facet_cut
from .test_core import MoonstreamDAOTestCase, MoonstreamDAOFullTestCase
from .test_core import MoonstreamDAOSingleContractTestCase, MoonstreamTokenTestCase
class TestDeployment(MoonstreamDAOTestCase):
class TestDeployment(MoonstreamDAOSingleContractTestCase):
def test_add_and_replace(self):
initializer = ERC20Initializer.ERC20Initializer(None)
initializer.deploy({"from": accounts[0]})
@ -72,7 +72,7 @@ class TestDeployment(MoonstreamDAOTestCase):
self.assertEqual(symbol, expected_symbol)
class TestRemoveFacet(MoonstreamDAOTestCase):
class TestRemoveFacet(MoonstreamDAOSingleContractTestCase):
def test_remove_facet(self):
initializer = ERC20Initializer.ERC20Initializer(None)
initializer.deploy({"from": accounts[0]})
@ -118,7 +118,7 @@ class TestRemoveFacet(MoonstreamDAOTestCase):
symbol = diamond_erc20.symbol()
class TestERC20(MoonstreamDAOFullTestCase):
class TestERC20(MoonstreamTokenTestCase):
def test_mint_fails_if_not_controller(self):
diamond_address = self.contracts["Diamond"]
diamond = ERC20Facet.ERC20Facet(diamond_address)

Wyświetl plik

@ -0,0 +1,654 @@
from typing import List
import unittest
from brownie import accounts
from . import ERC20Facet, TerminusFacet, TerminusInitializer
from .core import facet_cut
from .test_core import MoonstreamDAOSingleContractTestCase, TerminusTestCase
class TestDeployment(MoonstreamDAOSingleContractTestCase):
def test_add_and_replace(self):
initializer = TerminusInitializer.TerminusInitializer(None)
initializer.deploy({"from": accounts[0]})
terminus_facet = TerminusFacet.TerminusFacet(None)
terminus_facet.deploy({"from": accounts[0]})
diamond_address = self.contracts["Diamond"]
facet_cut(
diamond_address,
"TerminusFacet",
terminus_facet.address,
"add",
{"from": accounts[0]},
initializer.address,
)
diamond_terminus = TerminusFacet.TerminusFacet(diamond_address)
controller = diamond_terminus.terminus_controller()
self.assertEqual(controller, accounts[0].address)
class TestPoolCreation(TerminusTestCase):
def test_create_simple_pool(self):
moonstream_diamond_address = self.contracts["Diamond"]
diamond_moonstream = ERC20Facet.ERC20Facet(moonstream_diamond_address)
terminus_diamond_address = self.terminus_contracts["Diamond"]
diamond_terminus = TerminusFacet.TerminusFacet(terminus_diamond_address)
diamond_terminus.set_payment_token(
moonstream_diamond_address, {"from": accounts[0]}
)
payment_token = diamond_terminus.payment_token()
self.assertEqual(payment_token, moonstream_diamond_address)
diamond_terminus.set_pool_base_price(1000, {"from": accounts[0]})
pool_base_price = diamond_terminus.pool_base_price()
self.assertEqual(pool_base_price, 1000)
diamond_moonstream.mint(accounts[1], 1000, {"from": accounts[0]})
initial_payer_balance = diamond_moonstream.balance_of(accounts[1].address)
initial_terminus_balance = diamond_moonstream.balance_of(
terminus_diamond_address
)
initial_controller_balance = diamond_moonstream.balance_of(accounts[0].address)
diamond_moonstream.approve(
terminus_diamond_address, 1000, {"from": accounts[1]}
)
initial_total_pools = diamond_terminus.total_pools()
diamond_terminus.create_simple_pool(10, {"from": accounts[1]})
final_total_pools = diamond_terminus.total_pools()
self.assertEqual(final_total_pools, initial_total_pools + 1)
final_payer_balance = diamond_moonstream.balance_of(accounts[1].address)
intermediate_terminus_balance = diamond_moonstream.balance_of(
terminus_diamond_address
)
intermediate_controller_balance = diamond_moonstream.balance_of(
accounts[0].address
)
self.assertEqual(final_payer_balance, initial_payer_balance - 1000)
self.assertEqual(intermediate_terminus_balance, initial_terminus_balance + 1000)
self.assertEqual(intermediate_controller_balance, initial_controller_balance)
with self.assertRaises(Exception):
diamond_terminus.withdraw_payments(
accounts[1].address, 1000, {"from": accounts[1]}
)
with self.assertRaises(Exception):
diamond_terminus.withdraw_payments(
accounts[0].address, 1000, {"from": accounts[1]}
)
with self.assertRaises(Exception):
diamond_terminus.withdraw_payments(
accounts[1].address, 1000, {"from": accounts[0]}
)
diamond_terminus.withdraw_payments(
accounts[0].address, 1000, {"from": accounts[0]}
)
final_terminus_balance = diamond_moonstream.balance_of(terminus_diamond_address)
final_controller_balance = diamond_moonstream.balance_of(accounts[0].address)
self.assertEqual(final_terminus_balance, intermediate_terminus_balance - 1000)
self.assertEqual(
final_controller_balance, intermediate_controller_balance + 1000
)
with self.assertRaises(Exception):
diamond_terminus.withdraw_payments(
accounts[0].address,
final_terminus_balance + 1000,
{"from": accounts[0]},
)
pool_controller = diamond_terminus.terminus_pool_controller(final_total_pools)
self.assertEqual(pool_controller, accounts[1].address)
pool_capacity = diamond_terminus.terminus_pool_capacity(final_total_pools)
self.assertEqual(pool_capacity, 10)
class TestPoolOperations(TerminusTestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
moonstream_diamond_address = cls.contracts["Diamond"]
diamond_moonstream = ERC20Facet.ERC20Facet(moonstream_diamond_address)
terminus_diamond_address = cls.terminus_contracts["Diamond"]
diamond_terminus = TerminusFacet.TerminusFacet(terminus_diamond_address)
diamond_terminus.set_payment_token(
moonstream_diamond_address, {"from": accounts[0]}
)
diamond_terminus.set_pool_base_price(1000, {"from": accounts[0]})
diamond_moonstream.mint(accounts[1], 1000000, {"from": accounts[0]})
diamond_moonstream.approve(
terminus_diamond_address, 1000000, {"from": accounts[1]}
)
cls.diamond_terminus = diamond_terminus
cls.diamond_moonstream = diamond_moonstream
def setUp(self) -> None:
self.diamond_terminus.create_simple_pool(10, {"from": accounts[1]})
def test_mint(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
balance = self.diamond_terminus.balance_of(accounts[2].address, pool_id)
self.assertEqual(balance, 1)
supply = self.diamond_terminus.terminus_pool_supply(pool_id)
self.assertEqual(supply, 1)
def test_mint_fails_if_it_exceeds_capacity(self):
pool_id = self.diamond_terminus.total_pools()
with self.assertRaises(Exception):
self.diamond_terminus.mint(
accounts[2], pool_id, 11, b"", {"from": accounts[1]}
)
balance = self.diamond_terminus.balance_of(accounts[2].address, pool_id)
self.assertEqual(balance, 0)
supply = self.diamond_terminus.terminus_pool_supply(pool_id)
self.assertEqual(supply, 0)
def test_mint_batch(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint_batch(
accounts[2].address,
pool_i_ds=[pool_id],
amounts=[1],
data=b"",
transaction_config={"from": accounts[1]},
)
balance = self.diamond_terminus.balance_of(accounts[2].address, pool_id)
self.assertEqual(balance, 1)
supply = self.diamond_terminus.terminus_pool_supply(pool_id)
self.assertEqual(supply, 1)
def test_mint_batch_fails_if_it_exceeds_capacity(self):
pool_id = self.diamond_terminus.total_pools()
with self.assertRaises(Exception):
self.diamond_terminus.mint_batch(
accounts[2].address,
pool_i_ds=[pool_id],
amounts=[11],
data=b"",
transaction_config={"from": accounts[1]},
)
balance = self.diamond_terminus.balance_of(accounts[2].address, pool_id)
self.assertEqual(balance, 0)
supply = self.diamond_terminus.terminus_pool_supply(pool_id)
self.assertEqual(supply, 0)
def test_pool_mint_batch(self):
pool_id = self.diamond_terminus.total_pools()
target_accounts = [account.address for account in accounts[:5]]
target_amounts = [1 for _ in accounts[:5]]
num_accounts = len(accounts[:5])
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_balances: List[int] = []
for account in accounts[:5]:
initial_balances.append(
self.diamond_terminus.balance_of(account.address, pool_id)
)
self.diamond_terminus.pool_mint_batch(
pool_id, target_accounts, target_amounts, {"from": accounts[1]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
self.assertEqual(final_pool_supply, initial_pool_supply + num_accounts)
for i, account in enumerate(accounts[:5]):
final_balance = self.diamond_terminus.balance_of(account.address, pool_id)
self.assertEqual(final_balance, initial_balances[i] + 1)
def test_pool_mint_batch_as_contract_controller_not_pool_controller(self):
pool_id = self.diamond_terminus.total_pools()
target_accounts = [account.address for account in accounts[:5]]
target_amounts = [1 for _ in accounts[:5]]
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_balances: List[int] = []
for account in accounts[:5]:
initial_balances.append(
self.diamond_terminus.balance_of(account.address, pool_id)
)
with self.assertRaises(Exception):
self.diamond_terminus.pool_mint_batch(
pool_id, target_accounts, target_amounts, {"from": accounts[0]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
self.assertEqual(final_pool_supply, initial_pool_supply)
for i, account in enumerate(accounts[:5]):
final_balance = self.diamond_terminus.balance_of(account.address, pool_id)
self.assertEqual(final_balance, initial_balances[i])
def test_pool_mint_batch_as_unauthorized_third_party(self):
pool_id = self.diamond_terminus.total_pools()
target_accounts = [account.address for account in accounts[:5]]
target_amounts = [1 for _ in accounts[:5]]
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_balances: List[int] = []
for account in accounts[:5]:
initial_balances.append(
self.diamond_terminus.balance_of(account.address, pool_id)
)
with self.assertRaises(Exception):
self.diamond_terminus.pool_mint_batch(
pool_id, target_accounts, target_amounts, {"from": accounts[2]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
self.assertEqual(final_pool_supply, initial_pool_supply)
for i, account in enumerate(accounts[:5]):
final_balance = self.diamond_terminus.balance_of(account.address, pool_id)
self.assertEqual(final_balance, initial_balances[i])
def test_transfer(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
initial_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.diamond_terminus.safe_transfer_from(
accounts[2].address,
accounts[3].address,
pool_id,
1,
b"",
{"from": accounts[2]},
)
final_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
final_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.assertEqual(final_sender_balance, initial_sender_balance - 1)
self.assertEqual(final_receiver_balance, initial_receiver_balance + 1)
def test_transfer_as_pool_controller(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
initial_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.diamond_terminus.safe_transfer_from(
accounts[2].address,
accounts[3].address,
pool_id,
1,
b"",
{"from": accounts[1]},
)
final_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
final_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.assertEqual(final_sender_balance, initial_sender_balance - 1)
self.assertEqual(final_receiver_balance, initial_receiver_balance + 1)
def test_transfer_as_unauthorized_recipient(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
initial_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
with self.assertRaises(Exception):
self.diamond_terminus.safe_transfer_from(
accounts[2].address,
accounts[3].address,
pool_id,
1,
b"",
{"from": accounts[3]},
)
final_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
final_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.assertEqual(final_sender_balance, initial_sender_balance)
self.assertEqual(final_receiver_balance, initial_receiver_balance)
def test_transfer_as_authorized_recipient(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
initial_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.diamond_terminus.approve_for_pool(
pool_id, accounts[3].address, {"from": accounts[1]}
)
self.diamond_terminus.safe_transfer_from(
accounts[2].address,
accounts[3].address,
pool_id,
1,
b"",
{"from": accounts[3]},
)
final_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
final_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.assertEqual(final_sender_balance, initial_sender_balance - 1)
self.assertEqual(final_receiver_balance, initial_receiver_balance + 1)
def test_transfer_as_unauthorized_unrelated_party(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
initial_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
with self.assertRaises(Exception):
self.diamond_terminus.safe_transfer_from(
accounts[2].address,
accounts[3].address,
pool_id,
1,
b"",
{"from": accounts[4]},
)
final_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
final_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.assertEqual(final_sender_balance, initial_sender_balance)
self.assertEqual(final_receiver_balance, initial_receiver_balance)
def test_transfer_as_authorized_unrelated_party(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
initial_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.diamond_terminus.approve_for_pool(
pool_id, accounts[4].address, {"from": accounts[1]}
)
self.diamond_terminus.safe_transfer_from(
accounts[2].address,
accounts[3].address,
pool_id,
1,
b"",
{"from": accounts[4]},
)
final_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
final_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.assertEqual(final_sender_balance, initial_sender_balance - 1)
self.assertEqual(final_receiver_balance, initial_receiver_balance + 1)
def test_burn_fails_as_token_owner(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
with self.assertRaises(Exception):
self.diamond_terminus.burn(
accounts[2].address, pool_id, 1, {"from": accounts[2]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
final_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.assertEqual(final_pool_supply, initial_pool_supply)
self.assertEqual(final_owner_balance, initial_owner_balance)
def test_burn_fails_as_pool_controller(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
with self.assertRaises(Exception):
self.diamond_terminus.burn(
accounts[2].address, pool_id, 1, {"from": accounts[1]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
final_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.assertEqual(final_pool_supply, initial_pool_supply)
self.assertEqual(final_owner_balance, initial_owner_balance)
def test_burn_fails_as_third_party(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
with self.assertRaises(Exception):
self.diamond_terminus.burn(
accounts[2].address, pool_id, 1, {"from": accounts[3]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
final_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.assertEqual(final_pool_supply, initial_pool_supply)
self.assertEqual(final_owner_balance, initial_owner_balance)
def test_burn_fails_as_authorized_third_party(self):
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.diamond_terminus.approve_for_pool(
pool_id, accounts[3].address, {"from": accounts[1]}
)
with self.assertRaises(Exception):
self.diamond_terminus.burn(
accounts[2].address, pool_id, 1, {"from": accounts[3]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
final_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.assertEqual(final_pool_supply, initial_pool_supply)
self.assertEqual(final_owner_balance, initial_owner_balance)
class TestCreatePoolV1(TestPoolOperations):
def setUp(self):
self.diamond_terminus.create_pool_v1(10, True, False, {"from": accounts[1]})
def test_nontransferable_pool(self):
self.diamond_terminus.create_pool_v1(10, False, False, {"from": accounts[1]})
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
initial_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
with self.assertRaises(Exception):
self.diamond_terminus.safe_transfer_from(
accounts[2].address,
accounts[3].address,
pool_id,
1,
b"",
{"from": accounts[2]},
)
final_sender_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
final_receiver_balance = self.diamond_terminus.balance_of(
accounts[3].address, pool_id
)
self.assertEqual(final_sender_balance, initial_sender_balance)
self.assertEqual(final_receiver_balance, initial_receiver_balance)
def test_burnable_pool_burn_as_token_owner(self):
self.diamond_terminus.create_pool_v1(10, True, True, {"from": accounts[1]})
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.diamond_terminus.burn(
accounts[2].address, pool_id, 1, {"from": accounts[2]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
final_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.assertEqual(final_pool_supply, initial_pool_supply - 1)
self.assertEqual(final_owner_balance, initial_owner_balance - 1)
def test_burnable_pool_burn_as_pool_controller(self):
self.diamond_terminus.create_pool_v1(10, True, True, {"from": accounts[1]})
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.diamond_terminus.burn(
accounts[2].address, pool_id, 1, {"from": accounts[1]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
final_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.assertEqual(final_pool_supply, initial_pool_supply - 1)
self.assertEqual(final_owner_balance, initial_owner_balance - 1)
def test_burnable_pool_burn_as_authorized_third_party(self):
self.diamond_terminus.create_pool_v1(10, True, True, {"from": accounts[1]})
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.diamond_terminus.approve_for_pool(
pool_id, accounts[3].address, {"from": accounts[1]}
)
self.diamond_terminus.burn(
accounts[2].address, pool_id, 1, {"from": accounts[3]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
final_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.assertEqual(final_pool_supply, initial_pool_supply - 1)
self.assertEqual(final_owner_balance, initial_owner_balance - 1)
def test_burnable_pool_burn_as_unauthorized_third_party(self):
self.diamond_terminus.create_pool_v1(10, True, True, {"from": accounts[1]})
pool_id = self.diamond_terminus.total_pools()
self.diamond_terminus.mint(accounts[2], pool_id, 1, b"", {"from": accounts[1]})
initial_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
initial_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
with self.assertRaises(Exception):
self.diamond_terminus.burn(
accounts[2].address, pool_id, 1, {"from": accounts[3]}
)
final_pool_supply = self.diamond_terminus.terminus_pool_supply(pool_id)
final_owner_balance = self.diamond_terminus.balance_of(
accounts[2].address, pool_id
)
self.assertEqual(final_pool_supply, initial_pool_supply)
self.assertEqual(final_owner_balance, initial_owner_balance)
if __name__ == "__main__":
unittest.main()

Wyświetl plik

@ -12,7 +12,7 @@ setup(
extras_require={
"dev": [
"black",
"moonworm",
"moonworm >= 0.1.9",
],
"distribute": ["setuptools", "twine", "wheel"],
},

Wyświetl plik

@ -4,6 +4,8 @@
# You can set up the local copy of `dao` for development using:
# pip install -e .[dev]
set -e
usage() {
echo "Usage: $0" [TEST_SPEC ...]
echo