Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesduncombe committed Apr 9, 2024
1 parent b8ec128 commit 3d0d93d
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 17 deletions.
16 changes: 15 additions & 1 deletion contracts/fast/Crowdfund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pragma solidity 0.8.10;

import "../lib/LibAddressSet.sol";
import "../interfaces/IERC20.sol";
import "../common/AHasContext.sol";
import "../common/AHasForwarder.sol";
import "../common/AHasMembers.sol";
import "../common/AHasGovernors.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
Expand All @@ -11,7 +13,7 @@ import "@openzeppelin/contracts/utils/math/Math.sol";
* @title The `Crowdfund` FAST contract.
* @notice This contract is used to manage a crowdfunding campaign.
*/
contract Crowdfund {
contract Crowdfund is AHasContext {
using LibAddressSet for LibAddressSet.Data;

/// @notice Happens when a function requires an unmet phase.
Expand Down Expand Up @@ -118,6 +120,18 @@ contract Crowdfund {
creationBlock = block.number;
}

/// AHasContext implementation.

// The trusted forwarder in this instance is the parent FAST's trusted forwarder.
function _isTrustedForwarder(address forwarder) internal view override(AHasContext) returns (bool) {
return AHasForwarder(params.fast).isTrustedForwarder(forwarder);
}

// Override base classes to use the AHasContext implementation.
function _msgSender() internal view override(AHasContext) returns (address) {
return AHasContext._msgSender();
}

/// @dev Given a total and a fee in basis points, returns the fee amount rounded up.
function feeAmount() public view returns (uint256) {
return Math.mulDiv(collected, params.basisPointsFee, 10_000, Math.Rounding.Up);
Expand Down
16 changes: 15 additions & 1 deletion contracts/fast/Distribution.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 "../interfaces/IERC20.sol";
import "../common/AHasContext.sol";
import "../common/AHasForwarder.sol";
import "../common/AHasMembers.sol";
import "../common/AHasAutomatons.sol";
import "./FastAutomatonsFacet.sol";
Expand All @@ -19,7 +21,7 @@ import "./FastAutomatonsFacet.sol";
* - Withdrawal, during which each beneficiary can withdraw their proceeds.
* - Terminated, during which nothing is possible.
*/
contract Distribution {
contract Distribution is AHasContext {
using LibAddressSet for LibAddressSet.Data;

/// @notice Happens when a function requires an unmet phase.
Expand Down Expand Up @@ -129,6 +131,18 @@ contract Distribution {
creationBlock = block.number;
}

/// AHasContext implementation.

// The trusted forwarder in this instance is the parent FAST's trusted forwarder.
function _isTrustedForwarder(address forwarder) internal view override(AHasContext) returns (bool) {
return AHasForwarder(params.fast).isTrustedForwarder(forwarder);
}

// Override base classes to use the AHasContext implementation.
function _msgSender() internal view override(AHasContext) returns (address) {
return AHasContext._msgSender();
}

function advanceToFeeSetup() public onlyDuring(Phase.Funding) onlyFastCaller {
// Make sure that the current distribution has exactly the required amount locked.
uint256 balance = params.token.balanceOf(address(this));
Expand Down
24 changes: 18 additions & 6 deletions contracts/fast/FastCrowdfundsFacet.sol
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import "./lib/AFastFacet.sol";
import "./FastTopFacet.sol";
import "../lib/LibPaginate.sol";
import "../common/AHasContext.sol";
import "../interfaces/ICustomErrors.sol";
import "../issuer/IssuerAutomatonsFacet.sol";
import "./lib/AFastFacet.sol";
import "./lib/LibFastCrowdfunds.sol";
import "./FastTopFacet.sol";
import "./Crowdfund.sol";
import "../interfaces/ICustomErrors.sol";

/**
* @title The Fast Smart Contract.
* @notice The Fast Crowdfunds facet is in charge of deploying and keeping track of crowdfunds.
*/
contract FastCrowdfundsFacet is AFastFacet {
contract FastCrowdfundsFacet is AFastFacet, AHasContext {
using LibAddressSet for LibAddressSet.Data;

/// @notice Happens when there are insufficient funds somewhere.
error RequiresPrivilege(address who, uint32 privilege);

/// AHasContext implementation.

function _isTrustedForwarder(address forwarder) internal view override(AHasContext) returns (bool) {
return AHasForwarder(address(this)).isTrustedForwarder(forwarder);
}

// Override base classes to use the AHasContext implementation.
function _msgSender() internal view override(AHasContext) returns (address) {
return AHasContext._msgSender();
}

/**
* @notice Creates a crowdfund contract.
* @param token is the address of the ERC20 token that should be collected.
*/
function createCrowdfund(IERC20 token, address beneficiary, string memory ref) external onlyGovernor(msg.sender) {
function createCrowdfund(IERC20 token, address beneficiary, string memory ref) external onlyGovernor(_msgSender()) {
address issuer = FastTopFacet(address(this)).issuerAddress();
// Deploy a new Crowdfund contract.
Crowdfund crowdfund = new Crowdfund(
Crowdfund.Params({
owner: msg.sender,
owner: _msgSender(),
issuer: issuer,
fast: address(this),
beneficiary: beneficiary,
Expand Down
26 changes: 19 additions & 7 deletions contracts/fast/FastDistributionsFacet.sol
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import "./lib/AFastFacet.sol";
import "./FastTopFacet.sol";
import "../lib/LibPaginate.sol";
import "../common/AHasContext.sol";
import "./lib/AFastFacet.sol";
import "./lib/LibFastDistributions.sol";
import "./FastTopFacet.sol";
import "./Distribution.sol";

/**
* @title The Fast Smart Contract.
* @notice The Fast Distributions facet is in charge of deploying and keeping track of distributions.
*/
contract FastDistributionsFacet is AFastFacet {
contract FastDistributionsFacet is AFastFacet, AHasContext {
using LibAddressSet for LibAddressSet.Data;

/// @notice Happens when a call to the ERC20 token contract fails.
error TokenContractError();
/// @notice Happens when there are insufficient funds somewhere.
error InsufficientFunds(uint256 amount);

/// AHasContext implementation.

function _isTrustedForwarder(address forwarder) internal view override(AHasContext) returns (bool) {
return AHasForwarder(address(this)).isTrustedForwarder(forwarder);
}

// Override base classes to use the AHasContext implementation.
function _msgSender() internal view override(AHasContext) returns (address) {
return AHasContext._msgSender();
}

/**
* @notice Creates a distribution contract.
* @param token is the address of the ERC20 token that should be distributed.
Expand All @@ -30,9 +42,9 @@ contract FastDistributionsFacet is AFastFacet {
uint256 total,
uint256 blockLatch,
string memory ref
) external onlyMember(msg.sender) {
) external onlyMember(_msgSender()) {
// Make sure the current FAST contract has at least `total` allowance over the user's ERC20 tokens.
uint256 allowance = token.allowance(msg.sender, address(this));
uint256 allowance = token.allowance(_msgSender(), address(this));
if (allowance < total) revert InsufficientFunds(total - allowance);

// Deploy a new Distribution contract locked onto the current FAST and target currency token.
Expand All @@ -41,7 +53,7 @@ contract FastDistributionsFacet is AFastFacet {
issuer: FastTopFacet(address(this)).issuerAddress(),
fast: address(this),
blockLatch: blockLatch,
distributor: msg.sender,
distributor: _msgSender(),
token: token,
total: total,
ref: ref
Expand All @@ -50,7 +62,7 @@ contract FastDistributionsFacet is AFastFacet {
// Register our newly created distribution and keep track of it.
LibFastDistributions.data().distributionSet.add(address(dist), false);
// Transfer the ERC20 tokens to the distribution contract.
if (!token.transferFrom(msg.sender, address(dist), total)) revert TokenContractError();
if (!token.transferFrom(_msgSender(), address(dist), total)) revert TokenContractError();
// Advance to the FeeSetup phase - only the FAST contract can do that.
dist.advanceToFeeSetup();
// Emit!
Expand Down
28 changes: 27 additions & 1 deletion test/fast/FastCrowdfundsFacet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
IERC20,
Crowdfund,
FastFrontendFacet,
IForwarder,
Fast
} from "../../typechain";
import { fastFixtureFunc } from "../fixtures/fast";
import {
Expand All @@ -32,6 +34,8 @@ describe("FastCrowdfundsFacet", () => {
marketplace: FakeContract<Marketplace>,
erc20: FakeContract<IERC20>,
frontendMock: FakeContract<FastFrontendFacet>,
forwarder: FakeContract<IForwarder>,
fast: Fast,
crowdfunds: FastCrowdfundsFacet,
crowdfundsAsMember: FastCrowdfundsFacet,
crowdfundsAsGovernor: FastCrowdfundsFacet,
Expand All @@ -47,6 +51,7 @@ describe("FastCrowdfundsFacet", () => {
issuer = await smock.fake("Issuer");
marketplace = await smock.fake("Marketplace");
erc20 = await smock.fake("IERC20");
forwarder = await smock.fake("IForwarder");
marketplace.issuerAddress.returns(issuer.address);
});

Expand Down Expand Up @@ -84,7 +89,7 @@ describe("FastCrowdfundsFacet", () => {
name: "FastCrowdfundsFixture",
deployer: deployer.address,
afterDeploy: async (args) => {
const { fast } = args;
({ fast } = args);
({ frontendMock } = args);
crowdfunds = await ethers.getContractAt<FastCrowdfundsFacet>(
"FastCrowdfundsFacet",
Expand All @@ -108,6 +113,27 @@ describe("FastCrowdfundsFacet", () => {
});
});

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 fast.connect(issuerMember).setTrustedForwarder(forwarder.address);

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

describe("_msgSender", () => {
it("returns the original msg.sender");
});
});

describe("crowdfundsDefaultBasisPointFee", () => {
it("returns the default basis point fee", async () => {
const subject = await crowdfunds.crowdfundsDefaultBasisPointFee();
Expand Down
29 changes: 28 additions & 1 deletion test/fast/FastDistributionsFacet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
FastDistributionsFacet,
IERC20,
Distribution,
IForwarder,
Fast
} from "../../typechain";
import { fastFixtureFunc } from "../fixtures/fast";
import { DistributionPhase } from "../utils";
Expand All @@ -30,6 +32,8 @@ describe("FastDistributionsFacet", () => {
let issuer: FakeContract<Issuer>,
marketplace: FakeContract<Marketplace>,
erc20: FakeContract<IERC20>,
forwarder: FakeContract<IForwarder>,
fast: Fast,
distributions: FastDistributionsFacet,
distributionsAsMember: FastDistributionsFacet;

Expand All @@ -43,6 +47,7 @@ describe("FastDistributionsFacet", () => {
issuer = await smock.fake("Issuer");
marketplace = await smock.fake("Marketplace");
erc20 = await smock.fake("IERC20");
forwarder = await smock.fake("IForwarder");
marketplace.issuerAddress.returns(issuer.address);
});

Expand Down Expand Up @@ -76,7 +81,8 @@ describe("FastDistributionsFacet", () => {
opts: {
name: "FastDistributionsFixture",
deployer: deployer.address,
afterDeploy: async ({ fast }) => {
afterDeploy: async (args) => {
({ fast } = args);
await fast.connect(issuerMember).addGovernor(governor.address);
distributions = await ethers.getContractAt<FastDistributionsFacet>(
"FastDistributionsFacet",
Expand All @@ -97,6 +103,27 @@ describe("FastDistributionsFacet", () => {
});
});

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 fast.connect(issuerMember).setTrustedForwarder(forwarder.address);

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

describe("_msgSender", () => {
it("returns the original msg.sender");
});
});

/// Governorship related stuff.

describe("createDistribution", () => {
Expand Down

0 comments on commit 3d0d93d

Please sign in to comment.