Skip to content

Commit

Permalink
Merge pull request #33 from worldcoin/karan-add-allowance-module-worl…
Browse files Browse the repository at this point in the history
…dchain

use allowance module in RGD and Reservations
  • Loading branch information
karankurbur authored Sep 30, 2024
2 parents 8fb4169 + 92ae817 commit 3e8b175
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 26 deletions.
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

0 comments on commit 3e8b175

Please sign in to comment.