Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration implementation and deployment improvements #1

Merged
merged 7 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FORGE_ACCOUNT=<NAME-OF-FORGE-ACCOUNT-IF-NOT-USING-PK> # Only needed if you want to deploy with a forge account instead of a private key
BASESCAN_API_KEY=<BASESCAN-API-KEY> # Only needed if you want to verify the contract on Basescan

BASE_TOKEN_NAME=<TOKEN-NAME>
BASE_TOKEN_SYMBOL=<TOKEN-SYMBOL>
BASE_BENEFICIARY=<BENEFICIARY>
BASE_RPC_URL=<RPC-URL-OF-BASE-NETWORK>
BASE_CONTROL=<CONTROL>
BASE_FEE_COLLECTOR=<FEE-COLLECTOR>
BASE_PRICE_INCREASE=<PRICE-INCREASE> # Price increase in ether (wei), e.g. 200000000000000 for 0.0002 ETH

BASE_SEPOLIA_TOKEN_NAME=<TOKEN-NAME>
BASE_SEPOLIA_TOKEN_SYMBOL=<TOKEN-SYMBOL>
BASE_SEPOLIA_BENEFICIARY=<BENEFICIARY>
BASE_SEPOLIA_RPC_URL=<RPC-URL-OF-BASE-SEPOLIA-NETWORK>
BASE_SEPOLIA_CONTROL=<CONTROL>
BASE_SEPOLIA_FEE_COLLECTOR=<FEE-COLLECTOR>
BASE_SEPOLIA_PRICE_INCREASE=<PRICE-INCREASE> # Price increase in ether (wei), e.g. 200000000000000 for 0.0002 ETH
26 changes: 25 additions & 1 deletion deploy/base-sepolia.sh
Original file line number Diff line number Diff line change
@@ -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
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
25 changes: 25 additions & 0 deletions deploy/base.sh
Original file line number Diff line number Diff line change
@@ -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
1 change: 0 additions & 1 deletion deploy/local.sh

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
45 changes: 18 additions & 27 deletions script/DeployAgentKey.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,38 @@ 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) {
{
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";

// 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(
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();

key = IAgentKey(deployCode("AgentKey.sol:AgentKey", ctorArgs));
}

whitelist = address(new AgentKeyWhitelist());

key.updateConfig(
whitelist,
whitelist,
config.beneficiary,
config.control,
config.feeCollector,
config.feeBasisPoints,
config.revenueCommitmentBasisPoints,
1,
0
1, // minInvestment
0 // minDuration
);

vm.stopBroadcast();
Expand Down
66 changes: 32 additions & 34 deletions script/HelperConfig.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,62 @@ 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;
uint256 feeBasisPoints;
uint256 revenueCommitmentBasisPoints;
address payable beneficiary;
address control;
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
});
}
}
}
41 changes: 34 additions & 7 deletions src/AgentKey.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
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,
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
Expand All @@ -26,7 +29,31 @@ contract AgentKey is DecentralizedAutonomousTrust {
_setupFee,
_setupFeeRecipient,
_name,
_symbol
_symbol
);
}

/// @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
) {
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);
}
_;
}
}
14 changes: 3 additions & 11 deletions src/AgentKeyWhitelist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
}
34 changes: 12 additions & 22 deletions src/IAgentKey.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,28 @@ 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(
address _whitelistAddress,
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);
function transfer(address recipient, uint256 amount) external returns (bool);
function close() external;
}
function stopAndTransferReserve(address payable _recipient) external;
function isStopped() external view returns (bool);
}
Loading
Loading