Skip to content

Commit

Permalink
feat: Messages v2 (#59)
Browse files Browse the repository at this point in the history
* Add new messages and fix all tests

Most tests failed because the call_index changed for most messages.

* messages.sol: Add AddCurrency support

* test: AddCurrency encoding/decoding

* Update AddPool fields and tests

Drop currency and decimals and update all tests

* Support AllowPoolCurrency encoding/decoding + test

* Extend Connector's GatewayLike interface

* Implement Connector to Gateway delegation

* Connector: Impl Transfer outgoing message delegation

* Rename 'token' to 'currency'

* Implement Gateway to Router delegation

* Adapt AddTranche to contains decimals field

* Fix all possible warnings

* fmt

* tests: Fix AddTranche encoding/decoding

* Use AddTranche decimals in DeployTranche

* Drop todo

* gateway: Handle incoming AddCurrency + AllowPoolCurrency

* draft: Implement Connector.addCurrency

* draft: Implement Connector.allowPoolCurrency

* test: Add testAddingCurrencyWorks

* Add poolId to AllowPoolCurrency

* Refactor AddCurrency and AllowPoolCurrency

* Extend testing of AddCurrency

* fmt

* Clean up AddCurrency

* test: AllowPoolCurrencyWorks

* test: testAllowPoolCurrencyWithUnknownCurrencyFails

* Impl and test Transfer Centrifuge -> EVM

* test: Add testIncomingTransferWorks

* fmt

* Fix several warnings

* wip: Fix outgoing & incoming transfer logic

Facing some issues with the tests but the logic should be correct now.

* Fix unexpected revert

* Fix testIncomingTransferWorks

* fmt

* draft: Implement all outgoing message todo flows

* wip: Adapt addCurrency

* wip: Adapt allowPoolCurrency

* Check pool exists in allowPoolCurrency

* Fix increaseInvesOrder and interface GatewayLike

* fmt

* Address todos to fix currency param

* test: Add testDecreaseInvestOrder

* fmt

* Test increase/decrease redeem orders

* more tests

* Add testIncreaseRedeemOrderWithNotAllowedCurrencyFails

* fmt

* Prune outgoing messages `require`s

* Tests ++

* fmt

* Prune latest tests 🧹

* All all remaining tests

* Small improvements

* Refactor Connector.transfer interface to receive address

Instead of receiving uint128, we expect `address` instead to follow the
same logic used for the other outgoing messages reference a currency.

* ff

* Add `parseIncomingTransfer` for gas savings

* Implement todo around optimised parseTransfer

* Test parseTransfer <> parseIncomingTransfer equality

* fmt

* connector: Make escrow immutable

* Update tests to latest ERC20 contract

* fixup!

* fmt

* Drop CollectFor*

* wip: Fix TransferTrancheToken (d)encoding

* Fix TransferTrancheTokenst tests

* Drop todo

* Fix all TransferTrancheTokens tests & optimise parsing

* fmt

* AddCurrency: require address == 0

This stops us from being able to override a Currency <> Address mapping.

* Fix escrow approve and rely setup

* fmt

* AddCurrency: Also check that require(currencyAddressToId[currencyAddress] == 0

* Fix tests with vm.assume

* fmt

* Rename `caller` to `investor`

* Rename poolCurrencies to allowedPoolCurrencies

* Place CollectInvest before CollectRedeem

* Increase AddCurrency error's granularity

* Swap AllowPoolCurrency fields

* Drop Pool.currency field

* fmt

* fixup

* fmt
  • Loading branch information
NunoAlexandre authored Apr 27, 2023
1 parent 5f19c1c commit c1fc9c1
Show file tree
Hide file tree
Showing 10 changed files with 970 additions and 538 deletions.
168 changes: 123 additions & 45 deletions src/Connector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,29 @@ interface GatewayLike {
function transferTrancheTokensToCentrifuge(
uint64 poolId,
bytes16 trancheId,
address sender,
bytes32 destinationAddress,
uint128 amount
) external;
function transferTrancheTokensToEVM(
uint64 poolId,
bytes16 trancheId,
uint256 destinationChainId,
address sender,
uint64 destinationChainId,
address destinationAddress,
uint128 amount
) external;
function transfer(uint128 currency, address sender, bytes32 recipient, uint128 amount) external;
function increaseInvestOrder(uint64 poolId, bytes16 trancheId, address investor, uint128 currency, uint128 amount)
external;
function decreaseInvestOrder(uint64 poolId, bytes16 trancheId, address investor, uint128 currency, uint128 amount)
external;
function increaseRedeemOrder(uint64 poolId, bytes16 trancheId, address investor, uint128 currency, uint128 amount)
external;
function decreaseRedeemOrder(uint64 poolId, bytes16 trancheId, address investor, uint128 currency, uint128 amount)
external;
function collectInvest(uint64 poolId, bytes16 trancheId, address investor) external;
function collectRedeem(uint64 poolId, bytes16 trancheId, address investor) external;
}

interface EscrowLike {
Expand All @@ -29,7 +42,6 @@ interface EscrowLike {
struct Pool {
uint64 poolId;
uint256 createdAt;
address currency;
}

struct Tranche {
Expand All @@ -40,15 +52,22 @@ struct Tranche {
// This leads to duplicate storage (also in the ERC20 contract), ideally we should refactor this somehow
string tokenName;
string tokenSymbol;
uint8 decimals;
}

contract CentrifugeConnector {
mapping(address => uint256) public wards;
mapping(uint64 => Pool) public pools;
mapping(uint64 => mapping(bytes16 => Tranche)) public tranches;

mapping(uint128 => address) public currencyIdToAddress;
// The reverse mapping of `currencyIdToAddress`
mapping(address => uint128) public currencyAddressToId;

mapping(uint64 => mapping(address => bool)) public allowedPoolCurrencies;

GatewayLike public gateway;
EscrowLike public escrow;
EscrowLike public immutable escrow;

TrancheTokenFactoryLike public immutable tokenFactory;
MemberlistFactoryLike public immutable memberlistFactory;
Expand All @@ -57,7 +76,9 @@ contract CentrifugeConnector {
event Rely(address indexed user);
event Deny(address indexed user);
event File(bytes32 indexed what, address data);
event CurrencyAdded(uint128 indexed currency, address indexed currencyAddress);
event PoolAdded(uint256 indexed poolId);
event PoolCurrencyAllowed(uint128 currency, uint64 poolId);
event TrancheAdded(uint256 indexed poolId, bytes16 indexed trancheId);
event TrancheDeployed(uint256 indexed poolId, bytes16 indexed trancheId, address indexed token);

Expand Down Expand Up @@ -98,6 +119,17 @@ contract CentrifugeConnector {
}

// --- Outgoing message handling ---
function transfer(address currencyAddress, bytes32 recipient, uint128 amount) public {
uint128 currency = currencyAddressToId[currencyAddress];
require(currency != 0, "CentrifugeConnector/unknown-currency");

ERC20Like erc20 = ERC20Like(currencyAddress);
require(erc20.balanceOf(msg.sender) >= amount, "CentrifugeConnector/insufficient-balance");
require(erc20.transferFrom(msg.sender, address(escrow), amount), "CentrifugeConnector/currency-transfer-failed");

gateway.transfer(currency, msg.sender, recipient, amount);
}

function transferTrancheTokensToCentrifuge(
uint64 poolId,
bytes16 trancheId,
Expand All @@ -110,13 +142,13 @@ contract CentrifugeConnector {
require(token.balanceOf(msg.sender) >= amount, "CentrifugeConnector/insufficient-balance");
token.burn(msg.sender, amount);

gateway.transferTrancheTokensToCentrifuge(poolId, trancheId, destinationAddress, amount);
gateway.transferTrancheTokensToCentrifuge(poolId, trancheId, msg.sender, destinationAddress, amount);
}

function transferTrancheTokensToEVM(
uint64 poolId,
bytes16 trancheId,
uint256 destinationChainId,
uint64 destinationChainId,
address destinationAddress,
uint128 amount
) public {
Expand All @@ -126,75 +158,115 @@ contract CentrifugeConnector {
require(token.balanceOf(msg.sender) >= amount, "CentrifugeConnector/insufficient-balance");
token.burn(msg.sender, amount);

gateway.transferTrancheTokensToEVM(poolId, trancheId, destinationChainId, destinationAddress, amount);
gateway.transferTrancheTokensToEVM(
poolId, trancheId, msg.sender, destinationChainId, destinationAddress, amount
);
}

function increaseInvestOrder(uint64 poolId, bytes16 trancheId, uint128 amount) public {
Pool storage pool = pools[poolId];
require(pool.createdAt > 0, "CentrifugeConnector/invalid-pool");

function increaseInvestOrder(uint64 poolId, bytes16 trancheId, address currencyAddress, uint128 amount) public {
RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-token");
require(address(token) != address(0), "CentrifugeConnector/unknown-tranche-token");
require(token.hasMember(msg.sender), "CentrifugeConnector/not-a-member");

uint128 currency = currencyAddressToId[currencyAddress];
require(currency != 0, "CentrifugeConnector/unknown-currency");
require(allowedPoolCurrencies[poolId][currencyAddress], "CentrifugeConnector/pool-currency-not-allowed");

require(
ERC20Like(pool.currency).transferFrom(msg.sender, address(escrow), amount),
ERC20Like(currencyAddress).transferFrom(msg.sender, address(escrow), amount),
"Centrifuge/Connector/currency-transfer-failed"
);

// TODO: send message to the gateway. Depends on https://github.com/centrifuge/connectors/pull/52
gateway.increaseInvestOrder(poolId, trancheId, msg.sender, currency, amount);
}

function decreaseInvestOrder(uint64 poolId, bytes16 trancheId, uint128 amount) public {
Pool storage pool = pools[poolId];
require(pool.createdAt > 0, "CentrifugeConnector/invalid-pool");

function decreaseInvestOrder(uint64 poolId, bytes16 trancheId, address currencyAddress, uint128 amount) public {
RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-token");
require(address(token) != address(0), "CentrifugeConnector/unknown-tranche-token");
require(token.hasMember(msg.sender), "CentrifugeConnector/not-a-member");

// TODO: send message to the gateway. Depends on https://github.com/centrifuge/connectors/pull/52
}
uint128 currency = currencyAddressToId[currencyAddress];
require(currency != 0, "CentrifugeConnector/unknown-currency");
require(allowedPoolCurrencies[poolId][currencyAddress], "CentrifugeConnector/pool-currency-not-allowed");

function increaseRedeemOrder(uint64 poolId, bytes16 trancheId, uint128 amount) public {
// TODO(nuno)
gateway.decreaseInvestOrder(poolId, trancheId, msg.sender, currency, amount);
}

function decreaseRedeemOrder(uint64 poolId, bytes16 trancheId, uint128 amount) public {
// TODO(nuno)
}
function increaseRedeemOrder(uint64 poolId, bytes16 trancheId, address currencyAddress, uint128 amount) public {
RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-tranche-token");
require(token.hasMember(msg.sender), "CentrifugeConnector/not-a-member");

function collectRedeem(uint64 poolId, bytes16 trancheId) public {
// TODO(nuno)
uint128 currency = currencyAddressToId[currencyAddress];
require(currency != 0, "CentrifugeConnector/unknown-currency");
require(allowedPoolCurrencies[poolId][currencyAddress], "CentrifugeConnector/pool-currency-not-allowed");

gateway.increaseRedeemOrder(poolId, trancheId, msg.sender, currency, amount);
}

function collectForRedeem(uint64 poolId, bytes16 trancheId, bytes32 userAddress) public {
// TODO(nuno)
function decreaseRedeemOrder(uint64 poolId, bytes16 trancheId, address currencyAddress, uint128 amount) public {
RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-tranche-token");
require(token.hasMember(msg.sender), "CentrifugeConnector/not-a-member");

uint128 currency = currencyAddressToId[currencyAddress];
require(currency != 0, "CentrifugeConnector/unknown-currency");
require(allowedPoolCurrencies[poolId][currencyAddress], "CentrifugeConnector/pool-currency-not-allowed");

gateway.decreaseRedeemOrder(poolId, trancheId, msg.sender, currency, amount);
}

function collectInvest(uint64 poolId, bytes16 trancheId) public {
// TODO(nuno)
RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-tranche-token");
require(token.hasMember(msg.sender), "CentrifugeConnector/not-a-member");

gateway.collectInvest(poolId, trancheId, address(msg.sender));
}

function collectForInvest(uint64 poolId, bytes16 trancheId, bytes32 userAddress) public {
// TODO(nuno)
function collectRedeem(uint64 poolId, bytes16 trancheId) public {
RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-tranche-token");
require(token.hasMember(msg.sender), "CentrifugeConnector/not-a-member");

gateway.collectRedeem(poolId, trancheId, address(msg.sender));
}

// --- Incoming message handling ---
// todo(nuno): store currency and decimals
function addPool(uint64 poolId, uint128 currency, uint8 decimals) public onlyGateway {
function addCurrency(uint128 currency, address currencyAddress) public onlyGateway {
require(currencyIdToAddress[currency] == address(0), "CentrifugeConnector/currency-id-in-use");
require(currencyAddressToId[currencyAddress] == 0, "CentrifugeConnector/currency-address-in-use");

currencyIdToAddress[currency] = currencyAddress;
currencyAddressToId[currencyAddress] = currency;
emit CurrencyAdded(currency, currencyAddress);
}

function addPool(uint64 poolId) public onlyGateway {
Pool storage pool = pools[poolId];
require(pool.createdAt == 0, "CentrifugeConnector/pool-already-added");
pool.poolId = poolId;
pool.createdAt = block.timestamp;
emit PoolAdded(poolId);
}

function allowPoolCurrency(uint64 poolId, uint128 currency) public onlyGateway {
Pool storage pool = pools[poolId];
require(pool.createdAt > 0, "CentrifugeConnector/invalid-pool");

address currencyAddress = currencyIdToAddress[currency];
require(currencyAddress != address(0), "CentrifugeConnector/unknown-currency");

allowedPoolCurrencies[poolId][currencyAddress] = true;
emit PoolCurrencyAllowed(currency, poolId);
}

function addTranche(
uint64 poolId,
bytes16 trancheId,
string memory tokenName,
string memory tokenSymbol,
uint8 decimals,
uint128 price
) public onlyGateway {
Pool storage pool = pools[poolId];
Expand All @@ -206,6 +278,7 @@ contract CentrifugeConnector {
tranche.lastPriceUpdate = block.timestamp;
tranche.tokenName = tokenName;
tranche.tokenSymbol = tokenSymbol;
tranche.decimals = decimals;

emit TrancheAdded(poolId, trancheId);
}
Expand All @@ -215,10 +288,8 @@ contract CentrifugeConnector {
require(tranche.lastPriceUpdate > 0, "CentrifugeConnector/invalid-pool-or-tranche");
require(tranche.token == address(0), "CentrifugeConnector/tranche-already-deployed");

// TODO: use actual decimals
uint8 decimals = 18;
address token =
tokenFactory.newTrancheToken(poolId, trancheId, tranche.tokenName, tranche.tokenSymbol, decimals);
tokenFactory.newTrancheToken(poolId, trancheId, tranche.tokenName, tranche.tokenSymbol, tranche.decimals);
tranche.token = token;

address memberlist = memberlistFactory.newMemberlist();
Expand All @@ -242,14 +313,21 @@ contract CentrifugeConnector {
memberlist.updateMember(user, validUntil);
}

function handleTransferTrancheTokens(
uint64 poolId,
bytes16 trancheId,
uint256 destinationChainId,
address destinationAddress,
uint128 amount
) public onlyGateway {
require(destinationChainId == block.chainid, "CentrifugeConnector/invalid-chain-id");
function handleTransfer(uint128 currency, address recipient, uint128 amount) public onlyGateway {
address currencyAddress = currencyIdToAddress[currency];
require(currencyAddress != address(0), "CentrifugeConnector/unknown-currency");

EscrowLike(escrow).approve(currencyAddress, address(this), amount);
require(
ERC20Like(currencyAddress).transferFrom(address(escrow), recipient, amount),
"CentrifugeConnector/currency-transfer-failed"
);
}

function handleTransferTrancheTokens(uint64 poolId, bytes16 trancheId, address destinationAddress, uint128 amount)
public
onlyGateway
{
RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-token");

Expand Down
Loading

0 comments on commit c1fc9c1

Please sign in to comment.