Skip to content

Commit

Permalink
Adds the context behaviour to MarketplaceAccessFacet.
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesduncombe committed Feb 6, 2024
1 parent f7b873d commit 35964df
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 8 deletions.
24 changes: 19 additions & 5 deletions contracts/marketplace/MarketplaceAccessFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity 0.8.10;
import "../lib/LibAddressSet.sol";
import "../lib/LibPaginate.sol";
import "../common/AHasMembers.sol";
import "../common/AHasForwarder.sol";
import "../common/AHasContext.sol";
import "../issuer/IssuerTopFacet.sol";
import "../interfaces/ICustomErrors.sol";
import "../interfaces/IHasActiveMembers.sol";
Expand All @@ -15,9 +17,21 @@ import "./MarketplaceAutomatonsFacet.sol";
* @title The Marketplace Smart Contract.
* @notice The Marketplace Access facet is in charge of keeping track of marketplace members.
*/
contract MarketplaceAccessFacet is AMarketplaceFacet, AHasMembers, IHasActiveMembers {
contract MarketplaceAccessFacet is AMarketplaceFacet, AHasMembers, AHasContext, IHasActiveMembers {
using LibAddressSet for LibAddressSet.Data;

/// AHasContext implementation.

// TODO: Could _isTrustedForwarder actually have it's default implementation point to
// IHasForwarder(address(this)).isTrustedForwarder(forwarder) or similar?
function _isTrustedForwarder(address forwarder) internal view override(AHasContext) returns (bool) {
return AHasForwarder(address(this)).isTrustedForwarder(forwarder);
}

function _msgSender() internal view override(AHasMembers, AHasContext) returns (address) {
return AHasContext._msgSender();
}

/// AHasMembers implementation.

function isMembersManager(address who) internal view override(AHasMembers) returns (bool) {
Expand Down Expand Up @@ -56,25 +70,25 @@ contract MarketplaceAccessFacet is AMarketplaceFacet, AHasMembers, IHasActiveMem
*/
function memberAddedToFast(address member) external {
// Verify that the given address is in fact a registered FAST contract.
if (!IssuerTopFacet(LibMarketplace.data().issuer).isFastRegistered(msg.sender)) {
if (!IssuerTopFacet(LibMarketplace.data().issuer).isFastRegistered(_msgSender())) {
revert ICustomErrors.RequiresFastContractCaller();
}
// Keep track of the member's FAST membership.
// TODO: We don't throw until we've fixed the `marketplace.fastMemberships`.
LibMarketplaceAccess.data().fastMemberships[member].add(msg.sender, true);
LibMarketplaceAccess.data().fastMemberships[member].add(_msgSender(), true);
}

/**
* @notice Callback from FAST contracts allowing the Marketplace contract to keep track of FAST memberships.
* @param member The member for which a FAST membership has been removed.
*/
function memberRemovedFromFast(address member) external {
if (!IssuerTopFacet(LibMarketplace.data().issuer).isFastRegistered(msg.sender)) {
if (!IssuerTopFacet(LibMarketplace.data().issuer).isFastRegistered(_msgSender())) {
revert ICustomErrors.RequiresFastContractCaller();
}
// Remove the tracked membership.
// TODO: We don't throw until we've fixed the `marketplace.fastMemberships`.
LibMarketplaceAccess.data().fastMemberships[member].remove(msg.sender, true);
LibMarketplaceAccess.data().fastMemberships[member].remove(_msgSender(), true);
}

/// IHasActiveMembers implementation.
Expand Down
82 changes: 79 additions & 3 deletions test/marketplace/MarketplaceAccessFacet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Issuer,
Marketplace,
} from "../../typechain/hardhat-diamond-abi/HardhatDiamondABI.sol";
import { IForwarder } from "../../typechain";
chai.use(solidity);
chai.use(smock.matchers);

Expand All @@ -24,11 +25,13 @@ describe("MarketplaceAccessFacet", () => {
alice: SignerWithAddress,
bob: SignerWithAddress,
rob: SignerWithAddress,
john: SignerWithAddress;
john: SignerWithAddress,
trustedForwarder: SignerWithAddress;

let issuer: FakeContract<Issuer>,
fast: FakeContract<Fast>,
marketplace: Marketplace,
forwarder: FakeContract<IForwarder>,
access: MarketplaceAccessFacet,
issuerMemberAccess: MarketplaceAccessFacet;

Expand All @@ -50,10 +53,11 @@ describe("MarketplaceAccessFacet", () => {

before(async () => {
// Keep track of a few signers.
[deployer, issuerMember, alice, bob, rob, john] = await ethers.getSigners();
// Mock Issuer and Fast contracts.
[deployer, issuerMember, alice, bob, rob, john, trustedForwarder] = await ethers.getSigners();
// Mock Issuer, Fast and IForwarder contracts.
issuer = await smock.fake("Issuer");
fast = await smock.fake("Fast");
forwarder = await smock.fake("IForwarder");
});

beforeEach(async () => {
Expand All @@ -79,6 +83,29 @@ describe("MarketplaceAccessFacet", () => {
resetFastMock();
});

describe("AHasContext implementation", () => {
describe("_isTrustedForwarder", () => {
it("returns true if the address is a trusted forwarder", async () => {
// Set the trusted forwarder.
forwarder.supportsInterface.reset();
forwarder.supportsInterface.whenCalledWith(/* IForwarder */ "0x25e23e64").returns(true);
forwarder.supportsInterface.returns(false);

await marketplace.connect(issuerMember).setTrustedForwarder(forwarder.address);

// isTrustedForwarder() should delegate to _isTrustedForwarder().
const subject = await marketplace.connect(issuerMember).isTrustedForwarder(forwarder.address);
expect(subject).to.eq(true);
});
});

describe("_msgSender", () => {
it("returns the original msg.sender", async () => {
// Call a function on the Marketplace contract that's sponsored.
});
});
});

describe("IHasMembers", () => {
describe("isMember", () => {
beforeEach(async () => {
Expand Down Expand Up @@ -171,6 +198,39 @@ describe("MarketplaceAccessFacet", () => {
});

it("calls back onMemberAdded");

it("is a sponsorable action", async () => {
// Fix the forwarder to support the IForwarder interface.
forwarder.supportsInterface.reset();
forwarder.supportsInterface.whenCalledWith(/* IForwarder */ "0x25e23e64").returns(true);
forwarder.supportsInterface.returns(false);

// Set the trusted forwarder.
await marketplace.connect(issuerMember).setTrustedForwarder(forwarder.address);

// Impersonate the trusted forwarder contract.
const accessAsForwarder = await impersonateContract(access, forwarder.address);

// Build the data to call addMember() on the Marketplace contract.
// Pack the issuerMember address at the end - this is sponsored callers address.
const encodedFunctionCall = await access.interface.encodeFunctionData("addMember", [alice.address]);
const data = ethers.utils.solidityPack(
["bytes", "address"],
[encodedFunctionCall, issuerMember.address]
);

// As the forwarder send the packed transaction.
await accessAsForwarder.signer.sendTransaction(
{
data: data,
to: access.address,
}
);

// Check that Alice is a member.
const subject = await marketplace.isMember(alice.address);
expect(subject).to.eq(true);
});
});

describe("removeMember", () => {
Expand Down Expand Up @@ -235,6 +295,22 @@ describe("MarketplaceAccessFacet", () => {
});
});

describe("isTrustedForwarder", async () => {
it("checks if the passed forwarder is the stored forwarder");
});

describe("setTrustedForwarder", async () => {
it("reverts if passed forwarder doesn't support the interface", async () => {
// Set the trusted forwarder.
const subject = marketplace.connect(issuerMember).setTrustedForwarder(issuer.address);
expect(subject).to.be.revertedWith("InterfaceNotSupported");
});
});

describe("getTrustedForwarder", async () => {
it("returns the stored trusted forwarder");
});

describe("fastMemberships", () => {
beforeEach(async () => {
// This FAST is registered.
Expand Down

0 comments on commit 35964df

Please sign in to comment.