kopia lustrzana https://github.com/bugout-dev/dao
Merge pull request #9 from bugout-dev/terminus
The Terminus decentralized authorization contractpull/10/head
commit
c963d77430
|
@ -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.
|
||||
|
|
|
@ -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`
|
|
@ -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.
|
||||
|
|
|
@ -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`
|
|
@ -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`
|
|
@ -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`
|
|
@ -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);
|
|
@ -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.
|
||||
*/
|
||||
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
12
dao/cli.py
12
dao/cli.py
|
@ -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)
|
||||
|
||||
|
|
10
dao/core.py
10
dao/core.py
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
2
setup.py
2
setup.py
|
@ -12,7 +12,7 @@ setup(
|
|||
extras_require={
|
||||
"dev": [
|
||||
"black",
|
||||
"moonworm",
|
||||
"moonworm >= 0.1.9",
|
||||
],
|
||||
"distribute": ["setuptools", "twine", "wheel"],
|
||||
},
|
||||
|
|
2
test.sh
2
test.sh
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue