Skip to content

Commit 2505672

Browse files
committed
feat(tokenizer): allow erc20 attachment as ipt
- drops stoneage Synthesizer - extracts IIPToken interface to be reused on Tokenizer and Permissioner - fixes linearization of dependencies on IPToken - adds general erc20 metadata fields to shared IPT interface - wrapping test case - rollout script and disable fork test - drops size checks on builds - add sanity checks for ipt attachment on tokenizer
1 parent 0cc9189 commit 2505672

20 files changed

+1132
-434
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ jobs:
2525
- name: Install node dependencies
2626
run: yarn --frozen-lockfile
2727

28+
#todo: forge build --sizes
2829
- name: Run Forge build
2930
run: |
3031
forge --version
31-
forge build --sizes
32+
forge build
3233
id: build
3334

3435
- name: Run Forge tests

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ VDAO_TOKEN_ADDRESS=0x19A3036b828bffB5E14da2659E950E76f8e6BAA2
100100

101101
---
102102

103-
### upgrading to Tokenizer 1.3
103+
### upgrading to Tokenizer 1.4
104104

105-
forge script --private-key=$PRIVATE_KEY --rpc-url=$RPC_URL script/prod/RolloutTokenizerV13.s.sol --broadcast
105+
forge script --private-key=$PRIVATE_KEY --rpc-url=$RPC_URL script/prod/RolloutTokenizerV14.s.sol --broadcast
106106

107-
// 0xTokenizer 0xNewImpl 0xNewTokenImpl
108-
cast send --rpc-url=$RPC_URL --private-key=$PRIVATE_KEY 0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e "upgradeToAndCall(address,bytes)" 0x70e0bA845a1A0F2DA3359C97E0285013525FFC49 0x84646c1f000000000000000000000000998abeb3e57409262ae5b751f60747921b33613e
107+
// 0xTokenizer (address, bytes)(0xNewImpl, 0xNewWrappedIPTokenImpl 0xNewIPTokenImpl)
108+
cast send --rpc-url=$RPC_URL --private-key=$PRIVATE_KEY 0x58EB89C69CB389DBef0c130C6296ee271b82f436 "upgradeToAndCall(address,bytes)" 0x34A1D3fff3958843C43aD80F30b94c510645C316 0x8b3d19bb0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e14960000000000000000000000005b73c5498c1e3b4dba84de0f1833c4a029d90519
109109

110110
### Timelocked Tokens
111111

script/dev/Synthesizer.s.sol

Lines changed: 0 additions & 100 deletions
This file was deleted.

script/dev/Tokenizer.s.sol

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import "forge-std/Script.sol";
55
import "forge-std/console.sol";
66
import { IPNFT } from "../../src/IPNFT.sol";
77
import { Tokenizer } from "../../src/Tokenizer.sol";
8-
import { Metadata, IPToken } from "../../src/IPToken.sol";
8+
import { Metadata } from "../../src/IIPToken.sol";
9+
import { IPToken } from "../../src/IPToken.sol";
10+
import { WrappedIPToken } from "../../src/WrappedIPToken.sol";
11+
912
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
1013
import { IPermissioner, TermsAcceptedPermissioner } from "../../src/Permissioner.sol";
1114
import { CommonScript } from "./Common.sol";
@@ -23,9 +26,13 @@ contract DeployTokenizer is CommonScript {
2326
IPToken initialIpTokenImplementation = new IPToken();
2427
tokenizer.setIPTokenImplementation(initialIpTokenImplementation);
2528

29+
WrappedIPToken initialWrappedIpTokenImplementation = new WrappedIPToken();
30+
tokenizer.setWrappedIPTokenImplementation(initialWrappedIpTokenImplementation);
31+
2632
vm.stopBroadcast();
2733
console.log("TOKENIZER_ADDRESS=%s", address(tokenizer));
2834
console.log("iptoken implementation=%s", address(initialIpTokenImplementation));
35+
console.log("wrapped iptoken implementation=%s", address(initialWrappedIpTokenImplementation));
2936
}
3037
}
3138

