Skip to content

Instantly share code, notes, and snippets.

@elmariachi111
Last active July 29, 2024 16:59
Show Gist options
  • Save elmariachi111/330b2206b762e0c91ff978718e0e35da to your computer and use it in GitHub Desktop.
Save elmariachi111/330b2206b762e0c91ff978718e0e35da to your computer and use it in GitHub Desktop.
Catalyst Funding Session
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
import { Test } from "forge-std/Test.sol";
import "forge-std/console.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { LinearCurve } from "../../src/curves/LinearCurve.sol";
import { CurveParametersOutOfRange, TradeType } from "../../src/curves/IIPSeedCurve.sol";
import { CommonTest, Defaults } from "../helpers/CommonTest.sol";
import { ParameterMismatch, PremintTooLarge } from "../../src/IPSeedTrust.sol";
import {
IPSeed,
MarketData,
UnauthorizedAccess,
TokenAlreadyExists,
InvalidTokenId,
TradeSizeOutOfRange,
InsufficientPayment,
PriceDriftTooHigh,
BalanceTooLow,
TransferRestricted,
BadState,
MarketState,
MarketParameters,
BASIS_POINTS
} from "../../src/IPSeed.sol";
import {
ReentrancyBurnAttacker, ReentrancyWithdrawalsAttacker
} from "../helpers/ReentrancyAttacker.sol";
error OwnableUnauthorizedAccount(address account);
contract BugBashTest is CommonTest {
function testGoodFeeAndSlopeParameters() public {
address sourcer = makeAddr("kevin");
uint16 lpFeeBps = 500;
uint128 netFundingGoal = 1 ether;
uint128 premint = 500 ether;
uint256 tokenId = ipSeed.computeTokenId(sourcer, Defaults.projectId);
uint256 grossFundingGoal = (netFundingGoal * (BASIS_POINTS + lpFeeBps) / BASIS_POINTS);
uint256 slope = curve.computeSlope(grossFundingGoal, 10_000 ether, premint);
MarketParameters memory params = MarketParameters({
tokenId: tokenId,
projectId: Defaults.projectId,
sourcer: sourcer,
beneficiary: payable(beneficiaryMultisig),
priceCurve: curve,
curveParameters: bytes32(abi.encodePacked(uint128(slope), uint128(premint))),
fundingGoal: uint128(grossFundingGoal),
premint: premint,
deadline: uint64(block.timestamp + defaultDeadline)
});
vm.startPrank(sourcer);
ipSeed.spawn(params);
assertEq(ipSeed.totalSupply(), premint);
assertEq(ipSeed.balanceOf(beneficiaryMultisig, tokenId), Defaults.defaultPremint);
uint256 fundingGoalInTokens =
curve.supplyAtCollateral(params.curveParameters, params.fundingGoal);
assertEq(fundingGoalInTokens, 10_000.00000019229513999 ether);
vm.startPrank(alice);
vm.deal(alice, grossFundingGoal);
ipSeed.mint{ value: netFundingGoal }(tokenId, 0 ether);
assertEq(ipSeed.totalSupply(), 9771.050693198689432819 ether);
ipSeed.mint{ value: alice.balance }(tokenId, 0 ether);
assertEq(ipSeed.totalSupply(), 10000.000000095020357146 ether);
assertEq(ipSeed.collateral(tokenId), grossFundingGoal);
}
function testContradictingFeeAndSlopeParameters() public {
address sourcer = makeAddr("kevin");
uint16 lpFeeBps = 500;
uint128 netFundingGoal = 1 ether;
uint128 premint = 500 ether;
uint256 tokenId = ipSeed.computeTokenId(sourcer, Defaults.projectId);
uint256 grossFundingGoal = (netFundingGoal * (BASIS_POINTS + lpFeeBps) / BASIS_POINTS);
assertEq(grossFundingGoal, 1.05 ether);
uint256 slope = curve.computeSlope(grossFundingGoal, 10_000 ether, premint);
MarketParameters memory params = MarketParameters({
tokenId: tokenId,
projectId: Defaults.projectId,
sourcer: sourcer,
beneficiary: payable(beneficiaryMultisig),
priceCurve: curve,
curveParameters: bytes32(abi.encodePacked(uint128(slope), uint128(premint))),
fundingGoal: uint128(netFundingGoal),
premint: premint,
deadline: uint64(block.timestamp + defaultDeadline)
});
vm.startPrank(sourcer);
ipSeed.spawn(params);
assertEq(ipSeed.totalSupply(), premint);
assertEq(ipSeed.balanceOf(beneficiaryMultisig, tokenId), Defaults.defaultPremint);
uint256 fundingGoalInTokens =
curve.supplyAtCollateral(params.curveParameters, params.fundingGoal);
assertEq(fundingGoalInTokens, 9771.050693198726048531 ether);
vm.startPrank(alice);
vm.deal(alice, grossFundingGoal);
ipSeed.mint{ value: netFundingGoal }(tokenId, 0 ether);
assertEq(ipSeed.totalSupply(), 9771.050693198689432819 ether);
vm.expectRevert();
ipSeed.mint{ value: alice.balance }(tokenId, 0 ether);
assertEq(ipSeed.balanceOf(alice, tokenId), 9271.050693198689432819 ether);
assertEq(ipSeed.collateral(tokenId), netFundingGoal);
}
function testParameterConformitySilentlyFails() public {
uint256 baseSepoliaFork = vm.createFork(vm.envString("BASE_SEPOLIA_RPC_URL"), 13075689);
vm.selectFork(baseSepoliaFork);
bytes memory spawnedBytesLog =
hex"000000000000000000000000000000000000000000000000069bc4b712c1e79e00000000000000000000000000000000000000000000000000000000000000409e512fa617e26a1075d3c9bd244f6112fa173ff079b11bb42ae671ba8c2e863700000000000000000000000000000000000000000000000000000000000001200000000000000000000000009f5dead94c9861ec9af7e0353cf0a100189c6815000000000000000000000000ddef5eb6768e9c37781b32de644d01d0f56054b70000000000000000000000008ca737e2cdae1ceb332bef7ba9ea711a3a2f8037000000000000000000000002b57613e6000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000001b1ae4d6e2ef5000000000000000000000000000000000000000000000000000000000000066c9f395000000000000000000000000000000000000000000000000000000000000003b636174616c7973742d646576656c6f706d656e743a2f2f65663036326531332d383033302d343963662d626430332d6264343435363464323561650000000000";
(uint256 netFundingGoal, MarketParameters memory parameters) =
abi.decode(spawnedBytesLog, (uint256, MarketParameters));
LinearCurve curve = LinearCurve(address(parameters.priceCurve));
bytes32 curveParams = parameters.curveParameters;
(uint256 m, uint256 p) = curve.decodeParameters(curveParams);
//console.log(m, p, netFundingGoal);
console.log("Wrong Funding goal (gross,net)", parameters.fundingGoal, netFundingGoal);
uint256 finalSupply = curve.supplyAtCollateral(curveParams, parameters.fundingGoal);
//the slope above has been wrongly computed for a different funding goal
assertEq(finalSupply, 9771.050693198697770147 ether);
}
// replays a session on a spawned project that Kevin and me created on Thu Jul 25th 24
// here the market parameters funding goal (0.5) and the curve parameters were not in sync
/* Replay
Spawn 13075577n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x9F5DeAD94C9861eC9af7E0353cf0a100189c6815 476190476190476190n
Buy 13075689n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28 100000000000000000n 4646139914567699710827n
Buy 13075707n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x8FF4B0B37EEA9Fa19070D5d78B082E2d75cd4c7D 100000000000000000n 6363527298048148723968n
Buy 13075739n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x8FF4B0B37EEA9Fa19070D5d78B082E2d75cd4c7D 50000000000000000n 7055622812758093780516n
Buy 13075764n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xCAed763276288a8Fe8E274e03f2cdFF0996eAEcb 200000000000000000n 9295290946757250585940n
Exit 13075793n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28 95000000000000000n 5149151032189550875113n
Buy 13075823n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28 50000000000000000n 5996348188517514331439n
Buy 13075878n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x72A960b0F2173af825Ea486a0851dfC375F2501a 31400000000000000n 6467214838836238695857n
Buy 13075924n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x8FeEAAae1DB031E5F980F5E63fDbb277731e500e 21718280000000000n 6772250686967915384948n
Buy 13076000n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xe127A39da6eA2D7b1979372aE973a20baB08A80A 44000000000000000n 7348717996790147871298n
Exit 13076025n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x8FeEAAae1DB031E5F980F5E63fDbb277731e500e 20632366000000000n 7043682148658471182207n
Buy 13076128n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xE231B4e55fE1D0Afb3e746e64E78eEffB5b599d1 24599999999999999n 7359200623987468596685n
Funding Goal Reached 13076163n 71608873548347908551806579327355212747937605875372970477748660726076739061303n
Buy 13076163n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28 1n 7359200623987468596685n
Claimed Collateral 13076467n 71608873548347908551806579327355212747937605875372970477748660726076739061303n
Succeeded 13085032n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x2854e2ACC2530165a76623B99c2F5b4915484BdC
*/
function testBugbashSession1() public {
uint256 tokenId = 71608873548347908551806579327355212747937605875372970477748660726076739061303;
IPSeed ipSeed = IPSeed(0x1545dAbD87Bd880F4c03Bc4e5BC59d8C7711222d);
//spawn +1
uint256 baseSepoliaFork = vm.createFork(vm.envString("BASE_SEPOLIA_RPC_URL"), 13075578);
vm.selectFork(baseSepoliaFork);
assertEq(ipSeed.collateral(tokenId), 0 ether);
assertEq(ipSeed.totalSupply(tokenId), 500 ether);
LinearCurve curve = LinearCurve(address(0x8cA737E2cdaE1Ceb332bEf7ba9eA711a3a2f8037));
MarketParameters memory params = ipSeed.getMarketParams(tokenId);
uint256 expectedFinalSupply =
curve.supplyAtCollateral(params.curveParameters, params.fundingGoal);
assertEq(expectedFinalSupply, 9771.050693198697770147 ether);
//1 block past funding goal reached:
vm.rollFork(baseSepoliaFork, 13076164);
assertEq(ipSeed.totalSupply(tokenId), 7359.200623987468596685 ether);
assertEq(ipSeed.collateral(tokenId), 0.5 ether);
}
//what would've happened if we would've used the correct parameters?
function testBugbashSession1WithCorrectParameters() public {
uint256 baseSepoliaFork = vm.createFork(vm.envString("BASE_SEPOLIA_RPC_URL"), 13075576);
vm.selectFork(baseSepoliaFork);
uint256 tokenId = 71608873548347908551806579327355212747937605875372970477748660726076739061303;
address sourcer = 0x9F5DeAD94C9861eC9af7E0353cf0a100189c6815;
IPSeed ipSeed = IPSeed(0x1545dAbD87Bd880F4c03Bc4e5BC59d8C7711222d);
LinearCurve curve = LinearCurve(address(0x8cA737E2cdaE1Ceb332bEf7ba9eA711a3a2f8037));
uint256 fundingGoal = 0.55 ether;
uint256 premint = 500 ether;
uint256 slope = curve.computeSlope(fundingGoal, 10_000 ether, premint);
MarketParameters memory betterParameters = MarketParameters({
tokenId: tokenId,
projectId: "catalyst-development://ef062e13-8030-49cf-bd03-bd44564d25ae",
sourcer: sourcer,
beneficiary: payable(0xdDEf5eb6768E9C37781b32De644d01D0F56054B7),
priceCurve: curve,
curveParameters: bytes32(abi.encodePacked(uint128(slope), uint128(premint))),
fundingGoal: uint128(0.55 ether), //<-- this
premint: uint128(premint),
deadline: uint64(block.timestamp + defaultDeadline)
});
vm.startPrank(sourcer);
ipSeed.spawn(betterParameters);
uint256 expectedFinalSupply =
curve.supplyAtCollateral(betterParameters.curveParameters, betterParameters.fundingGoal);
assertEq(expectedFinalSupply, 10_000.000000377909357083 ether);
// lets replay the trades
vm.startPrank(0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28);
ipSeed.mint{ value: 0.1 ether }(tokenId, 0);
vm.startPrank(0x8FF4B0B37EEA9Fa19070D5d78B082E2d75cd4c7D);
ipSeed.mint{ value: 0.1 ether }(tokenId, 0);
ipSeed.mint{ value: 0.05 ether }(tokenId, 0);
vm.startPrank(0xCAed763276288a8Fe8E274e03f2cdFF0996eAEcb);
ipSeed.mint{ value: 0.2 ether }(tokenId, 0);
vm.startPrank(0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28);
ipSeed.exit(tokenId);
vm.startPrank(0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28);
ipSeed.mint{ value: 0.05 ether }(tokenId, 0);
vm.startPrank(0x72A960b0F2173af825Ea486a0851dfC375F2501a);
ipSeed.mint{ value: 0.0314 ether }(tokenId, 0);
vm.startPrank(0x8FeEAAae1DB031E5F980F5E63fDbb277731e500e);
ipSeed.mint{ value: 0.02171828 ether }(tokenId, 0);
vm.startPrank(0xe127A39da6eA2D7b1979372aE973a20baB08A80A);
ipSeed.mint{ value: 0.044 ether }(tokenId, 0);
vm.startPrank(0x8FeEAAae1DB031E5F980F5E63fDbb277731e500e);
ipSeed.exit(tokenId);
vm.startPrank(0xE231B4e55fE1D0Afb3e746e64E78eEffB5b599d1);
ipSeed.mint{ value: 0.024599999999999999 ether }(tokenId, 0);
//--- from here it's different; we now are far from being done:
assertEq(ipSeed.collateral(tokenId), 0.5 ether - 1);
//lets close it anyway (gross funding goal is 0.55)
ipSeed.mint{ value: 0.05 ether + 1 }(tokenId, 0);
MarketData memory data = ipSeed.getMarketData(tokenId);
assertEq(uint8(data.state), uint8(2));
assertEq(ipSeed.collateral(tokenId), 0.55 ether);
//--- that wouldve been the final supply when only the parameters would've been chosen correctly
assertEq(ipSeed.totalSupply(tokenId), 7787.97670264404973771 ether);
// lets compute the beneficiary's balance share of "IPTs"
uint256 beneficiaryShare = (
ipSeed.balanceOf(0xdDEf5eb6768E9C37781b32De644d01D0F56054B7, tokenId) * 1_000_000 ether
) / ipSeed.totalSupply(tokenId);
//that's 6.4% of the total IPT supply.
//with the "bad" parameter set above, we computed 67942.161866091749 / 6.79% instead
assertEq(beneficiaryShare, 64201.527442968334535956 ether);
}
//what would've happened if we would've used the correct parameters and just a happy path?
function testBugbashSessionWithHappyPathAndCorrectParameters() public {
uint256 baseSepoliaFork = vm.createFork(vm.envString("BASE_SEPOLIA_RPC_URL"), 13075576);
vm.selectFork(baseSepoliaFork);
uint256 tokenId = 71608873548347908551806579327355212747937605875372970477748660726076739061303;
address sourcer = 0x9F5DeAD94C9861eC9af7E0353cf0a100189c6815;
IPSeed ipSeed = IPSeed(0x1545dAbD87Bd880F4c03Bc4e5BC59d8C7711222d);
LinearCurve curve = LinearCurve(address(0x8cA737E2cdaE1Ceb332bEf7ba9eA711a3a2f8037));
uint256 fundingGoal = 0.55 ether;
uint256 premint = 500 ether;
uint256 slope = curve.computeSlope(fundingGoal, 10_000 ether, premint);
MarketParameters memory betterParameters = MarketParameters({
tokenId: tokenId,
projectId: "catalyst-development://ef062e13-8030-49cf-bd03-bd44564d25ae",
sourcer: sourcer,
beneficiary: payable(0xdDEf5eb6768E9C37781b32De644d01D0F56054B7),
priceCurve: curve,
curveParameters: bytes32(abi.encodePacked(uint128(slope), uint128(premint))),
fundingGoal: uint128(0.55 ether), //<-- this
premint: uint128(premint),
deadline: uint64(block.timestamp + defaultDeadline)
});
vm.startPrank(sourcer);
ipSeed.spawn(betterParameters);
uint256 expectedFinalSupply =
curve.supplyAtCollateral(betterParameters.curveParameters, betterParameters.fundingGoal);
assertEq(expectedFinalSupply, 10_000.000000377909357083 ether);
// lets just buy
vm.startPrank(0x8FF4B0B37EEA9Fa19070D5d78B082E2d75cd4c7D);
ipSeed.mint{ value: 0.1 ether }(tokenId, 0);
//ipSeed.mint{ value: 0.05 ether }(tokenId, 0);
vm.startPrank(0xCAed763276288a8Fe8E274e03f2cdFF0996eAEcb);
ipSeed.mint{ value: 0.2 ether }(tokenId, 0);
vm.startPrank(0x8FeEAAae1DB031E5F980F5E63fDbb277731e500e);
ipSeed.mint{ value: 0.25 ether }(tokenId, 0);
MarketData memory data = ipSeed.getMarketData(tokenId);
assertEq(uint8(data.state), uint8(2));
assertEq(ipSeed.collateral(tokenId), 0.55 ether);
//--- final supply is just great when no one exits
assertEq(ipSeed.totalSupply(tokenId), 10000.0 ether);
// // lets compute the beneficiary's balance share of "IPTs"
uint256 beneficiaryShare = (
ipSeed.balanceOf(0xdDEf5eb6768E9C37781b32De644d01D0F56054B7, tokenId) * 1_000_000 ether
) / ipSeed.totalSupply(tokenId);
//that's **precisely** 5% of the total IPT supply.
assertEq(beneficiaryShare, 50000 ether);
}
}
A replay of the events that happened on this token id (see https://sepolia.basescan.org/address/0x1545dabd87bd880f4c03bc4e5bc59d8c7711222d#events)
Spawn 13075577n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x9F5DeAD94C9861eC9af7E0353cf0a100189c6815 476190476190476190n
Buy 13075689n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28 100000000000000000n 4146139914567699710827n 4646139914567699710827n
Buy 13075707n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x8FF4B0B37EEA9Fa19070D5d78B082E2d75cd4c7D 100000000000000000n 1717387383480449013141n 6363527298048148723968n
Buy 13075739n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x8FF4B0B37EEA9Fa19070D5d78B082E2d75cd4c7D 50000000000000000n 692095514709945056548n 7055622812758093780516n
Buy 13075764n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xCAed763276288a8Fe8E274e03f2cdFF0996eAEcb 200000000000000000n 2239668133999156805424n 9295290946757250585940n
Exit 13075793n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28 95000000000000000n 4146139914567699710827n 5149151032189550875113n
Buy 13075823n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28 50000000000000000n 847197156327963456326n 5996348188517514331439n
Buy 13075878n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x72A960b0F2173af825Ea486a0851dfC375F2501a 31400000000000000n 470866650318724364418n 6467214838836238695857n
Buy 13075924n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x8FeEAAae1DB031E5F980F5E63fDbb277731e500e 21718280000000000n 305035848131676689091n 6772250686967915384948n
Buy 13076000n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xe127A39da6eA2D7b1979372aE973a20baB08A80A 44000000000000000n 576467309822232486350n 7348717996790147871298n
Exit 13076025n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x8FeEAAae1DB031E5F980F5E63fDbb277731e500e 20632366000000000n 305035848131676689091n 7043682148658471182207n
Buy 13076128n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xE231B4e55fE1D0Afb3e746e64E78eEffB5b599d1 24599999999999999n 315518475328997414478n 7359200623987468596685n
Funding Goal Reached 13076163n 71608873548347908551806579327355212747937605875372970477748660726076739061303n
Buy 13076163n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0xB07055Db7Bf811C5b914d60a4a8a5059c8F0Fd28 1n 0n 7359200623987468596685n
Claimed Collateral 13076467n 71608873548347908551806579327355212747937605875372970477748660726076739061303n
Succeeded 13085032n 71608873548347908551806579327355212747937605875372970477748660726076739061303n 0x2854e2ACC2530165a76623B99c2F5b4915484BdC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment