Skip to content

Commit

Permalink
Add support for external NFT as FutureToken
Browse files Browse the repository at this point in the history
  • Loading branch information
boyuanx committed Apr 18, 2024
1 parent fa6b0ee commit d3859e7
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 44 deletions.
58 changes: 58 additions & 0 deletions contracts/core/extensions/external-ft/TTUV2ExternalFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: AGPL v3
pragma solidity ^0.8.20;

import {TokenTableUnlockerV2} from "../../TokenTableUnlockerV2.sol";
import {Actual, Preset} from "../../../interfaces/TokenTableUnlockerV2DataModels.sol";

contract TTUV2ExternalFT is TokenTableUnlockerV2 {
error Unsupported();

function createActuals(
uint256[] calldata actualIds,
Actual[] calldata actuals_,
uint256[] calldata recipientIds,
uint256 batchId,
bytes calldata
) external virtual onlyOwner {
TokenTableUnlockerV2Storage
storage $ = _getTokenTableUnlockerV2Storage();
if (!$.isCreateable) revert NotPermissioned();
for (uint256 i = 0; i < actualIds.length; i++) {
_createActual(actualIds[i], actuals_[i], recipientIds[i], batchId);
}
_callHook(_msgData());
}

function createActuals(
address[] calldata,
Actual[] calldata,
uint256[] calldata,
uint256,
bytes calldata
) external pure virtual override {
revert Unsupported();
}

function _createActual(
uint256 actualId,
Actual memory actual,
uint256 recipientId,
uint256 batchId
) internal virtual {
TokenTableUnlockerV2Storage
storage $ = _getTokenTableUnlockerV2Storage();
address recipient = $.futureToken.ownerOf(actualId);
Preset storage preset = $._presets[actual.presetId];
if (_presetIsEmpty(preset)) revert PresetDoesNotExist();
if (actual.amountClaimed >= actual.totalAmount)
revert InvalidSkipAmount();
$.actuals[actualId] = actual;
emit ActualCreated(
actual.presetId,
actualId,
recipient,
recipientId,
batchId
);
}
}
20 changes: 20 additions & 0 deletions contracts/interfaces/ITTUDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,24 @@ interface ITTUDeployer {
)
external
returns (ITokenTableUnlockerV2, ITTFutureTokenV2, ITTTrackerTokenV2);

