Merge pull request #35 from bugout-dev/fix-safe-batch-transfer-from

Added fix for "safeBatchTransferFrom" issue
pull/45/head
Neeraj Kashyap 2022-04-20 16:58:25 -07:00 zatwierdzone przez GitHub
commit f13d8d68bb
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
7 zmienionych plików z 582 dodań i 7 usunięć

Wyświetl plik

@ -275,6 +275,11 @@ contract ERC1155WithTerminusStorage is
uint256 id = ids[i];
uint256 amount = amounts[i];
require(
!ts.poolNotTransferable[id],
"ERC1155WithTerminusStorage: _safeBatchTransferFrom -- pool is not transferable"
);
uint256 fromBalance = ts.poolBalances[id][from];
require(
fromBalance >= amount,

Wyświetl plik

@ -52,6 +52,8 @@ def facet_cut(
initializer_address: str = ZERO_ADDRESS,
ignore_methods: Optional[List[str]] = None,
ignore_selectors: Optional[List[str]] = None,
methods: Optional[List[str]] = None,
selectors: Optional[List[str]] = None,
) -> Any:
"""
Cuts the given facet onto the given Diamond contract.
@ -70,6 +72,10 @@ def facet_cut(
ignore_methods = []
if ignore_selectors is None:
ignore_selectors = []
if methods is None:
methods = []
if selectors is None:
selectors = []
project_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
abis = abi.project_abis(project_dir)
@ -86,15 +92,26 @@ def facet_cut(
facet_function_selectors: List[str] = []
facet_abi = abis.get(facet_name, [])
logical_operator = all
method_predicate = lambda method: method not in ignore_methods
selector_predicate = (
lambda selector: selector not in reserved_selectors
and selector not in ignore_selectors
)
if len(methods) > 0 or len(selectors) > 0:
logical_operator = any
method_predicate = lambda method: method in methods
selector_predicate = lambda selector: selector in selectors
for item in facet_abi:
if item["type"] == "function":
if item["name"] not in ignore_methods:
function_selector = abi.encode_function_signature(item)
if (
function_selector not in reserved_selectors
and function_selector not in ignore_selectors
):
facet_function_selectors.append(function_selector)
item_selector = abi.encode_function_signature(item)
if logical_operator(
[method_predicate(item["name"]), selector_predicate(item_selector)]
):
facet_function_selectors.append(item_selector)
target_address = facet_address
if FACET_ACTIONS[action] == 2:
@ -218,6 +235,8 @@ def handle_facet_cut(args: argparse.Namespace) -> None:
initializer_address=args.initializer_address,
ignore_methods=args.ignore_methods,
ignore_selectors=args.ignore_selectors,
methods=args.methods,
selectors=args.selectors,
)
@ -277,6 +296,16 @@ def generate_cli() -> argparse.ArgumentParser:
nargs="+",
help="Method selectors to ignore when cutting a facet onto or off of the diamond",
)
facet_cut_parser.add_argument(
"--methods",
nargs="+",
help="Names of methods to add (if set, --ignore-methods and --ignore-selectors are not used)",
)
facet_cut_parser.add_argument(
"--selectors",
nargs="+",
help="Selectors to add (if set, --ignore-methods and --ignore-selectors are not used)",
)
facet_cut_parser.set_defaults(func=handle_facet_cut)
gogogo_parser = subcommands.add_parser("gogogo")

Wyświetl plik

@ -742,6 +742,58 @@ class TestCreatePoolV1(TestPoolOperations):
self.assertEqual(final_pool_supply, initial_pool_supply)
self.assertEqual(final_owner_balance, initial_owner_balance)
def test_nontransferable_pool_safe_transfer_from(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_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.safe_transfer_from(
accounts[2].address,
accounts[3].address,
pool_id,
1,
b"",
{"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_nontransferable_pool_safe_batch_transfer_from(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_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.safe_batch_transfer_from(
accounts[2].address,
accounts[3].address,
[pool_id],
[1],
b"",
{"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)
if __name__ == "__main__":
unittest.main()

Wyświetl plik

@ -0,0 +1,188 @@
# Update 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 update the `TerminusFacet` on the Terminus diamond contract.
## Purpose of update
Make `safeBatchTransferFrom` respect non-transferable Terminus pools. (And add `setController`.)
## Environment variables
- [x] `export DAO_NETWORK=polygon-main`
- [x] `export DAO_OWNER=<redacted>`
- [x] `export DAO_OWNER_ADDRESS=$(jq -r .address $DAO_OWNER)`
- [x] `export GAS_PRICE="80 gwei"`
- [x] `export CONFIRMATIONS=5`
- [x] `export TERMINUS_DIAMOND=0x99A558BDBdE247C2B2716f0D4cFb0E246DFB697D`
- [x] `export TERMINUS_FACET_ADDRESS="0xaA91032E567fD3CF2e7102f65B1AaF21530583a0"`
- [x] Build [Inspector Facet](https://github.com/bugout-dev/inpsector-facet) report for Diamond contract:
```
- - -
Facet at address: 0x539d0E4A68F720b35c1670B6421673a852de52DB
Possible contracts: IDiamondCut, DiamondCutFacet
IDiamondCut:
Missing methods:
Mounted selectors:
Selector: 0x1f931c1c, Function: diamondCut
DiamondCutFacet:
Missing methods:
Mounted selectors:
Selector: 0x1f931c1c, Function: diamondCut
- - -
Facet at address: 0x024e974B4524f245fE3da60CbD70EC62875f8194
Possible contracts: DiamondLoupeFacet
DiamondLoupeFacet:
Missing methods:
Mounted selectors:
Selector: 0xcdffacc6, Function: facetAddress
Selector: 0x52ef6b2c, Function: facetAddresses
Selector: 0xadfca15e, Function: facetFunctionSelectors
Selector: 0x7a0ed627, Function: facets
Selector: 0x01ffc9a7, Function: supportsInterface
- - -
Facet at address: 0x35f2d4877C7a468eA76f2D6666d3D8a487D73A9B
Possible contracts: OwnershipFacet, IERC173
OwnershipFacet:
Missing methods:
Mounted selectors:
Selector: 0x8da5cb5b, Function: owner
Selector: 0xf2fde38b, Function: transferOwnership
IERC173:
Missing methods:
Mounted selectors:
Selector: 0x8da5cb5b, Function: owner
Selector: 0xf2fde38b, Function: transferOwnership
- - -
Facet at address: 0x6396813307826Fb315e65CA7138A41CFa09a8AB3
Possible contracts: TerminusFacet
TerminusFacet:
Missing methods:
Missing selector: 0x01ffc9a7, Function: supportsInterface
Mounted selectors:
Selector: 0x85bc82e2, Function: approveForPool
Selector: 0x00fdd58e, Function: balanceOf
Selector: 0x4e1273f4, Function: balanceOfBatch
Selector: 0xf5298aca, Function: burn
Selector: 0xe8a3d485, Function: contractURI
Selector: 0x3bad2d82, Function: createPoolV1
Selector: 0xb507ef52, Function: createSimplePool
Selector: 0xe985e9c5, Function: isApprovedForAll
Selector: 0x027b3fc2, Function: isApprovedForPool
Selector: 0x731133e9, Function: mint
Selector: 0x1f7fdffa, Function: mintBatch
Selector: 0x3013ce29, Function: paymentToken
Selector: 0x8925d013, Function: poolBasePrice
Selector: 0x21adca96, Function: poolMintBatch
Selector: 0x2eb2c2d6, Function: safeBatchTransferFrom
Selector: 0xf242432a, Function: safeTransferFrom
Selector: 0xa22cb465, Function: setApprovalForAll
Selector: 0x938e3d7b, Function: setContractURI
Selector: 0x92eefe9b, Function: setController
Selector: 0x6a326ab1, Function: setPaymentToken
Selector: 0x78cf2e84, Function: setPoolBasePrice
Selector: 0xdc55d0b2, Function: setPoolController
Selector: 0x862440e2, Function: setURI
Selector: 0x366e59e3, Function: terminusController
Selector: 0x5dc8bdf8, Function: terminusPoolCapacity
Selector: 0xd0c402e5, Function: terminusPoolController
Selector: 0xa44cfc82, Function: terminusPoolSupply
Selector: 0xab3c7e52, Function: totalPools
Selector: 0x0e89341c, Function: uri
Selector: 0x0e7afec5, Function: withdrawPayments
```
- [x] Replace existing `TerminusFacet` methods on 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 replace
```
- [ ] Build [Inspector Facet](https://github.com/bugout-dev/inpsector-facet) report for Diamond contract:
```
- - -
Facet at address: 0x539d0E4A68F720b35c1670B6421673a852de52DB
Possible contracts: IDiamondCut, DiamondCutFacet
IDiamondCut:
Missing methods:
Mounted selectors:
Selector: 0x1f931c1c, Function: diamondCut
DiamondCutFacet:
Missing methods:
Mounted selectors:
Selector: 0x1f931c1c, Function: diamondCut
- - -
Facet at address: 0x024e974B4524f245fE3da60CbD70EC62875f8194
Possible contracts: DiamondLoupeFacet
DiamondLoupeFacet:
Missing methods:
Mounted selectors:
Selector: 0xcdffacc6, Function: facetAddress
Selector: 0x52ef6b2c, Function: facetAddresses
Selector: 0xadfca15e, Function: facetFunctionSelectors
Selector: 0x7a0ed627, Function: facets
Selector: 0x01ffc9a7, Function: supportsInterface
- - -
Facet at address: 0x35f2d4877C7a468eA76f2D6666d3D8a487D73A9B
Possible contracts: OwnershipFacet, IERC173
OwnershipFacet:
Missing methods:
Mounted selectors:
Selector: 0x8da5cb5b, Function: owner
Selector: 0xf2fde38b, Function: transferOwnership
IERC173:
Missing methods:
Mounted selectors:
Selector: 0x8da5cb5b, Function: owner
Selector: 0xf2fde38b, Function: transferOwnership
- - -
Facet at address: 0xaA91032E567fD3CF2e7102f65B1AaF21530583a0
Possible contracts: TerminusFacet
TerminusFacet:
Missing methods:
Missing selector: 0x01ffc9a7, Function: supportsInterface
Mounted selectors:
Selector: 0x85bc82e2, Function: approveForPool
Selector: 0x00fdd58e, Function: balanceOf
Selector: 0x4e1273f4, Function: balanceOfBatch
Selector: 0xf5298aca, Function: burn
Selector: 0xe8a3d485, Function: contractURI
Selector: 0x3bad2d82, Function: createPoolV1
Selector: 0xb507ef52, Function: createSimplePool
Selector: 0xe985e9c5, Function: isApprovedForAll
Selector: 0x027b3fc2, Function: isApprovedForPool
Selector: 0x731133e9, Function: mint
Selector: 0x1f7fdffa, Function: mintBatch
Selector: 0x3013ce29, Function: paymentToken
Selector: 0x8925d013, Function: poolBasePrice
Selector: 0x21adca96, Function: poolMintBatch
Selector: 0x2eb2c2d6, Function: safeBatchTransferFrom
Selector: 0xf242432a, Function: safeTransferFrom
Selector: 0xa22cb465, Function: setApprovalForAll
Selector: 0x938e3d7b, Function: setContractURI
Selector: 0x92eefe9b, Function: setController
Selector: 0x6a326ab1, Function: setPaymentToken
Selector: 0x78cf2e84, Function: setPoolBasePrice
Selector: 0xdc55d0b2, Function: setPoolController
Selector: 0x862440e2, Function: setURI
Selector: 0x366e59e3, Function: terminusController
Selector: 0x5dc8bdf8, Function: terminusPoolCapacity
Selector: 0xd0c402e5, Function: terminusPoolController
Selector: 0xa44cfc82, Function: terminusPoolSupply
Selector: 0xab3c7e52, Function: totalPools
Selector: 0x0e89341c, Function: uri
Selector: 0x0e7afec5, Function: withdrawPayments
```

Wyświetl plik

@ -4,6 +4,10 @@ The Terminus contract is deployed as an EIP2535 Diamond proxy contract with a Te
This checklist describes how to update the `TerminusFacet` on the Terminus diamond contract.
## Purpose of update
Describe what the purpose of this update is.
## Deployed addresses
You will modify this section as you go through the checklist

Wyświetl plik

@ -0,0 +1,222 @@
# Update 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 update the `TerminusFacet` on the Terminus diamond contract.
## Purpose of update
Make `safeBatchTransferFrom` respect non-transferable Terminus pools. (And add `setController`.)
## Deployed addresses
You will modify this section as you go through the checklist
### `TerminusFacet` address
```
export TERMINUS_FACET_ADDRESS="0xaA91032E567fD3CF2e7102f65B1AaF21530583a0"
```
## Environment variables
- [x] `export DAO_NETWORK=polygon-main`
- [x] `export DAO_OWNER=<redacted>`
- [x] `export DAO_OWNER_ADDRESS=$(jq -r .address $DAO_OWNER)`
- [x] `export GAS_PRICE="80 gwei"`
- [x] `export CONFIRMATIONS=5`
- [x] `export TERMINUS_DIAMOND=0x062BEc5e84289Da2CD6147E0e4DA402B33B8f796`
- [x] `export POLYGONSCAN_TOKEN=<redacted>`
## Deploy `TerminusFacet`
- [x] Check out relevant commit and `brownie compile`.
- [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=0xaA91032E567fD3CF2e7102f65B1AaF21530583a0`
- [x] Store address of deployed contract under `Deployed addresses / TerminusFacet address` above
- [x] Verify `TerminusFacet` contract
```
dao terminus verify-contract --network $DAO_NETWORK --address $TERMINUS_FACET_ADDRESS
```
- [x] Build [Inspector Facet](https://github.com/bugout-dev/inpsector-facet) report for Diamond contract:
```
- - -
Facet at address: 0x539d0E4A68F720b35c1670B6421673a852de52DB
Possible contracts: IDiamondCut, DiamondCutFacet
IDiamondCut:
Missing methods:
Mounted selectors:
Selector: 0x1f931c1c, Function: diamondCut
DiamondCutFacet:
Missing methods:
Mounted selectors:
Selector: 0x1f931c1c, Function: diamondCut
- - -
Facet at address: 0x024e974B4524f245fE3da60CbD70EC62875f8194
Possible contracts: DiamondLoupeFacet
DiamondLoupeFacet:
Missing methods:
Mounted selectors:
Selector: 0xcdffacc6, Function: facetAddress
Selector: 0x52ef6b2c, Function: facetAddresses
Selector: 0xadfca15e, Function: facetFunctionSelectors
Selector: 0x7a0ed627, Function: facets
Selector: 0x01ffc9a7, Function: supportsInterface
- - -
Facet at address: 0x35f2d4877C7a468eA76f2D6666d3D8a487D73A9B
Possible contracts: OwnershipFacet, IERC173
OwnershipFacet:
Missing methods:
Mounted selectors:
Selector: 0x8da5cb5b, Function: owner
Selector: 0xf2fde38b, Function: transferOwnership
IERC173:
Missing methods:
Mounted selectors:
Selector: 0x8da5cb5b, Function: owner
Selector: 0xf2fde38b, Function: transferOwnership
- - -
Facet at address: 0x6396813307826Fb315e65CA7138A41CFa09a8AB3
Possible contracts: TerminusFacet
TerminusFacet:
Missing methods:
Missing selector: 0x01ffc9a7, Function: supportsInterface
Mounted selectors:
Selector: 0x85bc82e2, Function: approveForPool
Selector: 0x00fdd58e, Function: balanceOf
Selector: 0x4e1273f4, Function: balanceOfBatch
Selector: 0xf5298aca, Function: burn
Selector: 0xe8a3d485, Function: contractURI
Selector: 0x3bad2d82, Function: createPoolV1
Selector: 0xb507ef52, Function: createSimplePool
Selector: 0xe985e9c5, Function: isApprovedForAll
Selector: 0x027b3fc2, Function: isApprovedForPool
Selector: 0x731133e9, Function: mint
Selector: 0x1f7fdffa, Function: mintBatch
Selector: 0x3013ce29, Function: paymentToken
Selector: 0x8925d013, Function: poolBasePrice
Selector: 0x21adca96, Function: poolMintBatch
Selector: 0x2eb2c2d6, Function: safeBatchTransferFrom
Selector: 0xf242432a, Function: safeTransferFrom
Selector: 0xa22cb465, Function: setApprovalForAll
Selector: 0x938e3d7b, Function: setContractURI
Selector: 0x92eefe9b, Function: setController
Selector: 0x6a326ab1, Function: setPaymentToken
Selector: 0x78cf2e84, Function: setPoolBasePrice
Selector: 0xdc55d0b2, Function: setPoolController
Selector: 0x862440e2, Function: setURI
Selector: 0x366e59e3, Function: terminusController
Selector: 0x5dc8bdf8, Function: terminusPoolCapacity
Selector: 0xd0c402e5, Function: terminusPoolController
Selector: 0xa44cfc82, Function: terminusPoolSupply
Selector: 0xab3c7e52, Function: totalPools
Selector: 0x0e89341c, Function: uri
Selector: 0x0e7afec5, Function: withdrawPayments
```
- [x] Replace existing `TerminusFacet` methods on 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 replace
```
- [x] Build [Inspector Facet](https://github.com/bugout-dev/inpsector-facet) report for Diamond contract:
```
- - -
Facet at address: 0x539d0E4A68F720b35c1670B6421673a852de52DB
Possible contracts: IDiamondCut, DiamondCutFacet
IDiamondCut:
Missing methods:
Mounted selectors:
Selector: 0x1f931c1c, Function: diamondCut
DiamondCutFacet:
Missing methods:
Mounted selectors:
Selector: 0x1f931c1c, Function: diamondCut
- - -
Facet at address: 0x024e974B4524f245fE3da60CbD70EC62875f8194
Possible contracts: DiamondLoupeFacet
DiamondLoupeFacet:
Missing methods:
Mounted selectors:
Selector: 0xcdffacc6, Function: facetAddress
Selector: 0x52ef6b2c, Function: facetAddresses
Selector: 0xadfca15e, Function: facetFunctionSelectors
Selector: 0x7a0ed627, Function: facets
Selector: 0x01ffc9a7, Function: supportsInterface
- - -
Facet at address: 0x35f2d4877C7a468eA76f2D6666d3D8a487D73A9B
Possible contracts: OwnershipFacet, IERC173
OwnershipFacet:
Missing methods:
Mounted selectors:
Selector: 0x8da5cb5b, Function: owner
Selector: 0xf2fde38b, Function: transferOwnership
IERC173:
Missing methods:
Mounted selectors:
Selector: 0x8da5cb5b, Function: owner
Selector: 0xf2fde38b, Function: transferOwnership
- - -
Facet at address: 0xaA91032E567fD3CF2e7102f65B1AaF21530583a0
Possible contracts: TerminusFacet
TerminusFacet:
Missing methods:
Missing selector: 0x01ffc9a7, Function: supportsInterface
Mounted selectors:
Selector: 0x85bc82e2, Function: approveForPool
Selector: 0x00fdd58e, Function: balanceOf
Selector: 0x4e1273f4, Function: balanceOfBatch
Selector: 0xf5298aca, Function: burn
Selector: 0xe8a3d485, Function: contractURI
Selector: 0x3bad2d82, Function: createPoolV1
Selector: 0xb507ef52, Function: createSimplePool
Selector: 0xe985e9c5, Function: isApprovedForAll
Selector: 0x027b3fc2, Function: isApprovedForPool
Selector: 0x731133e9, Function: mint
Selector: 0x1f7fdffa, Function: mintBatch
Selector: 0x3013ce29, Function: paymentToken
Selector: 0x8925d013, Function: poolBasePrice
Selector: 0x21adca96, Function: poolMintBatch
Selector: 0x2eb2c2d6, Function: safeBatchTransferFrom
Selector: 0xf242432a, Function: safeTransferFrom
Selector: 0xa22cb465, Function: setApprovalForAll
Selector: 0x938e3d7b, Function: setContractURI
Selector: 0x92eefe9b, Function: setController
Selector: 0x6a326ab1, Function: setPaymentToken
Selector: 0x78cf2e84, Function: setPoolBasePrice
Selector: 0xdc55d0b2, Function: setPoolController
Selector: 0x862440e2, Function: setURI
Selector: 0x366e59e3, Function: terminusController
Selector: 0x5dc8bdf8, Function: terminusPoolCapacity
Selector: 0xd0c402e5, Function: terminusPoolController
Selector: 0xa44cfc82, Function: terminusPoolSupply
Selector: 0xab3c7e52, Function: totalPools
Selector: 0x0e89341c, Function: uri
Selector: 0x0e7afec5, Function: withdrawPayments
```

Wyświetl plik

@ -0,0 +1,75 @@
# Update 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 update the `TerminusFacet` on the Terminus diamond contract.
## Purpose of update
Fix `safeBatchTransferFrom` to respect non-transferable pools.
## Deployed addresses
You will modify this section as you go through the checklist
### `TerminusFacet` address
```
export TERMINUS_FACET_ADDRESS=0xc9414ea1B5d9029dAbBeAD51bc8B128D7EBaa161
```
## Environment variables
- [x] `export DAO_NETWORK=polygon-test`
- [x] `export DAO_OWNER=<redacted>`
- [x] `export DAO_OWNER_ADDRESS=$(jq -r .address $DAO_OWNER)`
- [x] `export GAS_PRICE="5 gwei"`
- [x] `export CONFIRMATIONS=2`
- [x] `export TERMINUS_DIAMOND=0x040Cf7Ee9752936d8d280062a447eB53808EBc08`
## 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=0xc9414ea1B5d9029dAbBeAD51bc8B128D7EBaa161`
- [x] Store address of deployed contract under `Deployed addresses / TerminusFacet address` above
- [x] Replace existing methods on `TerminusFacet`:
```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 replace \
--ignore-methods setController
```
- [x] Add `setController` method:
```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 \
--methods setController
```