Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPN-21 remove concept of "sales cycle" IPTs #159

Merged
Merged
34 changes: 4 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,38 +87,12 @@ VDAO_TOKEN_ADDRESS=0x19A3036b828bffB5E14da2659E950E76f8e6BAA2

---

### ~~Deprecated Goerli~~
### upgrading to Tokenizer 1.3

| Contract | Address | Actions |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| IP-NFT | [0xaf7358576C9F7cD84696D28702fC5ADe33cce0e9](https://goerli.etherscan.io/address/0xaf7358576C9F7cD84696D28702fC5ADe33cce0e9#code>) | <a href="https://thirdweb.com/goerli/0xaf7358576C9F7cD84696D28702fC5ADe33cce0e9?utm_source=contract_badge" target="_blank"><img width="200" height="45" src="https://badges.thirdweb.com/contract?address=0xaf7358576C9F7cD84696D28702fC5ADe33cce0e9&theme=dark&chainId=5" alt="View contract" /></a> |
| SchmackoSwap | [0x67D8ed102E2168A46FA342e39A5f7D16c103Bd0d](https://goerli.etherscan.io/address/0x67D8ed102E2168A46FA342e39A5f7D16c103Bd0d#code) | <a href="https://thirdweb.com/goerli/0x67D8ed102E2168A46FA342e39A5f7D16c103Bd0d?utm_source=contract_badge" target="_blank"><img width="200" height="45" src="https://badges.thirdweb.com/contract?address=0x67D8ed102E2168A46FA342e39A5f7D16c103Bd0d&theme=dark&chainId=5" alt="View contract" /></a> |
| Tokenizer | [0xb12494eeA6B992d0A1Db3C5423BE7a2d2337F58c](https://goerli.etherscan.io/address/0xb12494eeA6B992d0A1Db3C5423BE7a2d2337F58c#code) | <a href="https://thirdweb.com/goerli/0xb12494eeA6B992d0A1Db3C5423BE7a2d2337F58c?utm_source=contract_badge" target="_blank"><img width="200" height="45" src="https://badges.thirdweb.com/contract?address=0xb12494eeA6B992d0A1Db3C5423BE7a2d2337F58c&theme=dark&chainId=5" alt="View contract" /></a> |
| Permissioner | [0xd735d9504cce32F2cd665b779D699B4157686fcd](https://goerli.etherscan.io/address/0xd735d9504cce32F2cd665b779D699B4157686fcd#code) | <a href="https://thirdweb.com/goerli/0xd735d9504cce32F2cd665b779D699B4157686fcd?utm_source=contract_badge" target="_blank"><img width="200" height="45" src="https://badges.thirdweb.com/contract?address=0xd735d9504cce32F2cd665b779D699B4157686fcd&theme=dark&chainId=5" alt="View contract" /></a> |
| Crowdsale | [0x8c83DA72b4591bE526ca8C7cb848bC89c0e23373](https://goerli.etherscan.io/address/0x8c83DA72b4591bE526ca8C7cb848bC89c0e23373#code>) | <a href="https://thirdweb.com/goerli/0x8c83DA72b4591bE526ca8C7cb848bC89c0e23373?utm_source=contract_badge" target="_blank"><img width="200" height="45" src="https://badges.thirdweb.com/contract?address=0x8c83DA72b4591bE526ca8C7cb848bC89c0e23373&theme=dark&chainId=5" alt="View contract" /></a> |
| StakedLockingCrowdSale | [0x46c3369dece07176ad7164906d3593aa4c126d35](https://goerli.etherscan.io/address/0x46c3369dece07176ad7164906d3593aa4c126d35#code) | <a href="https://thirdweb.com/goerli/0x46c3369dece07176ad7164906d3593aa4c126d35?utm_source=contract_badge" target="_blank"><img width="200" height="45" src="https://badges.thirdweb.com/contract?address=0x46c3369dece07176ad7164906d3593aa4c126d35&theme=dark&chainId=5" alt="View contract" /></a> |
| SignedMintAuthorizer | [0x5e555eE24DB66825171Ac63EA614864987CEf1Af](https://goerli.etherscan.io/address/0x5e555eE24DB66825171Ac63EA614864987CEf1Af#code) | <a href="https://thirdweb.com/goerli/0x5e555eE24DB66825171Ac63EA614864987CEf1Af?utm_source=contract_badge" target="_blank"><img width="200" height="45" src="https://badges.thirdweb.com/contract?address=0x5e555eE24DB66825171Ac63EA614864987CEf1Af&theme=dark&chainId=5" alt="View contract" /></a> |
| IPToken Implementation | [0x38Ca0fEEc7cd48629f9388727bfA747859a6feE7](https://goerli.etherscan.io/address/0x38Ca0fEEc7cd48629f9388727bfA747859a6feE7#code) | <a href="https://thirdweb.com/goerli/0x38Ca0fEEc7cd48629f9388727bfA747859a6feE7?utm_source=contract_badge" target="_blank"><img width="200" height="45" src="https://badges.thirdweb.com/contract?address=0x38Ca0fEEc7cd48629f9388727bfA747859a6feE7&theme=dark&chainId=5" alt="View contract" /></a> |
forge script --private-key=$PRIVATE_KEY --rpc-url=$RPC_URL script/prod/RolloutTokenizerV13.s.sol --broadcast

- Subgraph: https://api.thegraph.com/subgraphs/name/moleculeprotocol/ip-nft-goerli

- Tokenizer Implementation 1.2: 0x18E5ae026CFC8020b2eDbA7050eA6144Fd313c02 (reinit 4) <https://goerli.etherscan.io/address/0x18E5ae026CFC8020b2eDbA7050eA6144Fd313c02#code>

- Bio pricefeed: 0x8647dEFdEAAdF5448d021B364B2F17815aba4360
<https://goerli.etherscan.io/address/0x8647defdeaadf5448d021b364b2f17815aba4360#code>

- old ("molecule") permissioner: 0x0045723801561079d94f0Bb1B65f322078E52635
<https://goerli.etherscan.io/address/0x0045723801561079d94f0Bb1B65f322078E52635#code>

- Blind Permissioner: 0xec68a1fc8d4c2834f8dfbdb56691f9f0a3d6be11
<https://goerli.etherscan.io/address/0xec68a1fc8d4c2834f8dfbdb56691f9f0a3d6be11#code>

#### ~~Tokens~~

| Token name | Symbol | address |
| ------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| BioDao Test token | BIODAO | [0x3110a768DC64f7aAB92F7Ae6E1371e5CE581F95F](https://goerli.etherscan.io/address/0x3110a768dc64f7aab92f7ae6e1371e5ce581f95f#code) |
| Vested BioDao Test token | vBIODAO | [0x6FFBd6325B2102F5f9AaB74d7418A27F9174c92f](https://goerli.etherscan.io/address/0x6FFBd6325B2102F5f9AaB74d7418A27F9174c92f) |
// 0xTokenizer 0xNewImpl 0xNewTokenImpl
cast send --rpc-url=$RPC_URL --private-key=$PRIVATE_KEY 0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e "upgradeToAndCall(address,bytes)" 0x70e0bA845a1A0F2DA3359C97E0285013525FFC49 0x84646c1f000000000000000000000000998abeb3e57409262ae5b751f60747921b33613e

## Prerequisites

Expand Down
6 changes: 5 additions & 1 deletion deploy/inittest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ $DC ps
./setupLocal.sh -f

cd subgraph
yarn prepare:local
yarn codegen
yarn build:local
yarn create:local
# that's a bad local config hack still required: the root's subgraph.yaml's network must be "mainnet" for foundry
sed -i '' -e 's/network\: foundry/network\: mainnet/g' build/subgraph.yaml
sed -i '' -e 's/network\: foundry/network\: mainnet/g' subgraph.yaml
yarn deploy:local -l v0.0.1
sed -i '' -e 's/network\: mainnet/network\: foundry/g' subgraph.yaml
cd ..

$DC exec -T postgres pg_dump -Fc -U graph-node -w graph-node -f after_setup.dump
Expand Down
8 changes: 4 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
#https://github.com/graphprotocol/graph-node/blob/master/docker/docker-compose.yml
version: '3'
services:
anvil:
image: ghcr.io/foundry-rs/foundry:nightly-a117fbfa41edbaa1618ed099d78d65727bff4790
image: ghcr.io/foundry-rs/foundry:nightly-a470d635cfcdce68609e9dc5762a3584351bacc1
command:
- 'anvil --host 0.0.0.0'
ports:
- '8545:8545'

graph-node:
image: graphprotocol/graph-node
image: graphprotocol/graph-node:845f8fa
ports:
- '8000:8000'
- '8001:8001'
Expand All @@ -19,6 +18,7 @@ services:
depends_on:
- ipfs
- postgres
- anvil
extra_hosts:
- host.docker.internal:host-gateway
environment:
Expand All @@ -28,7 +28,7 @@ services:
postgres_db: graph-node
ipfs: 'ipfs:5001'
ethereum: 'mainnet:http://anvil:8545'
GRAPH_LOG: info
GRAPH_LOG: debug
GRAPH_ALLOW_NON_DETERMINISTIC_IPFS: 1
ipfs:
image: ipfs/kubo:v0.28.0
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"license": "MIT",
"scripts": {
"test": "hardhat test --network hardhat"
"test": "hardhat test --network hardhat",
"clean": "rm -rf out cache_forge"
},
"devDependencies": {
"@nomicfoundation/hardhat-foundry": "^1.0.0",
Expand Down
10 changes: 2 additions & 8 deletions script/dev/Synthesizer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,7 @@ contract DeploySynthesizer is CommonScript {
function run() public {
prepareAddresses();
vm.startBroadcast(deployer);
Synthesizer synthesizer = Synthesizer(
address(
new ERC1967Proxy(
address(new Synthesizer()), ""
)
)
);
Synthesizer synthesizer = Synthesizer(address(new ERC1967Proxy(address(new Synthesizer()), "")));
MolTermsAcceptedPermissioner oldPermissioner = new MolTermsAcceptedPermissioner();

synthesizer.initialize(IPNFT(vm.envAddress("IPNFT_ADDRESS")), oldPermissioner);
Expand Down Expand Up @@ -97,7 +91,7 @@ contract UpgradeSynthesizerToTokenizer is CommonScript {
Tokenizer tokenizer = Tokenizer(address(synthesizer));

TermsAcceptedPermissioner newTermsPermissioner = new TermsAcceptedPermissioner();
tokenizer.reinit(newTermsPermissioner);
//todo tokenizer.reinit(newTermsPermissioner);
vm.stopBroadcast();

console.log("TOKENIZER_ADDRESS=%s", address(tokenizer)); //should equal synthesizer
Expand Down
1 change: 0 additions & 1 deletion script/dev/Tokenizer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,5 @@ contract FixtureTokenizer is CommonScript {
vm.stopBroadcast();

console.log("IPTS_ADDRESS=%s", address(tokenContract));
console.log("IPT round hash: %s", tokenContract.hash());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { Tokenizer } from "../../src/Tokenizer.sol";
import { IPToken } from "../../src/IPToken.sol";
import { console } from "forge-std/console.sol";

contract RolloutTokenizerV12 is Script {
contract RolloutTokenizerV13 is Script {
function run() public {
vm.startBroadcast();

IPToken newIpTokenImplementation = new IPToken();
Tokenizer newTokenizerImplementation = new Tokenizer();

bytes memory upgradeCallData = abi.encodeWithSelector(Tokenizer.setIPTokenImplementation.selector, address(newIpTokenImplementation));
bytes memory upgradeCallData = abi.encodeWithSelector(Tokenizer.reinit.selector, address(newIpTokenImplementation));

console.log("NEWTOKENIMPLEMENTATION=%s", address(newIpTokenImplementation));
console.log("NEWTOKENIZER=%s", address(newTokenizerImplementation));
Expand Down
11 changes: 11 additions & 0 deletions src/IControlIPTs.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

/**
* @title IControlIPTs 1.3
* @author molecule.xyz
* @notice must be implemented by contracts that should control IPTs
*/
interface IControlIPTs {
function controllerOf(uint256 ipnftId) external view returns (address);
}
56 changes: 25 additions & 31 deletions src/IPToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/to
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
import { Tokenizer, MustControlIpnft } from "./Tokenizer.sol";
import { IControlIPTs } from "./IControlIPTs.sol";

struct Metadata {
uint256 ipnftId;
Expand All @@ -13,75 +15,67 @@ struct Metadata {
}

error TokenCapped();
error OnlyIssuerOrOwner();

/**
* @title IPToken
* @author molecule.to
* @notice this is a template contract that's spawned by the Tokenizer
* @notice the owner of this contract is always the Tokenizer contract.
* the issuer of a token bears the right to increase the supply as long as the token is not capped.
* @title IPToken 1.3
* @author molecule.xyz
* @notice this is a template contract that's cloned by the Tokenizer
* @notice the owner of this contract is always the Tokenizer contract which enforces IPNFT holdership rules.
* The owner can increase the token supply as long as it's not explicitly capped.
* @dev formerly known as "molecules"
*/
contract IPToken is ERC20BurnableUpgradeable, OwnableUpgradeable {
event Capped(uint256 atSupply);

//this will only go up.
/// @notice the amount of tokens that ever have been issued (not necessarily == supply)
uint256 public totalIssued;
/**
* @notice when true, no one can ever mint tokens again.
*/

/// @notice when true, no one can ever mint tokens again.
bool public capped;

Metadata internal _metadata;

function initialize(string calldata name, string calldata symbol, Metadata calldata metadata_) external initializer {
function initialize(uint256 ipnftId, string calldata name, string calldata symbol, address originalOwner, string memory agreementCid)
external
initializer
{
__Ownable_init();
__ERC20_init(name, symbol);
_metadata = metadata_;
_metadata = Metadata({ ipnftId: ipnftId, originalOwner: originalOwner, agreementCid: agreementCid });
elmariachi111 marked this conversation as resolved.
Show resolved Hide resolved
}

constructor() {
_disableInitializers();
}

modifier onlyIssuerOrOwner() {
if (_msgSender() != _metadata.originalOwner && _msgSender() != owner()) {
revert OnlyIssuerOrOwner();
modifier onlyTokenizerOrIPNFTController() {
if (_msgSender() != owner() && _msgSender() != IControlIPTs(owner()).controllerOf(_metadata.ipnftId)) {
revert MustControlIpnft();
}
_;
}

function issuer() external view returns (address) {
return _metadata.originalOwner;
}

function metadata() external view returns (Metadata memory) {
return _metadata;
}
/**
* @notice ip tokens are identified by the original ipnft token holder and the underlying ip token id
* @return uint256 a token hash that's unique for [`originaOwner`,`ipnftid`]
*/

function hash() external view returns (uint256) {
return uint256(keccak256(abi.encodePacked(_metadata.originalOwner, _metadata.ipnftId)));
}

/**
* @notice we deliberately allow the synthesis initializer to increase the supply of IP Tokens at will as long as the underlying asset has not been sold yet
* @notice the supply of IP Tokens is controlled by the tokenizer contract.
* @param receiver address
* @param amount uint256
*/
function issue(address receiver, uint256 amount) external onlyIssuerOrOwner {
if (capped) revert TokenCapped();
function issue(address receiver, uint256 amount) external onlyTokenizerOrIPNFTController {
if (capped) {
revert TokenCapped();
}
totalIssued += amount;
_mint(receiver, amount);
}

/**
* @notice mark this token as capped. After calling this, no new tokens can be `issue`d
*/
function cap() external onlyIssuerOrOwner {
function cap() external onlyTokenizerOrIPNFTController {
capped = true;
emit Capped(totalIssued);
}
Expand Down
Loading