Skip to content

Commit c2ed4eb

Browse files
0xashuaquariuslt
andauthored
Release Dev PRs (2024-07-15) (#38)
Co-authored-by: aquariuslt <[email protected]>
1 parent cda0246 commit c2ed4eb

12 files changed

+99
-51
lines changed

.env.example

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
PRIVATE_KEY=
55

66
# RPC URLs
7-
OPTIMISM_SEPOLIA_RPC=
8-
OPTIMISM_MAINNET_RPC=
7+
OPTIMISM_TESTNET_RPC=https://sepolia.optimism.io
8+
OPTIMISM_MAINNET_RPC=https://mainnet.optimism.io
99

10-
CYBER_SEPOLIA_RPC=
11-
CYBER_MAINNET_RPC=
10+
CYBER_TESTNET_RPC=https://cyber-testnet.alt.technology
11+
CYBER_MAINNET_RPC=https://cyber.alt.technology
1212

1313
# Etherscan API keys
1414
ETHEREUM_ETHERSCAN_API_KEY=

Makefile

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
.PHONY: deploy-testnet deploy-mainnet deploy-aave
1+
SHELL := /bin/bash
2+
.PHONY: deploy-testnet deploy-mainnet deploy-aave-mainnet
23

34
DEPLOY_CMD=source .env && forge script scripts/Deploy.s.sol:DeployScript
45
DEPLOY_AAVE_CMD=source .env && forge script scripts/DeployAave.s.sol:DeployAaveScript
56
VERIFY_CMD=--etherscan-api-key $$OPTIMISM_ETHERSCAN_API_KEY --verify
67

78
deploy-testnet:
89
${DEPLOY_CMD} --rpc-url $$OPTIMISM_TESTNET_RPC --broadcast ${VERIFY_CMD} -vvvv
9-
${DEPLOY_CMD} --rpc-url $$CYBER_TESTNET_RPC --broadcast -vvvv
10+
#${DEPLOY_CMD} --rpc-url $$CYBER_TESTNET_RPC --broadcast -vvvv
1011

1112
deploy-mainnet:
1213
${DEPLOY_CMD} --rpc-url $$OPTIMISM_MAINNET_RPC --broadcast ${VERIFY_CMD} -vvvv
13-
${DEPLOY_CMD} --rpc-url $$CYBER_MAINNET_RPC --broadcast -vvvv
14+
#${DEPLOY_CMD} --rpc-url $$CYBER_MAINNET_RPC --broadcast -vvvv
1415

15-
deploy-aave:
16+
deploy-aave-testnet:
1617
${DEPLOY_AAVE_CMD} --rpc-url $$OPTIMISM_TESTNET_RPC --broadcast ${VERIFY_CMD} -vvvv
17-
${DEPLOY_AAVE_CMD} --rpc-url $$OPTIMISM_MAINNET_RPC --broadcast ${VERIFY_CMD} -vvvv
18+
19+
deploy-aave-mainnet:
20+
${DEPLOY_AAVE_CMD} --rpc-url $$OPTIMISM_MAINNET_RPC --broadcast ${VERIFY_CMD} -vvvv

README.md

+40-26
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
##
1313

14-
**A payment protocol designed for creators**, it bridges the gap between creators and fans, where fans can **donate, sponsor, subscribe**, and more, while creators receive **fees and yield**.
14+
**A payment protocol designed for creators**, it bridges the gap between creators and fans, where fans can **donate,
15+
sponsor, subscribe**, and more, while creators receive **fees and yield**.
1516

1617
- For fans, pay early and earn more;
1718
- For creators, long-term income from fees and yield;
@@ -21,20 +22,25 @@
2122
- Made for creators like startups, indie hackers and KOLs.
2223

2324
## Bug Bounty
25+
2426
If you are interested in the smart contracts, here's a simple bug bounty:
2527

26-
- Discover [hign / medium](https://docs.sherlock.xyz/audit/judging/judging#iv.-how-to-identify-a-high-issue) issues - $1200 *cc [email protected]*
28+
- Discover [hign / medium](https://docs.sherlock.xyz/audit/judging/judging#iv.-how-to-identify-a-high-issue) issues -
29+
$1200 *cc [email protected]*
2730
- Add/optimize test cases - $100 / PR
2831

2932
## How it works?
3033

31-
The contract uses a sigmoid bonding curve for dynamic pricing. When you buy, it mints tokens and drives prices up, then when you sell, it burns tokens and drives prices down. The staked ETH is allocated in an interest-rate market to generate sustainable rewards, which are then redistributed to the creators.
34+
The contract uses a sigmoid bonding curve for dynamic pricing. When you buy, it mints tokens and drives prices up, then
35+
when you sell, it burns tokens and drives prices down. The staked ETH is allocated in an interest-rate market to
36+
generate sustainable rewards, which are then redistributed to the creators.
3237

3338
<div align="center">
34-
<img src="images/curve.gif" width="80%">
39+
<img alt="curve" src="images/curve.gif" width="80%">
3540
</div>
3641

3742
## Why is it better?
43+
3844
<div align="center">
3945

4046
| Features | V4V | Friendtech | Patreon | Coinbase Commerce |
@@ -51,36 +57,41 @@ The contract uses a sigmoid bonding curve for dynamic pricing. When you buy, it
5157

5258
### NFT
5359

54-
> The token is a standard ERC1155 contract, with NFTs acting as shares in the bonding curve. When you trade shares, NFTs are minted or burned.
60+
> The token is a standard ERC1155 contract, with NFTs acting as shares in the bonding curve. When you trade shares, NFTs
61+
> are minted or burned.
5562
56-
| Network | Address |
57-
|------------------|--------------------------------------------|
58-
| Optimism Mainnet | N/A |
59-
| Optimism Sepolia | 0x4aCF2aF23f51b7008dF3518A1511871F87083C38 |
60-
| Cyber Mainnet | N/A |
61-
| Cyber Sepolia | 0x552d348657fafd661372f5864093dd9555a2ef06 |
63+
| Network | Address |
64+
|------------------|----------------------------------------------------------------------------------------------------------------------------------------|
65+
| Optimism Mainnet | N/A |
66+
| Optimism Sepolia | [0x02a04ee779587afbbe7f844180ff760bc5e73039](https://sepolia-optimism.etherscan.io/address/0x02a04ee779587afbbe7f844180ff760bc5e73039) |
67+
| Cyber Mainnet | N/A |
68+
| Cyber Sepolia | |
6269

6370
### Shares
6471

65-
> SharesFactory is the core contract that contains the bonding curve and yield aggregator logic where you can mint, buy, and sell shares, as well as change yield strategies and claim yields.
72+
> SharesFactory is the core contract that contains the bonding curve and yield aggregator logic where you can mint, buy,
73+
> and sell shares, as well as change yield strategies and claim yields.
6674
67-
| Network | Address |
68-
|------------------|--------------------------------------------|
69-
| Optimism Mainnet | N/A |
70-
| Optimism Sepolia | 0x9F94C75341D23EAb48793b2879F6062a400132e3 |
71-
| Cyber Mainnet | N/A |
72-
| Cyber Sepolia | 0x1b05f188388b49ee9053914d3109119d228060b5 |
75+
| Network | Address |
76+
|------------------|----------------------------------------------------------------------------------------------------------------------------------------|
77+
| Optimism Mainnet | N/A |
78+
| Optimism Sepolia | [0x4509932e2f18b622f454cf73fac8069901fbbebc](https://sepolia-optimism.etherscan.io/address/0x4509932e2f18b622f454cf73fac8069901fbbebc) |
79+
| Cyber Mainnet | N/A |
80+
| Cyber Sepolia | 0x1b05f188388b49ee9053914d3109119d228060b5 |
7381

7482
### Yield
7583

76-
> YieldAggregator is a yield strategy contract that provides a common interface for SharesFactory to use, such as deposit, withdraw, and claimable. However, the underlying logic can be any yield strategy, such as Aave, Pendle and LRT, or nothing at all.
84+
> YieldAggregator is a yield strategy contract that provides a common interface for SharesFactory to use, such as
85+
> deposit, withdraw, and claimable. However, the underlying logic can be any yield strategy, such as Aave, Pendle and
86+
> LRT,
87+
> or nothing at all.
7788
78-
| Network | Address |
79-
|------------------|--------------------------------------------|
80-
| Optimism Mainnet | N/A |
81-
| Optimism Sepolia | 0xc1eB8f8119De78Da6852F2607d5525d849FCBaaE |
82-
| Cyber Mainnet | N/A |
83-
| Cyber Sepolia | 0xba2553060e90551c797bebd48ee04909606bb04f |
89+
| Network | Address |
90+
|------------------|----------------------------------------------------------------------------------------------------------------------------------------|
91+
| Optimism Mainnet | N/A |
92+
| Optimism Sepolia | [0xa77fc72fbb2e7f6c4ba4f97f0f6eeee9f46a2b97](https://sepolia-optimism.etherscan.io/address/0xa77fc72fbb2e7f6c4ba4f97f0f6eeee9f46a2b97) |
93+
| Cyber Mainnet | N/A |
94+
| Cyber Sepolia | 0xba2553060e90551c797bebd48ee04909606bb04f |
8495

8596
## Test and Deploy
8697

@@ -100,4 +111,7 @@ deploy
100111

101112
## Acknowledgement
102113

103-
Thanks to [Simon de la Rouviere](https://docs.google.com/document/d/1VNkBjjGhcZUV9CyC0ccWYbqeOoVKT2maqX0rK3yXB20), whose ideas inspired *V4V* to combine curated market with bonding curves, and to the ideal S-curve model from [sound protocol](https://github.com/soundxyz/sound-protocol), we’ve also learned the principle of minimalism from [friend tech](https://www.friend.tech) and [bodhi](https://bodhi.wtf).
114+
Thanks to [Simon de la Rouviere](https://docs.google.com/document/d/1VNkBjjGhcZUV9CyC0ccWYbqeOoVKT2maqX0rK3yXB20), whose
115+
ideas inspired *V4V* to combine curated market with bonding curves, and to the ideal S-curve model
116+
from [sound protocol](https://github.com/soundxyz/sound-protocol), we’ve also learned the principle of minimalism
117+
from [friend tech](https://www.friend.tech) and [bodhi](https://bodhi.wtf).

contracts/core/SharesERC1155.sol

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { IShare } from "../interface/IShare.sol";
1111
contract SharesERC1155 is ERC1155Supply, Ownable, IShare {
1212
address public _FACTORY_;
1313
string private _baseURI;
14+
mapping(uint256 => string) public tokenURIs;
1415

1516
event Mint(address indexed user, uint256 indexed id, uint256 amount);
1617
event Burn(address indexed user, uint256 indexed id, uint256 amount);
@@ -32,6 +33,10 @@ contract SharesERC1155 is ERC1155Supply, Ownable, IShare {
3233
_baseURI = newURI;
3334
}
3435

36+
function setTokenURI(uint256 tokenId, string memory tokenURI) public onlyFactory {
37+
tokenURIs[tokenId] = tokenURI;
38+
}
39+
3540
function shareMint(address to, uint256 id, uint256 amount) public onlyFactory {
3641
_mint(to, id, amount, "");
3742
emit Mint(to, id, amount);

contracts/core/SharesFactoryV1.sol

+7-5
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {
4848
event ClaimYield(uint256 amount, address indexed to);
4949
event SetCurve(uint8 indexed curveType);
5050
event SetFee(uint256 indexed feePercent, string feeType);
51-
event Mint(uint256 indexed id, address indexed creator, uint8 indexed curveType);
51+
event Mint(uint256 indexed id, address indexed creator, uint8 indexed curveType, string uri);
5252
event Buy(uint256 indexed id, address indexed buyer, uint32 quantity, uint256 totalPrice);
5353
event Sell(uint256 indexed id, address indexed seller, uint32 quantity, uint256 totalPrice);
5454

@@ -182,24 +182,26 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {
182182
* @notice Mint a share and buy it in one transaction.
183183
* @param curveType The type of the curve.
184184
* @param quantity The quantity of shares.
185+
* @param uri The URI of the share. Arweave, IPFS or any permanent id.
185186
* @param referral The address of the referral fee recipient.
186187
*/
187-
function mintAndBuyShare(uint8 curveType, uint32 quantity, address referral) public payable {
188-
mintShare(curveType);
188+
function mintAndBuyShare(uint8 curveType, uint32 quantity, string memory uri, address referral) public payable {
189+
mintShare(curveType, uri);
189190
buyShare(shareIndex - 1, quantity, referral);
190191
}
191192

192193
/**
193194
* @notice Mint a share with an auto-incremented ID.
194195
* @dev The share ID is identical to the ERC1155 ID.
195196
*/
196-
function mintShare(uint8 curveType) public {
197+
function mintShare(uint8 curveType, string memory uri) public {
197198
require(curvesMap[curveType].exists, "Invalid curveType");
198199

199200
Share memory newShare = Share({ creator: msg.sender, curveType: curveType });
200201
sharesMap[shareIndex] = newShare;
202+
IShare(ERC1155).setTokenURI(shareIndex, uri);
201203

202-
emit Mint(shareIndex, msg.sender, curveType);
204+
emit Mint(shareIndex, msg.sender, curveType, uri);
203205

204206
shareIndex++;
205207
}

contracts/interface/IShare.sol

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
pragma solidity 0.8.25;
44

55
interface IShare {
6+
function setTokenURI(uint256 tokenId, string memory tokenURI) external;
7+
68
function shareMint(address to, uint256 id, uint256 amount) external;
79

810
function shareBurn(address from, uint256 id, uint256 amount) external;

scripts/Base.s.sol

+9-4
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,29 @@ contract BaseScript is Script {
2424
// set the factory address here so that the yieldAggregator can be deployed.
2525

2626
// Optimism Sepolia
27-
SHARES_FACTORY[OPTIMISM_TESTNET] = 0x9F94C75341D23EAb48793b2879F6062a400132e3;
27+
SHARES_FACTORY[OPTIMISM_TESTNET] = 0x4509932e2f18b622F454cf73FAc8069901fbbebC;
2828
WETH[OPTIMISM_TESTNET] = 0x4200000000000000000000000000000000000006;
2929
AAVE_POOL[OPTIMISM_TESTNET] = 0xb50201558B00496A145fE76f7424749556E326D8;
3030
AAVE_WETH_GATEWAY[OPTIMISM_TESTNET] = 0x589750BA8aF186cE5B55391B0b7148cAD43a1619;
3131

3232
// Optimism Mainnet
33-
SHARES_FACTORY[OPTIMISM_MAINNET] = address(0);
33+
SHARES_FACTORY[OPTIMISM_MAINNET] = 0x3eC4241152E5CEc606E1F0FB45448251221104E7;
3434
WETH[OPTIMISM_MAINNET] = 0x4200000000000000000000000000000000000006;
3535
AAVE_POOL[OPTIMISM_MAINNET] = 0x794a61358D6845594F94dc1DB02A252b5b4814aD;
3636
AAVE_WETH_GATEWAY[OPTIMISM_MAINNET] = 0xe9E52021f4e11DEAD8661812A0A6c8627abA2a54;
3737

38-
// Cyber Sepolia
38+
// Cyber Testnet
3939
WETH[CYBER_TESTNET] = 0x4200000000000000000000000000000000000006;
4040
SHARES_FACTORY[CYBER_TESTNET] = address(0);
41+
AAVE_POOL[CYBER_TESTNET] = address(0);
42+
AAVE_WETH_GATEWAY[CYBER_TESTNET] = address(0);
4143

4244
// Cyber Mainnet
43-
WETH[CYBER_MAINNET] = address(0);
4445
SHARES_FACTORY[CYBER_MAINNET] = address(0);
46+
WETH[CYBER_MAINNET] = 0x4200000000000000000000000000000000000006;
47+
AAVE_POOL[CYBER_MAINNET] = address(0);
48+
AAVE_WETH_GATEWAY[CYBER_MAINNET] = address(0);
49+
4550
}
4651

4752
modifier broadcast() {

test/integration/BaseIntegrationTest.t.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ contract BaseIntegrationTest is Test {
132132
vm.deal(LONG_TERM_TRADER, 100 ether);
133133

134134
vm.prank(DAILY_TRADER);
135-
sharesFactory.mintShare(DEFAULT_CURVE_TYPE);
135+
sharesFactory.mintShare(DEFAULT_CURVE_TYPE, '');
136136
uint256 shareId = sharesFactory.shareIndex() - 1;
137137

138138
for (uint32 i = 0; i < tradeCount; i++) {

test/integration/IncomeSimulator.t.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ contract IncomeSimulator is BaseIntegrationTest, LogUtil {
216216

217217
// mint shares
218218
vm.prank(FACTORY_OWNER);
219-
sharesFactory.mintShare(curveType);
219+
sharesFactory.mintShare(curveType, '');
220220

221221
// record start balance
222222
uint256 founderBalanceBefore = aWETH.balanceOf(address(SHARES_FOUNDER));

test/integration/handlers/BoundedIntegrationContextHandler.t.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ contract BoundedIntegrationContextHandler is StdUtils {
5656
uint256 shareId = 0;
5757

5858
vm.prank(factoryOwner);
59-
sharesFactory.mintShare(0);
59+
sharesFactory.mintShare(0, '');
6060

6161
vm.prank(TRADER);
6262

test/unit/SharesERC1155.t.sol

+11
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ contract ERC1155Tests is BaseTest {
3737
assertEq(userBal, 10);
3838
}
3939

40+
function test_setTokenURI() public {
41+
vm.expectRevert(bytes("Caller is not the factory"));
42+
sharesNFT.setTokenURI(0, "https://vv.com/0");
43+
44+
vm.prank(address(sharesFactory));
45+
sharesNFT.setTokenURI(0, "https://vv.com/0");
46+
47+
string memory tokenURI = sharesNFT.tokenURIs(0);
48+
assertEq(tokenURI, "https://vv.com/0");
49+
}
50+
4051
function test_shareMint() public {
4152
vm.prank(address(sharesFactory));
4253
sharesNFT.shareMint(addrUser, 0, 10);

test/unit/SharesFactory.t.sol

+10-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { IYieldAggregator } from "contracts/interface/IYieldAggregator.sol";
88
import { BaseTest } from "../BaseTest.t.sol";
99

1010
contract SharesFactoryTests is BaseTest {
11+
string private constant URI = "https://vv.com/uri/";
12+
1113
function setUp() public {
1214
createFactory();
1315
_setUpShare();
@@ -19,7 +21,7 @@ contract SharesFactoryTests is BaseTest {
1921

2022
// Alice mint & buy 1 share with 0 id
2123
vm.prank(addrAlice);
22-
sharesFactory.mintShare(defaultCurveType);
24+
sharesFactory.mintShare(defaultCurveType, URI);
2325
_buyShare(addrAlice, 0, 1, referralReceiver);
2426

2527
// Bob mintAndBuy 1 share with 1 id
@@ -39,16 +41,18 @@ contract SharesFactoryTests is BaseTest {
3941

4042
function test_mintShare() public {
4143
vm.prank(addrAlice);
42-
sharesFactory.mintShare(defaultCurveType);
44+
sharesFactory.mintShare(defaultCurveType, URI);
4345

4446
uint256 shareIndex = sharesFactory.shareIndex();
4547
(address creator, uint8 curveType) = sharesFactory.getShare(shareIndex - 1);
48+
string memory tokenURI = sharesNFT.tokenURIs(shareIndex - 1);
4649

4750
assertEq(creator, addrAlice);
4851
assertEq(curveType, defaultCurveType);
52+
assertEq(tokenURI, URI);
4953

5054
vm.expectRevert(bytes("Invalid curveType"));
51-
sharesFactory.mintShare(99);
55+
sharesFactory.mintShare(99, URI);
5256
}
5357

5458
function test_minAndBuyShare() public {
@@ -59,10 +63,12 @@ contract SharesFactoryTests is BaseTest {
5963
uint256 shareId = sharesFactory.shareIndex() - 1;
6064
uint256 bobShareBal = sharesNFT.shareBalanceOf(addrBob, shareId);
6165
(address creator, uint8 curveType) = sharesFactory.getShare(shareId);
66+
string memory tokenURI = sharesNFT.tokenURIs(shareId);
6267

6368
assertEq(creator, addrBob);
6469
assertEq(curveType, defaultCurveType);
6570
assertEq(bobShareBal, 99);
71+
assertEq(tokenURI, URI);
6672
}
6773

6874
function test_buyShares() public {
@@ -534,7 +540,7 @@ contract SharesFactoryTests is BaseTest {
534540
uint256 buyPrice = sharesFactory.getSubTotal(0, quantity, curveType);
535541

536542
vm.prank(address(sender));
537-
sharesFactory.mintAndBuyShare{ value: buyPrice * 110 / 100 }(curveType, quantity, referral);
543+
sharesFactory.mintAndBuyShare{ value: buyPrice * 110 / 100 }(curveType, quantity, URI, referral);
538544
}
539545

540546
function _buyShare(address sender, uint256 shareId, uint32 quantity, address referral) internal {

0 commit comments

Comments
 (0)