/**
* @notice Deploys and configures a new set of TokenTable products that uses an existing NFT as FutureToken.
* @dev Emits `TokenTableSuiteDeployed`. Throws: `AlreadyDeployed`.
* @param projectToken The project token address.
* @param projectId A unique projectId, otherwise it will revert.
* @param isUpgradeable When set to false, a `Clone` instead of a `BeaconProxy` is created to prevent future upgradeability.
* @param isCancelable Allow unlocking schedules to be cancelled in the Unlocker.
* @param isHookable Allow Unlocker to call an external hook.
* @param isWithdrawable Allow the founder to withdraw deposited funds.
*/
function deployTTSuite(
address projectToken,
address existingFutureToken,
string calldata projectId,
bool isUpgradeable,
bool isCancelable,
bool isHookable,
bool isWithdrawable
) external returns (ITokenTableUnlockerV2, ITTTrackerTokenV2);
}
128 changes: 116 additions & 12 deletions contracts/proxy/TTUDeployerLite.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ contract TTUDeployerLite is ITTUDeployer, Ownable, IVersionable {
if (!isUpgradeable) {
(unlocker, futureToken, trackerToken) = _deployClonesAndInitialize(
projectToken,
address(0),
isTransferable,
isCancelable,
isHookable,
Expand All @@ -63,7 +64,11 @@ contract TTUDeployerLite is ITTUDeployer, Ownable, IVersionable {
futureToken = ITTFutureTokenV2(
address(
new BeaconProxy(
address(beaconManager.futureTokenBeacon()),
address(
beaconManager.beacons(
beaconManager.getId("TTFutureTokenV2")
)
),
abi.encodeWithSelector(
ITTFutureTokenV2.initialize.selector,
projectToken,
Expand All @@ -75,23 +80,32 @@ contract TTUDeployerLite is ITTUDeployer, Ownable, IVersionable {
unlocker = ITokenTableUnlockerV2(
address(
new BeaconProxy(
address(beaconManager.unlockerBeacon()),
address(
beaconManager.beacons(
beaconManager.getId("TokenTableUnlockerV2")
)
),
abi.encodeWithSelector(
ITokenTableUnlockerV2.initialize.selector,
projectToken,
futureToken,
this,
isCancelable,
isHookable,
isWithdrawable
isWithdrawable,
false
)
)
)
);
trackerToken = ITTTrackerTokenV2(
address(
new BeaconProxy(
address(beaconManager.trackerTokenBeacon()),
address(
beaconManager.beacons(
beaconManager.getId("TTTrackerTokenV2")
)
),
abi.encodeWithSelector(
ITTTrackerTokenV2.initialize.selector,
address(unlocker)
Expand All @@ -112,12 +126,86 @@ contract TTUDeployerLite is ITTUDeployer, Ownable, IVersionable {
return (unlocker, futureToken, trackerToken);
}

function version() external pure virtual returns (string memory) {
return "2.5.7";
function deployTTSuite(
address projectToken,
address existingFutureToken,
string calldata projectId,
bool isUpgradeable,
bool isCancelable,
bool isHookable,
bool isWithdrawable
) external returns (ITokenTableUnlockerV2, ITTTrackerTokenV2) {
if (registry[projectId]) revert AlreadyDeployed();
registry[projectId] = true;

ITTFutureTokenV2 placeholder;
ITokenTableUnlockerV2 unlocker;
ITTTrackerTokenV2 trackerToken;
if (!isUpgradeable) {
(unlocker, placeholder, trackerToken) = _deployClonesAndInitialize(
projectToken,
existingFutureToken,
false,
isCancelable,
isHookable,
isWithdrawable
);
} else {
unlocker = ITokenTableUnlockerV2(
address(
new BeaconProxy(
address(
beaconManager.beacons(
beaconManager.getId("TTUV2ExternalFT")
)
),
abi.encodeWithSelector(
ITokenTableUnlockerV2.initialize.selector,
projectToken,
existingFutureToken,
this,
isCancelable,
isHookable,
isWithdrawable,
true
)
)
)
);
trackerToken = ITTTrackerTokenV2(
address(
new BeaconProxy(
address(
beaconManager.beacons(
beaconManager.getId("TTTrackerTokenV2")
)
),
abi.encodeWithSelector(
ITTTrackerTokenV2.initialize.selector,
address(unlocker)
)
)
)
);
}
unlocker.transferOwnership(msg.sender);
emit TokenTableSuiteDeployed(
msg.sender,
projectId,
address(unlocker),
address(existingFutureToken),
address(trackerToken)
);
return (unlocker, trackerToken);
}

function version() public pure virtual returns (string memory) {
return "2.6.0";
}

function _deployClonesAndInitialize(
address projectToken,
address existingFutureToken,
bool isTransferable,
bool isCancelable,
bool isHookable,
Expand All @@ -131,12 +219,24 @@ contract TTUDeployerLite is ITTUDeployer, Ownable, IVersionable {
ITTTrackerTokenV2 trackerToken
)
{
futureToken = ITTFutureTokenV2(
Clones.clone(beaconManager.futureTokenBeacon().implementation())
);
futureToken.initialize(projectToken, isTransferable);
if (existingFutureToken == address(0)) {
futureToken = ITTFutureTokenV2(
Clones.clone(
beaconManager
.beacons(beaconManager.getId("TTFutureTokenV2"))
.implementation()
)
);
futureToken.initialize(projectToken, isTransferable);
} else {
futureToken = ITTFutureTokenV2(existingFutureToken);
}
unlocker = ITokenTableUnlockerV2(
Clones.clone(beaconManager.unlockerBeacon().implementation())
Clones.clone(
beaconManager
.beacons(beaconManager.getId("TokenTableUnlockerV2"))
.implementation()
)
);
unlocker.initialize(
projectToken,
Expand All @@ -147,7 +247,11 @@ contract TTUDeployerLite is ITTUDeployer, Ownable, IVersionable {
isWithdrawable
);
trackerToken = ITTTrackerTokenV2(
Clones.clone(beaconManager.trackerTokenBeacon().implementation())
Clones.clone(
beaconManager
.beacons(beaconManager.getId("TTTrackerTokenV2"))
.implementation()
)
);
trackerToken.initialize(address(unlocker));
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/proxy/TTUDeployerLiteZkSync.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {TTFutureTokenV2} from "../core/TTFutureTokenV2.sol";
import {TTTrackerTokenV2} from "../core/TTTrackerTokenV2.sol";

contract TTUDeployerLiteZkSync is TTUDeployerLite {
function version() external pure virtual override returns (string memory) {
return "2.5.7-zkSync";
function version() public pure virtual override returns (string memory) {
return string.concat(super.version(), "-zkSync");
}

function _deployClonesAndInitialize(
Expand Down
49 changes: 19 additions & 30 deletions contracts/proxy/TTUV2BeaconManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,28 @@ import {IVersionable} from "../interfaces/IVersionable.sol";
* This contract should be deployed using TTUDeployer.
*/
contract TTUV2BeaconManager is Ownable, IVersionable {
UpgradeableBeacon public immutable unlockerBeacon;
UpgradeableBeacon public immutable futureTokenBeacon;
UpgradeableBeacon public immutable trackerTokenBeacon;

constructor(
address unlockerImpl,
address futureTokenImpl,
address trackerTokenImpl
) Ownable(_msgSender()) {
unlockerBeacon = new UpgradeableBeacon(unlockerImpl, address(this));
futureTokenBeacon = new UpgradeableBeacon(
futureTokenImpl,
address(this)
);
trackerTokenBeacon = new UpgradeableBeacon(
trackerTokenImpl,
address(this)
);
}

function upgradeUnlocker(address newImpl) external onlyOwner {
unlockerBeacon.upgradeTo(newImpl);
mapping(bytes32 => UpgradeableBeacon) public beacons;

constructor() Ownable(_msgSender()) {}

function upgradeCustomBeacon(
bytes32 id,
address newImpl
) external onlyOwner {
UpgradeableBeacon beacon = beacons[id];
if (address(beacon) == address(0)) {
beacon = new UpgradeableBeacon(newImpl, address(this));
} else {
beacon.upgradeTo(newImpl);
}
}

function upgradeFutureToken(address newImpl) external onlyOwner {
futureTokenBeacon.upgradeTo(newImpl);
}

function upgradePreviewToken(address newImpl) external onlyOwner {
trackerTokenBeacon.upgradeTo(newImpl);
function version() external pure returns (string memory) {
return "2.6.0";
}

function version() external pure returns (string memory) {
return "2.0.1";
// Reserved names: TokenTableUnlockerV2, TTFutureTokenV2, TTTrackerTokenV2
function getId(string memory name) external pure returns (bytes32) {
return keccak256(abi.encode(name));
}
}

0 comments on commit d3859e7

Please sign in to comment.