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

Clone vouchers using create2 #703

Merged
merged 29 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3824281
create new collection
zajck Mar 22, 2023
47df762
Manage cloneAddress protocol wide
zajck Mar 22, 2023
7728d09
Collection struct, domain test + old tests
zajck Mar 27, 2023
1d84e2d
seller tests
zajck Mar 27, 2023
07b6300
createOffer tests
zajck Mar 27, 2023
14ea68b
exchange handler tests
zajck Mar 28, 2023
9e07f7d
update natspec
zajck Mar 28, 2023
6023886
Merge branch 'main' into multiple-collections
zajck Apr 26, 2023
7f18712
Merge branch 'main' into multiple-collections
zajck May 10, 2023
ddc26fe
FIx failing tests
zajck May 17, 2023
d8dcb44
add missing tests
zajck May 17, 2023
2b67fc4
Merge branch 'main' into multiple-collections
anajuliabit Jun 5, 2023
2218b9e
review fixes
zajck Jun 6, 2023
a7662ad
minor optimization
zajck Jun 6, 2023
cbdb989
Merge branch 'main' into multiple-collections
zajck Jul 5, 2023
e20839d
fix tests
zajck Jul 5, 2023
2312bb0
create clones with create2
zajck Jul 3, 2023
51aa414
use original admin addresses in voucher salt
zajck Jul 4, 2023
3b16b6f
backfill seller creator
zajck Jul 4, 2023
abf854f
Deploy voucher proxy from protocol
zajck Jul 4, 2023
bd1757d
Fix unit tests
zajck Jul 5, 2023
817a1a6
Merge branch 'main' into voucher-create2
zajck Jul 5, 2023
301865e
initialization tests
zajck Jul 5, 2023
1ad0439
Complete initialization tests
zajck Jul 6, 2023
da7b4b4
Merge branch 'main' into voucher-create2
zajck Jul 6, 2023
e31d0c4
Merge branch 'main' into voucher-create2
zajck Jul 6, 2023
f608e92
Merge branch 'main' into voucher-create2
zajck Jul 7, 2023
c27cfc0
Removing wrong comments and adding todo notes
anajuliabit Jul 7, 2023
1ec9c3c
Update test/protocol/ProtocolInitializationHandlerTest.js
mischat Jul 7, 2023
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 contracts/domain/BosonConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ string constant ESCALATION_NOT_ALLOWED = "Disputes without dispute resolver cann
// Revert Reasons: Config related
string constant FEE_PERCENTAGE_INVALID = "Percentage representation must be less than 10000";
string constant VALUE_ZERO_NOT_ALLOWED = "Value must be greater than 0";
bytes32 constant VOUCHER_PROXY_SALT = keccak256(abi.encodePacked("BosonVoucherProxy"));

// EIP712Lib
string constant PROTOCOL_NAME = "Boson Protocol";
Expand Down
12 changes: 9 additions & 3 deletions contracts/protocol/bases/SellerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ contract SellerBase is ProtocolBase, IBosonAccountEvents {
storeSeller(_seller, _authToken, lookups);

// Create clone and store its address cloneAddress
address voucherCloneAddress = cloneBosonVoucher(sellerId, 0, _seller.assistant, _voucherInitValues);
address voucherCloneAddress = cloneBosonVoucher(sellerId, 0, sender, _seller.assistant, _voucherInitValues, "");
lookups.cloneAddress[sellerId] = voucherCloneAddress;

// Notify watchers of state change
Expand Down Expand Up @@ -142,36 +142,42 @@ contract SellerBase is ProtocolBase, IBosonAccountEvents {

// Map the seller's other addresses to the seller id. It's not necessary to map the treasury address, as it only receives funds
_lookups.sellerIdByAssistant[_seller.assistant] = _seller.id;
_lookups.sellerCreator[_seller.id] = msgSender();
}

/**
* @notice Creates a minimal clone of the Boson Voucher Contract.
*
* @param _sellerId - id of the seller
* @param _collectionIndex - index of the collection.
* @param _creator - address of the seller creator
* @param _assistant - address of the assistant
* @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct
* @param _externalId - external collection id ("" for the default collection)
* @return cloneAddress - the address of newly created clone
*/
function cloneBosonVoucher(
uint256 _sellerId,
uint256 _collectionIndex,
address _creator,
mischat marked this conversation as resolved.
Show resolved Hide resolved
address _assistant,
VoucherInitValues calldata _voucherInitValues
VoucherInitValues calldata _voucherInitValues,
string memory _externalId
mischat marked this conversation as resolved.
Show resolved Hide resolved
) internal returns (address cloneAddress) {
// Pointer to stored addresses
ProtocolLib.ProtocolAddresses storage pa = protocolAddresses();

// Load beacon proxy contract address
bytes20 targetBytes = bytes20(pa.beaconProxy);
bytes32 salt = keccak256(abi.encodePacked(_creator, _externalId));

// create a minimal clone
assembly {
let clone := mload(0x40)
mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x14), targetBytes)
mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
cloneAddress := create(0, clone, 0x37)
cloneAddress := create2(0, clone, 0x37, salt)
}

