IPNFT icon indicating copy to clipboard operation
IPNFT copied to clipboard

IP-NFTs are building blocks for the DeSci economy.

IPNFT

IP-NFTs allow their users to tokenize intellectual property. This repo contains code for IP-NFT smart contracts and compatible subgraphs. Details on how IP-NFTs are minted, their purpose and applications can be found here

Deployments

Mainnet

Contract Address Actions
IP-NFT 0xcaD88677CA87a7815728C72D74B4ff4982d54Fc1 View contract
SignedMintAuthorizer 0xBc5FbB45A2bbB64d9B2EeBFa327284a35d5C5865 View contract
SchmackoSwap 0xc09b8577c762b5e97a7d640f242e1d9bfaa7eb9d View contract
Tokenizer 0x58EB89C69CB389DBef0c130C6296ee271b82f436 View contract
Permissioner 0xC837E02982992B701A1B5e4E21fA01cEB0a628fA View contract
Crowdsale 0xf0a8d23f38e9cbbe01c4ed37f23bd519b65bc6c2 View contract
StakedLockingCrowdSale 0x35Bce29F52f51f547998717CD598068Afa2B29B7 View contract

Subgraph

API: https://api.thegraph.com/subgraphs/name/moleculeprotocol/ip-nft-mainnet Playground: https://api.thegraph.com/subgraphs/name/moleculeprotocol/ip-nft-mainnet/graphql

tokenizer implementation 1.2: 0xE8701330F196FeFe415b28dAA767AB076F42557A tokenizer implementation 1.1: 0x9C70FA8c87D7e94Fd63eeCCcA657D5c4224a36f3 iptoken implementation: 0x9E4fc6E6d1A64e3429aB852d3CB31AD7aa06997A ipnft implementation 2.4: 0x6B179Dffac5E190c670176606f552cB792847f80

Defender Relayer

signs off minting requests from our side: 0x3D30452c48F2448764d5819a9A2b684Ae2CC5AcF


Sepolia

Contract Address Explorer
IPNFT 0x152B444e60C526fe4434C721561a077269FcF61a View contract
Swap 0x9e4c638e703d0Af3a3B9eb488dE79A16d402698f View contract
Authorizer 0x7a9F3773352e4ee0Da6307Cd32C45fE89602129A View contract
Terms Permissioner 0xC05D649368d8A5e2E98CAa205d47795de5fCB599 View contract
Tokenizer 0xca63411FF5187431028d003eD74B57531408d2F9 View contract
Crowdsale 0x8cA737E2cdaE1Ceb332bEf7ba9eA711a3a2f8037 View contract
Staked Crowdsale 0xd1cE2EA7d3b0C9cAB025A4aD762FC00315141ad7 View contract

Subgraphs

on Satsuma, Tech Account

API: https://subgraph.satsuma-prod.com/techs-team--4017766/moleculexyz-ipnft-sepolia/version/v0.0.1/api Playground: https://subgraph.satsuma-prod.com/techs-team--4017766/moleculexyz-ipnft-sepolia/playground

Defender Relayer

signs off minting requests from our side: 0xd7B298c9fB0377124d01D4E826d9D5beFB7CD6FE

Tokens

Contract Address
USDC (6 decimals) 0xC7B1b8BEA20d559040928FA1e6a23a3c221286B1 View contract
MOL Dao 0xe0D404C22228b03D5b8a715Cb569C4944BC5A27A View contract
vested MOL Dao 0x8f80d1183CD983B01B0C9AC6777cC732Ec9800de View contract

old Plain Crowdsale 0xc272b3e980ee3c1e52a9814b1a1d6c48295e8d91 https://sepolia.etherscan.io/address/0xc272b3e980ee3c1e52a9814b1a1d6c48295e8d91

IPNFT_ADDRESS=0x152B444e60C526fe4434C721561a077269FcF61a
ipnft impl 0x67881bbE2d58f5eeb2f2cad3a1FB7Bb6CB834A5A
SOS_ADDRESS=0x9e4c638e703d0Af3a3B9eb488dE79A16d402698f
AUTHORIZER_ADDRESS=0x7a9F3773352e4ee0Da6307Cd32C45fE89602129A

