diff --git a/.env.local.example b/.env.local.example index ba1cb8c..98fb018 100644 --- a/.env.local.example +++ b/.env.local.example @@ -17,4 +17,5 @@ THESPACE_TEAM_TOKENS= THESPACE_INCENTIVES_ADDRESS= THESPACE_INCENTIVES_TOKENS= THESPACE_LP_ADDRESS= -THESPACE_LP_TOKENS= \ No newline at end of file +THESPACE_LP_TOKENS= +THESPACE_GASLESS_TRUSTED_FORWARDER= \ No newline at end of file diff --git a/.env.polygon-mainnet.example b/.env.polygon-mainnet.example index 5185d94..bbcb0da 100644 --- a/.env.polygon-mainnet.example +++ b/.env.polygon-mainnet.example @@ -19,3 +19,4 @@ THESPACE_INCENTIVES_ADDRESS= THESPACE_INCENTIVES_TOKENS= THESPACE_LP_ADDRESS= THESPACE_LP_TOKENS= +THESPACE_GASLESS_TRUSTED_FORWARDER= \ No newline at end of file diff --git a/.env.polygon-mumbai.example b/.env.polygon-mumbai.example index 57dcbad..2ec6423 100644 --- a/.env.polygon-mumbai.example +++ b/.env.polygon-mumbai.example @@ -18,4 +18,5 @@ THESPACE_TEAM_TOKENS= THESPACE_INCENTIVES_ADDRESS= THESPACE_INCENTIVES_TOKENS= THESPACE_LP_ADDRESS= -THESPACE_LP_TOKENS= \ No newline at end of file +THESPACE_LP_TOKENS= +THESPACE_GASLESS_TRUSTED_FORWARDER= \ No newline at end of file diff --git a/.gas-snapshot b/.gas-snapshot index 36339d9..e6f3b8b 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,12 +1,12 @@ LogbookTest:testClaim() (gas: 134551) -LogbookTest:testDonate(uint96) (runs: 256, μ: 146160, ~: 156782) -LogbookTest:testDonateWithCommission(uint96,uint96) (runs: 256, μ: 142810, ~: 140238) -LogbookTest:testFork(uint96,string) (runs: 256, μ: 426671, ~: 451048) -LogbookTest:testForkRecursively(uint8,uint96) (runs: 256, μ: 2238066, ~: 268025) -LogbookTest:testForkWithCommission(uint96,string,uint256) (runs: 256, μ: 425319, ~: 257305) +LogbookTest:testDonate(uint96) (runs: 256, μ: 145967, ~: 156782) +LogbookTest:testDonateWithCommission(uint96,uint96) (runs: 256, μ: 145124, ~: 140238) +LogbookTest:testFork(uint96,string) (runs: 256, μ: 425260, ~: 451048) +LogbookTest:testForkRecursively(uint8,uint96) (runs: 256, μ: 2326445, ~: 268025) +LogbookTest:testForkWithCommission(uint96,string,uint256) (runs: 256, μ: 409925, ~: 257299) LogbookTest:testMulticall() (gas: 286758) LogbookTest:testPublicSale() (gas: 202464) -LogbookTest:testPublish(string) (runs: 256, μ: 264899, ~: 264312) +LogbookTest:testPublish(string) (runs: 256, μ: 264910, ~: 264312) LogbookTest:testPublishEn1000() (gas: 243675) LogbookTest:testPublishEn140() (gas: 221330) LogbookTest:testPublishEn200() (gas: 223090) @@ -25,9 +25,9 @@ LogbookTest:testPublishZh5000() (gas: 607910) LogbookTest:testSetDescription() (gas: 141188) LogbookTest:testSetForkPrice() (gas: 154277) LogbookTest:testSetTitle() (gas: 167036) -LogbookTest:testSplitRoyalty(uint8,uint8,uint96) (runs: 256, μ: 1204465, ~: 310758) +LogbookTest:testSplitRoyalty(uint8,uint8,uint96) (runs: 256, μ: 1194757, ~: 310758) LogbookTest:testWithdraw() (gas: 7464895) -LogbookNFTSVGTest:testTokenURI(uint8,uint8,uint16) (runs: 256, μ: 2186202, ~: 1336263) +LogbookNFTSVGTest:testTokenURI(uint8,uint8,uint16) (runs: 256, μ: 2144428, ~: 1341199) SnapperTest:testCannotInitRegionByNotOwner() (gas: 11456) SnapperTest:testCannotReInitRegion() (gas: 14399) SnapperTest:testCannotTakeSnapshotBeforeInit() (gas: 15780) @@ -36,65 +36,65 @@ SnapperTest:testCannotTakeSnapshotWrongLastBlock() (gas: 49275) SnapperTest:testCannotTakeSnapshotWrongSnapshotBlock() (gas: 24002) SnapperTest:testInitRegion(uint256) (runs: 256, μ: 114657, ~: 114657) SnapperTest:testTakeSnapshot() (gas: 48060) -ACLManagerTest:testCannotGrantACLManagerRole() (gas: 12276) -ACLManagerTest:testCannotGrantRoleByNonACLManager() (gas: 17404) -ACLManagerTest:testCannotGrantRoleToZeroAddress() (gas: 12243) +ACLManagerTest:testCannotGrantACLManagerRole() (gas: 12343) +ACLManagerTest:testCannotGrantRoleByNonACLManager() (gas: 17538) +ACLManagerTest:testCannotGrantRoleToZeroAddress() (gas: 12310) ACLManagerTest:testCannotRenounceRoleByACLManager() (gas: 12148) ACLManagerTest:testCannotRenounceRoleByAttacker() (gas: 12416) -ACLManagerTest:testCannotTransferRoleByAttacker() (gas: 12564) -ACLManagerTest:testCannotTransferRoleToZeroAddress() (gas: 12241) -ACLManagerTest:testGrantRole() (gas: 23656) -ACLManagerTest:testRenounceRole() (gas: 27973) -ACLManagerTest:testRoles() (gas: 15436) -ACLManagerTest:testTransferRole() (gas: 21660) -TheSpaceTest:testBatchBid() (gas: 692298) -TheSpaceTest:testBatchSetPixels(uint16,uint8) (runs: 256, μ: 363366, ~: 367792) -TheSpaceTest:testBidDefaultedToken() (gas: 410646) -TheSpaceTest:testBidExistingToken() (gas: 356958) -TheSpaceTest:testBidNewToken() (gas: 301097) -TheSpaceTest:testCanTransferFromIfSettleTax() (gas: 357146) -TheSpaceTest:testCannotBidExceedAllowance() (gas: 60820) -TheSpaceTest:testCannotBidOutBoundTokens() (gas: 260259) -TheSpaceTest:testCannotBidPriceTooLow() (gas: 341477) +ACLManagerTest:testCannotTransferRoleByAttacker() (gas: 12542) +ACLManagerTest:testCannotTransferRoleToZeroAddress() (gas: 12219) +ACLManagerTest:testGrantRole() (gas: 23657) +ACLManagerTest:testRenounceRole() (gas: 27974) +ACLManagerTest:testRoles() (gas: 15370) +ACLManagerTest:testTransferRole() (gas: 21572) +TheSpaceTest:testBatchBid() (gas: 698412) +TheSpaceTest:testBatchSetPixels(uint16,uint8) (runs: 256, μ: 367313, ~: 372596) +TheSpaceTest:testBidDefaultedToken() (gas: 414260) +TheSpaceTest:testBidExistingToken() (gas: 361090) +TheSpaceTest:testBidNewToken() (gas: 304387) +TheSpaceTest:testCanTransferFromIfSettleTax() (gas: 360596) +TheSpaceTest:testCannotBidExceedAllowance() (gas: 63155) +TheSpaceTest:testCannotBidOutBoundTokens() (gas: 265833) +TheSpaceTest:testCannotBidPriceTooLow() (gas: 345213) TheSpaceTest:testCannotGetTaxWithNonExistingToken() (gas: 16448) -TheSpaceTest:testCannotGetTokenURIInLogicContract() (gas: 296845) -TheSpaceTest:testCannotSetColorByAttacker() (gas: 302930) +TheSpaceTest:testCannotGetTokenURIInLogicContract() (gas: 300339) +TheSpaceTest:testCannotSetColorByAttacker() (gas: 306442) TheSpaceTest:testCannotSetConfigByAttacker() (gas: 12100) -TheSpaceTest:testCannotSetPixel(uint256) (runs: 256, μ: 312268, ~: 312268) -TheSpaceTest:testCannotSetPriceByNonOwner() (gas: 303006) -TheSpaceTest:testCannotSetTokenImageURIByNonACLManager() (gas: 11909) +TheSpaceTest:testCannotSetPixel(uint256) (runs: 256, μ: 315781, ~: 315781) +TheSpaceTest:testCannotSetPriceByNonOwner() (gas: 306540) +TheSpaceTest:testCannotSetTokenImageURIByNonACLManager() (gas: 11976) TheSpaceTest:testCannotSetTotalSupplyByAttacker() (gas: 11883) -TheSpaceTest:testCannotTransferFromIfDefault() (gas: 395605) -TheSpaceTest:testCannotUpgradeByAttacker() (gas: 11608) -TheSpaceTest:testCollectableTax() (gas: 333183) -TheSpaceTest:testDefault() (gas: 391886) +TheSpaceTest:testCannotTransferFromIfDefault() (gas: 399202) +TheSpaceTest:testCannotUpgradeByAttacker() (gas: 11674) +TheSpaceTest:testCollectableTax() (gas: 336429) +TheSpaceTest:testDefault() (gas: 395407) TheSpaceTest:testGetConfig() (gas: 14302) -TheSpaceTest:testGetExistingPixel() (gas: 309333) -TheSpaceTest:testGetNonExistingPixel() (gas: 60249) +TheSpaceTest:testGetExistingPixel() (gas: 312668) +TheSpaceTest:testGetNonExistingPixel() (gas: 60294) TheSpaceTest:testGetNonExistingPrice() (gas: 19524) -TheSpaceTest:testGetOwner() (gas: 348906) -TheSpaceTest:testGetOwnerOfNonExistingToken() (gas: 13346) -TheSpaceTest:testGetPixelsByOwnerWithNoPixels() (gas: 24309) -TheSpaceTest:testGetPixelsByOwnerWithOnePixel() (gas: 319236) -TheSpaceTest:testGetPixelsPageByOwnerWithPixels() (gas: 586023) -TheSpaceTest:testGetPrice() (gas: 297936) -TheSpaceTest:testGetTax() (gas: 377402) +TheSpaceTest:testGetOwner() (gas: 352994) +TheSpaceTest:testGetOwnerOfNonExistingToken() (gas: 13302) +TheSpaceTest:testGetPixelsByOwnerWithNoPixels() (gas: 24444) +TheSpaceTest:testGetPixelsByOwnerWithOnePixel() (gas: 322597) +TheSpaceTest:testGetPixelsPageByOwnerWithPixels() (gas: 590565) +TheSpaceTest:testGetPrice() (gas: 301226) +TheSpaceTest:testGetTax() (gas: 381822) TheSpaceTest:testGetTokenImageURI() (gas: 14461) -TheSpaceTest:testGetTokenURI() (gas: 331478) -TheSpaceTest:testSetColor() (gas: 328913) -TheSpaceTest:testSetMintTax() (gas: 268980) -TheSpaceTest:testSetPixel(uint256) (runs: 256, μ: 400887, ~: 400887) -TheSpaceTest:testSetPrice(uint256) (runs: 256, μ: 302087, ~: 302087) -TheSpaceTest:testSetPriceByOperator(uint256) (runs: 256, μ: 349876, ~: 349876) -TheSpaceTest:testSetPriceTooHigh() (gas: 312185) -TheSpaceTest:testSetTaxRate() (gas: 345290) -TheSpaceTest:testSetTokenImageURI() (gas: 354173) -TheSpaceTest:testSetTotalSupply(uint256) (runs: 256, μ: 349455, ~: 349457) -TheSpaceTest:testSetTreasuryShare() (gas: 381549) -TheSpaceTest:testSettleTax() (gas: 336739) -TheSpaceTest:testTaxCalculation() (gas: 401824) -TheSpaceTest:testTokenShouldBeDefaulted() (gas: 322889) +TheSpaceTest:testGetTokenURI() (gas: 334972) +TheSpaceTest:testSetColor() (gas: 332403) +TheSpaceTest:testSetMintTax() (gas: 272026) +TheSpaceTest:testSetPixel(uint256) (runs: 256, μ: 405493, ~: 405493) +TheSpaceTest:testSetPrice(uint256) (runs: 256, μ: 305377, ~: 305377) +TheSpaceTest:testSetPriceByOperator(uint256) (runs: 256, μ: 353410, ~: 353410) +TheSpaceTest:testSetPriceTooHigh() (gas: 315719) +TheSpaceTest:testSetTaxRate() (gas: 348580) +TheSpaceTest:testSetTokenImageURI() (gas: 357938) +TheSpaceTest:testSetTotalSupply(uint256) (runs: 256, μ: 352765, ~: 352768) +TheSpaceTest:testSetTreasuryShare() (gas: 384904) +TheSpaceTest:testSettleTax() (gas: 340094) +TheSpaceTest:testTaxCalculation() (gas: 407374) +TheSpaceTest:testTokenShouldBeDefaulted() (gas: 326157) TheSpaceTest:testTotalSupply() (gas: 7568) -TheSpaceTest:testUpgradeTo() (gas: 3197912) -TheSpaceTest:testWithdrawTreasury() (gas: 352481) -TheSpaceTest:testWithdrawUBI() (gas: 375552) +TheSpaceTest:testUpgradeTo() (gas: 3306893) +TheSpaceTest:testWithdrawTreasury() (gas: 355836) +TheSpaceTest:testWithdrawUBI() (gas: 378863) diff --git a/.gitmodules b/.gitmodules index d9f2eed..5aa3b8e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,10 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/forwarder"] + path = lib/forwarder + url = https://github.com/opengsn/forwarder + branch = master +[submodule "lib/gsn"] + path = lib/gsn + url = https://github.com/opengsn/gsn diff --git a/Makefile b/Makefile index 1e3f189..ba43587 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ deploy-the-space-currency: clean @forge create SpaceToken --rpc-url ${ETH_RPC_URL} --private-key ${DEPLOYER_PRIVATE_KEY} --constructor-args ${THESPACE_TREASURY_ADDRESS} --constructor-args ${THESPACE_TREASURY_TOKENS} --constructor-args ${THESPACE_TEAM_ADDRESS} --constructor-args ${THESPACE_TEAM_TOKENS} --constructor-args ${THESPACE_INCENTIVES_ADDRESS} --constructor-args ${THESPACE_INCENTIVES_TOKENS} --constructor-args ${THESPACE_LP_ADDRESS} --constructor-args ${THESPACE_LP_TOKENS} --legacy --verify --etherscan-api-key ${ETHERSCAN_API_KEY} deploy-the-space: clean - @forge create TheSpace --rpc-url ${ETH_RPC_URL} --private-key ${DEPLOYER_PRIVATE_KEY} --constructor-args ${THESPACE_CURRENCY_ADDRESS} --constructor-args ${THESPACE_REGISTRY_ADDRESS} --constructor-args ${THESPACE_TOKEN_IMAGE_URI} --constructor-args ${THESPACE_ACL_MANAGER_ADDRESS} --constructor-args ${THESPACE_MARKET_ADMIN_ADDRESS} --constructor-args ${THESPACE_TREASURY_ADMIN_ADDRESS} --legacy --verify --etherscan-api-key ${ETHERSCAN_API_KEY} + @forge create TheSpace --rpc-url ${ETH_RPC_URL} --private-key ${DEPLOYER_PRIVATE_KEY} --constructor-args ${THESPACE_CURRENCY_ADDRESS} --constructor-args ${THESPACE_REGISTRY_ADDRESS} --constructor-args ${THESPACE_TOKEN_IMAGE_URI} --constructor-args ${THESPACE_ACL_MANAGER_ADDRESS} --constructor-args ${THESPACE_MARKET_ADMIN_ADDRESS} --constructor-args ${THESPACE_TREASURY_ADMIN_ADDRESS} --constructor-args ${THESPACE_GASLESS_TRUSTED_FORWARDER} --legacy --verify --etherscan-api-key ${ETHERSCAN_API_KEY} ## snapper deploy-snapper: clean diff --git a/foundry.toml b/foundry.toml index 0ebc615..4428248 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,6 +5,7 @@ optimizer = true optimizer_runs = 200 remappings = [ "@openzeppelin/=lib/openzeppelin-contracts/", + "@opengsn/contracts/=lib/gsn/packages/contracts/", "forge-std/=lib/forge-std/src/" ] verbosity = 3 diff --git a/lib/gsn b/lib/gsn new file mode 160000 index 0000000..4da0d24 --- /dev/null +++ b/lib/gsn @@ -0,0 +1 @@ +Subproject commit 4da0d248e2b3d96fa649dc099471602b348e503b diff --git a/src/TheSpace/ACLManager.sol b/src/TheSpace/ACLManager.sol index 16eb3d5..cc4bf90 100644 --- a/src/TheSpace/ACLManager.sol +++ b/src/TheSpace/ACLManager.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts/access/AccessControl.sol"; -import "@openzeppelin/contracts/utils/Context.sol"; +import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; import "./IACLManager.sol"; -contract ACLManager is IACLManager, Context { +contract ACLManager is IACLManager { mapping(Role => address) private _roles; constructor( @@ -25,7 +25,7 @@ contract ACLManager is IACLManager, Context { * @dev Throws if called by any address other than the role address. */ modifier onlyRole(Role role) { - if (!_hasRole(role, _msgSender())) revert RoleRequired(role); + if (!_hasRole(role, msg.sender)) revert RoleRequired(role); _; } diff --git a/src/TheSpace/ITheSpace.sol b/src/TheSpace/ITheSpace.sol index f3bd559..cf5a2cc 100644 --- a/src/TheSpace/ITheSpace.sol +++ b/src/TheSpace/ITheSpace.sol @@ -105,6 +105,16 @@ interface ITheSpace { */ function setTokenImageURI(string memory uri_) external; + /** + * @notice Change the trusted forwarder. + * + * @dev Access: only `Role.aclManager`. + * @dev Throws: `RoleRequired` error. + * + * @param trustedForwarder_ new address + */ + function setTrustedForwarder(address trustedForwarder_) external; + ////////////////////////////// /// Pixel ////////////////////////////// diff --git a/src/TheSpace/TheSpace.sol b/src/TheSpace/TheSpace.sol index 8fcdb83..cedd07c 100644 --- a/src/TheSpace/TheSpace.sol +++ b/src/TheSpace/TheSpace.sol @@ -5,13 +5,14 @@ import "@openzeppelin/contracts/utils/Multicall.sol"; import "@openzeppelin/contracts/utils/Base64.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@opengsn/contracts/src/ERC2771Recipient.sol"; import "./ACLManager.sol"; import "./TheSpaceRegistry.sol"; import "./ITheSpaceRegistry.sol"; import "./ITheSpace.sol"; -contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { +contract TheSpace is ITheSpace, ERC2771Recipient, Multicall, ReentrancyGuard, ACLManager { TheSpaceRegistry public registry; // token image shared by all tokens @@ -23,7 +24,8 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { string memory tokenImageURI_, address aclManager_, address marketAdmin_, - address treasuryAdmin_ + address treasuryAdmin_, + address trustedForwarder_ ) ACLManager(aclManager_, marketAdmin_, treasuryAdmin_) { // deploy logic contract only and upgrade later if (registryAddress_ != address(0)) { @@ -43,6 +45,8 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { } tokenImageURI = tokenImageURI_; + + _setTrustedForwarder(trustedForwarder_); } /** @@ -92,6 +96,11 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { tokenImageURI = uri_; } + /// @inheritdoc ITheSpace + function setTrustedForwarder(address trustedForwarder_) external onlyRole(Role.aclManager) { + _setTrustedForwarder(trustedForwarder_); + } + ////////////////////////////// /// Pixel ////////////////////////////// @@ -123,12 +132,12 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { ) external { bid(tokenId_, bidPrice_); setPrice(tokenId_, newPrice_); - _setColor(tokenId_, color_, msg.sender); + _setColor(tokenId_, color_, _msgSender()); } /// @inheritdoc ITheSpace function setColor(uint256 tokenId_, uint256 color_) public { - if (!registry.isApprovedOrOwner(msg.sender, tokenId_)) revert Unauthorized(); + if (!registry.isApprovedOrOwner(_msgSender(), tokenId_)) revert Unauthorized(); _setColor(tokenId_, color_, registry.ownerOf(tokenId_)); } @@ -203,7 +212,7 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { /// @inheritdoc ITheSpace function setPrice(uint256 tokenId_, uint256 price_) public { - if (!(registry.isApprovedOrOwner(msg.sender, tokenId_))) revert Unauthorized(); + if (!(registry.isApprovedOrOwner(_msgSender(), tokenId_))) revert Unauthorized(); if (price_ == _getPrice(tokenId_)) return; bool success = settleTax(tokenId_); @@ -248,7 +257,7 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { if (registry.exists(tokenId_)) { // skip if already own - if (owner == msg.sender) return; + if (owner == _msgSender()) return; // clear tax bool success = _collectTax(tokenId_); @@ -262,13 +271,13 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { if (price_ < bidPrice) revert PriceTooLow(); // settle ERC20 token - registry.transferCurrencyFrom(msg.sender, owner, bidPrice); + registry.transferCurrencyFrom(_msgSender(), owner, bidPrice); // settle ERC721 token - registry.safeTransferByMarket(owner, msg.sender, tokenId_); + registry.safeTransferByMarket(owner, _msgSender(), tokenId_); // emit deal event - registry.emitDeal(tokenId_, owner, msg.sender, bidPrice); + registry.emitDeal(tokenId_, owner, _msgSender(), bidPrice); return; } else { @@ -284,16 +293,16 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { if (price_ < bidPrice) revert PriceTooLow(); // settle ERC20 token - registry.transferCurrencyFrom(msg.sender, address(registry), bidPrice); + registry.transferCurrencyFrom(_msgSender(), address(registry), bidPrice); // record as tax income - _recordTax(tokenId_, msg.sender, mintTax); + _recordTax(tokenId_, _msgSender(), mintTax); // settle ERC721 token - registry.mint(msg.sender, tokenId_); + registry.mint(_msgSender(), tokenId_); // emit deal event - registry.emitDeal(tokenId_, address(0), msg.sender, bidPrice); + registry.emitDeal(tokenId_, address(0), _msgSender(), bidPrice); } ////////////////////////////// @@ -424,7 +433,7 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { /// @inheritdoc ITheSpace function _beforeTransferByRegistry(uint256 tokenId_) external returns (bool success) { - if (msg.sender != address(registry)) revert Unauthorized(); + if (_msgSender() != address(registry)) revert Unauthorized(); // clear tax or default settleTax(tokenId_); @@ -443,7 +452,7 @@ contract TheSpace is ITheSpace, Multicall, ReentrancyGuard, ACLManager { /// @inheritdoc ITheSpace function _tokenURI(uint256 tokenId_) external view returns (string memory uri) { - if (msg.sender != address(registry)) revert Unauthorized(); + if (_msgSender() != address(registry)) revert Unauthorized(); if (!registry.exists(tokenId_)) revert TokenNotExists(); diff --git a/src/test/TheSpace/BaseTheSpace.t.sol b/src/test/TheSpace/BaseTheSpace.t.sol index 9708dc5..f446afa 100644 --- a/src/test/TheSpace/BaseTheSpace.t.sol +++ b/src/test/TheSpace/BaseTheSpace.t.sol @@ -32,6 +32,7 @@ contract BaseTheSpaceTest is Test { address constant TEAM = address(206); address constant INCENTIVES = address(207); address constant LP = address(208); + address constant GASLESS_TRUSTED_FORWARDER = address(209); uint256 constant TREASURY_TOKENS = 662607015; uint256 constant TEAM_TOKENS = 150000000; uint256 constant INCENTIVES_TOKENS = 137392985; @@ -91,7 +92,8 @@ contract BaseTheSpaceTest is Test { TOKEN_IMAGE_URI, ACL_MANAGER, MARKET_ADMIN, - TREASURY_ADMIN + TREASURY_ADMIN, + GASLESS_TRUSTED_FORWARDER ); registry = thespace.registry(); diff --git a/src/test/TheSpace/TheSpace.t.sol b/src/test/TheSpace/TheSpace.t.sol index 1bb3ff4..d25f67d 100644 --- a/src/test/TheSpace/TheSpace.t.sol +++ b/src/test/TheSpace/TheSpace.t.sol @@ -15,7 +15,8 @@ contract TheSpaceTest is BaseTheSpaceTest { TOKEN_IMAGE_URI, ACL_MANAGER, MARKET_ADMIN, - TREASURY_ADMIN + TREASURY_ADMIN, + GASLESS_TRUSTED_FORWARDER ); assertEq(address(thespace2.registry()), address(registry)); assertEq(registry.owner(), address(thespace));