Skip to content

Commit

Permalink
optimism deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
technophile-04 committed Nov 27, 2024
1 parent 384c94f commit 3056005
Show file tree
Hide file tree
Showing 6 changed files with 1,088 additions and 62 deletions.
1 change: 0 additions & 1 deletion packages/hardhat/contracts/Stream.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ contract Stream is AccessControl {

mapping(address => BuilderGrantData[]) public builderGrants;

// uint256 public constant FULL_STREAM_UNLOCK_PERIOD = 180; // 3 minutes
uint256 public constant FULL_STREAM_UNLOCK_PERIOD = 2592000; // 30 days
uint256 public constant DUST_THRESHOLD = 1000000000000000; // 0.001 ETH

Expand Down
229 changes: 174 additions & 55 deletions packages/hardhat/deployments/optimism/Stream.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n"
},
"contracts/Stream.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0 <0.9.0;\n\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\n\ncontract Stream is AccessControl {\n\tbytes32 public constant OWNER_ROLE = keccak256(\"OWNER_ROLE\");\n\n\tstruct GrantStream {\n\t\tuint256 cap;\n\t\tuint256 last;\n\t\tuint256 amountLeft;\n\t\tuint8 grantNumber;\n\t\tuint8 stageNumber;\n\t\taddress builder;\n\t}\n\n\tmapping(uint256 => GrantStream) public grantStreams;\n\tuint256 public nextGrantId = 1;\n\n\tmapping(address => uint256[]) public builderGrants;\n\n\t// uint256 public constant FULL_STREAM_UNLOCK_PERIOD = 180; // 3 minutes\n\tuint256 public constant FULL_STREAM_UNLOCK_PERIOD = 2592000; // 30 days\n\tuint256 public constant DUST_THRESHOLD = 1000000000000000; // 0.001 ETH\n\n\tevent Withdraw(\n\t\taddress indexed to,\n\t\tuint256 amount,\n\t\tstring reason,\n\t\tuint256 grantId,\n\t\tuint8 grantNumber,\n\t\tuint8 stageNumber\n\t);\n\tevent AddGrant(uint256 indexed grantId, address indexed to, uint256 amount);\n\tevent MoveGrantToNextStage(\n\t\tuint256 indexed grantId,\n\t\taddress indexed to,\n\t\tuint256 amount,\n\t\tuint8 grantNumber,\n\t\tuint8 stageNumber\n\t);\n\tevent UpdateGrant(\n\t\tuint256 indexed grantId,\n\t\taddress indexed to,\n\t\tuint256 cap,\n\t\tuint256 last,\n\t\tuint256 amountLeft,\n\t\tuint8 grantNumber,\n\t\tuint8 stageNumber\n\t);\n\tevent AddOwner(address indexed newOwner, address indexed addedBy);\n\tevent RemoveOwner(address indexed removedOwner, address indexed removedBy);\n\n\t// Custom errors\n\terror NoActiveStream();\n\terror InsufficientContractFunds();\n\terror UnauthorizedWithdrawal();\n\terror InsufficientStreamFunds();\n\terror FailedToSendEther();\n\terror PreviousAmountNotFullyWithdrawn();\n\n\tconstructor(address[] memory _initialOwners) {\n\t\t_setRoleAdmin(OWNER_ROLE, OWNER_ROLE);\n\t\tfor (uint i = 0; i < _initialOwners.length; i++) {\n\t\t\t_grantRole(OWNER_ROLE, _initialOwners[i]);\n\t\t}\n\t}\n\n\tfunction unlockedGrantAmount(\n\t\tuint256 _grantId\n\t) public view returns (uint256) {\n\t\tGrantStream memory grantStream = grantStreams[_grantId];\n\t\tif (grantStream.cap == 0) revert NoActiveStream();\n\n\t\tif (grantStream.amountLeft == 0) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tuint256 elapsedTime = block.timestamp - grantStream.last;\n\t\tuint256 unlockedAmount = (grantStream.cap * elapsedTime) /\n\t\t\tFULL_STREAM_UNLOCK_PERIOD;\n\n\t\treturn\n\t\t\tunlockedAmount > grantStream.amountLeft\n\t\t\t\t? grantStream.amountLeft\n\t\t\t\t: unlockedAmount;\n\t}\n\n\tfunction addGrantStream(\n\t\taddress _builder,\n\t\tuint256 _cap,\n\t\tuint8 _grantNumber\n\t) public onlyRole(OWNER_ROLE) returns (uint256) {\n\t\tuint256 grantId = nextGrantId++;\n\t\tgrantStreams[grantId] = GrantStream({\n\t\t\tcap: _cap,\n\t\t\tlast: block.timestamp,\n\t\t\tamountLeft: _cap,\n\t\t\tgrantNumber: _grantNumber,\n\t\t\tstageNumber: 1,\n\t\t\tbuilder: _builder\n\t\t});\n\t\tbuilderGrants[_builder].push(grantId);\n\t\temit AddGrant(grantId, _builder, _cap);\n\t\treturn grantId;\n\t}\n\n\tfunction moveGrantToNextStage(\n\t\tuint256 _grantId,\n\t\tuint256 _cap\n\t) public onlyRole(OWNER_ROLE) {\n\t\tGrantStream storage grantStream = grantStreams[_grantId];\n\t\tif (grantStream.cap == 0) revert NoActiveStream();\n\n\t\tif (grantStream.amountLeft > DUST_THRESHOLD)\n\t\t\trevert PreviousAmountNotFullyWithdrawn();\n\n\t\tif (grantStream.amountLeft > 0) {\n\t\t\t(bool sent, ) = payable(grantStream.builder).call{\n\t\t\t\tvalue: grantStream.amountLeft\n\t\t\t}(\"\");\n\t\t\tif (!sent) revert FailedToSendEther();\n\t\t}\n\n\t\tgrantStream.cap = _cap;\n\t\tgrantStream.last = block.timestamp;\n\t\tgrantStream.amountLeft = _cap;\n\t\tgrantStream.stageNumber += 1;\n\n\t\temit MoveGrantToNextStage(\n\t\t\t_grantId,\n\t\t\tgrantStream.builder,\n\t\t\t_cap,\n\t\t\tgrantStream.grantNumber,\n\t\t\tgrantStream.stageNumber\n\t\t);\n\t}\n\n\tfunction updateGrant(\n\t\tuint256 _grantId,\n\t\tuint256 _cap,\n\t\tuint256 _last,\n\t\tuint256 _amountLeft,\n\t\tuint8 _stageNumber\n\t) public onlyRole(OWNER_ROLE) {\n\t\tGrantStream storage grantStream = grantStreams[_grantId];\n\t\tif (grantStream.cap == 0) revert NoActiveStream();\n\t\tgrantStream.cap = _cap;\n\t\tgrantStream.last = _last;\n\t\tgrantStream.amountLeft = _amountLeft;\n\t\tgrantStream.stageNumber = _stageNumber;\n\n\t\temit UpdateGrant(\n\t\t\t_grantId,\n\t\t\tgrantStream.builder,\n\t\t\t_cap,\n\t\t\tgrantStream.last,\n\t\t\tgrantStream.amountLeft,\n\t\t\tgrantStream.grantNumber,\n\t\t\tgrantStream.stageNumber\n\t\t);\n\t}\n\n\tfunction streamWithdraw(\n\t\tuint256 _grantId,\n\t\tuint256 _amount,\n\t\tstring memory _reason\n\t) public {\n\t\tif (address(this).balance < _amount) revert InsufficientContractFunds();\n\t\tGrantStream storage grantStream = grantStreams[_grantId];\n\t\tif (grantStream.cap == 0) revert NoActiveStream();\n\t\tif (msg.sender != grantStream.builder) revert UnauthorizedWithdrawal();\n\n\t\tuint256 totalAmountCanWithdraw = unlockedGrantAmount(_grantId);\n\t\tif (totalAmountCanWithdraw < _amount) revert InsufficientStreamFunds();\n\n\t\tuint256 elapsedTime = block.timestamp - grantStream.last;\n\t\tuint256 timeToDeduct = (elapsedTime * _amount) / totalAmountCanWithdraw;\n\n\t\tgrantStream.last = grantStream.last + timeToDeduct;\n\t\tgrantStream.amountLeft -= _amount;\n\n\t\t(bool sent, ) = msg.sender.call{ value: _amount }(\"\");\n\t\tif (!sent) revert FailedToSendEther();\n\n\t\temit Withdraw(\n\t\t\tmsg.sender,\n\t\t\t_amount,\n\t\t\t_reason,\n\t\t\t_grantId,\n\t\t\tgrantStream.grantNumber,\n\t\t\tgrantStream.stageNumber\n\t\t);\n\t}\n\n\tfunction getBuilderGrantCount(\n\t\taddress _builder\n\t) public view returns (uint256) {\n\t\treturn builderGrants[_builder].length;\n\t}\n\n\tfunction addOwner(address newOwner) public onlyRole(OWNER_ROLE) {\n\t\tgrantRole(OWNER_ROLE, newOwner);\n\t\temit AddOwner(newOwner, msg.sender);\n\t}\n\n\tfunction removeOwner(address owner) public onlyRole(OWNER_ROLE) {\n\t\trevokeRole(OWNER_ROLE, owner);\n\t\temit RemoveOwner(owner, msg.sender);\n\t}\n\n\treceive() external payable {}\n\n\tfallback() external payable {}\n}\n"
"content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0 <0.9.0;\n\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\n\ncontract Stream is AccessControl {\n\tbytes32 public constant OWNER_ROLE = keccak256(\"OWNER_ROLE\");\n\n\tstruct GrantStream {\n\t\tuint256 cap;\n\t\tuint256 last;\n\t\tuint256 amountLeft;\n\t\tuint8 grantNumber;\n\t\tuint8 stageNumber;\n\t\taddress builder;\n\t}\n\n\tstruct BuilderGrantData {\n\t\tuint256 grantId;\n\t\tuint8 grantNumber;\n\t}\n\n\tmapping(uint256 => GrantStream) public grantStreams;\n\tuint256 public nextGrantId = 1;\n\n\tmapping(address => BuilderGrantData[]) public builderGrants;\n\n\tuint256 public constant FULL_STREAM_UNLOCK_PERIOD = 2592000; // 30 days\n\tuint256 public constant DUST_THRESHOLD = 1000000000000000; // 0.001 ETH\n\n\tevent Withdraw(\n\t\taddress indexed to,\n\t\tuint256 amount,\n\t\tstring reason,\n\t\tuint256 grantId,\n\t\tuint8 grantNumber,\n\t\tuint8 stageNumber\n\t);\n\tevent AddGrant(uint256 indexed grantId, address indexed to, uint256 amount);\n\tevent ReinitializeGrant(\n\t\tuint256 indexed grantId,\n\t\taddress indexed to,\n\t\tuint256 amount\n\t);\n\tevent MoveGrantToNextStage(\n\t\tuint256 indexed grantId,\n\t\taddress indexed to,\n\t\tuint256 amount,\n\t\tuint8 grantNumber,\n\t\tuint8 stageNumber\n\t);\n\tevent ReinitializeNextStage(\n\t\tuint256 indexed grantId,\n\t\taddress indexed builder,\n\t\tuint256 amount,\n\t\tuint8 grantNumber,\n\t\tuint8 stageNumber\n\t);\n\tevent UpdateGrant(\n\t\tuint256 indexed grantId,\n\t\taddress indexed to,\n\t\tuint256 cap,\n\t\tuint256 last,\n\t\tuint256 amountLeft,\n\t\tuint8 grantNumber,\n\t\tuint8 stageNumber\n\t);\n\tevent AddOwner(address indexed newOwner, address indexed addedBy);\n\tevent RemoveOwner(address indexed removedOwner, address indexed removedBy);\n\n\t// Custom errors\n\terror NoActiveStream();\n\terror InsufficientContractFunds();\n\terror UnauthorizedWithdrawal();\n\terror InsufficientStreamFunds();\n\terror FailedToSendEther();\n\terror PreviousAmountNotFullyWithdrawn();\n\terror AlreadyWithdrawnFromGrant();\n\n\tconstructor(address[] memory _initialOwners) {\n\t\t_setRoleAdmin(OWNER_ROLE, OWNER_ROLE);\n\t\tfor (uint i = 0; i < _initialOwners.length; i++) {\n\t\t\t_grantRole(OWNER_ROLE, _initialOwners[i]);\n\t\t}\n\t}\n\n\tfunction unlockedGrantAmount(\n\t\tuint256 _grantId\n\t) public view returns (uint256) {\n\t\tGrantStream memory grantStream = grantStreams[_grantId];\n\t\tif (grantStream.cap == 0) revert NoActiveStream();\n\n\t\tif (grantStream.amountLeft == 0) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tuint256 elapsedTime = block.timestamp - grantStream.last;\n\t\tuint256 unlockedAmount = (grantStream.cap * elapsedTime) /\n\t\t\tFULL_STREAM_UNLOCK_PERIOD;\n\n\t\treturn\n\t\t\tunlockedAmount > grantStream.amountLeft\n\t\t\t\t? grantStream.amountLeft\n\t\t\t\t: unlockedAmount;\n\t}\n\n\tfunction addGrantStream(\n\t\taddress _builder,\n\t\tuint256 _cap,\n\t\tuint8 _grantNumber\n\t) public onlyRole(OWNER_ROLE) returns (uint256) {\n\t\t// check if grantStream with same grantNumber already exists\n\t\tuint256 existingGrantId;\n\t\tBuilderGrantData[] memory existingBuilderGrants = builderGrants[\n\t\t\t_builder\n\t\t];\n\t\tfor (uint i = 0; i < existingBuilderGrants.length; i++) {\n\t\t\tGrantStream memory existingGrant = grantStreams[\n\t\t\t\texistingBuilderGrants[i].grantId\n\t\t\t];\n\t\t\tif (existingGrant.grantNumber == _grantNumber) {\n\t\t\t\tif (existingGrant.cap != existingGrant.amountLeft) {\n\t\t\t\t\trevert AlreadyWithdrawnFromGrant();\n\t\t\t\t}\n\t\t\t\texistingGrantId = existingBuilderGrants[i].grantId;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// update existing grant or create new one\n\t\tuint256 grantId = existingGrantId != 0\n\t\t\t? existingGrantId\n\t\t\t: nextGrantId++;\n\n\t\tgrantStreams[grantId] = GrantStream({\n\t\t\tcap: _cap,\n\t\t\tlast: block.timestamp,\n\t\t\tamountLeft: _cap,\n\t\t\tgrantNumber: _grantNumber,\n\t\t\tstageNumber: 1,\n\t\t\tbuilder: _builder\n\t\t});\n\n\t\tif (existingGrantId == 0) {\n\t\t\tbuilderGrants[_builder].push(\n\t\t\t\tBuilderGrantData({\n\t\t\t\t\tgrantId: grantId,\n\t\t\t\t\tgrantNumber: _grantNumber\n\t\t\t\t})\n\t\t\t);\n\t\t\temit AddGrant(grantId, _builder, _cap);\n\t\t} else {\n\t\t\temit ReinitializeGrant(grantId, _builder, _cap);\n\t\t}\n\t\treturn grantId;\n\t}\n\n\tfunction moveGrantToNextStage(\n\t\tuint256 _grantId,\n\t\tuint256 _cap\n\t) public onlyRole(OWNER_ROLE) {\n\t\tGrantStream storage grantStream = grantStreams[_grantId];\n\t\tif (grantStream.cap == 0) revert NoActiveStream();\n\n\t\t// If amountLeft equals cap, reinitialize with same stage number\n\t\tif (grantStream.amountLeft == grantStream.cap) {\n\t\t\tgrantStream.cap = _cap;\n\t\t\tgrantStream.last = block.timestamp;\n\t\t\tgrantStream.amountLeft = _cap;\n\t\t\t// Stage number remains the same\n\t\t\temit ReinitializeNextStage(\n\t\t\t\t_grantId,\n\t\t\t\tgrantStream.builder,\n\t\t\t\t_cap,\n\t\t\t\tgrantStream.grantNumber,\n\t\t\t\tgrantStream.stageNumber\n\t\t\t);\n\t\t} else {\n\t\t\tif (grantStream.amountLeft > DUST_THRESHOLD)\n\t\t\t\trevert PreviousAmountNotFullyWithdrawn();\n\n\t\t\tif (grantStream.amountLeft > 0) {\n\t\t\t\t(bool sent, ) = payable(grantStream.builder).call{\n\t\t\t\t\tvalue: grantStream.amountLeft\n\t\t\t\t}(\"\");\n\t\t\t\tif (!sent) revert FailedToSendEther();\n\t\t\t}\n\n\t\t\tgrantStream.cap = _cap;\n\t\t\tgrantStream.last = block.timestamp;\n\t\t\tgrantStream.amountLeft = _cap;\n\t\t\tgrantStream.stageNumber += 1;\n\n\t\t\temit MoveGrantToNextStage(\n\t\t\t\t_grantId,\n\t\t\t\tgrantStream.builder,\n\t\t\t\t_cap,\n\t\t\t\tgrantStream.grantNumber,\n\t\t\t\tgrantStream.stageNumber\n\t\t\t);\n\t\t}\n\t}\n\n\tfunction updateGrant(\n\t\tuint256 _grantId,\n\t\tuint256 _cap,\n\t\tuint256 _last,\n\t\tuint256 _amountLeft,\n\t\tuint8 _stageNumber\n\t) public onlyRole(OWNER_ROLE) {\n\t\tGrantStream storage grantStream = grantStreams[_grantId];\n\t\tif (grantStream.cap == 0) revert NoActiveStream();\n\t\tgrantStream.cap = _cap;\n\t\tgrantStream.last = _last;\n\t\tgrantStream.amountLeft = _amountLeft;\n\t\tgrantStream.stageNumber = _stageNumber;\n\n\t\temit UpdateGrant(\n\t\t\t_grantId,\n\t\t\tgrantStream.builder,\n\t\t\t_cap,\n\t\t\tgrantStream.last,\n\t\t\tgrantStream.amountLeft,\n\t\t\tgrantStream.grantNumber,\n\t\t\tgrantStream.stageNumber\n\t\t);\n\t}\n\n\tfunction streamWithdraw(\n\t\tuint256 _grantId,\n\t\tuint256 _amount,\n\t\tstring memory _reason\n\t) public {\n\t\tif (address(this).balance < _amount) revert InsufficientContractFunds();\n\t\tGrantStream storage grantStream = grantStreams[_grantId];\n\t\tif (grantStream.cap == 0) revert NoActiveStream();\n\t\tif (msg.sender != grantStream.builder) revert UnauthorizedWithdrawal();\n\n\t\tuint256 totalAmountCanWithdraw = unlockedGrantAmount(_grantId);\n\t\tif (totalAmountCanWithdraw < _amount) revert InsufficientStreamFunds();\n\n\t\tuint256 elapsedTime = block.timestamp - grantStream.last;\n\t\tuint256 timeToDeduct = (elapsedTime * _amount) / totalAmountCanWithdraw;\n\n\t\tgrantStream.last = grantStream.last + timeToDeduct;\n\t\tgrantStream.amountLeft -= _amount;\n\n\t\t(bool sent, ) = msg.sender.call{ value: _amount }(\"\");\n\t\tif (!sent) revert FailedToSendEther();\n\n\t\temit Withdraw(\n\t\t\tmsg.sender,\n\t\t\t_amount,\n\t\t\t_reason,\n\t\t\t_grantId,\n\t\t\tgrantStream.grantNumber,\n\t\t\tgrantStream.stageNumber\n\t\t);\n\t}\n\n\tfunction getBuilderGrantCount(\n\t\taddress _builder\n\t) public view returns (uint256) {\n\t\treturn builderGrants[_builder].length;\n\t}\n\n\tfunction addOwner(address newOwner) public onlyRole(OWNER_ROLE) {\n\t\tgrantRole(OWNER_ROLE, newOwner);\n\t\temit AddOwner(newOwner, msg.sender);\n\t}\n\n\tfunction removeOwner(address owner) public onlyRole(OWNER_ROLE) {\n\t\trevokeRole(OWNER_ROLE, owner);\n\t\temit RemoveOwner(owner, msg.sender);\n\t}\n\n\tfunction getGrantIdByBuilderAndGrantNumber(\n\t\taddress _builder,\n\t\tuint8 _grantNumber\n\t) public view returns (uint256) {\n\t\tfor (uint256 i = 0; i < builderGrants[_builder].length; i++) {\n\t\t\tif (builderGrants[_builder][i].grantNumber == _grantNumber) {\n\t\t\t\treturn builderGrants[_builder][i].grantId;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\treceive() external payable {}\n\n\tfallback() external payable {}\n}\n"
}
},
"settings": {
Expand Down
3 changes: 0 additions & 3 deletions packages/nextjs/.env.development

This file was deleted.

Loading

0 comments on commit 3056005

Please sign in to comment.