TERMS_ACCEPTED_PERMISSIONER_ADDRESS=0xC05D649368d8A5e2E98CAa205d47795de5fCB599
TOKENIZER_ADDRESS=0xca63411FF5187431028d003eD74B57531408d2F9
CROWDSALE_ADDRESS=0x8cA737E2cdaE1Ceb332bEf7ba9eA711a3a2f8037
STAKED_LOCKING_CROWDSALE_ADDRESS=0xd1cE2EA7d3b0C9cAB025A4aD762FC00315141ad7

initial IP Token implementation=0xB16e92029De283800df9030De2F255DcB99F19e9
tokenizer imple 0x672d3389b5c5a050ad93100d548817d87edc8597

USDC_ADDRESS=0x309EFD49752803D0B3Ddba2B66A7A900F99B4E70
DAO_TOKEN_ADDRESS=0x62f3cBab2C84fbA31DEc50CD21dbb5577333C69a
VDAO_TOKEN_ADDRESS=0x19A3036b828bffB5E14da2659E950E76f8e6BAA2

~~Deprecated Goerli~~

Contract Address Actions
IP-NFT 0xaf7358576C9F7cD84696D28702fC5ADe33cce0e9 View contract
SchmackoSwap 0x67D8ed102E2168A46FA342e39A5f7D16c103Bd0d View contract
Tokenizer 0xb12494eeA6B992d0A1Db3C5423BE7a2d2337F58c View contract
Permissioner 0xd735d9504cce32F2cd665b779D699B4157686fcd View contract
Crowdsale 0x8c83DA72b4591bE526ca8C7cb848bC89c0e23373 View contract
StakedLockingCrowdSale 0x46c3369dece07176ad7164906d3593aa4c126d35 View contract
SignedMintAuthorizer 0x5e555eE24DB66825171Ac63EA614864987CEf1Af View contract
IPToken Implementation 0x38Ca0fEEc7cd48629f9388727bfA747859a6feE7 View contract

~~Tokens~~

Token name Symbol address
BioDao Test token BIODAO 0x3110a768DC64f7aAB92F7Ae6E1371e5CE581F95F
Vested BioDao Test token vBIODAO 0x6FFBd6325B2102F5f9AaB74d7418A27F9174c92f

Prerequisites