// Initialize the clone
Expand Down
7 changes: 6 additions & 1 deletion contracts/protocol/facets/ConfigHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DiamondLib } from "../../diamond/DiamondLib.sol";
import { ProtocolBase } from "../bases/ProtocolBase.sol";
import { ProtocolLib } from "../libs/ProtocolLib.sol";
import { EIP712Lib } from "../libs/EIP712Lib.sol";
import { BeaconClientProxy } from "../../protocol/clients/proxy/BeaconClientProxy.sol";

/**
* @title ConfigHandlerFacet
Expand All @@ -32,10 +33,10 @@ contract ConfigHandlerFacet is IBosonConfigHandler, ProtocolBase {
DiamondLib.addSupportedInterface(type(IBosonConfigHandler).interfaceId);

// Initialize protocol config params
// _addresses.beaconProxy is ignored, since it's deployed later in this function
setTokenAddress(_addresses.token);
setTreasuryAddress(_addresses.treasury);
setVoucherBeaconAddress(_addresses.voucherBeacon);
setBeaconProxyAddress(_addresses.beaconProxy);
setProtocolFeePercentage(_fees.percentage);
setProtocolFeeFlatBoson(_fees.flatBoson);
setMaxEscalationResponsePeriod(_limits.maxEscalationResponsePeriod);
Expand All @@ -62,6 +63,10 @@ contract ConfigHandlerFacet is IBosonConfigHandler, ProtocolBase {
ProtocolLib.ProtocolMetaTxInfo storage pmti = protocolMetaTxInfo();
pmti.domainSeparator = EIP712Lib.buildDomainSeparator(PROTOCOL_NAME, PROTOCOL_VERSION);
pmti.cachedChainId = block.chainid;

// Deploy Boson Voucher proxy contract
address beaconProxy = address(new BeaconClientProxy{ salt: VOUCHER_PROXY_SALT }());
setBeaconProxyAddress(beaconProxy);
zajck marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
40 changes: 30 additions & 10 deletions contracts/protocol/facets/ProtocolInitializationHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IBosonProtocolInitializationHandler } from "../../interfaces/handlers/I
import { ProtocolLib } from "../libs/ProtocolLib.sol";
import { ProtocolBase } from "../bases/ProtocolBase.sol";
import { DiamondLib } from "../../diamond/DiamondLib.sol";
import { BeaconClientProxy } from "../../protocol/clients/proxy/BeaconClientProxy.sol";

/**
* @title BosonProtocolInitializationHandler
Expand Down Expand Up @@ -93,11 +94,9 @@ contract ProtocolInitializationHandlerFacet is IBosonProtocolInitializationHandl
if (_isUpgrade) {
if (_version == bytes32("2.2.0")) {
initV2_2_0(_initializationData);
}
if (_version == bytes32("2.2.1")) {
} else if (_version == bytes32("2.2.1")) {
initV2_2_1();
}
if (_version == bytes32("2.3.0")) {
} else if (_version == bytes32("2.3.0")) {
initV2_3_0(_initializationData);
}
}
Expand Down Expand Up @@ -145,20 +144,41 @@ contract ProtocolInitializationHandlerFacet is IBosonProtocolInitializationHandl
* - Current version is not 2.2.1
* - There are already twins. This version adds a new mapping for twins which make it incompatible with previous versions.
* - minResolutionPeriond is not present in _initializationData parameter
* - length of seller creators does not match the length of seller ids
* - if some of seller creators is zero address
* - if some of seller ids does not bellong to a seller
*
* @param _initializationData - data representing uint256 _minResolutionPeriod
* @param _initializationData - data representing uint256 _minResolutionPeriod, uint256[] memory sellerIds, address[] memory sellerCreators
*/
function initV2_3_0(bytes calldata _initializationData) internal {
// Current version must be 2.2.1
require(protocolStatus().version == bytes32("2.2.1"), WRONG_CURRENT_VERSION);

require(protocolCounters().nextTwinId == 1, TWINS_ALREADY_EXIST);

// Decode initialization data
(uint256 _minResolutionPeriod, uint256[] memory sellerIds, address[] memory sellerCreators) = abi.decode(
_initializationData,
(uint256, uint256[], address[])
);

// Initialize limits.maxPremintedVouchers (configHandlerFacet initializer)
uint256 minResolutionPeriod = abi.decode(_initializationData, (uint256));
require(minResolutionPeriod != 0, VALUE_ZERO_NOT_ALLOWED);
protocolLimits().minResolutionPeriod = minResolutionPeriod;
emit MinResolutionPeriodChanged(minResolutionPeriod, msgSender());
require(_minResolutionPeriod != 0, VALUE_ZERO_NOT_ALLOWED);
protocolLimits().minResolutionPeriod = _minResolutionPeriod;
emit MinResolutionPeriodChanged(_minResolutionPeriod, msgSender());

// Initialize sellerCreators
require(sellerIds.length == sellerCreators.length, ARRAY_LENGTH_MISMATCH);
ProtocolLib.ProtocolLookups storage lookups = protocolLookups();
for (uint256 i = 0; i < sellerIds.length; i++) {
(bool exists, , ) = fetchSeller(sellerIds[i]);
require(exists, NO_SUCH_SELLER);
require(sellerCreators[i] != address(0), INVALID_ADDRESS);

lookups.sellerCreator[sellerIds[i]] = sellerCreators[i];
}

// Deploy a new voucher proxy
protocolAddresses().beaconProxy = address(new BeaconClientProxy{ salt: VOUCHER_PROXY_SALT }());
}

