Skip to content

Commit

Permalink
feat(node): support configurable staking collateral token (#1130)
Browse files Browse the repository at this point in the history
Co-authored-by: cryptoAtwill <willes.lau@protocol.ai>
Co-authored-by: raulk <raul@protocol.ai>
Co-authored-by: Karel Moravec <moravec.wdd@gmail.com>
Co-authored-by: raulk <raul.kripalani@gmail.com>
  • Loading branch information
5 people authored Sep 25, 2024
1 parent 294531c commit b0b1dd0
Show file tree
Hide file tree
Showing 47 changed files with 1,866 additions and 1,098 deletions.
340 changes: 170 additions & 170 deletions contracts/.storage-layouts/GatewayActorModifiers.json

Large diffs are not rendered by default.

338 changes: 169 additions & 169 deletions contracts/.storage-layouts/GatewayDiamond.json

Large diffs are not rendered by default.

356 changes: 182 additions & 174 deletions contracts/.storage-layouts/SubnetActorDiamond.json

Large diffs are not rendered by default.

358 changes: 183 additions & 175 deletions contracts/.storage-layouts/SubnetActorModifiers.json

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions contracts/contracts/SubnetActorDiamond.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ import {IERC165} from "./interfaces/IERC165.sol";
import {GatewayCannotBeZero, NotGateway, InvalidSubmissionPeriod, InvalidCollateral, InvalidMajorityPercentage, InvalidPowerScale} from "./errors/IPCErrors.sol";
import {BATCH_PERIOD, MAX_MSGS_PER_BATCH} from "./structs/CrossNet.sol";
import {LibDiamond} from "./lib/LibDiamond.sol";
import {PermissionMode, SubnetID, SupplyKind, SupplySource} from "./structs/Subnet.sol";
import {PermissionMode, SubnetID, AssetKind, Asset} from "./structs/Subnet.sol";
import {SubnetIDHelper} from "./lib/SubnetIDHelper.sol";
import {LibStaking} from "./lib/LibStaking.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SupplySourceHelper} from "./lib/SupplySourceHelper.sol";
import {AssetHelper} from "./lib/AssetHelper.sol";
error FunctionNotFound(bytes4 _functionSelector);

contract SubnetActorDiamond {
SubnetActorStorage internal s;

using SubnetIDHelper for SubnetID;
using SupplySourceHelper for SupplySource;
using AssetHelper for Asset;

struct ConstructorParams {
uint256 minActivationCollateral;
Expand All @@ -33,7 +33,8 @@ contract SubnetActorDiamond {
ConsensusType consensus;
int8 powerScale;
PermissionMode permissionMode;
SupplySource supplySource;
Asset supplySource;
Asset collateralSource;
SubnetID parentId;
address validatorGater;
}
Expand Down Expand Up @@ -96,6 +97,7 @@ contract SubnetActorDiamond {
s.changeSet.startConfigurationNumber = LibStaking.INITIAL_CONFIGURATION_NUMBER;
// Set the supply strategy.
s.supplySource = params.supplySource;
s.collateralSource = params.collateralSource;

if (params.validatorGater != address(0)) {
s.validatorGater = params.validatorGater;
Expand Down
46 changes: 28 additions & 18 deletions contracts/contracts/gateway/GatewayManagerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {SubnetActorGetterFacet} from "../subnet/SubnetActorGetterFacet.sol";
import {BURNT_FUNDS_ACTOR} from "../constants/Constants.sol";
import {IpcEnvelope} from "../structs/CrossNet.sol";
import {FvmAddress} from "../structs/FvmAddress.sol";
import {SubnetID, Subnet, SupplySource} from "../structs/Subnet.sol";
import {Membership, SupplyKind} from "../structs/Subnet.sol";
import {SubnetID, Subnet, Asset} from "../structs/Subnet.sol";
import {Membership, AssetKind} from "../structs/Subnet.sol";
import {AlreadyRegisteredSubnet, CannotReleaseZero, MethodNotAllowed, NotEnoughFunds, NotEnoughFundsToRelease, NotEnoughCollateral, NotEmptySubnetCircSupply, NotRegisteredSubnet, InvalidXnetMessage, InvalidXnetMessageReason} from "../errors/IPCErrors.sol";
import {LibGateway} from "../lib/LibGateway.sol";
import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol";
Expand All @@ -16,31 +16,34 @@ import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol";
import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {SupplySourceHelper} from "../lib/SupplySourceHelper.sol";
import {AssetHelper} from "../lib/AssetHelper.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

string constant ERR_CHILD_SUBNET_NOT_ALLOWED = "Subnet does not allow child subnets";

contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
using FilAddress for address payable;
using SubnetIDHelper for SubnetID;
using SupplySourceHelper for SupplySource;
using AssetHelper for Asset;
using EnumerableSet for EnumerableSet.Bytes32Set;

/// @notice register a subnet in the gateway. It is called by a subnet when it reaches the threshold stake
/// @dev The subnet can optionally pass a genesis circulating supply that would be pre-allocated in the
/// subnet from genesis (without having to wait for the subnet to be spawned to propagate the funds).
function register(uint256 genesisCircSupply) external payable {
function register(uint256 genesisCircSupply, uint256 collateral) external payable {
// If L2+ support is not enabled, only allow the registration of new
// subnets in the root
if (s.networkName.route.length + 1 >= s.maxTreeDepth) {
revert MethodNotAllowed(ERR_CHILD_SUBNET_NOT_ALLOWED);
}

if (msg.value < genesisCircSupply) {
revert NotEnoughFunds();
if (genesisCircSupply > 0) {
SubnetActorGetterFacet(msg.sender).supplySource().lock(genesisCircSupply);
}
if (collateral > 0) {
SubnetActorGetterFacet(msg.sender).collateralSource().lock(collateral);
}
uint256 collateral = msg.value - genesisCircSupply;

SubnetID memory subnetId = s.networkName.createSubnetId(msg.sender);

(bool registered, Subnet storage subnet) = LibGateway.getSubnet(subnetId);
Expand All @@ -58,18 +61,23 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
}

/// @notice addStake - add collateral for an existing subnet
function addStake() external payable {
if (msg.value == 0) {
function addStake(uint256 amount) external payable {
if (amount == 0) {
revert NotEnoughFunds();
}

// The fund flow for stake is from Validator -> SubnetActor -> Gateway.
// Because msg.sender is actually the subnet actor, this method sends the fund from
// the subnet actor caller the gateway.
SubnetActorGetterFacet(msg.sender).collateralSource().lock(amount);

(bool registered, Subnet storage subnet) = LibGateway.getSubnet(msg.sender);

if (!registered) {
revert NotRegisteredSubnet();
}

subnet.stake += msg.value;
subnet.stake += amount;
}

/// @notice release collateral for an existing subnet.
Expand All @@ -91,7 +99,10 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {

subnet.stake -= amount;

payable(subnet.id.getActor()).sendValue(amount);
// Release fund flows from Gateway -> SubnetActor -> ReleaseQueue (Locking) -> Validator.
// Because msg.sender is actually the subnet actor, this method sends the fund back to
// the subnet actor caller.
SubnetActorGetterFacet(msg.sender).collateralSource().transferFunds(payable(msg.sender), amount);
}

/// @notice kill an existing subnet.
Expand All @@ -114,8 +125,7 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
delete s.subnets[id];

s.subnetKeys.remove(id);

payable(msg.sender).sendValue(stake);
SubnetActorGetterFacet(msg.sender).collateralSource().transferFunds(payable(msg.sender), stake);
}

/// @notice credits the received value to the specified address in the specified child subnet.
Expand All @@ -137,8 +147,8 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
}

// Validate that the supply strategy is native.
SupplySource memory supplySource = SubnetActorGetterFacet(subnetId.getActor()).supplySource();
supplySource.expect(SupplyKind.Native);
Asset memory supplySource = SubnetActorGetterFacet(subnetId.getActor()).supplySource();
supplySource.expect(AssetKind.Native);

IpcEnvelope memory crossMsg = CrossMsgHelper.createFundMsg({
subnet: subnetId,
Expand Down Expand Up @@ -172,8 +182,8 @@ contract GatewayManagerFacet is GatewayActorModifiers, ReentrancyGuard {
// Check that the supply strategy is ERC20.
// There is no need to check whether the subnet exists. If it doesn't exist, the call to getter will revert.
// LibGateway.commitTopDownMsg will also revert if the subnet doesn't exist.
SupplySource memory supplySource = SubnetActorGetterFacet(subnetId.getActor()).supplySource();
supplySource.expect(SupplyKind.ERC20);
Asset memory supplySource = SubnetActorGetterFacet(subnetId.getActor()).supplySource();
supplySource.expect(AssetKind.ERC20);

// Locks a specified amount into custody, adjusting for tokens with transfer fees. This operation
// accommodates inflationary tokens, potentially reflecting a higher effective locked amount.
Expand Down
4 changes: 2 additions & 2 deletions contracts/contracts/gateway/GatewayMessengerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ pragma solidity ^0.8.23;
import {GatewayActorModifiers} from "../lib/LibGatewayActorStorage.sol";
import {IpcEnvelope, CallMsg, IpcMsgKind} from "../structs/CrossNet.sol";
import {IPCMsgType} from "../enums/IPCMsgType.sol";
import {SubnetID, SupplyKind, IPCAddress} from "../structs/Subnet.sol";
import {SubnetID, AssetKind, IPCAddress} from "../structs/Subnet.sol";
import {InvalidXnetMessage, InvalidXnetMessageReason, CannotSendCrossMsgToItself, MethodNotAllowed} from "../errors/IPCErrors.sol";
import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol";
import {LibGateway} from "../lib/LibGateway.sol";
import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol";
import {SupplySourceHelper} from "../lib/SupplySourceHelper.sol";
import {AssetHelper} from "../lib/AssetHelper.sol";
import {CrossMsgHelper} from "../lib/CrossMsgHelper.sol";
import {FvmAddressHelper} from "../lib/FvmAddressHelper.sol";

Expand Down
6 changes: 3 additions & 3 deletions contracts/contracts/gateway/router/XnetMessagingFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import {Subnet} from "../../structs/Subnet.sol";
import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol";
import {SubnetIDHelper} from "../../lib/SubnetIDHelper.sol";
import {CrossMsgHelper} from "../../lib/CrossMsgHelper.sol";
import {SupplySourceHelper} from "../../lib/SupplySourceHelper.sol";
import {SupplySource} from "../../structs/Subnet.sol";
import {AssetHelper} from "../../lib/AssetHelper.sol";
import {Asset} from "../../structs/Subnet.sol";

import {NotRegisteredSubnet} from "../../errors/IPCErrors.sol";

contract XnetMessagingFacet is GatewayActorModifiers {
using SubnetIDHelper for SubnetID;
using CrossMsgHelper for IpcEnvelope;
using SupplySourceHelper for SupplySource;
using AssetHelper for Asset;

/// @notice Applies top-down cross-net messages locally. This is invoked by IPC nodes when drawing messages from
/// their parent subnet for local execution. That's why the sender is restricted to the system sender,
Expand Down
8 changes: 4 additions & 4 deletions contracts/contracts/interfaces/IGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {FvmAddress} from "../structs/FvmAddress.sol";
interface IGateway {
/// @notice Register is called by subnet actors to put the required collateral
/// and register the subnet to the hierarchy.
function register(uint256 genesisCircSupply) external payable;
function register(uint256 genesisCircSupply, uint256 collateral) external payable;

/// @notice AddStake adds stake to the collateral of a subnet.
function addStake() external payable;
function addStake(uint256 amount) external payable;

/// @notice Release stake recovers some collateral of the subnet
function releaseStake(uint256 amount) external;
Expand All @@ -31,7 +31,7 @@ interface IGateway {
/// This functions ends up minting supply in the subnet equal to the value of the transaction. It does so by
/// committing the relevant top-down message, updating the top-down nonce along the way.
///
/// Calling this method on a subnet whose supply source is not 'native' will revert with UnexpectedSupplySource().
/// Calling this method on a subnet whose supply source is not 'native' will revert with UnexpectedAsset().
function fund(SubnetID calldata subnetId, FvmAddress calldata to) external payable;

/// @notice fundWithToken locks the specified amount of tokens in the ERC20 contract linked to the subnet, and
Expand All @@ -45,7 +45,7 @@ interface IGateway {
/// It's possible to call this method from an EOA or a contract. Regardless, it's recommended to approve strictly
/// the amount that will subsequently be deposited into the subnet. Keeping outstanding approvals is not recommended.
///
/// Calling this method on a subnet whose supply source is not 'ERC20' will revert with UnexpectedSupplySource().
/// Calling this method on a subnet whose supply source is not 'ERC20' will revert with UnexpectedAsset().
function fundWithToken(SubnetID calldata subnetId, FvmAddress calldata to, uint256 amount) external;

/// @notice Release creates a new check message to release funds in parent chain
Expand Down
Loading

0 comments on commit b0b1dd0

Please sign in to comment.