script/prod/RolloutTokenizerV13.s.sol

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.18;
3+
4+
import "forge-std/Script.sol";
5+
import { Tokenizer } from "../../src/Tokenizer.sol";
6+
import { IPToken } from "../../src/IPToken.sol";
7+
import { WrappedIPToken } from "../../src/WrappedIPToken.sol";
8+
import { console } from "forge-std/console.sol";
9+
10+
contract RolloutTokenizerV14 is Script {
11+
function run() public {
12+
vm.startBroadcast();
13+
14+
// Deploy new implementations
15+
IPToken ipTokenImplementation = new IPToken();
16+
WrappedIPToken wrappedIpTokenImplementation = new WrappedIPToken();
17+
Tokenizer newTokenizerImplementation = new Tokenizer();
18+
19+
// Prepare upgrade call data using reinit function
20+
bytes memory upgradeCallData =
21+
abi.encodeWithSelector(Tokenizer.reinit.selector, address(wrappedIpTokenImplementation), address(ipTokenImplementation));
22+
23+
console.log("IPTOKENIMPLEMENTATION=%s", address(ipTokenImplementation));
24+
console.log("WRAPPEDTOKENIMPLEMENTATION=%s", address(wrappedIpTokenImplementation));
25+
console.log("NEWTOKENIZER=%s", address(newTokenizerImplementation));
26+
console.log("UpgradeCallData:");
27+
console.logBytes(upgradeCallData);
28+
29+
vm.stopBroadcast();
30+
}
31+
}

src/IIPToken.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8;
3+
4+
struct Metadata {
5+
uint256 ipnftId;
6+
address originalOwner;
7+
string agreementCid;
8+
}
9+
10+
interface IIPToken {
11+
/// @notice the amount of tokens that ever have been issued (not necessarily == supply)
12+
function totalIssued() external view returns (uint256);
13+
function balanceOf(address account) external view returns (uint256);
14+
function metadata() external view returns (Metadata memory);
15+
function issue(address, uint256) external;
16+
function cap() external;
17+
function uri() external view returns (string memory);
18+
function name() external view returns (string memory);
19+
function symbol() external view returns (string memory);
20+
function decimals() external view returns (uint8);
21+
}

src/IPToken.sol

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.18;
33

4+
import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
45
import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
56
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
67
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
78
import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
89
import { Tokenizer, MustControlIpnft } from "./Tokenizer.sol";
910
import { IControlIPTs } from "./IControlIPTs.sol";
10-
11-
struct Metadata {
12-
uint256 ipnftId;
13-
address originalOwner;
14-
string agreementCid;
15-
}
11+
import { IIPToken, Metadata } from "./IIPToken.sol";
1612

1713
error TokenCapped();
1814

