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

use allowance module in RGD and Reservations #33

Merged
merged 3 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 9 additions & 1 deletion script/RecurringGrantDrop.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ contract DeployRecurringGrantDrop is Script {
uint256 public groupId = abi.decode(vm.parseJson(json, ".groupId"), (uint256));
address public erc20Address = abi.decode(vm.parseJson(json, ".erc20Address"), (address));
address public holder = abi.decode(vm.parseJson(json, ".holderAddress"), (address));

// Worldchain Address
address public allowanceModule = 0xa9bcF56d9FCc0178414EF27a3d893C9469e437B7;

// Optimism Address
// address public allowanceModule = 0xa9bcF56d9FCc0178414EF27a3d893C9469e437B7;

//bool public staging = abi.decode(vm.parseJson(json, ".staging"), (bool));
//uint256 public startMonth = abi.decode(vm.parseJson(json, ".startMonth"), (uint256));
//uint256 public startYear = abi.decode(vm.parseJson(json, ".startYear"), (uint256));
Expand All @@ -57,7 +64,8 @@ contract DeployRecurringGrantDrop is Script {
//if (staging) grant = new StagingGrant(startOffset, amount);
grant = new WLDGrant();

airdrop = new RecurringGrantDrop(worldIdRouter, groupId, token, holder, grant);
airdrop =
new RecurringGrantDrop(worldIdRouter, groupId, token, holder, grant, allowanceModule);

// if (!staging) {
// grantLegacy = new LaunchGrantLegacy();
Expand Down
9 changes: 8 additions & 1 deletion script/RecurringGrantDropReservations.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,19 @@ contract DeployRecurringGrantDropReservations is Script {

ERC20 public token = ERC20(erc20Address);

// Worldchain Address
address public allowanceModule = 0xa9bcF56d9FCc0178414EF27a3d893C9469e437B7;

// Optimism Address
// address public allowanceModule = 0xa9bcF56d9FCc0178414EF27a3d893C9469e437B7;


function run() external {
vm.startBroadcast(privateKey);

grant = new WLDGrantReservations();

airdrop = new RecurringGrantDropReservations(worldIdRouter, groupId, token, holder, grant);
airdrop = new RecurringGrantDropReservations(worldIdRouter, groupId, token, holder, grant, allowanceModule);

vm.stopBroadcast();
}
Expand Down
16 changes: 16 additions & 0 deletions src/IAllowanceModule.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// an interface for the AllowanceModule contract deployed at these addresses
// optimism: 0x948BDE4d8670500b0F62cF5c745C82ABe7c81A65
// worldchain: 0xa9bcF56d9FCc0178414EF27a3d893C9469e437B7
interface AllowanceModule {
function executeAllowanceTransfer(
GnosisSafe safe,
address token,
address payable to,
uint96 amount
) external;
}

interface GnosisSafe {}
35 changes: 24 additions & 11 deletions src/RecurringGrantDrop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {IGrant} from "./IGrant.sol";
import {IWorldIDGroups} from "world-id-contracts/interfaces/IWorldIDGroups.sol";
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import {GnosisSafe} from "./IAllowanceModule.sol";
import {AllowanceModule} from "./IAllowanceModule.sol";

/// @title RecurringGrantDrop
/// @author Worldcoin
Expand All @@ -29,10 +31,6 @@ contract RecurringGrantDrop is Ownable2Step {
/// @notice The ERC20 token airdropped
ERC20 public token;

/// @notice The address that holds the tokens that are being airdropped
/// @dev Make sure the holder has approved spending for this contract!
address public holder;

/// @notice The grant instance used
IGrant public grant;

Expand All @@ -42,6 +40,12 @@ contract RecurringGrantDrop is Ownable2Step {
/// @dev Allowed addresses to block a nullifierHash
mapping(address => bool) internal allowedNullifierHashBlockers;

/// @notice BVI Safe that grants allowances to this contract
GnosisSafe public holder;

/// @notice address of the Safe Allowance Module
AllowanceModule public allowanceModule;

///////////////////////////////////////////////////////////////////////////////
/// ERRORS ///
//////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -82,7 +86,9 @@ contract RecurringGrantDrop is Ownable2Step {
uint256 _groupId,
ERC20 _token,
address _holder,
IGrant _grant
IGrant _grant,
address _allowanceModuleAddress

);

/// @notice Emitted when a grant is successfully claimed
Expand Down Expand Up @@ -133,20 +139,25 @@ contract RecurringGrantDrop is Ownable2Step {
uint256 _groupId,
ERC20 _token,
address _holder,
IGrant _grant
IGrant _grant,
address _allowanceModuleAddress
) Ownable(msg.sender) {
if (address(_worldIdRouter) == address(0)) revert InvalidConfiguration();
if (address(_token) == address(0)) revert InvalidConfiguration();
if (address(_holder) == address(0)) revert InvalidConfiguration();
if (address(_grant) == address(0)) revert InvalidConfiguration();
if (address(_allowanceModuleAddress) == address(0)) revert InvalidConfiguration();

worldIdRouter = _worldIdRouter;
groupId = _groupId;
token = _token;
holder = _holder;
holder = GnosisSafe(_holder);
grant = _grant;
allowanceModule = AllowanceModule(_allowanceModuleAddress);

emit RecurringGrantDropInitialized(worldIdRouter, groupId, token, holder, grant);
emit RecurringGrantDropInitialized(
worldIdRouter, groupId, token, address(holder), grant, _allowanceModuleAddress
);
}

///////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -180,7 +191,9 @@ contract RecurringGrantDrop is Ownable2Step {

nullifierHashes[nullifierHash] = true;

SafeERC20.safeTransferFrom(token, holder, receiver, grant.getAmount(grantId));
allowanceModule.executeAllowanceTransfer(
holder, address(token), payable(receiver), uint96(grant.getAmount(grantId))
);

emit GrantClaimed(grantId, receiver);
}
Expand Down Expand Up @@ -266,9 +279,9 @@ contract RecurringGrantDrop is Ownable2Step {
/// @param _holder The new holder
function setHolder(address _holder) external onlyOwner {
if (address(_holder) == address(0)) revert InvalidConfiguration();
holder = _holder;
holder = GnosisSafe(_holder);

emit HolderUpdated(holder);
emit HolderUpdated(address(holder));
}

/// @notice Update the grant
Expand Down
34 changes: 23 additions & 11 deletions src/RecurringGrantDropReservations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {IGrantReservations} from "./IGrantReservations.sol";
import {IWorldIDGroups} from "world-id-contracts/interfaces/IWorldIDGroups.sol";
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import {GnosisSafe} from "./IAllowanceModule.sol";
import {AllowanceModule} from "./IAllowanceModule.sol";

/// @title RecurringGrantDropReservations
/// @author Worldcoin
Expand All @@ -29,10 +31,6 @@ contract RecurringGrantDropReservations is Ownable2Step {
/// @notice The ERC20 token airdropped
ERC20 public token;

/// @notice The address that holds the tokens that are being airdropped
/// @dev Make sure the holder has approved spending for this contract!
address public holder;

/// @notice The grant instance used
IGrantReservations public grant;

Expand All @@ -42,6 +40,12 @@ contract RecurringGrantDropReservations is Ownable2Step {
/// @dev Allowed addresses to sign a reservation
mapping(address => bool) internal allowedSigners;

/// @notice BVI Safe that grants allowances to this contract
GnosisSafe public holder;

/// @notice address of the Safe Allowance Module
AllowanceModule public allowanceModule;

///////////////////////////////////////////////////////////////////////////////
/// ERRORS ///
//////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -82,7 +86,8 @@ contract RecurringGrantDropReservations is Ownable2Step {
uint256 _groupId,
ERC20 _token,
address _holder,
IGrantReservations _grant
IGrantReservations _grant,
address _allowanceModuleAddress
);

/// @notice Emitted when a grant is successfully claimed
Expand Down Expand Up @@ -132,20 +137,25 @@ contract RecurringGrantDropReservations is Ownable2Step {
uint256 _groupId,
ERC20 _token,
address _holder,
IGrantReservations _grant
IGrantReservations _grant,
address _allowanceModuleAddress
) Ownable(msg.sender) {
if (address(_worldIdRouter) == address(0)) revert InvalidConfiguration();
if (address(_token) == address(0)) revert InvalidConfiguration();
if (address(_holder) == address(0)) revert InvalidConfiguration();
if (address(_grant) == address(0)) revert InvalidConfiguration();
if (address(_allowanceModuleAddress) == address(0)) revert InvalidConfiguration();

worldIdRouter = _worldIdRouter;
groupId = _groupId;
token = _token;
holder = _holder;
holder = GnosisSafe(_holder);
grant = _grant;
allowanceModule = AllowanceModule(_allowanceModuleAddress);

emit RecurringGrantDropInitialized(worldIdRouter, groupId, token, holder, grant);
emit RecurringGrantDropInitialized(
worldIdRouter, groupId, token, address(holder), grant, _allowanceModuleAddress
);
}

///////////////////////////////////////////////////////////////////////////////
Expand All @@ -172,7 +182,9 @@ contract RecurringGrantDropReservations is Ownable2Step {

nullifierHashes[nullifierHash] = true;

SafeERC20.safeTransferFrom(token, holder, receiver, grant.getAmount(grantId));
allowanceModule.executeAllowanceTransfer(
holder, address(token), payable(receiver), uint96(grant.getAmount(grantId))
);

emit GrantClaimed(grantId, receiver);
}
Expand Down Expand Up @@ -266,9 +278,9 @@ contract RecurringGrantDropReservations is Ownable2Step {
/// @param _holder The new holder
function setHolder(address _holder) external onlyOwner {
if (address(_holder) == address(0)) revert InvalidConfiguration();
holder = _holder;
holder = GnosisSafe(_holder);

emit HolderUpdated(holder);
emit HolderUpdated(address(holder));
}

/// @notice Update the grant
Expand Down
18 changes: 16 additions & 2 deletions src/test/RecurringGrantDrop.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {RecurringGrantDrop} from "../RecurringGrantDrop.sol";
import {WLDGrant} from "../WLDGrant.sol";
import {IGrant} from "../IGrant.sol";
import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";
import {MockAllowanceModule} from "./mock/MockAllowanceModule.sol";

/// @title RecurringGrantDrop Tests
/// @author Worldcoin
Expand All @@ -22,6 +23,7 @@ contract RecurringGrantDropTest is PRBTest {
address public manager;
address public caller;
address public holder;

uint256 public startTime = 1690167600; // Monday, 24 July 2023 03:00:00
uint256 public preGrant4_claimTime = 1699239600; // Monday, 23 October 2023 03:00:00
uint256 public grant4_grant39_claimTime = 1722470400; // Thursday, 01 August 2024 00:00:00
Expand All @@ -44,11 +46,19 @@ contract RecurringGrantDropTest is PRBTest {
user = address(0x3);
holder = address(0x4);

MockAllowanceModule allowanceModule = new MockAllowanceModule(address(token), holder);

proof = [0, 0, 0, 0, 0, 0, 0, 0];

vm.prank(manager);
airdrop =
new RecurringGrantDrop(worldIDIdentityManagerRouterMock, groupId, token, holder, grant);
airdrop = new RecurringGrantDrop(
worldIDIdentityManagerRouterMock,
groupId,
token,
holder,
grant,
address(allowanceModule)
);
vm.prank(manager);

///////////////////////////////////////////////////////////////////
Expand All @@ -69,6 +79,9 @@ contract RecurringGrantDropTest is PRBTest {
// Approve spending from the airdrop contract
vm.prank(holder);
token.approve(address(airdrop), type(uint256).max);

vm.prank(holder);
token.approve(address(allowanceModule), type(uint256).max);
}

////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -325,3 +338,4 @@ contract RecurringGrantDropTest is PRBTest {
assertEq(address(airdrop.grant()), address(grant));
}
}

Loading
Loading