Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ERC20 wrapper #38

Merged
merged 34 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9545b43
feat: add erc20 wrapper
amarinkovic Nov 21, 2022
a67bd5b
Revert "fix!: restrict internalTransfer() to transfer between entitie…
amarinkovic Nov 22, 2022
1d2e54f
feat: add erc20 wrapper transfer methods
amarinkovic Nov 22, 2022
a89f39d
test: fix tests after making tokenization mandatory
amarinkovic Nov 22, 2022
95210a8
test: assert several internal transfer requirements
amarinkovic Nov 22, 2022
6692215
feat: wrap entity token
amarinkovic Nov 23, 2022
57d3619
feat: scope certain internal transfers to wrappers only
amarinkovic Nov 23, 2022
6752ec4
refactor: fix facet method names
amarinkovic Nov 23, 2022
06c78f8
feat: diamond address is sender
amarinkovic Nov 23, 2022
266d425
test: token wrapping
amarinkovic Nov 23, 2022
ce8d4b0
fix: wrapper test
amarinkovic Nov 23, 2022
8a526d1
debug CI test
amarinkovic Nov 23, 2022
5831814
Revert "debug CI test"
amarinkovic Nov 23, 2022
d764918
test: basic wrapper props
amarinkovic Nov 23, 2022
03151ee
test: stabilise external deposit fuzzer
amarinkovic Nov 23, 2022
52ea3a8
test-refactor: rename variables
amarinkovic Nov 23, 2022
f869c05
test: wrapper balance; minor code cleanup
amarinkovic Nov 24, 2022
da83c7f
test: wrapper allowance and transfer
amarinkovic Nov 24, 2022
be3d35f
test: assert allowance decreases on transfer, assert reverts for inva…
amarinkovic Nov 24, 2022
04d7293
test: assert wrap requirements
amarinkovic Nov 24, 2022
c1320f1
refactor: use lib to assert entity
amarinkovic Nov 24, 2022
d81f586
feat: wrapper erc20 permit
amarinkovic Nov 24, 2022
e270092
doc: update with natspec changes
amarinkovic Nov 24, 2022
b4d05ea
refactor: remove console import
amarinkovic Nov 28, 2022
e141cec
refactor: store token name and symbol as strings
amarinkovic Nov 28, 2022
515b207
Merge branch 'main' into erc20_wrapper
amarinkovic Nov 28, 2022
8395665
refactor: better method naming for wrapper requirements
amarinkovic Nov 29, 2022
df0aade
refactor: minor naming improvement
amarinkovic Nov 29, 2022
3bc7efd
refactor: add not wrapped already requirement to wrapper constructor
amarinkovic Nov 29, 2022
12f7030
refactor: move wrap token into SystemFacet/LibObject
amarinkovic Nov 29, 2022
9517800
feat: make wrapper non-reentrant
amarinkovic Nov 30, 2022
cc02308
fix: apply check-effects-interactions pattern to transfer and transfe…
kevin-fruitful Nov 30, 2022
dec84c5
Revert "fix: apply check-effects-interactions pattern to transfer and…
amarinkovic Nov 30, 2022
3bd06d8
fix: re-apply check-effects-interactions pattern to transfer and tran…
amarinkovic Nov 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/diamonds/nayms/AppStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ struct AppStorage {
mapping(bytes32 => bytes32) objectParent; // objectId => parentId
mapping(bytes32 => bytes32) objectDataHashes;
mapping(bytes32 => bytes32) objectTokenSymbol;
mapping(bytes32 => bytes32) objectTokenName;
amarinkovic marked this conversation as resolved.
Show resolved Hide resolved
mapping(bytes32 => address) objectTokenWrapper;
mapping(bytes32 => bool) existingEntities; // entityId => is an entity?
mapping(bytes32 => bool) existingSimplePolicies; // simplePolicyId => is a simple policy?
//// ENTITY ////
Expand Down
6 changes: 6 additions & 0 deletions src/diamonds/nayms/Modifiers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,10 @@ contract Modifiers {
require(LibACL._isInGroup(_objectId, _contextId, _group), "not in group");
_;
}

modifier assertERC20Wrapper(bytes32 _tokenId) {
(, , , , address erc20Wrapper) = LibObject._getObjectMeta(_tokenId);
require(msg.sender == erc20Wrapper, "only wrapper calls allowed");
_;
}
}
4 changes: 4 additions & 0 deletions src/diamonds/nayms/facets/AdminFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,8 @@ contract AdminFacet is Modifiers {
function getSystemId() external pure returns (bytes32) {
return LibAdmin._getSystemId();
}

function isObjectTokenizable(bytes32 _objectId) external view returns (bool) {
return LibObject._isObjectTokenizable(_objectId);
}
}
17 changes: 15 additions & 2 deletions src/diamonds/nayms/facets/EntityFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ contract EntityFacet is Modifiers, ReentrancyGuard {
* @notice Enable an entity to be tokenized
* @param _objectId ID of the entity
* @param _symbol The symbol assigned to the entity token
* @param _name The name assigned to the entity token
*/
function enableEntityTokenization(bytes32 _objectId, string memory _symbol) external assertSysAdmin {
LibObject._enableObjectTokenization(_objectId, _symbol);
function enableEntityTokenization(
bytes32 _objectId,
string memory _symbol,
string memory _name
) external assertSysAdmin {
LibObject._enableObjectTokenization(_objectId, _symbol, _name);
}

/**
Expand All @@ -70,6 +75,14 @@ contract EntityFacet is Modifiers, ReentrancyGuard {
LibEntity._startTokenSale(_entityId, _amount, _totalPrice);
}

/**
* @notice Wrap an entity token as ERC20
* @param _entityId ID of the entity
*/
function wrapToken(bytes32 _entityId) external nonReentrant assertSysMgr {
LibEntity._wrapToken(_entityId);
}

/**
* @notice Update entity metadata
* @param _entityId ID of the entity
Expand Down
6 changes: 5 additions & 1 deletion src/diamonds/nayms/facets/SystemFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,18 @@ contract SystemFacet is Modifiers {
* @return parent object parent
* @return dataHash object data hash
* @return tokenSymbol object token symbol
* @return tokenName object token name
* @return tokenWrapper object token ERC20 wrapper address
*/
function getObjectMeta(bytes32 _id)
external
view
returns (
bytes32 parent,
bytes32 dataHash,
bytes32 tokenSymbol
bytes32 tokenSymbol,
bytes32 tokenName,
address tokenWrapper
)
{
return LibObject._getObjectMeta(_id);
Expand Down
24 changes: 19 additions & 5 deletions src/diamonds/nayms/facets/TokenizedVaultFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,32 @@ contract TokenizedVaultFacet is Modifiers {
* @param to token receiver
* @param tokenId Internal ID of the token
*/
function internalTransfer(
function internalTransferFromEntity(
bytes32 to,
bytes32 tokenId,
uint256 amount
) external assertEntityAdmin(LibObject._getParent(LibHelpers._getSenderId())) {
// require(LibTokenizedVault._internalBalanceOf(senderId, tokenId) >= amount, "internalTransfer: insufficient balance");
bytes32 senderId = LibHelpers._getIdForAddress(msg.sender);
bytes32 senderEntityId = LibObject._getParent(senderId);
) external assertEntityAdmin(LibObject._getParentFromAddress(msg.sender)) {
bytes32 senderEntityId = LibObject._getParentFromAddress(msg.sender);
require(LibHelpers._stringToBytes32(LibConstants.STM_IDENTIFIER) != tokenId, "internalTransfer: can't transfer internal veNAYM");
LibTokenizedVault._internalTransfer(senderEntityId, to, tokenId, amount);
}

/**
* @notice Internal transfer of `amount` tokens `from` -> `to`
* @dev Transfer tokens internally between two IDs
* @param from token sender
* @param to token receiver
* @param tokenId Internal ID of the token
*/
function wrapperInternalTransferFrom(
bytes32 from,
bytes32 to,
bytes32 tokenId,
uint256 amount
) external assertERC20Wrapper(tokenId) {
LibTokenizedVault._internalTransfer(from, to, tokenId, amount);
}

function internalBurn(
bytes32 from,
bytes32 tokenId,
Expand Down
6 changes: 6 additions & 0 deletions src/diamonds/nayms/interfaces/IAdminFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,10 @@ interface IAdminFacet {
* @return System Identifier
*/
function getSystemId() external pure returns (bytes32);

/**
* @notice Check if object can be tokenized
* @param _objectId ID of the object
*/
function isObjectTokenizable(bytes32 _objectId) external returns (bool);
}
13 changes: 12 additions & 1 deletion src/diamonds/nayms/interfaces/IEntityFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ interface IEntityFacet {
* @notice Enable an entity to be tokenized
* @param _entityId ID of the entity
* @param _symbol The symbol assigned to the entity token
* @param _name The name assigned to the entity token
*/
function enableEntityTokenization(bytes32 _entityId, string memory _symbol) external;
function enableEntityTokenization(
bytes32 _entityId,
string memory _symbol,
string memory _name
) external;

/**
* @notice Start token sale of `_amount` tokens for total price of `_totalPrice`
Expand All @@ -53,6 +58,12 @@ interface IEntityFacet {
uint256 _totalPrice
) external;

/**
* @notice Wrap an entity token as ERC20
* @param _entityId ID of the entity
*/
function wrapToken(bytes32 _entityId) external;

/**
* @notice Update entity metadata
* @param _entityId ID of the entity
Expand Down
6 changes: 5 additions & 1 deletion src/diamonds/nayms/interfaces/ISystemFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,17 @@ interface ISystemFacet {
* @return parent object parent
* @return dataHash object data hash
* @return tokenSymbol object token symbol
* @return tokenName object token name
* @return tokenWrapper object token ERC20 wrapper address
*/
function getObjectMeta(bytes32 _id)
external
view
returns (
bytes32 parent,
bytes32 dataHash,
bytes32 tokenSymbol
bytes32 tokenSymbol,
bytes32 tokenName,
address tokenWrapper
);
}
16 changes: 15 additions & 1 deletion src/diamonds/nayms/interfaces/ITokenizedVaultFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,21 @@ interface ITokenizedVaultFacet {
* @param to token receiver
* @param tokenId Internal ID of the token
*/
function internalTransfer(
function internalTransferFromEntity(
bytes32 to,
bytes32 tokenId,
uint256 amount
) external;

/**
* @notice Internal transfer of `amount` tokens `from` -> `to`
* @dev Transfer tokens internally between two IDs
* @param from token sender
* @param to token receiver
* @param tokenId Internal ID of the token
*/
function wrapperInternalTransferFrom(
bytes32 from,
bytes32 to,
bytes32 tokenId,
uint256 amount
Expand Down
21 changes: 19 additions & 2 deletions src/diamonds/nayms/libs/LibEntity.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { LibObject } from "./LibObject.sol";
import { LibACL } from "./LibACL.sol";
import { LibTokenizedVault } from "./LibTokenizedVault.sol";
import { LibMarket } from "./LibMarket.sol";
import { ERC20Wrapper } from "../../../erc20/ERC20Wrapper.sol";

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

Expand All @@ -24,7 +25,8 @@ library LibEntity {
event EntityCreated(bytes32 entityId, bytes32 entityAdmin);
event EntityUpdated(bytes32 entityId);
event SimplePolicyCreated(bytes32 indexed id, bytes32 entityId);
event TokenSaleStarted(bytes32 indexed entityId, uint256 offerId);
event TokenSaleStarted(bytes32 indexed entityId, uint256 offerId, bytes32 tokenSymbol, bytes32 tokenName);
event TokenWrapped(bytes32 indexed entityId, address tokenWrapper);

/**
* @dev If an entity passes their checks to create a policy, ensure that the entity's capacity is appropriately decreased by the amount of capital that will be tied to the new policy being created.
Expand Down Expand Up @@ -123,6 +125,7 @@ library LibEntity {
) internal {
require(_amount > 0, "mint amount must be > 0");
require(_totalPrice > 0, "total price must be > 0");
require(LibObject._isObjectTokenizable(_entityId), "must be tokenizable");

AppStorage storage s = LibAppStorage.diamondStorage();
Entity memory entity = s.entities[_entityId];
Expand All @@ -131,7 +134,21 @@ library LibEntity {

(uint256 offerId, , ) = LibMarket._executeLimitOffer(_entityId, _entityId, _amount, entity.assetId, _totalPrice, LibConstants.FEE_SCHEDULE_STANDARD);

emit TokenSaleStarted(_entityId, offerId);
emit TokenSaleStarted(_entityId, offerId, s.objectTokenSymbol[_entityId], s.objectTokenName[_entityId]);
}

function _wrapToken(bytes32 _entityId) internal {
AppStorage storage s = LibAppStorage.diamondStorage();

require(LibObject._isObjectTokenizable(_entityId), "must be tokenizable");
require(!LibObject._isObjectTokenized(_entityId), "must not be tokenized already");

ERC20Wrapper erc20Wrapper = new ERC20Wrapper(_entityId);
address wrapper = address(erc20Wrapper);

s.objectTokenWrapper[_entityId] = wrapper;

emit TokenWrapped(_entityId, wrapper);
}

function _createEntity(
Expand Down
4 changes: 2 additions & 2 deletions src/diamonds/nayms/libs/LibHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ library LibHelpers {

// Conversion Utilities

function _addressToBytes32(address addr) internal pure returns (bytes32 result) {
function _addressToBytes32(address addr) internal pure returns (bytes32) {
return _bytesToBytes32(abi.encode(addr));
}

function _stringToBytes32(string memory strIn) internal pure returns (bytes32 result) {
function _stringToBytes32(string memory strIn) internal pure returns (bytes32) {
return _bytesToBytes32(bytes(strIn));
}

Expand Down
7 changes: 4 additions & 3 deletions src/diamonds/nayms/libs/LibMarket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { LibAdmin } from "./LibAdmin.sol";
import { LibTokenizedVault } from "./LibTokenizedVault.sol";
import { LibConstants } from "./LibConstants.sol";
import { LibFeeRouter } from "./LibFeeRouter.sol";
import { LibEntity } from "./LibEntity.sol";

library LibMarket {
/// @notice order has been added
Expand Down Expand Up @@ -362,11 +363,11 @@ library LibMarket {
) internal view {
AppStorage storage s = LibAppStorage.diamondStorage();

require(_entityId != 0 && s.existingEntities[_entityId], "must belong to entity to make an offer");
require(_entityId != 0 && LibEntity._isEntity(_entityId), "must belong to entity to make an offer");

bool sellTokenIsEntity = s.existingEntities[_sellToken];
bool sellTokenIsEntity = LibEntity._isEntity(_sellToken);
bool sellTokenIsSupported = s.externalTokenSupported[LibHelpers._getAddressFromId(_sellToken)];
bool buyTokenIsEntity = s.existingEntities[_buyToken];
bool buyTokenIsEntity = LibEntity._isEntity(_buyToken);
bool buyTokenIsSupported = s.externalTokenSupported[LibHelpers._getAddressFromId(_buyToken)];

_assertAmounts(_sellAmount, _buyAmount);
Expand Down
20 changes: 17 additions & 3 deletions src/diamonds/nayms/libs/LibObject.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,22 @@ library LibObject {
return (s.objectTokenSymbol[_objectId] != LibAdmin._getEmptyId());
}

function _enableObjectTokenization(bytes32 _objectId, string memory _symbol) internal {
function _isObjectTokenized(bytes32 _objectId) internal view returns (bool) {
AppStorage storage s = LibAppStorage.diamondStorage();
return (s.objectTokenWrapper[_objectId] != address(0));
}

function _enableObjectTokenization(
bytes32 _objectId,
string memory _symbol,
string memory _name
) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
require(bytes(_symbol).length < 16, "symbol more than 16 characters");
require(s.objectTokenSymbol[_objectId] == LibAdmin._getEmptyId(), "object already tokenized");
require(s.objectTokenSymbol[_objectId] == LibAdmin._getEmptyId(), "tokenization enabled already");

s.objectTokenSymbol[_objectId] = LibHelpers._stringToBytes32(_symbol);
s.objectTokenName[_objectId] = LibHelpers._stringToBytes32(_name);
}

function _isObject(bytes32 _id) internal view returns (bool) {
Expand All @@ -91,12 +101,16 @@ library LibObject {
returns (
bytes32 parent,
bytes32 dataHash,
bytes32 tokenSymbol
bytes32 tokenSymbol,
bytes32 tokenName,
address tokenWrapper
)
{
AppStorage storage s = LibAppStorage.diamondStorage();
parent = s.objectParent[_id];
dataHash = s.objectDataHashes[_id];
tokenSymbol = s.objectTokenSymbol[_id];
tokenName = s.objectTokenName[_id];
tokenWrapper = s.objectTokenWrapper[_id];
}
}
Loading