-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6da3a45
commit 107a056
Showing
4 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.19; | ||
/** | ||
* @dev Provides information about the current execution context, including the | ||
* sender of the transaction and its data. While these are generally available | ||
* via msg.sender and msg.data, they should not be accessed in such a direct | ||
* manner, since when dealing with meta-transactions the account sending and | ||
* paying for execution may not be the actual sender (as far as an application | ||
* is concerned). | ||
* | ||
* This contract is only required for intermediate, library-like contracts. | ||
*/ | ||
abstract contract Context { | ||
function _msgSender() internal view virtual returns (address) { | ||
return msg.sender; | ||
} | ||
|
||
function _msgData() internal view virtual returns (bytes calldata) { | ||
return msg.data; | ||
} | ||
|
||
function _contextSuffixLength() internal view virtual returns (uint256) { | ||
return 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import { Ownable2Step } from "./Ownable2Step.sol"; | ||
import { Ownable } from "./Ownable.sol"; | ||
|
||
contract Grants4FirstBatch is Ownable2Step { | ||
//////////////////////////////////////////////////////////////// | ||
// ERRORS // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Error that is thrown if the caller is not allowed to call the function | ||
error OnlyAllowedCaller(); | ||
|
||
/// @notice Error that is thrown if the input arrays have different lengths | ||
error LengthMismatch(); | ||
|
||
//////////////////////////////////////////////////////////////// | ||
// EVENTS // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Event emitted when a caller is added | ||
event CallerAdded(address indexed caller); | ||
|
||
/// @notice Event emitted when a caller is removed | ||
event CallerRemoved(address indexed caller); | ||
|
||
/// @notice Event emitted when the AllowanceModule is set | ||
event AllowanceModuleSet(address indexed allowanceModule); | ||
|
||
/// @notice Event emitted when the WLD token is set | ||
event WldTokenSet(address indexed wldToken); | ||
|
||
/// @notice Event emitted when the Holder is set | ||
event HolderSet(address indexed holder); | ||
|
||
/// @notice Event emitted when the RecurringGrantDrop is set | ||
event RecurringGrantDropSet(address indexed recurringGrantDrop); | ||
|
||
/// @notice Event emitted when a batch has been processed | ||
event BatchProcessed(uint256 batchSize, uint256 successfulExecutions); | ||
|
||
//////////////////////////////////////////////////////////////// | ||
/// CONFIG STORAGE /// | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice address of the Safe Allowance Module | ||
AllowanceModule public ALLOWANCE_MODULE; | ||
|
||
/// @notice Worldcoin token address | ||
address public WLD_TOKEN; | ||
|
||
/// @notice BVI Safe that grants allowances to this contract | ||
GnosisSafe public HOLDER; | ||
|
||
/// @notice Recurring Grant Drop contract | ||
IRecurringGrantDrop public RECURRING_GRANT_DROP; | ||
|
||
/// @notice addresses that can call the batch function | ||
mapping(address => bool) public allowedCallers; | ||
|
||
//////////////////////////////////////////////////////////////// | ||
/// CONSTRUCTOR /// | ||
//////////////////////////////////////////////////////////////// | ||
|
||
constructor( | ||
address _allowanceModuleAddress, | ||
address _wldToken, | ||
address _holder, | ||
address _recurringGrantDrop | ||
) | ||
Ownable(msg.sender) | ||
{ | ||
ALLOWANCE_MODULE = AllowanceModule(_allowanceModuleAddress); | ||
WLD_TOKEN = _wldToken; | ||
HOLDER = GnosisSafe(_holder); | ||
RECURRING_GRANT_DROP = IRecurringGrantDrop(_recurringGrantDrop); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////// | ||
/// FUNCTIONS /// | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Batch setNullifierHash + transfer WLD tokens to multiple addresses | ||
/// @param _nullifierHashes array of nullifier hashes | ||
/// @param _recipients array of recipient addresses | ||
/// @param _amounts array of amounts to transfer | ||
function batch( | ||
uint256[] calldata _nullifierHashes, | ||
address[] calldata _recipients, | ||
uint256[] calldata _amounts | ||
) | ||
external | ||
{ | ||
if (!(_nullifierHashes.length == _recipients.length && _recipients.length == _amounts.length)) { | ||
revert LengthMismatch(); | ||
} | ||
|
||
if (!allowedCallers[msg.sender]) { | ||
revert OnlyAllowedCaller(); | ||
} | ||
|
||
uint256 batchSize = _nullifierHashes.length; | ||
uint256 successfulExecutions = 0; | ||
for (uint256 i = 0; i < _recipients.length; i++) { | ||
try RECURRING_GRANT_DROP.setNullifierHash(_nullifierHashes[i]) { | ||
AllowanceModule(ALLOWANCE_MODULE).executeAllowanceTransfer( | ||
HOLDER, WLD_TOKEN, payable(_recipients[i]), uint96(_amounts[i]) | ||
); | ||
successfulExecutions++; | ||
} catch (bytes memory) { } | ||
} | ||
|
||
emit BatchProcessed(batchSize, successfulExecutions); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////// | ||
/// CONFIG FUNCTIONS /// | ||
//////////////////////////////////////////////////////////////// | ||
|
||
function setAllowanceModule(address _allowanceModuleAddress) external onlyOwner { | ||
ALLOWANCE_MODULE = AllowanceModule(_allowanceModuleAddress); | ||
emit AllowanceModuleSet(_allowanceModuleAddress); | ||
} | ||
|
||
function setWldToken(address _wldToken) external onlyOwner { | ||
WLD_TOKEN = _wldToken; | ||
emit WldTokenSet(_wldToken); | ||
} | ||
|
||
function setHolder(address _holder) external onlyOwner { | ||
HOLDER = GnosisSafe(_holder); | ||
emit HolderSet(_holder); | ||
} | ||
|
||
function setRecurringGrantDrop(IRecurringGrantDrop _recurringGrantDrop) external onlyOwner { | ||
RECURRING_GRANT_DROP = _recurringGrantDrop; | ||
emit RecurringGrantDropSet(address(_recurringGrantDrop)); | ||
} | ||
|
||
function addCaller(address _caller) external onlyOwner { | ||
allowedCallers[_caller] = true; | ||
emit CallerAdded(_caller); | ||
} | ||
|
||
function removeCaller(address _caller) external onlyOwner { | ||
delete allowedCallers[_caller]; | ||
emit CallerRemoved(_caller); | ||
} | ||
} | ||
|
||
interface GnosisSafe { | ||
/// @dev Allows a Module to execute a Safe transaction without any further confirmations. | ||
/// @param to Destination address of module transaction. | ||
/// @param value Ether value of module transaction. | ||
/// @param data Data payload of module transaction. | ||
/// @param operation Operation type of module transaction. | ||
function execTransactionFromModule( | ||
address to, | ||
uint256 value, | ||
bytes calldata data, | ||
Enum.Operation operation | ||
) | ||
external | ||
returns (bool success); | ||
} | ||
|
||
contract Enum { | ||
enum Operation { | ||
Call, | ||
DelegateCall | ||
} | ||
} | ||
|
||
interface IRecurringGrantDrop { | ||
function setNullifierHash(uint256 nullifierHash) external; | ||
} | ||
|
||
interface AllowanceModule { | ||
function executeAllowanceTransfer(GnosisSafe safe, address token, address payable to, uint96 amount) external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.19; | ||
import { Context } from "./Context.sol"; | ||
|
||
/** | ||
* @dev Contract module which provides a basic access control mechanism, where | ||
* there is an account (an owner) that can be granted exclusive access to | ||
* specific functions. | ||
* | ||
* The initial owner is set to the address provided by the deployer. This can | ||
* later be changed with {transferOwnership}. | ||
* | ||
* This module is used through inheritance. It will make available the modifier | ||
* `onlyOwner`, which can be applied to your functions to restrict their use to | ||
* the owner. | ||
*/ | ||
abstract contract Ownable is Context { | ||
address private _owner; | ||
|
||
/** | ||
* @dev The caller account is not authorized to perform an operation. | ||
*/ | ||
error OwnableUnauthorizedAccount(address account); | ||
|
||
/** | ||
* @dev The owner is not a valid owner account. (eg. `address(0)`) | ||
*/ | ||
error OwnableInvalidOwner(address owner); | ||
|
||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | ||
|
||
/** | ||
* @dev Initializes the contract setting the address provided by the deployer as the initial owner. | ||
*/ | ||
constructor(address initialOwner) { | ||
if (initialOwner == address(0)) { | ||
revert OwnableInvalidOwner(address(0)); | ||
} | ||
_transferOwnership(initialOwner); | ||
} | ||
|
||
/** | ||
* @dev Throws if called by any account other than the owner. | ||
*/ | ||
modifier onlyOwner() { | ||
_checkOwner(); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev Returns the address of the current owner. | ||
*/ | ||
function owner() public view virtual returns (address) { | ||
return _owner; | ||
} | ||
|
||
/** | ||
* @dev Throws if the sender is not the owner. | ||
*/ | ||
function _checkOwner() internal view virtual { | ||
if (owner() != _msgSender()) { | ||
revert OwnableUnauthorizedAccount(_msgSender()); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Leaves the contract without owner. It will not be possible to call | ||
* `onlyOwner` functions. Can only be called by the current owner. | ||
* | ||
* NOTE: Renouncing ownership will leave the contract without an owner, | ||
* thereby disabling any functionality that is only available to the owner. | ||
*/ | ||
function renounceOwnership() public virtual onlyOwner { | ||
_transferOwnership(address(0)); | ||
} | ||
|
||
/** | ||
* @dev Transfers ownership of the contract to a new account (`newOwner`). | ||
* Can only be called by the current owner. | ||
*/ | ||
function transferOwnership(address newOwner) public virtual onlyOwner { | ||
if (newOwner == address(0)) { | ||
revert OwnableInvalidOwner(address(0)); | ||
} | ||
_transferOwnership(newOwner); | ||
} | ||
|
||
/** | ||
* @dev Transfers ownership of the contract to a new account (`newOwner`). | ||
* Internal function without access restriction. | ||
*/ | ||
function _transferOwnership(address newOwner) internal virtual { | ||
address oldOwner = _owner; | ||
_owner = newOwner; | ||
emit OwnershipTransferred(oldOwner, newOwner); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.19; | ||
|
||
import { Ownable } from "./Ownable.sol"; | ||
|
||
/** | ||
* @dev Contract module which provides access control mechanism, where | ||
* there is an account (an owner) that can be granted exclusive access to | ||
* specific functions. | ||
* | ||
* The initial owner is specified at deployment time in the constructor for `Ownable`. This | ||
* can later be changed with {transferOwnership} and {acceptOwnership}. | ||
* | ||
* This module is used through inheritance. It will make available all functions | ||
* from parent (Ownable). | ||
*/ | ||
abstract contract Ownable2Step is Ownable { | ||
address private _pendingOwner; | ||
|
||
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); | ||
|
||
/** | ||
* @dev Returns the address of the pending owner. | ||
*/ | ||
function pendingOwner() public view virtual returns (address) { | ||
return _pendingOwner; | ||
} | ||
|
||
/** | ||
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is | ||
* one. | ||
* Can only be called by the current owner. | ||
*/ | ||
function transferOwnership(address newOwner) public virtual override onlyOwner { | ||
_pendingOwner = newOwner; | ||
emit OwnershipTransferStarted(owner(), newOwner); | ||
} | ||
|
||
/** | ||
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. | ||
* Internal function without access restriction. | ||
*/ | ||
function _transferOwnership(address newOwner) internal virtual override { | ||
delete _pendingOwner; | ||
super._transferOwnership(newOwner); | ||
} | ||
|
||
/** | ||
* @dev The new owner accepts the ownership transfer. | ||
*/ | ||
function acceptOwnership() public virtual { | ||
address sender = _msgSender(); | ||
if (pendingOwner() != sender) { | ||
revert OwnableUnauthorizedAccount(sender); | ||
} | ||
_transferOwnership(sender); | ||
} | ||
} |