To work with this repository you have to install Foundry (https://getfoundry.sh). Run the following command in your terminal, then follow the onscreen instructions (macOS and Linux):

curl -L https://foundry.paradigm.xyz | bash

The above command will install foundryup. Then install Foundry by running foundryup in your terminal.

(Check out the Foundry book for a Windows installation guide: https://book.getfoundry.sh)

Usage

install dependencies and build

Run forge install. This will clone dependency repos as submodules into the lib folder.

Run forge build

Testing

Run forge test

Run forge test --gas-report for gas usage reports

Run forge test --match-contract IPNFTV2 -vvv -w to watch only relevant tests an include meaningful output

Hardhat tests

We also added a basic hardhat environment to this project. While foundry stays our primary tool for contract development, hardhat allows us to test e.g. JSON / metadata related features of the contracts. After installing all js dependencies (yarn), you can execute the hardhat tests like:

yarn hardhat test --network hardhat

Deployment

General config

  • The deploy scripts are located in script
  • Copy .env.example to .env
  • Set the ETHERSCAN_KEY if you want to verify deployed contracts on Etherscan.
  • Set a moderator address that's going to be enabled to issue and revoke mintpasses (only needed for "real" deployments)

You can place required env vars in your .env file and run source .env to get them into your current terminal session or provide them when invoking the command.

Deployment scripts

  • a fresh, proxied IPNFT deployment can be created by forge script script/IPNFT.sol
  • to rollout a new upgrade on a live network without calling the proxy's upgrade function, you can use forge script script/UpgradeImplementation.s.sol:DeployImplementation and invoke the upgrade function manually (e.g. from your multisig)
  • for the "real" thing you'll need to add -f and --private-key and finally --broadcast params .

Deploy for local development

Quickstart

  • You can use the shell script ./setupLocal.sh to deploy all contracts and add the optional -f or --fixture flag to also run the fixture scripts.

Manual

  • the dev scripts are supposed to run on your local environment and depend on contract addresses on your local environment. Use source .env to pull deterministic local contract addresses to your local session.

  • Anvil is a local testnet node shipped with Foundry. You can use it for testing your contracts from frontends or for interacting over RPC. You can also use the anvil node from docker, see the accompanying README in the subgraph folder.

  • Run anvil -h 0.0.0.0 in a terminal window and keep it running

To just deploy all contracts using the default mnemonic's first account, run forge script script/dev/Ipnft.s.sol:DeployIpnft -f $RPC_URL --broadcast

To issue a mintpass, reserve and mint a test IPNFT for the 1st user, run forge script script/dev/Ipnft.s.sol:FixtureIpnft -f $RPC_URL --broadcast. This requires you to have executed Dev.s.sol before. This also creates a listing on Schmackoswap but doesn't accept it.

To deploy the Synthesizer, run forge script script/dev/Synthesizer.s.sol:DeploySynthesizer -f $RPC_URL --broadcast To synthesize the test IPNFT, run forge script script/dev/Synthesizer.s.sol:FixtureSynthesizer -f $RPC_URL --broadcast

To deploy the StakedLockingCrowdSale contract, run forge script script/dev/CrowdSale.s.sol:DeployCrowdSale -f $RPC_URL --broadcast To test a simple StakedLockingCrowdSale with Molecules, run forge script script/dev/CrowdSale.s.sol:FixtureCrowdSale -f $RPC_URL --broadcast

To approve and finalize the sales listing, run forge script script/dev/ApproveAndBuy.s.sol -f $RPC_URL --broadcast. See the inline comment on why this is a separate script.

Deploy to a live network

The easiest way to deploy contracts without exposing a local private key is the thirdweb. Here's how you initialize the process from the root folder: npx thirdweb@latest deploy

To manually broadcast a bundle of deploy transactions, you can use Deploy.s.sol. It deploys all three relevant contracts (IPNFT, Schmackoswap and Mintpass) and sets up a first moderator (defined by the MODERATOR_ADDRESS env var). Make sure that you're using the correct moderator address for the network you're deploying to.

  1. Make sure you have the private key for your deployer account at hand and that it has ETH on the target network on it.
  2. Run forge script script/Deploy.s.sol:DeployScript -f $RPC_URL --interactives 1 --sender <deployer address> --broadcast -vvvv
  3. Paste the private key for the deployer account
  4. to verify the contract during deployment, get an Etherscan API key and add --verify --etherscan-api-key $ETHERSCAN_API_KEY to the command.

Deploying the Synthesizer suite

You can deploy the Synthesizer individually, but we created a deployment script that deploys all relevant contracts in the recommended order. These are

  • BioPriceFeed
  • TermsAcceptedPermissioner
  • Synthesizer
  • StakedLockingCrowdSale

You can deploy them all in one go (requires the current network's IPNFT address):

IPNFT_ADDRESS=... forge script script/DeploySynthesizer.s.sol:DeploySynthesizerInfrastructure --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast

The crowdsale computation model can be tried out here: https://docs.google.com/spreadsheets/d/1vvGzs6n0nGqSBewJFKPsX4umMDCwNhKVqqhGELY543g/edit?usp=sharing

Deploying and verifying a single contract without the help of any script forge create --rpc-url $RPC_URL --private-key $PRIVATE_KEY --chain 5 --etherscan-api-key $ETHERSCAN_API_KEY --verify src/crowdsale/StakedLockingCrowdSale.sol:StakedLockingCrowdSale

Deploying (vested) test tokens

To test staked / vested token interactions, you need some test tokens. Here are 2 convenient script to get them running:

NAME=Vita SYMBOL=VITA SUPPLY_ETH=10000000 forge script script/Tokens.s.sol:DeployTestTokensManually --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast

and to create the vested tokens counterpart:

TOKEN=0xaddress forge script script/Tokens.s.sol:DeployTokenVesting --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast

Testing a manual upgrade

deploy the old version

forge script script/IPNFT.s.sol -f $RPC_URL -vvvv --broadcast --private-key ...

switch your branch or get the new contract impl at hand

PROXY_ADDRESS=<the proxy address> forge script script/UpgradeImplementation.s.sol -f $RPC_URL --sender <proxy-owner-address>

(or use your pk and --broadcast to submit it)

Manually verify contracts on Etherscan

full docs: https://book.getfoundry.sh/reference/forge/forge-verify-contract

forge verify-contract --chain-id 5 <address> IPNFT

or, if you need to verify with constructor arguments:

forge verify-contract --chain-id 5 <address> Mintpass --constructor-args $(cast abi-encode "constructor(address)" "0xabcdef")

ERC1967 Proxies are verified using their implementation contstructor call

forge verify-contract --chain-id 5 <proxyaddress> ERC1967Proxy --constructor-args $(cast abi-encode "constructor(address,bytes)" "<impladdress>" "")

checking with mythril on docker

docker run -m 6G --cpus=8 -w /tmp -v $(pwd):/tmp mythril/myth analyze /tmp/src/IPNFT.sol --solc-json /tmp/mythril.config.json

Creating coverage reports

requires the lcov suite installed on your machine

forge coverage --report lcov && genhtml lcov.info -o report --branch-coverage

Interacting with cast

cast is another CLI command installed by Foundry and allows you to query/manipulate your deployed contracts easily. Find out more here: https://book.getfoundry.sh/cast/

When having an RPC_URL in your local env, you e.g. can simply call view functions like this:
cast call $IPNFT_ADDRESS "tokenURI(uint256)" 1 | cast --to-ascii

manual interaction playbook

Here are some helpful interaction examples with the contracts that you can execute from your command line. Ensure your local environment contains all contract addresses and is sourced to your terminal. We're using your local PRIVATE_KEY here

Manually issue 2 mintpasses to anvil address #0

cast send -i $MINTPASS_ADDRESS --private-key $PRIVATE_KEY "batchMint(address,uint256)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 2

Create a reservation

cast send -i $IPNFT_ADDRESS --private-key $PRIVATE_KEY "reserve()(uint256)"

mint an IP-NFT to the first account

cast send --private-key $PRIVATE_KEY -i $IPNFT_ADDRESS --value 0.001ether --broadcast "mintReservation(address,uint256,uint256,string)(uint256)" 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 1 1 "ipfs://test"

approve SchmackoSwap to spend token 0

cast send -i $IPNFT_ADDRESS --private-key $PRIVATE_KEY "approve(address, uint256)()" $SOS_ADDRESS 0

Create a Listing for 10 sample tokens

cast send -i $SOS_ADDRESS --private-key $PRIVATE_KEY "list(address, uint256, address, uint256)(uint256)" $IPNFT_ADDRESS 0 $ERC20_ADDRESS 10

take note of the resulting listing id

Cancel a listing

cast send -i $SOS_ADDRESS --private-key $PRIVATE_KEY "cancel(uint256)()" <listingid>

Create a new Listing (take down id)

cast send -i $SOS_ADDRESS --private-key $PRIVATE_KEY "list(address, uint256, address, uint256)(uint256)" $IPNFT_ADDRESS 0 $ERC20_ADDRESS 10

allow Account(1)

cast send -i $SOS_ADDRESS "changeBuyerAllowance(uint256, address, bool)()" <listingid> 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 true

supply Account(1) with ERC20

cast send -i $ERC20_ADDRESS --private-key $PRIVATE_KEY "mint(address, uint256)()" 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 10

allow SOS to spend ERC20

cast send --i $ERC20_ADDRESS --private-key <account1 private key> "increaseAllowance(address, uint256)()" $SOS_ADDRESS 10

let account(1) fulfill the listing

cast send -i \$SOS_ADDRESS --private-key <account1 private key> "fulfill(uint256)()" <listingid>

grant read access to another party

cast send --private-key $PRIVATE_KEY -i $IPNFT_ADDRESS "grantReadAccess(address,uint256,uint256)" 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 1 1680265071

Actions

We are using Tenderly Web3Actions to trigger actions based on emitted Events from our deployed Contracts.

These are setup under the moleculeprotocol organization on Tenderly. The QueryIds and API-KEY are stored in the Tenderly context and can be accessed via the Tenderly Frontend. To update these actions you need the Tenderly login credentials.

  • StakedLockingCrowdSale (Mainnet & Goerli): BidEvent => Triggers a POST request that executes Dune Queries to update the Dune Visualizations.

You can find out more about Web3Actions on Tenderly here: https://docs.tenderly.co/web3-actions/intro-to-web3-actions How to init & deploy new Web3Actions: https://docs.tenderly.co/web3-actions/tutorials-and-quickstarts/deploy-web3-action-via-cli