@@ -24,7 +20,7 @@ error TokenCapped();
2420
* The owner can increase the token supply as long as it's not explicitly capped.
2521
* @dev formerly known as "molecules"
2622
*/
27-
contract IPToken is ERC20BurnableUpgradeable, OwnableUpgradeable {
23+
contract IPToken is IIPToken, ERC20Upgradeable, ERC20BurnableUpgradeable, OwnableUpgradeable {
2824
event Capped(uint256 atSupply);
2925

3026
/// @notice the amount of tokens that ever have been issued (not necessarily == supply)
@@ -35,12 +31,12 @@ contract IPToken is ERC20BurnableUpgradeable, OwnableUpgradeable {
3531

3632
Metadata internal _metadata;
3733

38-
function initialize(uint256 ipnftId, string calldata name, string calldata symbol, address originalOwner, string memory agreementCid)
34+
function initialize(uint256 ipnftId, string calldata name_, string calldata symbol_, address originalOwner, string memory agreementCid)
3935
external
4036
initializer
4137
{
4238
__Ownable_init();
43-
__ERC20_init(name, symbol);
39+
__ERC20_init(name_, symbol_);
4440
_metadata = Metadata({ ipnftId: ipnftId, originalOwner: originalOwner, agreementCid: agreementCid });
4541
}
4642

@@ -59,11 +55,27 @@ contract IPToken is ERC20BurnableUpgradeable, OwnableUpgradeable {
5955
return _metadata;
6056
}
6157

58+
function balanceOf(address account) public view override(ERC20Upgradeable, IIPToken) returns (uint256) {
59+
return ERC20Upgradeable.balanceOf(account);
60+
}
61+
62+
function name() public view override(ERC20Upgradeable, IIPToken) returns (string memory) {
63+
return ERC20Upgradeable.name();
64+
}
65+
66+
function symbol() public view override(ERC20Upgradeable, IIPToken) returns (string memory) {
67+
return ERC20Upgradeable.symbol();
68+
}
69+
70+
function decimals() public view override(ERC20Upgradeable, IIPToken) returns (uint8) {
71+
return ERC20Upgradeable.decimals();
72+
}
6273
/**
6374
* @notice the supply of IP Tokens is controlled by the tokenizer contract.
6475
* @param receiver address
6576
* @param amount uint256
6677
*/
78+
6779
function issue(address receiver, uint256 amount) external onlyTokenizerOrIPNFTController {
6880
if (capped) {
6981
revert TokenCapped();
@@ -109,7 +121,7 @@ contract IPToken is ERC20BurnableUpgradeable, OwnableUpgradeable {
109121
string.concat(
110122
'{"name": "IP Tokens of IPNFT #',
111123
tokenId,
112-
'","description": "IP Tokens, derived from IP-NFTs, are ERC-20 tokens governing IP pools.","decimals": 18,"external_url": "https://molecule.to","image": "",',
124+
'","description": "IP Tokens, derived from IP-NFTs, are ERC-20 tokens governing IP pools.","decimals": 18,"external_url": "https://molecule.xyz","image": "",',
113125
props,
114126
"}"
115127
)

src/Permissioner.sol

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pragma solidity 0.8.18;
44
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
55
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
66
import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
7-
import { IPToken, Metadata } from "./IPToken.sol";
7+
import { IIPToken, Metadata } from "./IIPToken.sol";
88

99
error InvalidSignature();
1010
error Denied();
@@ -16,17 +16,17 @@ interface IPermissioner {
1616
* @param _for address
1717
* @param data bytes
1818
*/
19-
function accept(IPToken tokenContract, address _for, bytes calldata data) external;
19+
function accept(IIPToken tokenContract, address _for, bytes calldata data) external;
2020
}
2121

2222
contract BlindPermissioner is IPermissioner {
23-
function accept(IPToken tokenContract, address _for, bytes calldata data) external {
23+
function accept(IIPToken tokenContract, address _for, bytes calldata data) external {
2424
//empty
2525
}
2626
}
2727

2828
contract ForbidAllPermissioner is IPermissioner {
29-
function accept(IPToken, address, bytes calldata) external pure {
29+
function accept(IIPToken, address, bytes calldata) external pure {
3030
revert Denied();
3131
}
3232
}
@@ -44,7 +44,7 @@ contract TermsAcceptedPermissioner is IPermissioner {
4444
* @param _for address the account that has created `signature`
4545
* @param signature bytes encoded signature, for eip155: `abi.encodePacked(r, s, v)`
4646
*/
47-
function accept(IPToken tokenContract, address _for, bytes calldata signature) external {
47+
function accept(IIPToken tokenContract, address _for, bytes calldata signature) external {
4848
if (!isValidSignature(tokenContract, _for, signature)) {
4949
revert InvalidSignature();
5050
}
@@ -55,7 +55,7 @@ contract TermsAcceptedPermissioner is IPermissioner {
5555
* @notice checks whether `signer`'s `signature` of `specificTermsV1` on `tokenContract.metadata.ipnftId` is valid
5656
* @param tokenContract IPToken
5757
*/
58-
function isValidSignature(IPToken tokenContract, address signer, bytes calldata signature) public view returns (bool) {
58+
function isValidSignature(IIPToken tokenContract, address signer, bytes calldata signature) public view returns (bool) {
5959
bytes32 termsHash = ECDSA.toEthSignedMessageHash(bytes(specificTermsV1(tokenContract)));
6060
return SignatureChecker.isValidSignatureNow(signer, termsHash, signature);
6161
}
@@ -78,7 +78,7 @@ contract TermsAcceptedPermissioner is IPermissioner {
7878
* @notice this yields the message text that claimers must present to proof they have accepted all terms
7979
* @param tokenContract IPToken
8080
*/
81-
function specificTermsV1(IPToken tokenContract) public view returns (string memory) {
81+
function specificTermsV1(IIPToken tokenContract) public view returns (string memory) {
8282
return (specificTermsV1(tokenContract.metadata()));
8383
}
8484
}

0 commit comments

Comments
 (0)