From 7cee65d274ce45907f8598e700c2f0ae194ee6c1 Mon Sep 17 00:00:00 2001 From: nerfZael Date: Mon, 4 Nov 2024 16:31:36 +0100 Subject: [PATCH 1/6] implemented stopAndTransferReserve for migrating to another curve, improved deployment scripts and added tests --- .env.example | 18 +++ deploy/base-sepolia.sh | 26 ++++- deploy/base.sh | 25 +++++ deploy/local.sh | 1 - package.json | 2 +- script/DeployAgentKey.s.sol | 26 ++--- script/HelperConfig.s.sol | 60 +++++----- src/AgentKey.sol | 27 +++++ src/IAgentKey.sol | 2 + test/AgentKey.t.sol | 215 ++++++++++++++++++++++++++++++++---- yarn.lock | 5 +- 11 files changed, 329 insertions(+), 78 deletions(-) create mode 100644 .env.example create mode 100755 deploy/base.sh delete mode 100755 deploy/local.sh diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e4c3a36 --- /dev/null +++ b/.env.example @@ -0,0 +1,18 @@ +FORGE_ACCOUNT= # Only needed if you want to deploy with a forge account instead of a private key +BASESCAN_API_KEY= # Only needed if you want to verify the contract on Basescan + +BASE_TOKEN_NAME= +BASE_TOKEN_SYMBOL= +BASE_BENEFICIARY= +BASE_RPC_URL= +BASE_CONTROL= +BASE_FEE_COLLECTOR= +BASE_PRICE_INCREASE= # Price increase in ether (wei), e.g. 200000000000000 for 0.0002 ETH + +BASE_SEPOLIA_TOKEN_NAME= +BASE_SEPOLIA_TOKEN_SYMBOL= +BASE_SEPOLIA_BENEFICIARY= +BASE_SEPOLIA_RPC_URL= +BASE_SEPOLIA_CONTROL= +BASE_SEPOLIA_FEE_COLLECTOR= +BASE_SEPOLIA_PRICE_INCREASE= # Price increase in ether (wei), e.g. 200000000000000 for 0.0002 ETH \ No newline at end of file diff --git a/deploy/base-sepolia.sh b/deploy/base-sepolia.sh index 01e61f3..1f990b6 100755 --- a/deploy/base-sepolia.sh +++ b/deploy/base-sepolia.sh @@ -1 +1,25 @@ -forge script ./script/DeployAgentKey.s.sol --rpc-url https://sepolia.base.org/ --broadcast --interactives 1 -g 200 --force --verify --verifier-url https://api-sepolia.basescan.org/api --etherscan-api-key $1 \ No newline at end of file +if [[ $1 = "pk" ]]; then + export $(cat .env | xargs) && \ + forge script ./script/DeployAgentKey.s.sol \ + --rpc-url $BASE_SEPOLIA_RPC_URL \ + --broadcast \ + -g 200 \ + --force \ + --verify \ + --verifier-url https://api-sepolia.basescan.org/api \ + --etherscan-api-key $BASESCAN_API_KEY \ + --interactives 1 \ + --slow +else + export $(cat .env | xargs) && \ + forge script ./script/DeployAgentKey.s.sol \ + --rpc-url $BASE_SEPOLIA_RPC_URL \ + --broadcast \ + -g 200 \ + --force \ + --verify \ + --verifier-url https://api-sepolia.basescan.org/api \ + --etherscan-api-key $BASESCAN_API_KEY \ + --account $FORGE_ACCOUNT \ + --slow +fi \ No newline at end of file diff --git a/deploy/base.sh b/deploy/base.sh new file mode 100755 index 0000000..1da4250 --- /dev/null +++ b/deploy/base.sh @@ -0,0 +1,25 @@ +if [[ $1 = "pk" ]]; then + export $(cat .env | xargs) && \ + forge script ./script/DeployAgentKey.s.sol \ + --rpc-url $BASE_RPC_URL \ + --broadcast \ + -g 200 \ + --force \ + --verify \ + --verifier-url https://api.basescan.org/api \ + --etherscan-api-key $BASESCAN_API_KEY \ + --interactives 1 \ + --slow +else + export $(cat .env | xargs) && \ + forge script ./script/DeployAgentKey.s.sol \ + --rpc-url $BASE_RPC_URL \ + --broadcast \ + -g 200 \ + --force \ + --verify \ + --verifier-url https://api.basescan.org/api \ + --etherscan-api-key $BASESCAN_API_KEY \ + --account $FORGE_ACCOUNT \ + --slow +fi \ No newline at end of file diff --git a/deploy/local.sh b/deploy/local.sh deleted file mode 100755 index c4b9fc1..0000000 --- a/deploy/local.sh +++ /dev/null @@ -1 +0,0 @@ -forge script ./script/DeployAgentKey.s.sol --rpc-url http://localhost:8545 --gas-estimate-multiplier 200 --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ No newline at end of file diff --git a/package.json b/package.json index c6bae08..4a7a3cd 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,6 @@ "scripts": {}, "dependencies": {}, "devDependencies": { - "@fairmint/c-org-contracts": "^2.4.24" + "@fairmint/c-org-contracts": "git+https://github.com/Fairmint/c-org#1770aa5d527175a676269ad80f458c46b30456ac" } } diff --git a/script/DeployAgentKey.s.sol b/script/DeployAgentKey.s.sol index d2c3274..a9153cc 100644 --- a/script/DeployAgentKey.s.sol +++ b/script/DeployAgentKey.s.sol @@ -20,25 +20,17 @@ contract DeployAgentKey is Script { HelperConfig.AgentKeyConfig memory config ) public returns (IAgentKey key, address whitelist) { { - uint256 initReserve = 0 ether; - address currencyAddress = address(0); - uint256 initGoal = 0; - uint256 setupFee = 0; - address payable setupFeeRecipient = payable(address(0)); - string memory name = "Agent Keys"; - string memory symbol = "KEYS"; - bytes memory ctorArgs = abi.encode( - initReserve, - currencyAddress, - initGoal, - config.buySlopeNum, - config.buySlopeDen, + 0 ether, // initReserve + address(0), // currencyAddress + 0, // initGoal + 2, // buySlopeNum + 50000000 * config.priceIncrease, // buySlopeDen config.investmentReserveBasisPoints, - setupFee, - setupFeeRecipient, - name, - symbol + 0, // setupFee + payable(address(0)), // setupFeeRecipient + config.name, + config.symbol ); vm.startBroadcast(); diff --git a/script/HelperConfig.s.sol b/script/HelperConfig.s.sol index b187c27..18be90a 100644 --- a/script/HelperConfig.s.sol +++ b/script/HelperConfig.s.sol @@ -4,14 +4,15 @@ pragma solidity ^0.8.13; import {Script, console} from "forge-std/Script.sol"; abstract contract Constants { - uint256 public constant CHAIN_ID_LOCAL = 31337; + uint256 public constant CHAIN_ID_BASE = 8453; uint256 public constant CHAIN_ID_BASE_SEPOLIA = 84532; } contract HelperConfig is Constants, Script { struct AgentKeyConfig { - uint256 buySlopeNum; - uint256 buySlopeDen; + string name; + string symbol; + uint256 priceIncrease; uint256 investmentReserveBasisPoints; uint feeBasisPoints; uint revenueCommitmentBasisPoints; @@ -20,47 +21,44 @@ contract HelperConfig is Constants, Script { address payable feeCollector; } - AgentKeyConfig public localAgentKeyConfig; - mapping (uint256 chainId => AgentKeyConfig) public agentKeyConfigs; - - constructor() { - agentKeyConfigs[CHAIN_ID_LOCAL] = getLocalAnvilConfig(); - agentKeyConfigs[CHAIN_ID_BASE_SEPOLIA] = getBaseSepoliaConfig(); - } - function getConfig() public view returns (AgentKeyConfig memory) { return getConfigByChainId(block.chainid); } function getConfigByChainId(uint256 chainId) private view returns (AgentKeyConfig memory) { - return agentKeyConfigs[chainId]; + if (chainId == CHAIN_ID_BASE_SEPOLIA) { + return getBaseSepoliaConfig(); + } else if (chainId == CHAIN_ID_BASE) { + return getBaseConfig(); + } else { + revert("Unsupported chain id"); + } } - function getLocalAnvilConfig() private pure returns (AgentKeyConfig memory) { + function getBaseConfig() private view returns (AgentKeyConfig memory) { return AgentKeyConfig({ - buySlopeNum: 2, - buySlopeDen: 10000 * 1e18, - investmentReserveBasisPoints: 9500, - // First address derived from mnemonic: test test test test test test test test test test test junk - beneficiary: payable(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266), - // Second address - control: address(0x70997970C51812dc3A010C7d01b50e0d17dc79C8), - // Third address - feeCollector: payable(0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC), - feeBasisPoints: 1000, + name: vm.envString("BASE_TOKEN_NAME"), + symbol: vm.envString("BASE_TOKEN_SYMBOL"), + priceIncrease: vm.envUint("BASE_PRICE_INCREASE"), + investmentReserveBasisPoints: 9000, + beneficiary: payable(vm.envAddress("BASE_BENEFICIARY")), + control: vm.envAddress("BASE_CONTROL"), + feeCollector: payable(vm.envAddress("BASE_FEE_COLLECTOR")), + feeBasisPoints: 5000, revenueCommitmentBasisPoints: 9500 }); } - function getBaseSepoliaConfig() private pure returns (AgentKeyConfig memory) { + function getBaseSepoliaConfig() private view returns (AgentKeyConfig memory) { return AgentKeyConfig({ - buySlopeNum: 2, - buySlopeDen: 10000 * 1e18, - investmentReserveBasisPoints: 9500, - beneficiary: payable(0x857766085629c1d68704989974A968cbdbf2fc3f), - control: 0x857766085629c1d68704989974A968cbdbf2fc3f, - feeCollector: payable(0x857766085629c1d68704989974A968cbdbf2fc3f), - feeBasisPoints: 1000, + name: vm.envString("BASE_SEPOLIA_TOKEN_NAME"), + symbol: vm.envString("BASE_SEPOLIA_TOKEN_SYMBOL"), + priceIncrease: vm.envUint("BASE_SEPOLIA_PRICE_INCREASE"), + investmentReserveBasisPoints: 9000, + beneficiary: payable(vm.envAddress("BASE_SEPOLIA_BENEFICIARY")), + control: vm.envAddress("BASE_SEPOLIA_CONTROL"), + feeCollector: payable(vm.envAddress("BASE_SEPOLIA_FEE_COLLECTOR")), + feeBasisPoints: 5000, revenueCommitmentBasisPoints: 9500 }); } diff --git a/src/AgentKey.sol b/src/AgentKey.sol index e095a9c..24d3f1a 100644 --- a/src/AgentKey.sol +++ b/src/AgentKey.sol @@ -2,8 +2,11 @@ pragma solidity 0.5.17; import {DecentralizedAutonomousTrust} from "@fairmint/contracts/DecentralizedAutonomousTrust.sol"; +import "@openzeppelin/contracts-ethereum-package/contracts/utils/Address.sol"; contract AgentKey is DecentralizedAutonomousTrust { + bool public isStopped; + constructor( uint _initReserve, address _currencyAddress, @@ -29,4 +32,28 @@ contract AgentKey is DecentralizedAutonomousTrust { _symbol ); } + + function stopAndTransferReserve(address payable _recipient) external { + require(msg.sender == beneficiary, "BENEFICIARY_ONLY"); + isStopped = true; + Address.sendValue(_recipient, address(this).balance); + } + + modifier authorizeTransfer( + address _from, + address _to, + uint _value, + bool _isSell + ) // Overrides the modifier in ContinuousOffering + { + if (isStopped) { + revert("Contract is stopped"); + } + if(address(whitelist) != address(0)) + { + // This is not set for the minting of initialReserve + whitelist.authorizeTransfer(_from, _to, _value, _isSell); + } + _; + } } diff --git a/src/IAgentKey.sol b/src/IAgentKey.sol index bb17931..6077415 100644 --- a/src/IAgentKey.sol +++ b/src/IAgentKey.sol @@ -37,4 +37,6 @@ interface IAgentKey { function feeBasisPoints() external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function close() external; + function stopAndTransferReserve(address payable _recipient) external; + function isStopped() external view returns (bool); } \ No newline at end of file diff --git a/test/AgentKey.t.sol b/test/AgentKey.t.sol index 605e5d0..ac55860 100644 --- a/test/AgentKey.t.sol +++ b/test/AgentKey.t.sol @@ -21,11 +21,12 @@ contract AgentKeyTest is Test { (key, whitelist) = keyDeployer.deploy( HelperConfig.AgentKeyConfig({ - buySlopeNum: 2, - buySlopeDen: 10000 * 1e18, - investmentReserveBasisPoints: 9500, - feeBasisPoints: 2000, - revenueCommitmentBasisPoints: 9000, + name: "Agent keys", + symbol: "KEY", + priceIncrease: 0.0002 * 1e18, + investmentReserveBasisPoints: 9000, + feeBasisPoints: 5000, + revenueCommitmentBasisPoints: 9500, beneficiary: payable(beneficiary), control: control, feeCollector: payable(feeCollector) @@ -35,9 +36,9 @@ contract AgentKeyTest is Test { function test_canBuyTokens() public { uint amountToSpend = 1 ether; - uint expectedBeneficiaryFee = 0.04 ether; - uint expectedFeeCollectorFee = 0.01 ether; - uint expectedReserve = 0.95 ether; + uint expectedBeneficiaryFee = 0.05 ether; + uint expectedFeeCollectorFee = 0.05 ether; + uint expectedReserve = 0.9 ether; vm.deal(user, amountToSpend); @@ -58,7 +59,7 @@ contract AgentKeyTest is Test { uint balance = key.balanceOf(user); - assertGt(balance, 0); + assertGe(balance, minBuyAmount); assertEq(key.balanceOf(address(beneficiary)), 0); assertEq(key.balanceOf(address(feeCollector)), 0); @@ -69,7 +70,75 @@ contract AgentKeyTest is Test { assertEq(key.buybackReserve(), expectedReserve); } - function test_curveBehavesAccordingToFormula() public { + function test_curveBehavesAccordingToFormula1() public { + // Initial price is 100 KEY for 1 ETH + // Formula: price = (tokens ** 2) / 2 * buySlopeNum / buySlopeDen + + uint amountToSpend = 600 ether; + + vm.deal(user, amountToSpend); + + assertEq(key.balanceOf(user), 0); + assertEq(beneficiary.balance, 0); + assertEq(feeCollector.balance, 0); + + vm.startPrank(user); + + uint minBuyAmount1 = key.estimateBuyValue(100 ether); + assertEq(minBuyAmount1, 1000 ether); + + key.buy{ + value: 100 ether + }( + user, 100 ether, minBuyAmount1 + ); + } + + function test_curveBehavesAccordingToFormula2() public { + // Initial price is 100 KEY for 1 ETH + // Formula: price = (tokens ** 2) / 2 * buySlopeNum / buySlopeDen + + uint amountToSpend = 600 ether; + + vm.deal(user, amountToSpend); + + assertEq(key.balanceOf(user), 0); + assertEq(beneficiary.balance, 0); + assertEq(feeCollector.balance, 0); + + vm.startPrank(user); + + uint minBuyAmount1 = key.estimateBuyValue(1); + + key.buy{ + value: 1 + }( + user, 1, minBuyAmount1 + ); + + uint balance1 = key.balanceOf(user); + assertEq(balance1, minBuyAmount1); + uint price1 = minBuyAmount1; + + uint minBuyAmount2 = key.estimateBuyValue(1); + uint price2 = minBuyAmount2; + + key.buy{ + value: 1 + }( + user, 1, minBuyAmount2 + ); + + console.logUint(price1); + console.logUint(price2); + + + uint minBuyAmount3 = key.estimateBuyValue(1); + uint price3 = minBuyAmount3; + console.logUint(price3); + } + + function test_curveBehavesAccordingToFormula3() public { // Initial price is 100 KEY for 1 ETH // Formula: price = (tokens ** 2) / 2 * buySlopeNum / buySlopeDen @@ -127,9 +196,9 @@ contract AgentKeyTest is Test { function test_priceIncreasesWhenSupplyIncreases() public { uint amountToSpend = 1 ether; - uint expectedBeneficiaryFee = 0.04 ether; - uint expectedFeeCollectorFee = 0.01 ether; - uint expectedReserve = 0.95 ether; + uint expectedBeneficiaryFee = 0.05 ether; + uint expectedFeeCollectorFee = 0.05 ether; + uint expectedReserve = 0.9 ether; vm.deal(user, amountToSpend); vm.startPrank(user); @@ -222,9 +291,9 @@ contract AgentKeyTest is Test { function test_canSellTokens() public { uint amountToSpend = 1 ether; - uint expectedBeneficiaryFee = 0.04 ether; - uint expectedFeeCollectorFee = 0.01 ether; - uint expectedReserve = 0.95 ether; + uint expectedBeneficiaryFee = 0.05 ether; + uint expectedFeeCollectorFee = 0.05 ether; + uint expectedReserve = 0.9 ether; // Some of the buybackReserve is left over even after selling all tokens // Most likely due to rounding errors or because of the fees uint expectedMaxReserveAfterSell = 0.001 ether; @@ -266,9 +335,9 @@ contract AgentKeyTest is Test { function test_priceDecreasesWhenSupplyDecreases() public { uint amountToSpend = 1 ether; - uint expectedBeneficiaryFee = 0.04 ether; - uint expectedFeeCollectorFee = 0.01 ether; - uint expectedReserve = 0.95 ether; + uint expectedBeneficiaryFee = 0.05 ether; + uint expectedFeeCollectorFee = 0.05 ether; + uint expectedReserve = 0.9 ether; vm.deal(user, amountToSpend); vm.startPrank(user); @@ -322,8 +391,8 @@ contract AgentKeyTest is Test { function test_pay() public { uint amountToPay = 10 ether; - uint revenueFee = amountToPay * 1000 / 10000; // 10% - uint expectedReserve = 9 ether; + uint revenueFee = amountToPay * 5 / 100; // 5% + uint expectedReserve = 9.5 ether; vm.deal(user, amountToPay); @@ -448,7 +517,7 @@ contract AgentKeyTest is Test { payable(control), payable(feeCollector), 0, - 9000, + 9500, 1, 0 ); @@ -461,7 +530,7 @@ contract AgentKeyTest is Test { payable(control), payable(feeCollector), 0, - 9000, + 9500, 1, 0 ); @@ -473,9 +542,107 @@ contract AgentKeyTest is Test { payable(control), payable(feeCollector), 0, - 9000, + 9500, 1, 0 ); } + + function test_contractCanBeStopped() public { + vm.prank(beneficiary); + key.stopAndTransferReserve(payable(recipient)); + assertEq(key.isStopped(), true); + } + + function test_buysAndSellsAreDisabledWhenContractIsStopped() public { + vm.deal(user, 2 ether); + vm.prank(user); + uint minBuyAmount = key.estimateBuyValue(1 ether); + + key.buy{ + value: 1 ether + }( + user, 1 ether, minBuyAmount + ); + + vm.prank(beneficiary); + key.stopAndTransferReserve(payable(recipient)); + + vm.prank(user); + vm.expectRevert("Contract is stopped"); + key.buy{ + value: 1 ether + }( + user, 1 ether, 1 + ); + + vm.prank(user); + vm.expectRevert("PRICE_SLIPPAGE"); // Error is PRICE_SLIPPAGE because the reserve check is done before the stopped check + key.sell( + payable(user), + 1 ether, + 1 + ); + } + + function test_reserveIsTransferredAfterStop() public { + vm.deal(user, 2 ether); + vm.prank(user); + uint minBuyAmount = key.estimateBuyValue(1 ether); + + key.buy{ + value: 1 ether + }( + user, 1 ether, minBuyAmount + ); + + uint256 reserveBefore = key.buybackReserve(); + + assertGt(reserveBefore, 0); + assertEq(address(key).balance, reserveBefore); + + vm.prank(beneficiary); + key.stopAndTransferReserve(payable(recipient)); + + assertEq(key.buybackReserve(), 0); + assertEq(address(key).balance, 0); + + assertEq(recipient.balance, reserveBefore); + } + + function test_transfersAreDisabledWhenContractIsStopped() public { + vm.deal(user, 2 ether); + vm.prank(user); + uint minBuyAmount = key.estimateBuyValue(1 ether); + + key.buy{ + value: 1 ether + }( + user, 1 ether, minBuyAmount + ); + + vm.prank(beneficiary); + key.stopAndTransferReserve(payable(recipient)); + + vm.prank(user); + vm.expectRevert("Contract is stopped"); + key.transfer(makeAddr("new-recipient"), minBuyAmount); + } + + function test_onlyBeneficiaryCanStopTheContract() public { + assertEq(key.isStopped(), false); + + vm.prank(user); + vm.expectRevert("BENEFICIARY_ONLY"); + key.stopAndTransferReserve(payable(recipient)); + + vm.prank(control); + vm.expectRevert("BENEFICIARY_ONLY"); + key.stopAndTransferReserve(payable(recipient)); + + vm.prank(beneficiary); + key.stopAndTransferReserve(payable(recipient)); + + assertEq(key.isStopped(), true); + } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 9c0b472..32bb623 100644 --- a/yarn.lock +++ b/yarn.lock @@ -507,10 +507,9 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@fairmint/c-org-contracts@^2.4.24": +"@fairmint/c-org-contracts@git+https://github.com/Fairmint/c-org#1770aa5d527175a676269ad80f458c46b30456ac": version "2.4.24" - resolved "https://registry.yarnpkg.com/@fairmint/c-org-contracts/-/c-org-contracts-2.4.24.tgz#b41f53e81b4d579c221492a212aa580a97bd6972" - integrity sha512-qvRz2WrdioJdDIe40+deNfOVlYsxTOAkxstHVvcdDGhrsJZwdDTVq0y7jDeFT36OQe0t5z2VFmFZ5qdoCIjC4A== + resolved "git+https://github.com/Fairmint/c-org#1770aa5d527175a676269ad80f458c46b30456ac" dependencies: "@openzeppelin/contracts" "2.5.1" "@openzeppelin/contracts-ethereum-package" "2.5.0" From fe73eb634b7883da5f39b260111ce9b263f42d9b Mon Sep 17 00:00:00 2001 From: nerfZael Date: Mon, 4 Nov 2024 16:40:21 +0100 Subject: [PATCH 2/6] forge format --- script/DeployAgentKey.s.sol | 14 +- script/HelperConfig.s.sol | 6 +- src/AgentKey.sol | 24 ++- src/AgentKeyWhitelist.sol | 14 +- src/IAgentKey.sol | 32 +--- test/AgentKey.t.sol | 364 ++++++++++++------------------------ 6 files changed, 152 insertions(+), 302 deletions(-) diff --git a/script/DeployAgentKey.s.sol b/script/DeployAgentKey.s.sol index a9153cc..ec54bf2 100644 --- a/script/DeployAgentKey.s.sol +++ b/script/DeployAgentKey.s.sol @@ -16,9 +16,7 @@ contract DeployAgentKey is Script { deploy(helper.getConfig()); } - function deploy( - HelperConfig.AgentKeyConfig memory config - ) public returns (IAgentKey key, address whitelist) { + function deploy(HelperConfig.AgentKeyConfig memory config) public returns (IAgentKey key, address whitelist) { { bytes memory ctorArgs = abi.encode( 0 ether, // initReserve @@ -31,17 +29,17 @@ contract DeployAgentKey is Script { payable(address(0)), // setupFeeRecipient config.name, config.symbol - ); + ); vm.startBroadcast(); - + key = IAgentKey(deployCode("AgentKey.sol:AgentKey", ctorArgs)); } - + whitelist = address(new AgentKeyWhitelist()); - + key.updateConfig( - whitelist, + whitelist, config.beneficiary, config.control, config.feeCollector, diff --git a/script/HelperConfig.s.sol b/script/HelperConfig.s.sol index 18be90a..8b8089d 100644 --- a/script/HelperConfig.s.sol +++ b/script/HelperConfig.s.sol @@ -14,8 +14,8 @@ contract HelperConfig is Constants, Script { string symbol; uint256 priceIncrease; uint256 investmentReserveBasisPoints; - uint feeBasisPoints; - uint revenueCommitmentBasisPoints; + uint256 feeBasisPoints; + uint256 revenueCommitmentBasisPoints; address payable beneficiary; address control; address payable feeCollector; @@ -62,4 +62,4 @@ contract HelperConfig is Constants, Script { revenueCommitmentBasisPoints: 9500 }); } -} \ No newline at end of file +} diff --git a/src/AgentKey.sol b/src/AgentKey.sol index 24d3f1a..a8af28c 100644 --- a/src/AgentKey.sol +++ b/src/AgentKey.sol @@ -8,13 +8,13 @@ contract AgentKey is DecentralizedAutonomousTrust { bool public isStopped; constructor( - uint _initReserve, + uint256 _initReserve, address _currencyAddress, - uint _initGoal, - uint _buySlopeNum, - uint _buySlopeDen, - uint _investmentReserveBasisPoints, - uint _setupFee, + uint256 _initGoal, + uint256 _buySlopeNum, + uint256 _buySlopeDen, + uint256 _investmentReserveBasisPoints, + uint256 _setupFee, address payable _setupFeeRecipient, string memory _name, string memory _symbol @@ -29,7 +29,7 @@ contract AgentKey is DecentralizedAutonomousTrust { _setupFee, _setupFeeRecipient, _name, - _symbol + _symbol ); } @@ -42,15 +42,13 @@ contract AgentKey is DecentralizedAutonomousTrust { modifier authorizeTransfer( address _from, address _to, - uint _value, - bool _isSell - ) // Overrides the modifier in ContinuousOffering - { + uint256 _value, + bool _isSell // Overrides the modifier in ContinuousOffering + ) { if (isStopped) { revert("Contract is stopped"); } - if(address(whitelist) != address(0)) - { + if (address(whitelist) != address(0)) { // This is not set for the minting of initialReserve whitelist.authorizeTransfer(_from, _to, _value, _isSell); } diff --git a/src/AgentKeyWhitelist.sol b/src/AgentKeyWhitelist.sol index 6ea9fec..7070ad0 100644 --- a/src/AgentKeyWhitelist.sol +++ b/src/AgentKeyWhitelist.sol @@ -2,15 +2,7 @@ pragma solidity ^0.8.13; contract AgentKeyWhitelist { - function authorizeTransfer( - address _from, - address _to, - uint, - bool - ) external pure { - require( - _from == address(0) || _to == address(0), - "TRANSFERS_DISABLED" - ); + function authorizeTransfer(address _from, address _to, uint256, bool) external pure { + require(_from == address(0) || _to == address(0), "TRANSFERS_DISABLED"); } -} \ No newline at end of file +} diff --git a/src/IAgentKey.sol b/src/IAgentKey.sol index 6077415..60edebb 100644 --- a/src/IAgentKey.sol +++ b/src/IAgentKey.sol @@ -3,22 +3,10 @@ pragma solidity ^0.8.13; interface IAgentKey { function approve(address spender, uint256 amount) external returns (bool); - function buy( - address _to, - uint256 _currencyValue, - uint256 _minTokensBought - ) external payable; - function sell( - address payable _to, - uint _quantityToSell, - uint _minCurrencyReturned - ) external; - function estimateBuyValue( - uint256 _currencyValue - ) external view returns (uint256); - function estimateSellValue( - uint _quantityToSell - ) external view returns(uint256); + function buy(address _to, uint256 _currencyValue, uint256 _minTokensBought) external payable; + function sell(address payable _to, uint256 _quantityToSell, uint256 _minCurrencyReturned) external; + function estimateBuyValue(uint256 _currencyValue) external view returns (uint256); + function estimateSellValue(uint256 _quantityToSell) external view returns (uint256); function balanceOf(address _owner) external view returns (uint256); function state() external view returns (uint256); function updateConfig( @@ -26,12 +14,12 @@ interface IAgentKey { address payable _beneficiary, address _control, address payable _feeCollector, - uint _feeBasisPoints, - uint _revenueCommitmentBasisPoints, - uint _minInvestment, - uint _minDuration + uint256 _feeBasisPoints, + uint256 _revenueCommitmentBasisPoints, + uint256 _minInvestment, + uint256 _minDuration ) external; - function pay(uint _currencyValue) external payable; + function pay(uint256 _currencyValue) external payable; function totalSupply() external view returns (uint256); function buybackReserve() external view returns (uint256); function feeBasisPoints() external view returns (uint256); @@ -39,4 +27,4 @@ interface IAgentKey { function close() external; function stopAndTransferReserve(address payable _recipient) external; function isStopped() external view returns (bool); -} \ No newline at end of file +} diff --git a/test/AgentKey.t.sol b/test/AgentKey.t.sol index ac55860..fd0b481 100644 --- a/test/AgentKey.t.sol +++ b/test/AgentKey.t.sol @@ -16,7 +16,7 @@ contract AgentKeyTest is Test { address user = makeAddr("user"); address recipient = makeAddr("recipient"); - function setUp() public { + function setUp() public { DeployAgentKey keyDeployer = new DeployAgentKey(); (key, whitelist) = keyDeployer.deploy( @@ -35,10 +35,10 @@ contract AgentKeyTest is Test { } function test_canBuyTokens() public { - uint amountToSpend = 1 ether; - uint expectedBeneficiaryFee = 0.05 ether; - uint expectedFeeCollectorFee = 0.05 ether; - uint expectedReserve = 0.9 ether; + uint256 amountToSpend = 1 ether; + uint256 expectedBeneficiaryFee = 0.05 ether; + uint256 expectedFeeCollectorFee = 0.05 ether; + uint256 expectedReserve = 0.9 ether; vm.deal(user, amountToSpend); @@ -48,16 +48,12 @@ contract AgentKeyTest is Test { vm.startPrank(user); - uint minBuyAmount = key.estimateBuyValue(amountToSpend); + uint256 minBuyAmount = key.estimateBuyValue(amountToSpend); assertGt(minBuyAmount, 0); - - key.buy{ - value: amountToSpend - }( - user, amountToSpend, minBuyAmount - ); - uint balance = key.balanceOf(user); + key.buy{value: amountToSpend}(user, amountToSpend, minBuyAmount); + + uint256 balance = key.balanceOf(user); assertGe(balance, minBuyAmount); assertEq(key.balanceOf(address(beneficiary)), 0); @@ -74,8 +70,8 @@ contract AgentKeyTest is Test { // Initial price is 100 KEY for 1 ETH // Formula: price = (tokens ** 2) / 2 * buySlopeNum / buySlopeDen - uint amountToSpend = 600 ether; - + uint256 amountToSpend = 600 ether; + vm.deal(user, amountToSpend); assertEq(key.balanceOf(user), 0); @@ -84,22 +80,18 @@ contract AgentKeyTest is Test { vm.startPrank(user); - uint minBuyAmount1 = key.estimateBuyValue(100 ether); + uint256 minBuyAmount1 = key.estimateBuyValue(100 ether); assertEq(minBuyAmount1, 1000 ether); - - key.buy{ - value: 100 ether - }( - user, 100 ether, minBuyAmount1 - ); + + key.buy{value: 100 ether}(user, 100 ether, minBuyAmount1); } function test_curveBehavesAccordingToFormula2() public { // Initial price is 100 KEY for 1 ETH // Formula: price = (tokens ** 2) / 2 * buySlopeNum / buySlopeDen - uint amountToSpend = 600 ether; - + uint256 amountToSpend = 600 ether; + vm.deal(user, amountToSpend); assertEq(key.balanceOf(user), 0); @@ -108,42 +100,33 @@ contract AgentKeyTest is Test { vm.startPrank(user); - uint minBuyAmount1 = key.estimateBuyValue(1); - - key.buy{ - value: 1 - }( - user, 1, minBuyAmount1 - ); + uint256 minBuyAmount1 = key.estimateBuyValue(1); + + key.buy{value: 1}(user, 1, minBuyAmount1); - uint balance1 = key.balanceOf(user); + uint256 balance1 = key.balanceOf(user); assertEq(balance1, minBuyAmount1); - uint price1 = minBuyAmount1; + uint256 price1 = minBuyAmount1; - uint minBuyAmount2 = key.estimateBuyValue(1); - uint price2 = minBuyAmount2; + uint256 minBuyAmount2 = key.estimateBuyValue(1); + uint256 price2 = minBuyAmount2; - key.buy{ - value: 1 - }( - user, 1, minBuyAmount2 - ); + key.buy{value: 1}(user, 1, minBuyAmount2); console.logUint(price1); - console.logUint(price2); + console.logUint(price2); - - uint minBuyAmount3 = key.estimateBuyValue(1); - uint price3 = minBuyAmount3; - console.logUint(price3); + uint256 minBuyAmount3 = key.estimateBuyValue(1); + uint256 price3 = minBuyAmount3; + console.logUint(price3); } function test_curveBehavesAccordingToFormula3() public { // Initial price is 100 KEY for 1 ETH // Formula: price = (tokens ** 2) / 2 * buySlopeNum / buySlopeDen - uint amountToSpend = 600 ether; - + uint256 amountToSpend = 600 ether; + vm.deal(user, amountToSpend); assertEq(key.balanceOf(user), 0); @@ -152,42 +135,30 @@ contract AgentKeyTest is Test { vm.startPrank(user); - uint minBuyAmount1 = key.estimateBuyValue(100 ether); + uint256 minBuyAmount1 = key.estimateBuyValue(100 ether); assertEq(minBuyAmount1, 1000 ether); - - key.buy{ - value: 100 ether - }( - user, 100 ether, minBuyAmount1 - ); - uint balance1 = key.balanceOf(user); + key.buy{value: 100 ether}(user, 100 ether, minBuyAmount1); + + uint256 balance1 = key.balanceOf(user); assertEq(balance1, 1000 ether); - uint minBuyAmount2 = key.estimateBuyValue(200 ether); + uint256 minBuyAmount2 = key.estimateBuyValue(200 ether); assertEq(minBuyAmount2, 732.050807568877293527 ether); - key.buy{ - value: 200 ether - }( - user, 200 ether, minBuyAmount2 - ); + key.buy{value: 200 ether}(user, 200 ether, minBuyAmount2); - uint balance2 = key.balanceOf(user); + uint256 balance2 = key.balanceOf(user); assertEq(balance2, 1732.050807568877293527 ether); - uint minBuyAmount3 = key.estimateBuyValue(300 ether); + uint256 minBuyAmount3 = key.estimateBuyValue(300 ether); assertEq(minBuyAmount3, 717.438935214300804669 ether); - key.buy{ - value: 300 ether - }( - user, 300 ether, minBuyAmount3 - ); + key.buy{value: 300 ether}(user, 300 ether, minBuyAmount3); - uint balance3 = key.balanceOf(user); + uint256 balance3 = key.balanceOf(user); assertEq(balance3, 2449.489742783178098196 ether); @@ -195,24 +166,20 @@ contract AgentKeyTest is Test { } function test_priceIncreasesWhenSupplyIncreases() public { - uint amountToSpend = 1 ether; - uint expectedBeneficiaryFee = 0.05 ether; - uint expectedFeeCollectorFee = 0.05 ether; - uint expectedReserve = 0.9 ether; + uint256 amountToSpend = 1 ether; + uint256 expectedBeneficiaryFee = 0.05 ether; + uint256 expectedFeeCollectorFee = 0.05 ether; + uint256 expectedReserve = 0.9 ether; vm.deal(user, amountToSpend); vm.startPrank(user); - uint minBuyAmount1 = key.estimateBuyValue(amountToSpend / 2); + uint256 minBuyAmount1 = key.estimateBuyValue(amountToSpend / 2); assertGt(minBuyAmount1, 0); - key.buy{ - value: amountToSpend / 2 - }( - user, amountToSpend / 2, minBuyAmount1 - ); + key.buy{value: amountToSpend / 2}(user, amountToSpend / 2, minBuyAmount1); - uint balance1 = key.balanceOf(user); + uint256 balance1 = key.balanceOf(user); assertGt(balance1, 0); @@ -222,17 +189,13 @@ contract AgentKeyTest is Test { assertEq(key.totalSupply(), balance1); assertEq(key.buybackReserve(), expectedReserve / 2); - uint minBuyAmount2 = key.estimateBuyValue(amountToSpend / 2); + uint256 minBuyAmount2 = key.estimateBuyValue(amountToSpend / 2); assertGt(minBuyAmount2, 0); assertLt(minBuyAmount2, minBuyAmount1); - key.buy{ - value: amountToSpend / 2 - }( - user, amountToSpend / 2, minBuyAmount2 - ); + key.buy{value: amountToSpend / 2}(user, amountToSpend / 2, minBuyAmount2); - uint balance2 = key.balanceOf(user); + uint256 balance2 = key.balanceOf(user); assertGt(balance2, 0); assertGt(balance2 - balance1, 0); @@ -247,21 +210,17 @@ contract AgentKeyTest is Test { } function test_transfersAreDisabled() public { - uint amountToSpend = 1 ether; - + uint256 amountToSpend = 1 ether; + vm.deal(user, amountToSpend); - - uint minBuyAmount = key.estimateBuyValue(amountToSpend); + + uint256 minBuyAmount = key.estimateBuyValue(amountToSpend); assertGt(minBuyAmount, 0); vm.startPrank(user); - key.buy{ - value: amountToSpend - }( - user, amountToSpend, 1 - ); + key.buy{value: amountToSpend}(user, amountToSpend, 1); - uint userBalance = key.balanceOf(user); + uint256 userBalance = key.balanceOf(user); assertGe(userBalance, minBuyAmount); vm.expectRevert("TRANSFERS_DISABLED"); @@ -272,13 +231,9 @@ contract AgentKeyTest is Test { vm.deal(beneficiary, amountToSpend); vm.startPrank(beneficiary); - key.buy{ - value: amountToSpend - }( - beneficiary, amountToSpend, 1 - ); + key.buy{value: amountToSpend}(beneficiary, amountToSpend, 1); - uint beneficiaryBalance = key.balanceOf(beneficiary); + uint256 beneficiaryBalance = key.balanceOf(beneficiary); assertGe(beneficiaryBalance, key.estimateBuyValue(amountToSpend)); vm.expectRevert("TRANSFERS_DISABLED"); @@ -290,27 +245,23 @@ contract AgentKeyTest is Test { } function test_canSellTokens() public { - uint amountToSpend = 1 ether; - uint expectedBeneficiaryFee = 0.05 ether; - uint expectedFeeCollectorFee = 0.05 ether; - uint expectedReserve = 0.9 ether; + uint256 amountToSpend = 1 ether; + uint256 expectedBeneficiaryFee = 0.05 ether; + uint256 expectedFeeCollectorFee = 0.05 ether; + uint256 expectedReserve = 0.9 ether; // Some of the buybackReserve is left over even after selling all tokens // Most likely due to rounding errors or because of the fees - uint expectedMaxReserveAfterSell = 0.001 ether; + uint256 expectedMaxReserveAfterSell = 0.001 ether; vm.deal(user, amountToSpend); - - uint minBuyAmount = key.estimateBuyValue(amountToSpend); + + uint256 minBuyAmount = key.estimateBuyValue(amountToSpend); assertGt(minBuyAmount, 0); vm.startPrank(user); - key.buy{ - value: amountToSpend - }( - user, amountToSpend, minBuyAmount - ); + key.buy{value: amountToSpend}(user, amountToSpend, minBuyAmount); - uint balance = key.balanceOf(user); + uint256 balance = key.balanceOf(user); assertGt(balance, 0); @@ -318,11 +269,7 @@ contract AgentKeyTest is Test { assertEq(feeCollector.balance, expectedFeeCollectorFee); assertEq(key.buybackReserve(), expectedReserve); - key.sell( - payable(user), - balance, - 1 - ); + key.sell(payable(user), balance, 1); assertEq(key.balanceOf(user), 0); @@ -334,24 +281,20 @@ contract AgentKeyTest is Test { } function test_priceDecreasesWhenSupplyDecreases() public { - uint amountToSpend = 1 ether; - uint expectedBeneficiaryFee = 0.05 ether; - uint expectedFeeCollectorFee = 0.05 ether; - uint expectedReserve = 0.9 ether; + uint256 amountToSpend = 1 ether; + uint256 expectedBeneficiaryFee = 0.05 ether; + uint256 expectedFeeCollectorFee = 0.05 ether; + uint256 expectedReserve = 0.9 ether; vm.deal(user, amountToSpend); vm.startPrank(user); - uint minBuyAmount1 = key.estimateBuyValue(amountToSpend / 2); + uint256 minBuyAmount1 = key.estimateBuyValue(amountToSpend / 2); assertGt(minBuyAmount1, 0); - - key.buy{ - value: amountToSpend / 2 - }( - user, amountToSpend / 2, minBuyAmount1 - ); - uint balance1 = key.balanceOf(user); + key.buy{value: amountToSpend / 2}(user, amountToSpend / 2, minBuyAmount1); + + uint256 balance1 = key.balanceOf(user); assertGt(balance1, 0); @@ -361,38 +304,30 @@ contract AgentKeyTest is Test { assertEq(key.totalSupply(), balance1); assertEq(key.buybackReserve(), expectedReserve / 2); - uint minBuyAmount2 = key.estimateBuyValue(amountToSpend / 2); + uint256 minBuyAmount2 = key.estimateBuyValue(amountToSpend / 2); assertGt(minBuyAmount2, 0); assertLt(minBuyAmount2, minBuyAmount1); - key.buy{ - value: amountToSpend / 2 - }( - user, amountToSpend / 2, minBuyAmount2 - ); + key.buy{value: amountToSpend / 2}(user, amountToSpend / 2, minBuyAmount2); - uint balance2 = key.balanceOf(user); + uint256 balance2 = key.balanceOf(user); - uint minBuyAmount3 = key.estimateBuyValue(amountToSpend / 2); + uint256 minBuyAmount3 = key.estimateBuyValue(amountToSpend / 2); assertLt(minBuyAmount3, minBuyAmount2); - key.sell( - payable(user), - balance2 - balance1, - 1 - ); + key.sell(payable(user), balance2 - balance1, 1); assertEq(key.balanceOf(user), balance1); assertEq(key.totalSupply(), balance1); - uint minBuyAmount4 = key.estimateBuyValue(amountToSpend / 2); + uint256 minBuyAmount4 = key.estimateBuyValue(amountToSpend / 2); assertGt(minBuyAmount4, minBuyAmount3); } function test_pay() public { - uint amountToPay = 10 ether; - uint revenueFee = amountToPay * 5 / 100; // 5% - uint expectedReserve = 9.5 ether; + uint256 amountToPay = 10 ether; + uint256 revenueFee = amountToPay * 5 / 100; // 5% + uint256 expectedReserve = 9.5 ether; vm.deal(user, amountToPay); @@ -405,10 +340,8 @@ contract AgentKeyTest is Test { assertEq(feeCollector.balance, 0); vm.startPrank(user); - key.pay{ - value: amountToPay - }(amountToPay); - + key.pay{value: amountToPay}(amountToPay); + assertEq(key.totalSupply(), 0); assertEq(key.buybackReserve(), expectedReserve); @@ -418,8 +351,8 @@ contract AgentKeyTest is Test { } function test_payByTransfer() public { - uint amountToPay = 10 ether; - uint expectedReserve = amountToPay; + uint256 amountToPay = 10 ether; + uint256 expectedReserve = amountToPay; vm.deal(user, amountToPay); @@ -433,7 +366,7 @@ contract AgentKeyTest is Test { vm.startPrank(user); payable(address(key)).transfer(amountToPay); - + assertEq(key.totalSupply(), 0); assertEq(key.buybackReserve(), expectedReserve); @@ -442,56 +375,44 @@ contract AgentKeyTest is Test { } function test_sellPriceIncreasesAfterPay() public { - uint amountForBuy = 1 ether; - uint amountToPay = 10 ether; + uint256 amountForBuy = 1 ether; + uint256 amountToPay = 10 ether; vm.deal(user, amountForBuy + amountToPay); vm.startPrank(user); - uint minBuyAmount = key.estimateBuyValue(amountForBuy); + uint256 minBuyAmount = key.estimateBuyValue(amountForBuy); assertGt(minBuyAmount, 0); - - key.buy{ - value: amountForBuy - }( - user, amountForBuy, minBuyAmount - ); - uint minSellAmount = key.estimateSellValue(minBuyAmount); + key.buy{value: amountForBuy}(user, amountForBuy, minBuyAmount); + + uint256 minSellAmount = key.estimateSellValue(minBuyAmount); assertGt(minSellAmount, 0); - key.pay{ - value: amountToPay - }(amountToPay); + key.pay{value: amountToPay}(amountToPay); - uint minSellAmountAfterPay = key.estimateSellValue(minBuyAmount); + uint256 minSellAmountAfterPay = key.estimateSellValue(minBuyAmount); assertGt(minSellAmountAfterPay, minSellAmount); } - function test_buyPriceRemainsSameAfterPay() public { - uint amountForBuy = 1 ether; - uint amountToPay = 10 ether; + function test_buyPriceRemainsSameAfterPay() public { + uint256 amountForBuy = 1 ether; + uint256 amountToPay = 10 ether; vm.deal(user, amountForBuy + amountToPay); vm.startPrank(user); - uint minBuyAmount = key.estimateBuyValue(amountForBuy); + uint256 minBuyAmount = key.estimateBuyValue(amountForBuy); assertGt(minBuyAmount, 0); - key.buy{ - value: amountForBuy - }( - user, amountForBuy, minBuyAmount - ); + key.buy{value: amountForBuy}(user, amountForBuy, minBuyAmount); - uint minBuyAmountBeforePay = key.estimateBuyValue(amountForBuy); + uint256 minBuyAmountBeforePay = key.estimateBuyValue(amountForBuy); assertGt(minBuyAmountBeforePay, 0); - key.pay{ - value: amountToPay - }(amountToPay); + key.pay{value: amountToPay}(amountToPay); - uint minBuyAmountAfterPay = key.estimateBuyValue(amountForBuy); + uint256 minBuyAmountAfterPay = key.estimateBuyValue(amountForBuy); assertEq(minBuyAmountAfterPay, minBuyAmountBeforePay); } @@ -511,41 +432,14 @@ contract AgentKeyTest is Test { function test_onlyControlCanUpdateConfig() public { vm.prank(user); vm.expectRevert("CONTROL_ONLY"); - key.updateConfig( - whitelist, - payable(beneficiary), - payable(control), - payable(feeCollector), - 0, - 9500, - 1, - 0 - ); + key.updateConfig(whitelist, payable(beneficiary), payable(control), payable(feeCollector), 0, 9500, 1, 0); vm.prank(beneficiary); vm.expectRevert("CONTROL_ONLY"); - key.updateConfig( - whitelist, - payable(beneficiary), - payable(control), - payable(feeCollector), - 0, - 9500, - 1, - 0 - ); + key.updateConfig(whitelist, payable(beneficiary), payable(control), payable(feeCollector), 0, 9500, 1, 0); vm.prank(control); - key.updateConfig( - whitelist, - payable(beneficiary), - payable(control), - payable(feeCollector), - 0, - 9500, - 1, - 0 - ); + key.updateConfig(whitelist, payable(beneficiary), payable(control), payable(feeCollector), 0, 9500, 1, 0); } function test_contractCanBeStopped() public { @@ -557,44 +451,28 @@ contract AgentKeyTest is Test { function test_buysAndSellsAreDisabledWhenContractIsStopped() public { vm.deal(user, 2 ether); vm.prank(user); - uint minBuyAmount = key.estimateBuyValue(1 ether); + uint256 minBuyAmount = key.estimateBuyValue(1 ether); - key.buy{ - value: 1 ether - }( - user, 1 ether, minBuyAmount - ); + key.buy{value: 1 ether}(user, 1 ether, minBuyAmount); vm.prank(beneficiary); key.stopAndTransferReserve(payable(recipient)); vm.prank(user); vm.expectRevert("Contract is stopped"); - key.buy{ - value: 1 ether - }( - user, 1 ether, 1 - ); + key.buy{value: 1 ether}(user, 1 ether, 1); vm.prank(user); vm.expectRevert("PRICE_SLIPPAGE"); // Error is PRICE_SLIPPAGE because the reserve check is done before the stopped check - key.sell( - payable(user), - 1 ether, - 1 - ); + key.sell(payable(user), 1 ether, 1); } function test_reserveIsTransferredAfterStop() public { vm.deal(user, 2 ether); vm.prank(user); - uint minBuyAmount = key.estimateBuyValue(1 ether); + uint256 minBuyAmount = key.estimateBuyValue(1 ether); - key.buy{ - value: 1 ether - }( - user, 1 ether, minBuyAmount - ); + key.buy{value: 1 ether}(user, 1 ether, minBuyAmount); uint256 reserveBefore = key.buybackReserve(); @@ -613,13 +491,9 @@ contract AgentKeyTest is Test { function test_transfersAreDisabledWhenContractIsStopped() public { vm.deal(user, 2 ether); vm.prank(user); - uint minBuyAmount = key.estimateBuyValue(1 ether); + uint256 minBuyAmount = key.estimateBuyValue(1 ether); - key.buy{ - value: 1 ether - }( - user, 1 ether, minBuyAmount - ); + key.buy{value: 1 ether}(user, 1 ether, minBuyAmount); vm.prank(beneficiary); key.stopAndTransferReserve(payable(recipient)); @@ -631,7 +505,7 @@ contract AgentKeyTest is Test { function test_onlyBeneficiaryCanStopTheContract() public { assertEq(key.isStopped(), false); - + vm.prank(user); vm.expectRevert("BENEFICIARY_ONLY"); key.stopAndTransferReserve(payable(recipient)); @@ -645,4 +519,4 @@ contract AgentKeyTest is Test { assertEq(key.isStopped(), true); } -} \ No newline at end of file +} From 9e0ea828b14f8fb76ecc4c0e4005f1b0a4cd481b Mon Sep 17 00:00:00 2001 From: nerfZael Date: Mon, 4 Nov 2024 19:04:19 +0100 Subject: [PATCH 3/6] removed temp test --- test/AgentKey.t.sol | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/test/AgentKey.t.sol b/test/AgentKey.t.sol index fd0b481..d76eaad 100644 --- a/test/AgentKey.t.sol +++ b/test/AgentKey.t.sol @@ -100,41 +100,6 @@ contract AgentKeyTest is Test { vm.startPrank(user); - uint256 minBuyAmount1 = key.estimateBuyValue(1); - - key.buy{value: 1}(user, 1, minBuyAmount1); - - uint256 balance1 = key.balanceOf(user); - assertEq(balance1, minBuyAmount1); - uint256 price1 = minBuyAmount1; - - uint256 minBuyAmount2 = key.estimateBuyValue(1); - uint256 price2 = minBuyAmount2; - - key.buy{value: 1}(user, 1, minBuyAmount2); - - console.logUint(price1); - console.logUint(price2); - - uint256 minBuyAmount3 = key.estimateBuyValue(1); - uint256 price3 = minBuyAmount3; - console.logUint(price3); - } - - function test_curveBehavesAccordingToFormula3() public { - // Initial price is 100 KEY for 1 ETH - // Formula: price = (tokens ** 2) / 2 * buySlopeNum / buySlopeDen - - uint256 amountToSpend = 600 ether; - - vm.deal(user, amountToSpend); - - assertEq(key.balanceOf(user), 0); - assertEq(beneficiary.balance, 0); - assertEq(feeCollector.balance, 0); - - vm.startPrank(user); - uint256 minBuyAmount1 = key.estimateBuyValue(100 ether); assertEq(minBuyAmount1, 1000 ether); From 3361d56bafac55992d6efb1cab6e5847615f836d Mon Sep 17 00:00:00 2001 From: nerfZael Date: Mon, 4 Nov 2024 19:22:54 +0100 Subject: [PATCH 4/6] added comments to some methods --- src/AgentKey.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AgentKey.sol b/src/AgentKey.sol index a8af28c..ff19402 100644 --- a/src/AgentKey.sol +++ b/src/AgentKey.sol @@ -33,17 +33,19 @@ contract AgentKey is DecentralizedAutonomousTrust { ); } + /// @notice Stops the contract and transfers the reserve to the recipient. To be used in case of a migration to a new contract. function stopAndTransferReserve(address payable _recipient) external { require(msg.sender == beneficiary, "BENEFICIARY_ONLY"); isStopped = true; Address.sendValue(_recipient, address(this).balance); } + /// @dev Overrides the modifier in ContinuousOffering modifier authorizeTransfer( address _from, address _to, uint256 _value, - bool _isSell // Overrides the modifier in ContinuousOffering + bool _isSell ) { if (isStopped) { revert("Contract is stopped"); From 1d79b74b09f0e8737fc4bcad4f49a062de3f407f Mon Sep 17 00:00:00 2001 From: nerfZael Date: Mon, 4 Nov 2024 19:33:47 +0100 Subject: [PATCH 5/6] added comments --- script/DeployAgentKey.s.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script/DeployAgentKey.s.sol b/script/DeployAgentKey.s.sol index ec54bf2..505870c 100644 --- a/script/DeployAgentKey.s.sol +++ b/script/DeployAgentKey.s.sol @@ -18,6 +18,7 @@ contract DeployAgentKey is Script { function deploy(HelperConfig.AgentKeyConfig memory config) public returns (IAgentKey key, address whitelist) { { + // buySlopeNum and buySlopeDen are used for the formula in: https://github.com/Fairmint/c-org/blob/781d1ed8d70d733eed57c5e7fff8931b096de0e9/contracts/ContinuousOffering.sol#L495 bytes memory ctorArgs = abi.encode( 0 ether, // initReserve address(0), // currencyAddress @@ -45,8 +46,8 @@ contract DeployAgentKey is Script { config.feeCollector, config.feeBasisPoints, config.revenueCommitmentBasisPoints, - 1, - 0 + 1, // minInvestment + 0 // minDuration ); vm.stopBroadcast(); From d06a23211dc5601f9271ff6714da380384dafdec Mon Sep 17 00:00:00 2001 From: nerfZael Date: Mon, 4 Nov 2024 19:58:34 +0100 Subject: [PATCH 6/6] added comment --- test/AgentKey.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/AgentKey.t.sol b/test/AgentKey.t.sol index d76eaad..ee9e5e5 100644 --- a/test/AgentKey.t.sol +++ b/test/AgentKey.t.sol @@ -81,7 +81,7 @@ contract AgentKeyTest is Test { vm.startPrank(user); uint256 minBuyAmount1 = key.estimateBuyValue(100 ether); - assertEq(minBuyAmount1, 1000 ether); + assertEq(minBuyAmount1, 1000 ether); // 1000 KEY tokens for 100 ETH key.buy{value: 100 ether}(user, 100 ether, minBuyAmount1); }