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 15 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
74 changes: 71 additions & 3 deletions contracts/IVoucherHub.sol
Original file line number Diff line number Diff line change
@@ -1,19 +1,87 @@
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <[email protected]>
// SPDX-License-Identifier: Apache-2.0

jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
pragma solidity ^0.8.20;

/**
* @title Interface of the voucher hub contract.
*/
interface IVoucherHub {
struct VoucherType {
string description;
uint256 duration;
}
event VoucherCreated(address indexed voucher, address owner, 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);

/**
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
* @notice Creates a new voucher for the specified owner.
gfournieriExec marked this conversation as resolved.
Show resolved Hide resolved
* @param owner The address of the voucher owner.
* @param voucherType The ID of the voucher type.
* @return voucherAddress The address of the created voucher contract.
*/
function createVoucher(
address account,
uint256 expiration
address owner,
uint256 voucherType
) external returns (address voucherAddress);

/**
* @notice Get the voucher address of a given account.
* @param account The address of the voucher owner.
* @return voucherAddress The address of the voucher contract.
*/
function getVoucher(address account) external view returns (address voucherAddress);

/**
* @notice Get the voucher beacon address.
* @return voucherBeacon The address of the voucher beacon.
*/
function getVoucherBeacon() external view returns (address voucherBeacon);
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved

/**
* @notice Get the iExec Poco address.
gfournieriExec marked this conversation as resolved.
Show resolved Hide resolved
* @return iexecPoco The address of the iExec Poco contract.
*/
function getIexecPoco() external view returns (address iexecPoco);

/**
* @notice Get the count of voucher types.
gfournieriExec marked this conversation as resolved.
Show resolved Hide resolved
* @return count The count of voucher types.
*/
function getVoucherTypeCount() external view returns (uint256 count);
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved

/**
* @notice Get the voucher type details by ID.
* @param id The ID of the voucher type.
* @return VoucherType The details of the voucher type.
*/
function getVoucherType(uint256 id) external view returns (VoucherType memory);

/**
* @notice Add an eligible asset for a voucher type.
gfournieriExec marked this conversation as resolved.
Show resolved Hide resolved
* @param voucherTypeId The ID of the voucher type.
* @param asset The address of the asset to add.
*/
function addEligibleAsset(uint256 voucherTypeId, address asset) external;

/**
* @notice Remove an eligible asset for a voucher type.
gfournieriExec marked this conversation as resolved.
Show resolved Hide resolved
* @param voucherTypeId The ID of the voucher type.
* @param asset The address of the asset to remove.
*/
function removeEligibleAsset(uint256 voucherTypeId, address asset) external;

/**
* @notice 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.
* @return isEligible True if the asset is eligible, otherwise False.
*/
function isAssetEligibleToMatchOrdersSponsoring(
uint256 voucherTypeId,
address asset
) external view returns (bool isEligible);
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
}
16 changes: 7 additions & 9 deletions contracts/VoucherHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ 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 @@ -88,7 +84,7 @@ contract VoucherHub is OwnableUpgradeable, UUPSUpgradeable, IVoucherHub {

function getVoucherType(
uint256 id
) public view whenVoucherTypeExists(id) returns (VoucherType memory) {
) public view override whenVoucherTypeExists(id) returns (VoucherType memory) {
VoucherHubStorage storage $ = _getVoucherHubStorage();
return $.voucherTypes[id];
}
Expand Down Expand Up @@ -145,19 +141,21 @@ contract VoucherHub is OwnableUpgradeable, UUPSUpgradeable, IVoucherHub {
* address should never be changed.
*
* @param owner voucher owner
* @param expiration voucher expiration
* @param voucherType voucher expiration
*/
function createVoucher(
address owner,
uint256 expiration
uint256 voucherType
zguesmi marked this conversation as resolved.
Show resolved Hide resolved
) external override onlyOwner returns (address voucherAddress) {
VoucherHubStorage storage $ = _getVoucherHubStorage();
uint256 voucherExpiration = getVoucherType(voucherType).duration + block.timestamp;
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
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, voucherExpiration);
zguesmi marked this conversation as resolved.
Show resolved Hide resolved
// Create voucher and call initialize() function.
gfournieriExec marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
40 changes: 38 additions & 2 deletions contracts/beacon/IVoucher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,43 @@ pragma solidity ^0.8.20;
* @title Interface of the voucher contract.
*/
interface IVoucher {
event ExpirationUpdated(uint256 newExpiration);
event AuthorizationSet(address indexed account);
event AuthorizationUnset(address indexed account);
zguesmi marked this conversation as resolved.
Show resolved Hide resolved

function getExpiration() external view returns (uint256);
/**
* @notice Retrieves the expiration timestamp of the voucher.
gfournieriExec marked this conversation as resolved.
Show resolved Hide resolved
* @return expirationTimestamp The expiration timestamp.
*/
function getExpiration() external view returns (uint256 expirationTimestamp);

/**
* @notice Retrieves the type of the voucher.
* @return voucherType The type of the voucher.
*/
function getType() external view returns (uint256 voucherType);

/**
* @notice Retrieves the address of the voucher hub associated with the voucher.
* @return voucherHubAddress The address of the voucher hub.
*/
function getHub() external view returns (address voucherHubAddress);
zguesmi marked this conversation as resolved.
Show resolved Hide resolved

/**
* @notice Sets authorization for an account.
* @param account The account to authorize.
*/
function setAuthorization(address account) external;
zguesmi marked this conversation as resolved.
Show resolved Hide resolved

/**
* @notice Unsets authorization for an account.
* @param account The account to remove authorization from.
*/
function unsetAuthorization(address account) external;
zguesmi marked this conversation as resolved.
Show resolved Hide resolved

/**
* @notice Checks if an account is authorized.
* @param account The account to check.
* @return isAuthorized True if the account is authorized, false otherwise.
*/
function isAccountAuthorized(address account) external view returns (bool isAuthorized);
}
55 changes: 47 additions & 8 deletions contracts/beacon/Voucher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@ import {IVoucher} from "./IVoucher.sol";
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();
Expand All @@ -30,21 +39,51 @@ contract Voucher is OwnableUpgradeable, IVoucher {
* Initialize implementation contract.
* @param expiration initial expiration.
*/
function initialize(address owner, uint256 expiration) external initializer {
function initialize(
address owner,
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
uint256 vtype,
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
uint256 expiration,
address voucherHub
) external initializer {
__Ownable_init(owner);
VoucherStorage storage $ = _getVoucherStorage();
$._type = vtype;
$._voucherHub = voucherHub;
$._expiration = expiration;
emit ExpirationUpdated(expiration);
$._authorizedAccounts[owner] = true;
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
// deposit
emit AuthorizationSet(owner);
}

function getExpiration() external view override returns (uint256) {
function getHub() external view returns (address voucherHubAddress) {
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
VoucherStorage storage $ = _getVoucherStorage();
return $._expiration;
voucherHubAddress = $._voucherHub;
}

function _getVoucherStorage() private pure returns (VoucherStorage storage $) {
assembly {
$.slot := VOUCHER_STORAGE_LOCATION
}
function getExpiration() external view override returns (uint256 expirationTimestamp) {
VoucherStorage storage $ = _getVoucherStorage();
expirationTimestamp = $._expiration;
}
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved

function getType() external view returns (uint256 voucherType) {
VoucherStorage storage $ = _getVoucherStorage();
voucherType = $._type;
}

function setAuthorization(address account) external onlyOwner {
VoucherStorage storage voucherStorage = _getVoucherStorage();
voucherStorage._authorizedAccounts[account] = true;
emit AuthorizationSet(account);
}

function unsetAuthorization(address account) external onlyOwner {
VoucherStorage storage voucherStorage = _getVoucherStorage();
voucherStorage._authorizedAccounts[account] = false;
emit AuthorizationUnset(account);
}

function isAccountAuthorized(address account) external view returns (bool isAuthorized) {
VoucherStorage storage voucherStorage = _getVoucherStorage();
isAuthorized = voucherStorage._authorizedAccounts[account];
}
}
15 changes: 15 additions & 0 deletions contracts/mocks/IVoucherV2Mock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <[email protected]>
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.20;

/**
* @title Interface of the voucher contract.
*/
interface IVoucher {
jeremyjams marked this conversation as resolved.
Show resolved Hide resolved
/**
* @notice Retrieves the expiration timestamp of the voucher.
* @return expirationTimestamp The expiration timestamp.
*/
function getExpiration() external view returns (uint256 expirationTimestamp);
}
7 changes: 5 additions & 2 deletions contracts/mocks/VoucherV2Mock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
pragma solidity ^0.8.20;

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

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

Expand All @@ -32,7 +35,7 @@ contract VoucherV2Mock is OwnableUpgradeable, IVoucher {
$._newStateVariable = newStateVariable;
}

function getExpiration() external view override returns (uint256) {
function getExpiration() external view returns (uint256) {
VoucherStorage storage $ = _getVoucherStorage();
return $._expiration;
}
Expand Down
32 changes: 32 additions & 0 deletions scripts/common.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file needed for other files in scripts ? If not and it's only used for tests it should go under tests/utils/ or tests/common-utils.ts, ...

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ContractTransactionReceipt } from 'ethers';
import { ethers } from 'hardhat';
import { Voucher, VoucherProxy } from '../../typechain-types';
import { VoucherV2Mock } from '../../typechain-types/contracts/mocks';

export async function getVoucher(voucherAddress: string): Promise<Voucher> {
return await ethers.getContractAt('Voucher', voucherAddress);
}

export async function getVoucherV2(voucherAddress: string): Promise<VoucherV2Mock> {
return await ethers.getContractAt('VoucherV2Mock', voucherAddress);
}

export async function getVoucherAsProxy(voucherAddress: string): Promise<VoucherProxy> {
return await ethers.getContractAt('VoucherProxy', voucherAddress);
}

export async function getExpectedExpiration(
voucherDuration: number,
txReceipt: ContractTransactionReceipt | null,
): Promise<number> {
if (txReceipt != null) {
const block = await ethers.provider.getBlock(txReceipt.blockNumber);
if (block) {
return block.timestamp + voucherDuration;
} else {
return 0;
}
} else {
return 0;
}
}
Loading