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

Connectors v1 #16

Merged
merged 46 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
e7b05c4
add withdraw & deposit
ilinzweilin Jul 22, 2022
ff49895
make withdraw function public
ilinzweilin Jul 26, 2022
1ab18a8
add sendMessages to router
ilinzweilin Jul 28, 2022
ddf1023
update deploy script and add test
AStox Jul 31, 2022
40049f6
switch Nomad to XCM router in tests
ilinzweilin Aug 2, 2022
d98626e
add happy case deposit & withdraw tests
ilinzweilin Aug 4, 2022
f32024b
remove unused code
ilinzweilin Aug 4, 2022
ed9515f
add xAppConnectionManager
ilinzweilin Aug 9, 2022
2ed1b5a
add assertions for withdraw happu case
ilinzweilin Aug 9, 2022
b1284e0
add edges cases for transfer route
ilinzweilin Aug 19, 2022
47ec66f
add edge case tests for transferTo
ilinzweilin Aug 19, 2022
abc64b2
fix merge conflicts
ilinzweilin Aug 19, 2022
fd2ecde
remove misleading messages
ilinzweilin Aug 19, 2022
335820c
remove misleading messages
ilinzweilin Aug 19, 2022
d45f17e
update origin address
AStox Aug 25, 2022
3cb96e8
Merge branch 'main' into update-deploy-script
AStox Aug 25, 2022
71d553a
Use correct checksummed origin address
AStox Aug 25, 2022
e1cfc14
Strip out nomad and testing dependencies from XCMRouter. Add ping fun…
AStox Aug 26, 2022
2e47470
Update router
hieronx Aug 29, 2022
8338a3b
fix naming
ilinzweilin Aug 30, 2022
33a8191
Update centrifuge origin address
AStox Sep 29, 2022
1c57fa7
fix updateMember parsing
AStox Oct 5, 2022
65ed9bb
roll back updateMember change
AStox Nov 17, 2022
6593be2
Clean up
hieronx Nov 18, 2022
7da41b5
forge install: memview-sol
hieronx Nov 18, 2022
84c7f39
Fix all the tests
hieronx Nov 18, 2022
8fe2b8b
Merge branch 'update-deploy-script' of https://github.com/centrifuge/…
hieronx Nov 18, 2022
644dbb2
Fix deps
hieronx Nov 18, 2022
7f3870b
Remove yarn steps from workflows
hieronx Nov 18, 2022
827797c
Remove deploy test
hieronx Nov 18, 2022
e2f01b2
Merge branch 'add-withdaw-deposit' of https://github.com/centrifuge/c…
hieronx Nov 18, 2022
a6d8f58
merge main
AStox Jan 6, 2023
c8d6c3b
Remove nomad router's send message logic and tests pertaining to it
AStox Jan 16, 2023
1a287d9
Fix memview-sol branch submodule not found
NunoAlexandre Jan 26, 2023
f744dd2
Drop "== true" blocks
NunoAlexandre Jan 30, 2023
e2832fe
Fix `UpdateMember` encoding & decoding (#32)
NunoAlexandre Jan 30, 2023
2d91780
Fix 9-24: trancheId (16 bytes) docs
NunoAlexandre Jan 30, 2023
afb740c
feat: Add `price` field to `AddTranche` (#33)
NunoAlexandre Jan 31, 2023
2ae621d
Make `UpdateTokenPrice.price` and `Tranche.latestPrice` `u128` (#34)
NunoAlexandre Jan 31, 2023
d1898d8
Use cent-chain AddTranche message
NunoAlexandre Jan 31, 2023
aa41811
Align tests with cent chain (#36)
NunoAlexandre Feb 2, 2023
78edd87
Chore/add tests and optimizations (#38)
AStox Feb 6, 2023
8a20e95
Feat/domain update (#35)
AStox Feb 7, 2023
54dd4bb
Fine-tune transfers encoding, decoding, and tests (#39)
NunoAlexandre Feb 7, 2023
695f1f0
Add TODO and more tests
AStox Feb 8, 2023
0b28d84
Update test/Connector.t.sol
Feb 9, 2023
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: 1 addition & 1 deletion script/Connector-XCM.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ contract ConnectorXCMScript is Script {
CentrifugeConnector connector = new CentrifugeConnector(tokenFactory_, memberlistFactory_);

// TODO: add centrifugeChainOrigin_ arg
ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0));
ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0), address(0));
connector.file("router", address(router));
}
}
39 changes: 34 additions & 5 deletions src/Connector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ pragma abicoder v2;
import { RestrictedTokenFactoryLike, MemberlistFactoryLike } from "./token/factory.sol";
import { RestrictedTokenLike } from "./token/restricted.sol";
import { MemberlistLike } from "./token/memberlist.sol";
import "forge-std/Test.sol";

interface RouterLike {
function sendMessage(uint32 destinationDomain, uint64 poolId, bytes16 trancheId, uint256 amount, address user) external;
}

contract CentrifugeConnector is Test {
contract CentrifugeConnector {

RouterLike public router;
RestrictedTokenFactoryLike public immutable tokenFactory;
Expand All @@ -31,11 +31,14 @@ contract CentrifugeConnector is Test {
mapping(uint64 => Pool) public pools;
mapping(uint64 => mapping(bytes16 => Tranche)) public tranches;
mapping(address => uint256) public wards;
mapping(bytes32 => uint32) public domainLookup;
hieronx marked this conversation as resolved.
Show resolved Hide resolved


// --- Events ---
event Rely(address indexed user);
event Deny(address indexed user);
event File(bytes32 indexed what, address data);
event File(bytes32 indexed what, string data);
event PoolAdded(uint256 indexed poolId);
event TrancheAdded(uint256 indexed poolId, bytes16 indexed trancheId, address indexed token);

Expand Down Expand Up @@ -73,6 +76,14 @@ contract CentrifugeConnector is Test {
emit File(what, data);
}

function file(bytes32 name, string memory domainName, uint32 domainId) public auth {
NunoAlexandre marked this conversation as resolved.
Show resolved Hide resolved
if(name == "domain") {
domainLookup[keccak256(bytes(domainName))] = domainId;
emit File(name, domainName);
} else { revert ("unknown name");}

}

// --- Internal ---
function addPool(uint64 poolId) public onlyRouter {
Pool storage pool = pools[poolId];
NunoAlexandre marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -96,7 +107,7 @@ contract CentrifugeConnector is Test {

hieronx marked this conversation as resolved.
Show resolved Hide resolved
address memberlist = memberlistFactory.newMemberlist();
RestrictedTokenLike(token).depend("memberlist", memberlist);

MemberlistLike(memberlist).updateMember(address(this), uint(-1)); // required to be able to receive tokens in case of withdrawals
ilinzweilin marked this conversation as resolved.
Show resolved Hide resolved
NunoAlexandre marked this conversation as resolved.
Show resolved Hide resolved
emit TrancheAdded(poolId, trancheId, token);
}

Expand Down Expand Up @@ -124,15 +135,33 @@ contract CentrifugeConnector is Test {
memberlist.updateMember(user, validUntil);
}

function transferTo(
function transfer(
ilinzweilin marked this conversation as resolved.
Show resolved Hide resolved
uint64 poolId,
bytes16 trancheId,
address user,
uint256 amount
) public onlyRouter {
RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-token");
NunoAlexandre marked this conversation as resolved.
Show resolved Hide resolved
require(token.hasMember(user), "CentrifugeConnector/not-a-member");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a test for this already, i.e. testing that a transfer to a non-whitelisted address fails? I think this check is actually redundant as the underlying erc20 token already checks that the destination address is a member.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bumping this ^ @NunoAlexandre @AStox

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minting doesn't check the memberlist. This was the change I had made to restricted.sol, since it seems kind of odd that a permissioned token can mint to any address

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. I would keep it like this for now, to not change the existing restricted token implementation. But let's discuss this a bit more and we can still change it in V2 optionally.

token.mint(user, amount);
}


function transferTo(
ilinzweilin marked this conversation as resolved.
Show resolved Hide resolved
uint64 poolId,
bytes16 trancheId,
address user,
uint256 amount,
string memory domainName
) public {
uint32 domainId = domainLookup[keccak256(bytes(domainName))];
NunoAlexandre marked this conversation as resolved.
Show resolved Hide resolved
hieronx marked this conversation as resolved.
Show resolved Hide resolved
require(domainId > 0, "CentrifugeConnector/domain-does-not-exist");

RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token);
require(address(token) != address(0), "CentrifugeConnector/unknown-token");
require(token.balanceOf(user) >= amount, "CentrifugeConnector/insufficient-balance");
require(token.transferFrom(user, address(this), amount), "CentrifugeConnector/token-transfer-failed");
token.burn(address(this), amount);
router.sendMessage(domainId, poolId, trancheId, amount, user);
}
}
27 changes: 26 additions & 1 deletion src/Messages.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ library ConnectorMessages {
AddTranche,
UpdateTokenPrice,
UpdateMember,
TransferTo
Transfer
}

function messageType(bytes29 _msg) internal pure returns (Call _call) {
Expand Down Expand Up @@ -135,4 +135,29 @@ library ConnectorMessages {
price = uint256(_msg.index(25, 32));
}

/**
* Transfer
*
* 0: call type (uint8 = 1 byte)
* 1-8: poolId (uint64 = 8 bytes)
* 9-25: trancheId (16 bytes)
* 26-46: user (Ethereum address, 20 bytes)
* 47-78: amount (uint256 = 32 bytes)
*
*/
function formatTransfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) internal pure returns (bytes memory) {
return abi.encodePacked(uint8(Call.Transfer), poolId, trancheId, user, amount);
}

function isTransfer(bytes29 _msg) internal pure returns (bool) {
return messageType(_msg) == Call.Transfer;
}

function parseTransfer(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint256 amount) {
hieronx marked this conversation as resolved.
Show resolved Hide resolved
poolId = uint64(_msg.indexUint(1, 8));
trancheId = bytes16(_msg.index(9, 16));
user = address(bytes20(_msg.index(25, 20)));
amount = uint256(_msg.index(45, 32));
}

}
44 changes: 40 additions & 4 deletions src/routers/xcm/Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma abicoder v2;

import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol";
import {Router} from "@nomad-xyz/contracts-router/contracts/Router.sol";
import {Home} from "@nomad-xyz/contracts-core/contracts/Home.sol";
import {ConnectorMessages} from "../..//Messages.sol";
import "forge-std/Test.sol";

Expand All @@ -12,6 +13,7 @@ interface ConnectorLike {
function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) external;
function updateMember(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) external;
function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) external;
function transfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external;
}

contract ConnectorXCMRouter is Router, Test {
Expand All @@ -24,16 +26,25 @@ contract ConnectorXCMRouter is Router, Test {

address immutable centrifugeChainOrigin;

constructor(address connector_, address centrifugeChainOrigin_) {
constructor(address connector_, address centrifugeChainOrigin_, address xAppConnectionManager) {
connector = ConnectorLike(connector_);
centrifugeChainOrigin = centrifugeChainOrigin_;
}

__Ownable_init();
__XAppConnectionClient_initialize(xAppConnectionManager);
}


modifier onlyCentrifugeChainOrigin() {
require(msg.sender == address(centrifugeChainOrigin), "ConnectorXCMRouter/invalid-origin");
_;
}

modifier onlyConnector() {
require(msg.sender == address(connector), "ConnectorXCMRouter/only-connector-allowed-to-call");
_;
}

function handle(
uint32 _origin,
uint32 _nonce,
Expand All @@ -48,13 +59,38 @@ contract ConnectorXCMRouter is Router, Test {
(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) = ConnectorMessages.parseAddTranche(_msg);
connector.addTranche(poolId, trancheId, tokenName, tokenSymbol);
} else if (ConnectorMessages.isUpdateMember(_msg) == true) {
(uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseUpdateMember(_msg);
connector.updateMember(poolId, trancheId, user, amount);
(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) = ConnectorMessages.parseUpdateMember(_msg);
connector.updateMember(poolId, trancheId, user, validUntil);
} else if (ConnectorMessages.isUpdateTokenPrice(_msg) == true) {
(uint64 poolId, bytes16 trancheId, uint256 price) = ConnectorMessages.parseUpdateTokenPrice(_msg);
connector.updateTokenPrice(poolId, trancheId, price);
} else if (ConnectorMessages.isTransfer(_msg) == true) {
NunoAlexandre marked this conversation as resolved.
Show resolved Hide resolved
(uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(_msg);
connector.transfer(poolId, trancheId, user, amount);
} else {
require(false, "invalid-message");
}
}

function sendMessage(uint32 destinationDomain, uint64 poolId, bytes16 trancheId, uint256 amount, address user) external onlyConnector {
bytes32 remoteAddress = _mustHaveRemote(destinationDomain);
Home(xAppConnectionManager.home()).dispatch(
NunoAlexandre marked this conversation as resolved.
Show resolved Hide resolved
destinationDomain,
remoteAddress,
ConnectorMessages.formatTransfer(poolId, trancheId, user, amount));
}

function bytes32ToString(bytes32 _bytes32) internal returns (string memory) {
uint8 i = 0;
while(i < 32 && _bytes32[i] != 0) {
i++;
}

bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}

}
5 changes: 5 additions & 0 deletions src/token/restricted.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ interface ERC20Like {
function mint(address usr, uint wad) external;
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function balanceOf(address usr) external view returns (uint wad);
function burn(address usr, uint wad) external;
function transferFrom(address from, address to, uint amount) external returns (bool);
function totalSupply() external returns (uint);
function approve(address _spender, uint256 _value) external returns (bool);
}

interface RestrictedTokenLike is ERC20Like {
Expand Down
Loading