/**
Expand Down
12 changes: 10 additions & 2 deletions contracts/protocol/facets/SellerHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -353,16 +353,24 @@ contract SellerHandlerFacet is SellerBase {
string calldata _externalId,
VoucherInitValues calldata _voucherInitValues
) external sellersNotPaused {
ProtocolLib.ProtocolLookups storage lookups = protocolLookups();
address assistant = msgSender();

(bool exists, uint256 sellerId) = getSellerIdByAssistant(assistant);
require(exists, NO_SUCH_SELLER);

Collection[] storage sellersAdditionalCollections = protocolLookups().additionalCollections[sellerId];
Collection[] storage sellersAdditionalCollections = lookups.additionalCollections[sellerId];
uint256 collectionIndex = sellersAdditionalCollections.length + 1; // 0 is reserved for the original collection

// Create clone and store its address to additionalCollections
address voucherCloneAddress = cloneBosonVoucher(sellerId, collectionIndex, assistant, _voucherInitValues);
address voucherCloneAddress = cloneBosonVoucher(
sellerId,
collectionIndex,
lookups.sellerCreator[sellerId],
assistant,
_voucherInitValues,
_externalId
);

// Store collection details
Collection storage newCollection = sellersAdditionalCollections.push();
Expand Down
2 changes: 2 additions & 0 deletions contracts/protocol/libs/ProtocolLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ library ProtocolLib {
mapping(uint256 => mapping(uint256 => uint256)) conditionalCommitsByTokenId;
// seller id => collections
mapping(uint256 => BosonTypes.Collection[]) additionalCollections;
// seller id => address that was used to create it
mapping(uint256 => address) sellerCreator;
}

// Incrementing id counters
Expand Down
1 change: 1 addition & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { task } = require("hardhat/config");
require("@nomicfoundation/hardhat-toolbox");
require("@nomiclabs/hardhat-web3");
require("hardhat-contract-sizer");
require("hardhat-preprocessor");

const lazyImport = async (module) => {
return await require(module);
Expand Down
6 changes: 0 additions & 6 deletions scripts/deploy-suite.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,6 @@ async function main(env, facetConfig) {
);
await transactionResponse.wait(confirmations);

transactionResponse = await bosonConfigHandler.setBeaconProxyAddress(
await bosonVoucherProxy.getAddress(),
await getFees(maxPriorityFeePerGas)
);
await transactionResponse.wait(confirmations);

// Add NFT auth token addresses to protocol config
// LENS
transactionResponse = await bosonConfigHandler.setAuthTokenContract(
Expand Down
2 changes: 1 addition & 1 deletion scripts/util/deploy-protocol-clients.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async function deployProtocolClients(protocolClientArgs, maxPriorityFeePerGas, i
// Deploy Protocol Client proxy contracts
const protocolClientProxies = await deployProtocolClientProxies(protocolClientBeacons, maxPriorityFeePerGas);

// Cast the proxies to their implementation interfaces
// Cast the proxies to their implementation interfaces ?? ToDo: what is this even needed?
zajck marked this conversation as resolved.
Show resolved Hide resolved
const protocolClients = await castProtocolClientProxies(protocolClientProxies);

return [protocolClientImpls, protocolClientBeacons, protocolClientProxies, protocolClients];
Expand Down
10 changes: 6 additions & 4 deletions scripts/util/estimate-limits.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const {
mockTwin,
accountId,
} = require("../../test/util/mock");
const { setNextBlockTimestamp, getFacetsWithArgs, calculateContractAddress } = require("../../test/util/utils.js");
const { setNextBlockTimestamp, getFacetsWithArgs, calculateCloneAddress } = require("../../test/util/utils.js");

// Common vars
let deployer,
Expand All @@ -54,7 +54,7 @@ let protocolDiamond,
groupHandler,
offerHandler,
twinHandler;
let bosonVoucher;
let bosonVoucher, proxy;
let protocolFeePercentage, protocolFeeFlatBoson, buyerEscalationDepositPercentage;
let handlers = {};
let result = {};
Expand Down Expand Up @@ -788,7 +788,9 @@ setupEnvironment["maxPremintedVouchers"] = async function (tokenCount = 10) {
await offerHandler.connect(sellerWallet1).reserveRange(offer.id, length);

// update bosonVoucher address
handlers.IBosonVoucher = bosonVoucher.attach(calculateContractAddress(await accountHandler.getAddress(), seller1.id));
handlers.IBosonVoucher = bosonVoucher.attach(
calculateCloneAddress(await accountHandler.getAddress(), await proxy.getAddress(), seller1.admin, "")
);

// make an empty array of length tokenCount
const amounts = new Array(tokenCount);
Expand Down Expand Up @@ -944,7 +946,7 @@ async function setupCommonEnvironment() {
const protocolClientArgs = [await protocolDiamond.getAddress()];
const [, beacons, proxies, bv] = await deployProtocolClients(protocolClientArgs, gasLimit);
const [beacon] = beacons;
const [proxy] = proxies;
[proxy] = proxies;
[bosonVoucher] = bv;

// Set protocolFees
Expand Down
16 changes: 14 additions & 2 deletions test/integration/01-update-account-roles-addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const { RevertReasons } = require("../../scripts/config/revert-reasons.js");
const { oneMonth } = require("../util/constants");
const {
setNextBlockTimestamp,
calculateContractAddress,
calculateCloneAddress,
calculateBosonProxyAddress,
prepareDataSignatureParameters,
applyPercentage,
setupTestEnvironment,
Expand All @@ -35,6 +36,7 @@ describe("[@skip-on-coverage] Update account roles addresses", function () {
let assistant, admin, clerk, treasury, buyer, rando, assistantDR, adminDR, clerkDR, treasuryDR, agent;
let buyerEscalationDepositPercentage, redeemedDate;
let snapshotId;
let beaconProxyAddress;

before(async function () {
accountId.next(true);
Expand All @@ -48,7 +50,9 @@ describe("[@skip-on-coverage] Update account roles addresses", function () {
disputeHandler: "IBosonDisputeHandler",
};

let protocolDiamondAddress;
({
diamondAddress: protocolDiamondAddress,
signers: [admin, treasury, buyer, rando, adminDR, treasuryDR, agent],
contractInstances: { accountHandler, offerHandler, exchangeHandler, fundsHandler, disputeHandler },
protocolConfig: [, , { buyerEscalationDepositPercentage }],
Expand All @@ -59,6 +63,9 @@ describe("[@skip-on-coverage] Update account roles addresses", function () {
assistantDR = adminDR;
clerk = clerkDR = { address: ZeroAddress };

// Get the beacon proxy address
beaconProxyAddress = await calculateBosonProxyAddress(protocolDiamondAddress);

// Get snapshot id
snapshotId = await getSnapshot();
});
Expand All @@ -76,7 +83,12 @@ describe("[@skip-on-coverage] Update account roles addresses", function () {
let expectedCloneAddress, emptyAuthToken, voucherInitValues;

beforeEach(async function () {
expectedCloneAddress = calculateContractAddress(await accountHandler.getAddress(), "1");
expectedCloneAddress = calculateCloneAddress(
await accountHandler.getAddress(),
beaconProxyAddress,
admin.address,
""
);
emptyAuthToken = mockAuthToken();
expect(emptyAuthToken.isValid()).is.true;
voucherInitValues = mockVoucherInitValues();
Expand Down
13 changes: 11 additions & 2 deletions test/integration/04-DR-removes-fees.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const {
const { DisputeResolverFee } = require("../../scripts/domain/DisputeResolverFee");
const {
setNextBlockTimestamp,
calculateContractAddress,
calculateCloneAddress,
calculateBosonProxyAddress,
applyPercentage,
setupTestEnvironment,
getSnapshot,
Expand Down Expand Up @@ -49,7 +50,9 @@ describe("[@skip-on-coverage] DR removes fee", function () {
disputeHandler: "IBosonDisputeHandler",
};

let protocolDiamondAddress;
({
diamondAddress: protocolDiamondAddress,
signers: [admin, treasury, buyer, adminDR, treasuryDR],
contractInstances: { accountHandler, offerHandler, exchangeHandler, fundsHandler, disputeHandler },
protocolConfig: [, , { buyerEscalationDepositPercentage }],
Expand All @@ -60,7 +63,13 @@ describe("[@skip-on-coverage] DR removes fee", function () {
assistantDR = adminDR;
clerk = clerkDR = { address: ZeroAddress };

expectedCloneAddress = calculateContractAddress(await accountHandler.getAddress(), "1");
const beaconProxyAddress = await calculateBosonProxyAddress(protocolDiamondAddress);
expectedCloneAddress = calculateCloneAddress(
await accountHandler.getAddress(),
beaconProxyAddress,
admin.address,
""
);
emptyAuthToken = mockAuthToken();
expect(emptyAuthToken.isValid()).is.true;
voucherInitValues = mockVoucherInitValues();
Expand Down
16 changes: 14 additions & 2 deletions test/protocol/BuyerHandlerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ const Buyer = require("../../scripts/domain/Buyer");
const { DisputeResolverFee } = require("../../scripts/domain/DisputeResolverFee");
const PausableRegion = require("../../scripts/domain/PausableRegion.js");
const { RevertReasons } = require("../../scripts/config/revert-reasons.js");
const { calculateContractAddress, setupTestEnvironment, getSnapshot, revertToSnapshot } = require("../util/utils.js");
const {
calculateCloneAddress,
calculateBosonProxyAddress,
setupTestEnvironment,
getSnapshot,
revertToSnapshot,
} = require("../util/utils.js");
const {
mockOffer,
mockSeller,
Expand Down Expand Up @@ -400,7 +406,13 @@ describe("BuyerHandler", function () {
.connect(other1)
.commitToOffer(await other1.getAddress(), offerId, { value: offer.price });

const bosonVoucherCloneAddress = calculateContractAddress(await exchangeHandler.getAddress(), "1");
const beaconProxyAddress = await calculateBosonProxyAddress(await accountHandler.getAddress());
const bosonVoucherCloneAddress = calculateCloneAddress(
await accountHandler.getAddress(),
beaconProxyAddress,
admin.address,
""
);
bosonVoucher = await getContractAt("IBosonVoucher", bosonVoucherCloneAddress);
const balance = await bosonVoucher.connect(rando).balanceOf(await other1.getAddress());
expect(balance).equal(1);
Expand Down
Loading