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

Create voucher with , VoucherHub address, Set account as owner, expiration and deal with Authorization #6

Merged
merged 64 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
a172953
start implementing voucher with type and split test files
gfournieriExec Mar 26, 2024
3a335d6
update test and plug voucher with type
gfournieriExec Mar 27, 2024
2459613
clean and add test to get full coverage
gfournieriExec Mar 27, 2024
b460510
set creditERC20 as voucherHub
gfournieriExec Mar 27, 2024
2a303d0
update interfaces
gfournieriExec Mar 28, 2024
b12c70c
Merge branch 'develop' into feature/create-typed-voucher
gfournieriExec Mar 29, 2024
f65b38e
add get voucher test for coverage
gfournieriExec Mar 29, 2024
6e3fb6e
Update naming files
gfournieriExec Mar 29, 2024
67847b9
update interface voucher
gfournieriExec Mar 29, 2024
d7ad6a0
add set authorisation
gfournieriExec Mar 29, 2024
31f130c
add unauthorization for not owner coverage
gfournieriExec Mar 29, 2024
b6db8db
create common function file in script
gfournieriExec Mar 29, 2024
0c1c650
back spdx
gfournieriExec Mar 29, 2024
8aebbb0
clean unused function in VH test
gfournieriExec Mar 29, 2024
f658893
modify IT name
gfournieriExec Apr 2, 2024
3670867
compute voucherExpiration with block timestamp first
gfournieriExec Apr 2, 2024
26160e9
Erase comment
gfournieriExec Apr 2, 2024
cf631e7
update return in getter functions
gfournieriExec Apr 2, 2024
0e58e98
update name of storage variable here
gfournieriExec Apr 2, 2024
b81040b
remove duration0 mistack in IT title
gfournieriExec Apr 2, 2024
574b403
change revert with custom error on voucher IT
gfournieriExec Apr 2, 2024
ca53968
remove useless override function
gfournieriExec Apr 2, 2024
46ac1c5
update coverage to check if owner is authorized
gfournieriExec Apr 2, 2024
abb77b7
update Changelog
gfournieriExec Apr 2, 2024
6a40912
added back blank line
gfournieriExec Apr 2, 2024
b96a993
Update contracts/IVoucherHub.sol
gfournieriExec Apr 2, 2024
8bd50fb
Update contracts/IVoucherHub.sol
gfournieriExec Apr 2, 2024
584a62b
Apply suggestions from code review
gfournieriExec Apr 2, 2024
cf49ac9
update comments
gfournieriExec Apr 2, 2024
0166523
add voucher type to event on VoucherCreated
gfournieriExec Apr 2, 2024
55d206b
update voucher authorization event name and add emit checks in UT
gfournieriExec Apr 2, 2024
1f45c8c
modify name of authorizations functions
gfournieriExec Apr 2, 2024
c472e15
update get voucher hub function name and remove return name argument
gfournieriExec Apr 2, 2024
8df087b
factorize set unset authorization
gfournieriExec Apr 2, 2024
0a4de00
update voucher type init arg name
gfournieriExec Apr 2, 2024
3f5f5f4
update voucher V2 mock
gfournieriExec Apr 2, 2024
be417d1
Erase double UT, merge voucher Type x
gfournieriExec Apr 3, 2024
a1f64df
clean functions description only in implem
gfournieriExec Apr 3, 2024
945824b
add function descriptions on voucher
gfournieriExec Apr 3, 2024
c90061a
erase comment
gfournieriExec Apr 3, 2024
9d63a0a
erase interface title mention
gfournieriExec Apr 3, 2024
048240d
move up pragma mention
gfournieriExec Apr 3, 2024
1e972ff
erase return name argument on interfaces
gfournieriExec Apr 3, 2024
a38b5ec
update isAuthorized in internal function
gfournieriExec Apr 3, 2024
72a8765
move private functions at the bottom of the file
gfournieriExec Apr 3, 2024
bb714a9
erase event emission on voucher init + revert when owner setAuthorize
gfournieriExec Apr 3, 2024
c81c4fe
moved back create voucher in voucherHub test
gfournieriExec Apr 3, 2024
b4fd967
remove useless transaction
gfournieriExec Apr 3, 2024
eaf789a
reorganise voucher init arg disposition and emited event on creation
gfournieriExec Apr 3, 2024
6e02605
remove blank line
gfournieriExec Apr 4, 2024
e47eac3
modify unauthorize test with non owner voucher
gfournieriExec Apr 4, 2024
80984b8
update voucher initialize voucherType Id arg name and add it in inter…
gfournieriExec Apr 4, 2024
c1ea7f7
remove initialize from i voucher
gfournieriExec Apr 4, 2024
df8886c
commenting test for CI
gfournieriExec Apr 4, 2024
657a535
uncomment
gfournieriExec Apr 4, 2024
36eb1d6
testing for CI
gfournieriExec Apr 4, 2024
0b1446f
remove debug CI
gfournieriExec Apr 4, 2024
1b3e0e4
comment test to pass CI
gfournieriExec Apr 4, 2024
76f5195
test only init
gfournieriExec Apr 4, 2024
6d351a5
remove init
gfournieriExec Apr 4, 2024
f815043
add clean workspace and test it
gfournieriExec Apr 4, 2024
ce443af
erase bad stage
gfournieriExec Apr 4, 2024
65bb9e0
re-add tests after clean ws CI
gfournieriExec Apr 4, 2024
2c764ec
voucher 2 should have duration1
gfournieriExec Apr 4, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## vNEXT
- Create voucher from VoucherHub with : type, expiration, authorize list. (#6)
- Create vouchers with create2. (#5)
- Create upgradeable voucher contract. (#4)
- Add voucher type structure, duration, description and asset eligible. (#3)
Expand Down
35 changes: 31 additions & 4 deletions contracts/IVoucherHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,43 @@
pragma solidity ^0.8.20;

interface IVoucherHub {
event VoucherCreated(address indexed voucher, address owner, uint256 expiration);
struct VoucherType {
string description;
uint256 duration;
}
event VoucherCreated(
address indexed voucher,
address owner,
uint256 voucherType,
uint256 expiration
);
event VoucherTypeCreated(uint256 indexed id, string description, uint256 duration);
event VoucherTypeDescriptionUpdated(uint256 indexed id, string description);
event VoucherTypeDurationUpdated(uint256 indexed id, uint256 duration);
event EligibleAssetAdded(uint256 indexed id, address asset);
event EligibleAssetRemoved(uint256 indexed id, address asset);

function createVoucher(
address account,
uint256 expiration
address owner,
uint256 voucherType
) external returns (address voucherAddress);
function getVoucher(address account) external view returns (address voucherAddress);

function getVoucher(address account) external view returns (address);

function getVoucherBeacon() external view returns (address);

function getIexecPoco() external view returns (address);

function getVoucherTypeCount() external view returns (uint256);

function getVoucherType(uint256 id) external view returns (VoucherType memory);

function addEligibleAsset(uint256 voucherTypeId, address asset) external;

function removeEligibleAsset(uint256 voucherTypeId, address asset) external;

function isAssetEligibleToMatchOrdersSponsoring(
uint256 voucherTypeId,
address asset
) external view returns (bool);
}
61 changes: 42 additions & 19 deletions contracts/VoucherHub.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <[email protected]>
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.20;

import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
Expand All @@ -9,13 +11,7 @@ import {Voucher} from "./beacon/Voucher.sol";
import {VoucherProxy} from "./beacon/VoucherProxy.sol";
import {IVoucherHub} from "./IVoucherHub.sol";

pragma solidity ^0.8.20;

contract VoucherHub is OwnableUpgradeable, UUPSUpgradeable, IVoucherHub {
struct VoucherType {
string description;
uint256 duration;
}
/// @custom:storage-location erc7201:iexec.voucher.storage.VoucherHub
struct VoucherHubStorage {
address _iexecPoco;
Expand Down Expand Up @@ -86,33 +82,49 @@ contract VoucherHub is OwnableUpgradeable, UUPSUpgradeable, IVoucherHub {
emit VoucherTypeDurationUpdated(id, duration);
}

/**
* Get the voucher type details by ID.
*/
function getVoucherType(
uint256 id
) public view whenVoucherTypeExists(id) returns (VoucherType memory) {
VoucherHubStorage storage $ = _getVoucherHubStorage();
return $.voucherTypes[id];
}

/**
* Get voucher types count.
*/
function getVoucherTypeCount() public view returns (uint256) {
VoucherHubStorage storage $ = _getVoucherHubStorage();
return $.voucherTypes.length;
}

/**
* Add an eligible asset to a voucher type.
* @param voucherTypeId The ID of the voucher type.
* @param asset The address of the asset to add.
*/
function addEligibleAsset(uint256 voucherTypeId, address asset) external onlyOwner {
_setAssetEligibility(voucherTypeId, asset, true);
emit EligibleAssetAdded(voucherTypeId, asset);
}

/**
* Remove an eligible asset to a voucher type.
* @param voucherTypeId The ID of the voucher type.
* @param asset The address of the asset to remove.
*/
function removeEligibleAsset(uint256 voucherTypeId, address asset) external onlyOwner {
_setAssetEligibility(voucherTypeId, asset, false);
emit EligibleAssetRemoved(voucherTypeId, asset);
}

function _setAssetEligibility(uint256 voucherTypeId, address asset, bool isEligible) private {
VoucherHubStorage storage $ = _getVoucherHubStorage();
$.matchOrdersEligibility[voucherTypeId][asset] = isEligible;
}

/**
* Check if an asset is eligible to match orders sponsoring.
* @param voucherTypeId The ID of the voucher type.
* @param asset The address of the asset to check.
*/
function isAssetEligibleToMatchOrdersSponsoring(
uint256 voucherTypeId,
address asset
Expand All @@ -121,6 +133,9 @@ contract VoucherHub is OwnableUpgradeable, UUPSUpgradeable, IVoucherHub {
return $.matchOrdersEligibility[voucherTypeId][asset];
}

/**
* Get iExec Poco address used by vouchers.
*/
function getIexecPoco() public view returns (address) {
VoucherHubStorage storage $ = _getVoucherHubStorage();
return $._iexecPoco;
Expand All @@ -144,30 +159,33 @@ contract VoucherHub is OwnableUpgradeable, UUPSUpgradeable, IVoucherHub {
* changes, but this should not happen since the beacon is upgradeable, hence the
* address should never be changed.
*
* @param owner voucher owner
* @param expiration voucher expiration

* @param owner The address of the voucher owner.
zguesmi marked this conversation as resolved.
Show resolved Hide resolved
* @param voucherType The ID of the voucher type.
* @return voucherAddress The address of the created voucher contract.
*/
function createVoucher(
address owner,
uint256 expiration
) external override onlyOwner returns (address voucherAddress) {
uint256 voucherType
zguesmi marked this conversation as resolved.
Show resolved Hide resolved
) external onlyOwner returns (address voucherAddress) {
VoucherHubStorage storage $ = _getVoucherHubStorage();
uint256 voucherExpiration = block.timestamp + getVoucherType(voucherType).duration;
voucherAddress = address(new VoucherProxy{salt: _getCreate2Salt(owner)}($._voucherBeacon));
// Initialize the created proxy contract.
// The proxy contract does a delegatecall to its implementation.
// Re-Entrancy safe because the target contract is controlled.
Voucher(voucherAddress).initialize(owner, expiration);
emit VoucherCreated(voucherAddress, owner, expiration);
Voucher(voucherAddress).initialize(owner, voucherType, voucherExpiration, address(this));
emit VoucherCreated(voucherAddress, owner, voucherType, voucherExpiration);
}

/**
* TODO return Voucher structure.
*
* Get voucher address of a given account.
* Returns address(0) if voucher is not found.
* @param account owner address.
* @param account voucher's owner address.
*/
function getVoucher(address account) public view override returns (address voucherAddress) {
function getVoucher(address account) public view returns (address voucherAddress) {
VoucherHubStorage storage $ = _getVoucherHubStorage();
voucherAddress = Create2.computeAddress(
_getCreate2Salt(account), // salt
Expand All @@ -178,6 +196,11 @@ contract VoucherHub is OwnableUpgradeable, UUPSUpgradeable, IVoucherHub {

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

function _setAssetEligibility(uint256 voucherTypeId, address asset, bool isEligible) private {
VoucherHubStorage storage $ = _getVoucherHubStorage();
$.matchOrdersEligibility[voucherTypeId][asset] = isEligible;
}

function _getCreate2Salt(address account) private pure returns (bytes32) {
return bytes32(uint256(uint160(account)));
}
Expand Down
16 changes: 12 additions & 4 deletions contracts/beacon/IVoucher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@

pragma solidity ^0.8.20;

/**
* @title Interface of the voucher contract.
*/
interface IVoucher {
event ExpirationUpdated(uint256 newExpiration);
event AccountAuthorized(address indexed account);
event AccountUnauthorized(address indexed account);

function getExpiration() external view returns (uint256);

function getType() external view returns (uint256);

function getVoucherHub() external view returns (address);

function authorizeAccount(address account) external;

function unauthorizeAccount(address account) external;

function isAccountAuthorized(address account) external view returns (bool);
}
92 changes: 83 additions & 9 deletions contracts/beacon/Voucher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,117 @@ import {IVoucher} from "./IVoucher.sol";

/**
* @title Implementation of the voucher contract.
* @notice Deployed along the Beacon contract using "Upgrades" plugin of OZ.
* Deployed along the Beacon contract using "Upgrades" plugin of OZ.
*/
contract Voucher is OwnableUpgradeable, IVoucher {
/// @custom:storage-location erc7201:iexec.voucher.storage.Voucher
struct VoucherStorage {
address _voucherHub;
uint256 _expiration;
uint256 _type;
mapping(address => bool) _authorizedAccounts;
}

// keccak256(abi.encode(uint256(keccak256("iexec.voucher.storage.Voucher")) - 1))
// & ~bytes32(uint256(0xff));
bytes32 private constant VOUCHER_STORAGE_LOCATION =
0xc2e244293dc04d6c7fa946e063317ff8e6770fd48cbaff411a60f1efc8a7e800;

function _getVoucherStorage() private pure returns (VoucherStorage storage $) {
assembly {
$.slot := VOUCHER_STORAGE_LOCATION
}
}

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

/**
* Initialize implementation contract.
* @param expiration initial expiration.
* @param owner The owner of the contract.
* @param voucherType The type of the voucher.
* @param expiration The expiration timestamp of the voucher.
* @param voucherHub The address of the voucher hub.
*/
function initialize(address owner, uint256 expiration) external initializer {
function initialize(
address owner,
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
uint256 voucherType,
uint256 expiration,
address voucherHub
) external initializer {
__Ownable_init(owner);
VoucherStorage storage $ = _getVoucherStorage();
$._type = voucherType;
$._voucherHub = voucherHub;
$._expiration = expiration;
emit ExpirationUpdated(expiration);
// TODO: deposit sRLC.
}

function getExpiration() external view override returns (uint256) {
/**
* Retrieve the address of the voucher hub associated with the voucher.
* @return voucherHubAddress The address of the voucher hub.
*/
function getVoucherHub() external view returns (address) {
VoucherStorage storage $ = _getVoucherStorage();
return $._voucherHub;
}

/**
* Retrieve the expiration timestamp of the voucher.
* @return expirationTimestamp The expiration timestamp.
*/
function getExpiration() external view returns (uint256) {
VoucherStorage storage $ = _getVoucherStorage();
return $._expiration;
}

function _getVoucherStorage() private pure returns (VoucherStorage storage $) {
assembly {
$.slot := VOUCHER_STORAGE_LOCATION
}
/**
* Retrieve the type of the voucher.
* @return voucherType The type of the voucher.
*/
function getType() external view returns (uint256) {
VoucherStorage storage $ = _getVoucherStorage();
return $._type;
}

/**
* Sets authorization for an account.
* @param account The account to authorize.
*/
function authorizeAccount(address account) external onlyOwner {
_setAccountAuthorization(account, true);
emit AccountAuthorized(account);
}

/**
* Unsets authorization for an account.
* @param account The account to remove authorization from.
*/
function unauthorizeAccount(address account) external onlyOwner {
_setAccountAuthorization(account, false);
emit AccountUnauthorized(account);
}

/**
* Checks if an account is authorized for.
* @param account The account to check.
* @return isAuthorized True if the account is authorized, false otherwise.
*/
function isAccountAuthorized(address account) external view returns (bool) {
VoucherStorage storage $ = _getVoucherStorage();
return account == owner() || $._authorizedAccounts[account];
}

/**
* Internal function to set authorization for an account.
* @param account The account to set authorization for.
* @param isAuthorized Whether to authorize or unauthorize the account.
*/
function _setAccountAuthorization(address account, bool isAuthorized) private {
require(account != owner(), "Voucher: owner is already authorized.");
VoucherStorage storage $ = _getVoucherStorage();
$._authorizedAccounts[account] = isAuthorized;
}
}
10 changes: 6 additions & 4 deletions contracts/mocks/VoucherV2Mock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
pragma solidity ^0.8.20;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {IVoucher} from "../beacon/IVoucher.sol";

contract VoucherV2Mock is OwnableUpgradeable, IVoucher {
contract VoucherV2Mock is OwnableUpgradeable {
/// @custom:storage-location erc7201:iexec.voucher.storage.Voucher
struct VoucherStorage {
address _voucherHub;
uint256 _expiration;
uint256 _type;
mapping(address => bool) _authorizedAccounts;
uint256 _newStateVariable;
}

Expand All @@ -27,12 +29,12 @@ contract VoucherV2Mock is OwnableUpgradeable, IVoucher {
* Initialize new implementation contract.
* @param newStateVariable test variable.
*/
function initialize(uint256 newStateVariable) external reinitializer(2) {
function initializeV2(uint256 newStateVariable) external reinitializer(2) {
VoucherStorage storage $ = _getVoucherStorage();
$._newStateVariable = newStateVariable;
}

function getExpiration() external view override returns (uint256) {
function getExpiration() external view returns (uint256) {
VoucherStorage storage $ = _getVoucherStorage();
return $._expiration;
}
Expand Down
Loading