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

Enhance commit to offer meta tx message #817

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
13 changes: 9 additions & 4 deletions contracts/domain/BosonConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,20 @@ bytes32 constant META_TRANSACTION_TYPEHASH = keccak256(
"MetaTransaction(uint256 nonce,address from,address contractAddress,string functionName,bytes functionSignature)"
)
);
bytes32 constant OFFER_DETAILS_TYPEHASH = keccak256("MetaTxOfferDetails(address buyer,uint256 offerId)");
bytes32 constant OFFER_PARAMETERS_TYPEHASH = keccak256(
"MetaTxOfferParameters(uint256 offerId,address exchangeToken,uint256 price,uint256 sellerDeposit,uint256 buyerCancelPenalty,string voucherRedeemableFrom,string disputePeriod,string resolutionPeriod)"
);
bytes32 constant OFFER_DETAILS_TYPEHASH = keccak256(
"MetaTxOfferDetails(address buyer,MetaTxOfferParameters offerParameters)MetaTxOfferParameters(uint256 offerId,address exchangeToken,uint256 price,uint256 sellerDeposit,uint256 buyerCancelPenalty,string voucherRedeemableFrom,string disputePeriod,string resolutionPeriod)"
);
bytes32 constant META_TX_COMMIT_TO_OFFER_TYPEHASH = keccak256(
"MetaTxCommitToOffer(uint256 nonce,address from,address contractAddress,string functionName,MetaTxOfferDetails offerDetails)MetaTxOfferDetails(address buyer,uint256 offerId)"
"MetaTxCommitToOffer(uint256 nonce,address from,address contractAddress,string functionName,MetaTxOfferDetails offerDetails)MetaTxOfferDetails(address buyer,MetaTxOfferParameters offerParameters)MetaTxOfferParameters(uint256 offerId,address exchangeToken,uint256 price,uint256 sellerDeposit,uint256 buyerCancelPenalty,string voucherRedeemableFrom,string disputePeriod,string resolutionPeriod)"
);
bytes32 constant CONDITIONAL_OFFER_DETAILS_TYPEHASH = keccak256(
"MetaTxConditionalOfferDetails(address buyer,uint256 offerId,uint256 tokenId)"
"MetaTxConditionalOfferDetails(address buyer,MetaTxOfferParameters offerParameters,uint256 tokenId)MetaTxOfferParameters(uint256 offerId,address exchangeToken,uint256 price,uint256 sellerDeposit,uint256 buyerCancelPenalty,string voucherRedeemableFrom,string disputePeriod,string resolutionPeriod)"
);
bytes32 constant META_TX_COMMIT_TO_CONDITIONAL_OFFER_TYPEHASH = keccak256(
"MetaTxCommitToConditionalOffer(uint256 nonce,address from,address contractAddress,string functionName,MetaTxConditionalOfferDetails offerDetails)MetaTxConditionalOfferDetails(address buyer,uint256 offerId,uint256 tokenId)"
"MetaTxCommitToConditionalOffer(uint256 nonce,address from,address contractAddress,string functionName,MetaTxConditionalOfferDetails offerDetails)MetaTxConditionalOfferDetails(address buyer,MetaTxOfferParameters offerParameters,uint256 tokenId)MetaTxOfferParameters(uint256 offerId,address exchangeToken,uint256 price,uint256 sellerDeposit,uint256 buyerCancelPenalty,string voucherRedeemableFrom,string disputePeriod,string resolutionPeriod)"
);
bytes32 constant EXCHANGE_DETAILS_TYPEHASH = keccak256("MetaTxExchangeDetails(uint256 exchangeId)");
bytes32 constant META_TX_EXCHANGE_TYPEHASH = keccak256(
Expand Down
2 changes: 1 addition & 1 deletion contracts/domain/BosonTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ contract BosonTypes {

struct HashInfo {
bytes32 typeHash;
function(bytes memory) internal pure returns (bytes32) hashFunction;
function(bytes memory) internal view returns (bytes32) hashFunction;
}

struct OfferFees {
Expand Down
71 changes: 67 additions & 4 deletions contracts/protocol/facets/MetaTransactionsHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import { ProtocolLib } from "../libs/ProtocolLib.sol";
import { ProtocolBase } from "../bases/ProtocolBase.sol";
import { EIP712Lib } from "../libs/EIP712Lib.sol";
import { DateTime } from "@quant-finance/solidity-datetime/contracts/DateTime.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";

/**
* @title MetaTransactionsHandlerFacet
Expand Down Expand Up @@ -111,10 +113,12 @@
* @param _offerDetails - the offer details
* @return the hashed representation of the offer details struct
*/
function hashOfferDetails(bytes memory _offerDetails) internal pure returns (bytes32) {
function hashOfferDetails(bytes memory _offerDetails) internal view returns (bytes32) {
// The buyer and offerId are part of calldata
(address buyer, uint256 offerId) = abi.decode(_offerDetails, (address, uint256));
return keccak256(abi.encode(OFFER_DETAILS_TYPEHASH, buyer, offerId));

return keccak256(abi.encode(OFFER_DETAILS_TYPEHASH, buyer, hashOfferParameters(offerId)));
}

Check warning

Code scanning / Slither

Dead-code Warning

MetaTransactionsHandlerFacet.hashOfferDetails(bytes) is never used and should be removed

/**
* @notice Returns hashed representation of the conditional offer details struct.
Expand All @@ -122,10 +126,69 @@
* @param _offerDetails - the conditional offer details
* @return the hashed representation of the conditional offer details struct
*/
function hashConditionalOfferDetails(bytes memory _offerDetails) internal pure returns (bytes32) {
function hashConditionalOfferDetails(bytes memory _offerDetails) internal view returns (bytes32) {
(address buyer, uint256 offerId, uint256 tokenId) = abi.decode(_offerDetails, (address, uint256, uint256));
return keccak256(abi.encode(CONDITIONAL_OFFER_DETAILS_TYPEHASH, buyer, offerId, tokenId));
return keccak256(abi.encode(CONDITIONAL_OFFER_DETAILS_TYPEHASH, buyer, hashOfferParameters(offerId), tokenId));
}
Dismissed Show dismissed Hide dismissed

/**
* @notice Returns hashed representation of the offer parameters struct.
* This is reused for both the offer and conditional offer.
*
* @param _offerId - the offer id
* @return the hashed representation of the offer details struct
*/
function hashOfferParameters(uint256 _offerId) internal view returns (bytes32) {
// Get other offer details from the protocol
Offer storage offer = getValidOffer(_offerId);
bytes memory redeemableFrom;
{
OfferDates storage offerDates = fetchOfferDates(_offerId);

(uint256 year, uint256 month, uint256 day, uint256 hour, uint256 minute, uint256 second) = DateTime
.timestampToDateTime(offerDates.voucherRedeemableFrom);
redeemableFrom = abi.encodePacked(
Strings.toString(year),
"/",
Strings.toString(month),
"/",
Strings.toString(day),
" ",
Strings.toString(hour),
":",
Strings.toString(minute),
":",
Strings.toString(second)
);
}

OfferDurations storage offerDurations = fetchOfferDurations(_offerId);

return
keccak256(
abi.encode(
OFFER_PARAMETERS_TYPEHASH,
_offerId,
offer.exchangeToken,
offer.price,
offer.sellerDeposit,
offer.buyerCancelPenalty,
keccak256(redeemableFrom),
keccak256(
abi.encodePacked(
Strings.toString(offerDurations.disputePeriod / DateTime.SECONDS_PER_DAY),
" days"
)
),
keccak256(
abi.encodePacked(
Strings.toString(offerDurations.resolutionPeriod / DateTime.SECONDS_PER_DAY),
" days"
)
)
)
);
}

Check warning

Code scanning / Slither

Dead-code Warning

MetaTransactionsHandlerFacet.hashOfferParameters(uint256) is never used and should be removed

/**
* @notice Returns hashed representation of the exchange details struct.
Expand Down
13 changes: 12 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
},
"dependencies": {
"@openzeppelin/contracts": "^4.9.0",
"@openzeppelin/contracts-upgradeable": "4.9.3"
"@openzeppelin/contracts-upgradeable": "4.9.3",
"@quant-finance/solidity-datetime": "^2.2.0"
},
"devDependencies": {
"@bosonprotocol/solidoc": "3.0.3",
Expand Down
91 changes: 78 additions & 13 deletions test/protocol/MetaTransactionsHandlerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const {
setupTestEnvironment,
getSnapshot,
revertToSnapshot,
timestampToDateTime,
} = require("../util/utils.js");
const {
mockOffer,
Expand All @@ -42,7 +43,7 @@ const {
mockExchange,
mockCondition,
} = require("../util/mock");
const { oneMonth } = require("../util/constants");
const { oneMonth, SECONDS_PER_DAY } = require("../util/constants");
const {
getSelectors,
FacetCutAction,
Expand Down Expand Up @@ -74,7 +75,8 @@ describe("IBosonMetaTransactionsHandler", function () {
let metaTransactionsHandler, nonce, functionSignature;
let seller, offerId, buyerId;
let validOfferDetails,
offerType,
offerDetailsType,
offerParametersType,
metaTransactionType,
metaTxExchangeType,
customTransactionType,
Expand Down Expand Up @@ -1645,9 +1647,20 @@ describe("IBosonMetaTransactionsHandler", function () {
.createOffer(offer, offerDates, offerDurations, disputeResolver.id, agentId);

// Set the offer Type
offerType = [
{ name: "buyer", type: "address" },
offerParametersType = [
{ name: "offerId", type: "uint256" },
{ name: "exchangeToken", type: "address" },
{ name: "price", type: "uint256" },
{ name: "sellerDeposit", type: "uint256" },
{ name: "buyerCancelPenalty", type: "uint256" },
{ name: "voucherRedeemableFrom", type: "string" },
{ name: "disputePeriod", type: "string" },
{ name: "resolutionPeriod", type: "string" },
];

offerDetailsType = [
{ name: "buyer", type: "address" },
{ name: "offerParameters", type: "MetaTxOfferParameters" },
];

// Set the message Type
Expand All @@ -1662,7 +1675,8 @@ describe("IBosonMetaTransactionsHandler", function () {

customTransactionType = {
MetaTxCommitToOffer: metaTransactionType,
MetaTxOfferDetails: offerType,
MetaTxOfferDetails: offerDetailsType,
MetaTxOfferParameters: offerParametersType,
};

// prepare validOfferDetails
Expand All @@ -1671,8 +1685,27 @@ describe("IBosonMetaTransactionsHandler", function () {
offerId: offer.id,
};

let [year, month, day, hour, minute, second] = timestampToDateTime(
Number(offerDates.voucherRedeemableFrom)
);
let voucherRedeemableFrom = `${year}/${month}/${day} ${hour}:${minute}:${second}`;

const extendedOfferDetails = {
buyer: validOfferDetails.buyer,
offerParameters: {
offerId: validOfferDetails.offerId,
exchangeToken: offer.exchangeToken,
price: offer.price,
sellerDeposit: offer.sellerDeposit,
buyerCancelPenalty: offer.buyerCancelPenalty,
voucherRedeemableFrom: voucherRedeemableFrom,
disputePeriod: `${BigInt(offerDurations.disputePeriod) / SECONDS_PER_DAY} days`,
resolutionPeriod: `${BigInt(offerDurations.resolutionPeriod) / SECONDS_PER_DAY} days`,
},
};

// Prepare the message
message.offerDetails = validOfferDetails;
message.offerDetails = extendedOfferDetails;

// Deposit native currency to the same seller id
await fundsHandler
Expand Down Expand Up @@ -1733,7 +1766,7 @@ describe("IBosonMetaTransactionsHandler", function () {
validOfferDetails.offerId = offerId;

// Prepare the message
message.offerDetails = validOfferDetails;
message.offerDetails.offerParameters.offerId = offerId;

// Collect the signature components
let { r, s, v } = await prepareDataSignatureParameters(
Expand Down Expand Up @@ -1870,9 +1903,20 @@ describe("IBosonMetaTransactionsHandler", function () {
.createOfferWithCondition(offer, offerDates, offerDurations, disputeResolver.id, condition, agentId);

// Set the offer Type
offerType = [
{ name: "buyer", type: "address" },
offerParametersType = [
{ name: "offerId", type: "uint256" },
{ name: "exchangeToken", type: "address" },
{ name: "price", type: "uint256" },
{ name: "sellerDeposit", type: "uint256" },
{ name: "buyerCancelPenalty", type: "uint256" },
{ name: "voucherRedeemableFrom", type: "string" },
{ name: "disputePeriod", type: "string" },
{ name: "resolutionPeriod", type: "string" },
];

offerDetailsType = [
{ name: "buyer", type: "address" },
{ name: "offerParameters", type: "MetaTxOfferParameters" },
{ name: "tokenId", type: "uint256" },
];

Expand All @@ -1888,7 +1932,8 @@ describe("IBosonMetaTransactionsHandler", function () {

customTransactionType = {
MetaTxCommitToConditionalOffer: metaTransactionType,
MetaTxConditionalOfferDetails: offerType,
MetaTxConditionalOfferDetails: offerDetailsType,
MetaTxOfferParameters: offerParametersType,
};

// prepare validOfferDetails
Expand All @@ -1898,8 +1943,28 @@ describe("IBosonMetaTransactionsHandler", function () {
tokenId: "0",
};

let [year, month, day, hour, minute, second] = timestampToDateTime(
Number(offerDates.voucherRedeemableFrom)
);
let voucherRedeemableFrom = `${year}/${month}/${day} ${hour}:${minute}:${second}`;

const extendedOfferDetails = {
buyer: validOfferDetails.buyer,
offerParameters: {
offerId: validOfferDetails.offerId,
exchangeToken: offer.exchangeToken,
price: offer.price,
sellerDeposit: offer.sellerDeposit,
buyerCancelPenalty: offer.buyerCancelPenalty,
voucherRedeemableFrom: voucherRedeemableFrom,
disputePeriod: `${BigInt(offerDurations.disputePeriod) / SECONDS_PER_DAY} days`,
resolutionPeriod: `${BigInt(offerDurations.resolutionPeriod) / SECONDS_PER_DAY} days`,
},
tokenId: validOfferDetails.tokenId,
};

// Prepare the message
message.offerDetails = validOfferDetails;
message.offerDetails = extendedOfferDetails;

// Deposit native currency to the same seller id
await fundsHandler
Expand Down Expand Up @@ -1960,7 +2025,7 @@ describe("IBosonMetaTransactionsHandler", function () {
validOfferDetails.offerId = offerId;

// Prepare the message
message.offerDetails = validOfferDetails;
message.offerDetails.offerParameters.offerId = offerId;

// Collect the signature components
let { r, s, v } = await prepareDataSignatureParameters(
Expand Down Expand Up @@ -1999,7 +2064,7 @@ describe("IBosonMetaTransactionsHandler", function () {
validOfferDetails.tokenId = tokenId;

// Prepare the message
message.offerDetails = validOfferDetails;
message.offerDetails.tokenId = tokenId;

// Collect the signature components
let { r, s, v } = await prepareDataSignatureParameters(
Expand Down
6 changes: 6 additions & 0 deletions test/util/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const oneDay = 86400n; // 1 day in seconds
const ninetyDays = oneDay * 90n; // 90 days in seconds
const oneWeek = oneDay * 7n; // 7 days in seconds
const oneMonth = oneDay * 31n; // 31 days in seconds
const SECONDS_PER_DAY = 24n * 60n * 60n;
const SECONDS_PER_HOUR = 60n * 60n;
const SECONDS_PER_MINUTE = 60n;
const VOUCHER_NAME = "Boson Voucher (rNFT)";
const VOUCHER_SYMBOL = "BOSON_VOUCHER_RNFT";
const SEAPORT_ADDRESS = "0x00000000000001ad428e4906aE43D8F9852d0dD6"; // 1.4
Expand All @@ -18,3 +21,6 @@ exports.VOUCHER_NAME = VOUCHER_NAME;
exports.VOUCHER_SYMBOL = VOUCHER_SYMBOL;
exports.maxPriorityFeePerGas = maxPriorityFeePerGas;
exports.SEAPORT_ADDRESS = SEAPORT_ADDRESS;
exports.SECONDS_PER_DAY = SECONDS_PER_DAY;
exports.SECONDS_PER_HOUR = SECONDS_PER_HOUR;
exports.SECONDS_PER_MINUTE = SECONDS_PER_MINUTE;
Loading