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
Contract | Address | Actions |
---|---|---|
IP-NFT | 0xcaD88677CA87a7815728C72D74B4ff4982d54Fc1 | |
SignedMintAuthorizer | 0xBc5FbB45A2bbB64d9B2EeBFa327284a35d5C5865 | |
SchmackoSwap | 0xc09b8577c762b5e97a7d640f242e1d9bfaa7eb9d | |
Tokenizer | 0x58EB89C69CB389DBef0c130C6296ee271b82f436 | |
Permissioner | 0xC837E02982992B701A1B5e4E21fA01cEB0a628fA | |
Crowdsale | 0xf0a8d23f38e9cbbe01c4ed37f23bd519b65bc6c2 | |
StakedLockingCrowdSale | 0x35Bce29F52f51f547998717CD598068Afa2B29B7 |
API: https://subgraph.satsuma-prod.com/742d8952ab24/molecule--4039244/ip-nft-mainnet/api Playground: https://subgraph.satsuma-prod.com/molecule--4039244/ip-nft-mainnet/playground
tokenizer implementation 1.3: 0x6517DD48908F4C1FF4eD74FfD780908241a3654C
tokenizer implementation 1.2: 0xE8701330F196FeFe415b28dAA767AB076F42557A
tokenizer implementation 1.1: 0x9C70FA8c87D7e94Fd63eeCCcA657D5c4224a36f3
iptoken implementation 1.3: 0x89a14Be8f7824d4775053Edad0f2fA2d6767b72B iptoken implementation: 0x9E4fc6E6d1A64e3429aB852d3CB31AD7aa06997A
ipnft implementation 2.4: 0x6B179Dffac5E190c670176606f552cB792847f80
Deprecated after migrating to Defender 2 (was 0x3D30452c48F2448764d5819a9A2b684Ae2CC5AcF). We're using a key signoff with 0x8626c6293B5101E5E534B5B60F411a37294D8cBE.
on Satsuma, Techprod Account
API: https://subgraph.satsuma-prod.com/742d8952ab24/molecule--4039244/ip-nft-sepolia/api Playground: https://subgraph.satsuma-prod.com/molecule--4039244/ip-nft-sepolia/playground
Deprecated after migrating to Defender 2 (was 0xd7B298c9fB0377124d01D4E826d9D5beFB7CD6FE). We're using a key signoff with 0x8626c6293B5101E5E534B5B60F411a37294D8cBE.
Contract | Address | |
---|---|---|
USDC (6 decimals) | 0xC7B1b8BEA20d559040928FA1e6a23a3c221286B1 | |
MOL Dao | 0xe0D404C22228b03D5b8a715Cb569C4944BC5A27A | |
vested MOL Dao | 0x8f80d1183CD983B01B0C9AC6777cC732Ec9800de |
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
forge script --private-key=$PRIVATE_KEY --rpc-url=$RPC_URL script/prod/RolloutTokenizerV13.s.sol --broadcast
// 0xTokenizer 0xNewImpl 0xNewTokenImpl cast send --rpc-url=$RPC_URL --private-key=$PRIVATE_KEY 0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e "upgradeToAndCall(address,bytes)" 0x70e0bA845a1A0F2DA3359C97E0285013525FFC49 0x84646c1f000000000000000000000000998abeb3e57409262ae5b751f60747921b33613e
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)
Run forge install
. This will clone dependency repos as submodules into the lib
folder.
Run forge build
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
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
- 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.
- 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 .
You need Docker.
yarn localenv
sets up everything- use
./setupLocal.sh
to deploy all contracts. Add the optional-f
or--fixture
flag to also run the fixture scripts to tokenize one IPNFT or-fx
to create two crowdsale instances.
-
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.
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.
- Make sure you have the private key for your deployer account at hand and that it has ETH on the target network on it.
- Run
forge script script/Deploy.s.sol:DeployScript -f $RPC_URL --interactives 1 --sender <deployer address> --broadcast -vvvv
- Paste the private key for the deployer account
- to verify the contract during deployment, get an Etherscan API key and add
--verify --etherscan-api-key $ETHERSCAN_API_KEY
to the command.
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
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
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)
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>" "")
docker run -m 6G --cpus=8 -w /tmp -v $(pwd):/tmp mythril/myth analyze /tmp/src/IPNFT.sol --solc-json /tmp/mythril.config.json
requires the lcov suite installed on your machine
forge coverage --report lcov && genhtml lcov.info -o report --branch-coverage
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
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
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