From e7b05c4475b42db87f615b2e00b020f0617fd822 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Fri, 22 Jul 2022 14:48:41 +0100 Subject: [PATCH 01/41] add withdraw & deposit --- src/Connector.sol | 16 ++++++++++-- src/Messages.sol | 53 +++++++++++++++++++++++++++++++++++++- src/routers/xcm/Router.sol | 12 +++++++-- src/token/restricted.sol | 3 +++ 4 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index be6d35a8..87ff3c28 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -96,7 +96,7 @@ contract CentrifugeConnector is Test { 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 emit TrancheAdded(poolId, trancheId, token); } @@ -124,7 +124,7 @@ contract CentrifugeConnector is Test { memberlist.updateMember(user, validUntil); } - function transferTo( + function deposit( uint64 poolId, bytes16 trancheId, address user, @@ -134,5 +134,17 @@ contract CentrifugeConnector is Test { require(token.hasMember(user), "CentrifugeConnector/not-a-member"); token.mint(user, amount); } + + function withdraw( + uint64 poolId, + bytes16 trancheId, + address user, + uint256 amount + ) public onlyRouter { + RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token); + require(token.balanceOf(user) >= amount, "CentrifugeConnector/insufficient-balance"); // optional + require(token.transferFrom(user, address(this), amount), "CentrifugeConnector/token-transfer-failed"); + token.burn(address(this), amount); + } } diff --git a/src/Messages.sol b/src/Messages.sol index 1f7d81cc..b21dcdb1 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -13,7 +13,8 @@ library ConnectorMessages { AddTranche, UpdateTokenPrice, UpdateMember, - TransferTo + Deposit, + Withdraw } function messageType(bytes29 _msg) internal pure returns (Call _call) { @@ -135,4 +136,54 @@ library ConnectorMessages { price = uint256(_msg.index(25, 32)); } + /** + * Deposit + * + * 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 formatDeposit(uint64 poolId, bytes16 trancheId, address user, uint256 amount) internal pure returns (bytes memory) { + return abi.encodePacked(uint8(Call.Deposit), poolId, trancheId, user, amount); + } + + function isDeposit(bytes29 _msg) internal pure returns (bool) { + return messageType(_msg) == Call.Deposit; + } + + function parseDeposit(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint256 amount) { + 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)); + } + + /** + * Withdraw + * + * 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 formatWithdraw(uint64 poolId, bytes16 trancheId, address user, uint256 amount) internal pure returns (bytes memory) { + return abi.encodePacked(uint8(Call.Withdraw), poolId, trancheId, user, amount); + } + + function isWithdraw(bytes29 _msg) internal pure returns (bool) { + return messageType(_msg) == Call.Withdraw; + } + + function parseWithdraw(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint256 amount) { + 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)); + } + } \ No newline at end of file diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 7fd0b5bb..b2f033be 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -12,6 +12,8 @@ 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 deposit(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; + function withdraw(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; } contract ConnectorXCMRouter is Router, Test { @@ -48,11 +50,17 @@ 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.isDeposit(_msg) == true) { + (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseDeposit(_msg); + connector.deposit(poolId, trancheId, user, amount); + } else if (ConnectorMessages.isWithdraw(_msg) == true) { + (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseWithdraw(_msg); + connector.withdraw(poolId, trancheId, user, amount); } else { require(false, "invalid-message"); } diff --git a/src/token/restricted.sol b/src/token/restricted.sol index 466e679d..b688c929 100644 --- a/src/token/restricted.sol +++ b/src/token/restricted.sol @@ -12,6 +12,9 @@ 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); } interface RestrictedTokenLike is ERC20Like { From ff498953aecd4edf78ab440c39c8fc6f6cdd7d94 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Tue, 26 Jul 2022 14:32:22 +0100 Subject: [PATCH 02/41] make withdraw function public --- src/Connector.sol | 3 ++- src/Messages.sol | 38 ++++++-------------------------------- src/routers/xcm/Router.sol | 7 ++----- 3 files changed, 10 insertions(+), 38 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index 87ff3c28..6a25c71b 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -140,11 +140,12 @@ contract CentrifugeConnector is Test { bytes16 trancheId, address user, uint256 amount - ) public onlyRouter { + ) public { RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token); require(token.balanceOf(user) >= amount, "CentrifugeConnector/insufficient-balance"); // optional require(token.transferFrom(user, address(this), amount), "CentrifugeConnector/token-transfer-failed"); token.burn(address(this), amount); + // router.send_message(destination_domain, Transfer { pool_id, tranche_id, amount, destination_domain, destination_address }) } } diff --git a/src/Messages.sol b/src/Messages.sol index b21dcdb1..dd682c06 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -13,8 +13,7 @@ library ConnectorMessages { AddTranche, UpdateTokenPrice, UpdateMember, - Deposit, - Withdraw + Transfer } function messageType(bytes29 _msg) internal pure returns (Call _call) { @@ -146,40 +145,15 @@ library ConnectorMessages { * 47-78: amount (uint256 = 32 bytes) * */ - function formatDeposit(uint64 poolId, bytes16 trancheId, address user, uint256 amount) internal pure returns (bytes memory) { - return abi.encodePacked(uint8(Call.Deposit), poolId, trancheId, user, amount); + 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 isDeposit(bytes29 _msg) internal pure returns (bool) { - return messageType(_msg) == Call.Deposit; + function isTransfer(bytes29 _msg) internal pure returns (bool) { + return messageType(_msg) == Call.Transfer; } - function parseDeposit(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint256 amount) { - 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)); - } - - /** - * Withdraw - * - * 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 formatWithdraw(uint64 poolId, bytes16 trancheId, address user, uint256 amount) internal pure returns (bytes memory) { - return abi.encodePacked(uint8(Call.Withdraw), poolId, trancheId, user, amount); - } - - function isWithdraw(bytes29 _msg) internal pure returns (bool) { - return messageType(_msg) == Call.Withdraw; - } - - function parseWithdraw(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint256 amount) { + function parseTransfer(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint256 amount) { poolId = uint64(_msg.indexUint(1, 8)); trancheId = bytes16(_msg.index(9, 16)); user = address(bytes20(_msg.index(25, 20))); diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index b2f033be..e6213af6 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -55,12 +55,9 @@ contract ConnectorXCMRouter is Router, Test { } else if (ConnectorMessages.isUpdateTokenPrice(_msg) == true) { (uint64 poolId, bytes16 trancheId, uint256 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); connector.updateTokenPrice(poolId, trancheId, price); - } else if (ConnectorMessages.isDeposit(_msg) == true) { - (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseDeposit(_msg); + } else if (ConnectorMessages.isTransfer(_msg) == true) { + (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(_msg); connector.deposit(poolId, trancheId, user, amount); - } else if (ConnectorMessages.isWithdraw(_msg) == true) { - (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseWithdraw(_msg); - connector.withdraw(poolId, trancheId, user, amount); } else { require(false, "invalid-message"); } From 1ab18a87b2d4ba959d6852776bb8cdfbc8a2170c Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Thu, 28 Jul 2022 13:01:06 +0100 Subject: [PATCH 03/41] add sendMessages to router --- src/Connector.sol | 23 +++++++++++++++++++---- src/routers/xcm/Router.sol | 15 +++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index 6a25c71b..8f97d5f4 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -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; @@ -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; + // --- 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); @@ -73,6 +76,14 @@ contract CentrifugeConnector is Test { emit File(what, data); } + function file(bytes32 name, string memory domainName, uint32 domainId) public auth { + 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]; @@ -139,13 +150,17 @@ contract CentrifugeConnector is Test { uint64 poolId, bytes16 trancheId, address user, - uint256 amount + uint256 amount, + string memory domainName ) public { + uint32 domainId = domainLookup[keccak256(bytes(domainName))]; + require(domainId > 0, "CentrifugeConnector/domain-does-not-exist"); + RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token); require(token.balanceOf(user) >= amount, "CentrifugeConnector/insufficient-balance"); // optional require(token.transferFrom(user, address(this), amount), "CentrifugeConnector/token-transfer-failed"); token.burn(address(this), amount); - // router.send_message(destination_domain, Transfer { pool_id, tranche_id, amount, destination_domain, destination_address }) + router.sendMessage(domainId, poolId, trancheId, amount, user); } } diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index e6213af6..0c198c42 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -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"; @@ -36,6 +37,11 @@ contract ConnectorXCMRouter is Router, Test { _; } + modifier onlyConnector() { + require(msg.sender == address(connector), "ConnectorXCMRouter/only-connector-allowed-to-call"); + _; + } + function handle( uint32 _origin, uint32 _nonce, @@ -62,4 +68,13 @@ contract ConnectorXCMRouter is Router, Test { 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( + destinationDomain, + remoteAddress, + ConnectorMessages.formatTransfer(poolId, trancheId, user, amount)); + } } From ddf1023af92dc2632cae7273bebef2370d8f86f5 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Sun, 31 Jul 2022 19:47:22 -0400 Subject: [PATCH 04/41] update deploy script and add test --- script/Connector-XCM.s.sol | 7 ++++--- test/Deploy.t.sol | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 test/Deploy.t.sol diff --git a/script/Connector-XCM.s.sol b/script/Connector-XCM.s.sol index 0eac9078..5655fe4c 100644 --- a/script/Connector-XCM.s.sol +++ b/script/Connector-XCM.s.sol @@ -11,14 +11,15 @@ contract ConnectorXCMScript is Script { function setUp() public {} function run() public { - vm.broadcast(); + vm.startBroadcast(); address tokenFactory_ = address(new RestrictedTokenFactory()); address memberlistFactory_ = address(new MemberlistFactory()); CentrifugeConnector connector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - // TODO: add centrifugeChainOrigin_ arg - ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0)); + // TODO: add centrifugeChainOrigin_ arg. Using Kovan Admin Account in the meantime + ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0x0A735602a357802f553113F5831FE2fbf2F0E2e0)); connector.file("router", address(router)); + vm.stopBroadcast(); } } diff --git a/test/Deploy.t.sol b/test/Deploy.t.sol new file mode 100644 index 00000000..fccfa37c --- /dev/null +++ b/test/Deploy.t.sol @@ -0,0 +1,12 @@ +import {ConnectorXCMScript} from "../script/Connector-XCM.s.sol"; + +import "forge-std/Test.sol"; + +contract DeployTest is Test { + + function testXCMDeployWorks() public { + ConnectorXCMScript script = new ConnectorXCMScript(); + script.run(); + } + +} \ No newline at end of file From 40049f68564662630795bb933869b4e5801a4b38 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Tue, 2 Aug 2022 14:25:49 +0100 Subject: [PATCH 05/41] switch Nomad to XCM router in tests --- src/routers/xcm/Router.sol | 3 +-- test/Connector.t.sol | 34 ++++++++++++++++++++++++--------- test/mock/MockHomeConnector.sol | 7 +++++-- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 0c198c42..f633ead1 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -14,7 +14,6 @@ interface ConnectorLike { function updateMember(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) external; function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) external; function deposit(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; - function withdraw(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; } contract ConnectorXCMRouter is Router, Test { @@ -71,7 +70,7 @@ contract ConnectorXCMRouter is Router, Test { function sendMessage(uint32 destinationDomain, uint64 poolId, bytes16 trancheId, uint256 amount, address user) external onlyConnector { bytes32 remoteAddress = _mustHaveRemote(destinationDomain); - + Home(xAppConnectionManager.home()).dispatch( destinationDomain, remoteAddress, diff --git a/test/Connector.t.sol b/test/Connector.t.sol index b06c464b..0a2007e4 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -7,13 +7,13 @@ import { RestrictedTokenFactory, MemberlistFactory } from "src/token/factory.sol import { RestrictedTokenLike } from "src/token/restricted.sol"; import { MemberlistLike } from "src/token/memberlist.sol"; import { MockHomeConnector } from "./mock/MockHomeConnector.sol"; -import { ConnectorNomadRouter } from "src/routers/nomad/Router.sol"; +import { ConnectorXCMRouter } from "src/routers/xcm/Router.sol"; import "forge-std/Test.sol"; contract ConnectorTest is Test { CentrifugeConnector bridgedConnector; - ConnectorNomadRouter bridgedRouter; + ConnectorXCMRouter bridgedRouter; MockHomeConnector homeConnector; function setUp() public { @@ -22,10 +22,12 @@ contract ConnectorTest is Test { bridgedConnector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); // TODO: pass _xAppConnectionManager - bridgedRouter = new ConnectorNomadRouter(address(bridgedConnector), address(0)); + homeConnector = new MockHomeConnector(); + bridgedRouter = new ConnectorXCMRouter(address(bridgedConnector), address(homeConnector)); + homeConnector.setRouter(address(bridgedRouter)); bridgedConnector.file("router", address(bridgedRouter)); - homeConnector = new MockHomeConnector(address(bridgedRouter)); + } function testAddingPoolWorks(uint64 poolId) public { @@ -109,6 +111,7 @@ contract ConnectorTest is Test { vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); bridgedConnector.updateMember(poolId, trancheId, user, validUntil); } + function testUpdatingMemberForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { vm.assume(validUntil > block.timestamp); vm.assume(user != address(0)); @@ -118,6 +121,7 @@ contract ConnectorTest is Test { bridgedConnector.updateMember(poolId, trancheId, user, validUntil); } + function testUpdatingTokenPriceWorks(uint64 poolId, bytes16 trancheId, uint256 price) public { homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); @@ -133,23 +137,35 @@ contract ConnectorTest is Test { homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); vm.expectRevert(bytes("CentrifugeConnector/not-the-router")); bridgedConnector.updateTokenPrice(poolId, trancheId, price); - } + function testUpdatingTokenPriceForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, uint256 price) public { bridgedConnector.file("router", address(this)); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); bridgedConnector.updateTokenPrice(poolId, trancheId, price); } + function testUpdatingTokenPriceForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, uint256 price) public { bridgedConnector.file("router", address(this)); bridgedConnector.addPool(poolId); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); bridgedConnector.updateTokenPrice(poolId, trancheId, price); } + + function testDepositWorks(uint64 poolId) public { - function testTransferToWorks(uint64 poolId) public { } - function testTransferToAsNonRouterFails(uint64 poolId) public { } - function testTransferToForNonExistentPoolFails(uint64 poolId) public { } - function testTransferToForNonExistentTrancheFails(uint64 poolId) public { } + } + // function testDepositFromOtherChainsFails(uint64 poolId) public { } + // function testDepositForNonExistentPoolFails(uint64 poolId) public { } + // function testDepositForNonExistentTrancheFails(uint64 poolId) public { } + // function testDepositWithoutAllowanceFails(uint64 poolId) public { } + + + // function testWithdrawelWorks(uint64 poolId) public { } + // function testWithdrawalFailsUnknownDomain(uint64 poolId) public { } + // function testWithdrawalFailsNotConnector(uint64 poolId) public { } + // function testWithdrawalFailsNotEnoughBalance(uint64 poolId) public { } + // function testWithdrawalFailsNotEnoughBalance(uint64 poolId) public { } + // function testWithdrawalFailsPoolDoesNotExist(uint64 poolId) public { } } \ No newline at end of file diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index f1c64efb..81793b1b 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -12,7 +12,7 @@ contract MockHomeConnector is Test { using TypedMemView for bytes29; using ConnectorMessages for bytes29; - IMessageRecipient public immutable home; + IMessageRecipient public home; uint32 immutable CENTRIFUGE_CHAIN_DOMAIN = 3000; @@ -22,7 +22,10 @@ contract MockHomeConnector is Test { AddPool } - constructor(address home_) { + constructor() { + } + + function setRouter(address home_) public { home = IMessageRecipient(home_); } From d98626e1e46a4d6b8213969f416e2e7aa36fa747 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Thu, 4 Aug 2022 15:35:09 +0100 Subject: [PATCH 06/41] add happy case deposit & withdraw tests --- src/Connector.sol | 9 +- src/routers/xcm/Router.sol | 6 ++ src/token/memberlist.sol | 4 +- src/token/restricted.sol | 1 + test/Connector.t.sol | 160 ++++++++++++++++++++++++++++---- test/mock/MockHomeConnector.sol | 6 +- 6 files changed, 163 insertions(+), 23 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index 8f97d5f4..dd12d9b2 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -31,7 +31,7 @@ contract CentrifugeConnector { mapping(uint64 => Pool) public pools; mapping(uint64 => mapping(bytes16 => Tranche)) public tranches; mapping(address => uint256) public wards; - mapping (bytes32 => uint32) public domainLookup; + mapping(bytes32 => uint32) public domainLookup; // --- Events --- @@ -149,15 +149,16 @@ contract CentrifugeConnector { function withdraw( uint64 poolId, bytes16 trancheId, - address user, + address user, uint256 amount, string memory domainName - ) public { + ) public auth { uint32 domainId = domainLookup[keccak256(bytes(domainName))]; require(domainId > 0, "CentrifugeConnector/domain-does-not-exist"); RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token); - require(token.balanceOf(user) >= amount, "CentrifugeConnector/insufficient-balance"); // optional + 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); diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index f633ead1..35c967f4 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -29,7 +29,13 @@ contract ConnectorXCMRouter is Router, Test { constructor(address connector_, address centrifugeChainOrigin_) { connector = ConnectorLike(connector_); centrifugeChainOrigin = centrifugeChainOrigin_; + __Ownable_init(); + //_owner + // _owner = msg.sender; } + + + modifier onlyCentrifugeChainOrigin() { require(msg.sender == address(centrifugeChainOrigin), "ConnectorXCMRouter/invalid-origin"); diff --git a/src/token/memberlist.sol b/src/token/memberlist.sol index 9896aa45..9f6dc31b 100644 --- a/src/token/memberlist.sol +++ b/src/token/memberlist.sol @@ -8,7 +8,7 @@ interface MemberlistLike { contract Memberlist { - uint constant minimumDelay = 7 days; + uint public constant minimumDelay = 7 days; mapping (address => uint) public members; @@ -28,7 +28,7 @@ contract Memberlist { } function updateMember(address usr, uint validUntil) public auth { - require((safeAdd(block.timestamp, minimumDelay)) < validUntil); + require(((safeAdd(block.timestamp, minimumDelay)) < validUntil), "invalid-validUntil"); members[usr] = validUntil; } diff --git a/src/token/restricted.sol b/src/token/restricted.sol index b688c929..9f5e65be 100644 --- a/src/token/restricted.sol +++ b/src/token/restricted.sol @@ -15,6 +15,7 @@ interface ERC20Like { 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); } interface RestrictedTokenLike is ERC20Like { diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 0a2007e4..60aee366 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -5,7 +5,7 @@ pragma abicoder v2; import { CentrifugeConnector } from "src/Connector.sol"; import { RestrictedTokenFactory, MemberlistFactory } from "src/token/factory.sol"; import { RestrictedTokenLike } from "src/token/restricted.sol"; -import { MemberlistLike } from "src/token/memberlist.sol"; +import { MemberlistLike, Memberlist } from "src/token/memberlist.sol"; import { MockHomeConnector } from "./mock/MockHomeConnector.sol"; import { ConnectorXCMRouter } from "src/routers/xcm/Router.sol"; import "forge-std/Test.sol"; @@ -16,6 +16,8 @@ contract ConnectorTest is Test { ConnectorXCMRouter bridgedRouter; MockHomeConnector homeConnector; + uint minimumDelay; + function setUp() public { address tokenFactory_ = address(new RestrictedTokenFactory()); address memberlistFactory_ = address(new MemberlistFactory()); @@ -25,9 +27,9 @@ contract ConnectorTest is Test { homeConnector = new MockHomeConnector(); bridgedRouter = new ConnectorXCMRouter(address(bridgedConnector), address(homeConnector)); homeConnector.setRouter(address(bridgedRouter)); - bridgedConnector.file("router", address(bridgedRouter)); + bridgedConnector.file("router", address(bridgedRouter)); - + minimumDelay = new Memberlist().minimumDelay(); } function testAddingPoolWorks(uint64 poolId) public { @@ -52,8 +54,12 @@ contract ConnectorTest is Test { assertTrue(token_ != address(0)); RestrictedTokenLike token = RestrictedTokenLike(token_); - assertEq(token.name(), tokenName); - assertEq(token.symbol(), tokenSymbol); + + // Comparing raw input to output can erroneously fail when a byte string is given. + // Intended behaviour is that byte strings will be treated as bytes and converted to strings instead of treated as strings themselves. + // This conversion from string to bytes32 to string is used to simulate this intended behaviour. + assertEq(token.name(), bytes32ToString(stringToBytes32(tokenName))); + assertEq(token.symbol(), bytes32ToString(stringToBytes32(tokenSymbol))); } function testAddingMultipleTranchesWorks(uint64 poolId, bytes16[] calldata trancheIds, string memory tokenName, string memory tokenSymbol) public { @@ -81,9 +87,8 @@ contract ConnectorTest is Test { } function testUpdatingMemberWorks(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { - vm.assume(validUntil > block.timestamp); - vm.assume(user != address(0)); - + vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); + // vm.assume(user != address(0)); -> not blocked by the memberlist contract homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); homeConnector.updateMember(poolId, trancheId, user, validUntil); @@ -98,7 +103,7 @@ contract ConnectorTest is Test { function testUpdatingMemberAsNonRouterFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { vm.assume(validUntil > block.timestamp); - vm.assume(user != address(0)); + // vm.assume(user != address(0)); vm.expectRevert(bytes("CentrifugeConnector/not-the-router")); bridgedConnector.updateMember(poolId, trancheId, user, validUntil); @@ -106,15 +111,25 @@ contract ConnectorTest is Test { function testUpdatingMemberForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { vm.assume(validUntil > block.timestamp); - vm.assume(user != address(0)); + // vm.assume(user != address(0)); bridgedConnector.file("router", address(this)); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); bridgedConnector.updateMember(poolId, trancheId, user, validUntil); } + function testUpdatingMemberBeforeMinimumDelayFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { + vm.assume(validUntil < safeAdd(block.timestamp, minimumDelay)); + // vm.assume(user != address(0)); -> not blocked by the memberlist contract + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, validUntil); + + vm.expectRevert("invalid-validUntil"); + } + function testUpdatingMemberForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { vm.assume(validUntil > block.timestamp); - vm.assume(user != address(0)); + // vm.assume(user != address(0)); bridgedConnector.file("router", address(this)); bridgedConnector.addPool(poolId); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); @@ -152,20 +167,133 @@ contract ConnectorTest is Test { bridgedConnector.updateTokenPrice(poolId, trancheId, price); } - function testDepositWorks(uint64 poolId) public { + function testDepositWorks(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { + vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); + + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, validUntil); + (address token_,,) = bridgedConnector.tranches(poolId, trancheId); + RestrictedTokenLike token = RestrictedTokenLike(token_); + MemberlistLike memberlist = MemberlistLike(token.memberlist()); + uint totalSupplyBefore = token.totalSupply(); + + homeConnector.deposit(poolId, trancheId, user, amount); + + + assertEq(memberlist.members(user), validUntil); + assertTrue(token.hasMember(user)); + assertEq(token.balanceOf(user), amount); + assertEq(token.totalSupply(), safeAdd(totalSupplyBefore, amount)); } - // function testDepositFromOtherChainsFails(uint64 poolId) public { } // function testDepositForNonExistentPoolFails(uint64 poolId) public { } // function testDepositForNonExistentTrancheFails(uint64 poolId) public { } // function testDepositWithoutAllowanceFails(uint64 poolId) public { } + // function testDepositFromOtherOriginFails(uint64 poolId) public { } + + function testWithdrawalWorks(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + string memory domainName = "Centrifuge"; + // vm.assume(keccak256(abi.encodePacked(domainName)) == keccak256(abi.encodePacked(domainName))); + + uint32 domainId = 3000; + // add Centrifuge domain to router + bridgedRouter.enrollRemoteRouter(domainId, stringToBytes32("testAddress")); + + // add Centrifuge domain to connector + assertEq(bridgedConnector.wards(address(this)), 1); + bridgedConnector.file("domain", domainName, domainId); + // bridgedConnector.deny(address(this)); // revoke ward permissions to test publicfunctions + // address user = address(this); // test contract = user + + + // fund user + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, uint(-1)); + homeConnector.deposit(poolId, trancheId, user, amount); - // function testWithdrawelWorks(uint64 poolId) public { } - // function testWithdrawalFailsUnknownDomain(uint64 poolId) public { } + bridgedConnector.withdraw(poolId, trancheId, user, amount, domainName); + } + + + // function testWithdrawalFailsUnknownDomainConnector(uint64 poolId) public { } + // function testWithdrawalFailsUnknownDomainRouter(uint64 poolId) public { } + // function testWithdrawalFailsUNoPermissions(uint64 poolId) public { } // function testWithdrawalFailsNotConnector(uint64 poolId) public { } // function testWithdrawalFailsNotEnoughBalance(uint64 poolId) public { } // function testWithdrawalFailsNotEnoughBalance(uint64 poolId) public { } // function testWithdrawalFailsPoolDoesNotExist(uint64 poolId) public { } -} \ No newline at end of file + // function fails domain does not exist + + + // helpers + function safeAdd(uint x, uint y) internal pure returns (uint z) { + require((z = x + y) >= x, "math-add-overflow"); + } + + function stringToBytes32(string memory source) internal pure returns (bytes32 result) { + bytes memory tempEmptyStringTest = bytes(source); + if (tempEmptyStringTest.length == 0) { + return 0x0; + } + + assembly { + result := mload(add(source, 32)) + } + } + + 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); + } + + // Convert an hexadecimal string to raw bytes + function fromHex(string memory s) internal pure returns (bytes memory) { + bytes memory ss = bytes(s); + require(ss.length % 2 == 0); // length must be even + bytes memory r = new bytes(ss.length / 2); + + for (uint256 i = 0; i < ss.length / 2; ++i) { + r[i] = bytes1( + fromHexChar(uint8(ss[2 * i])) * + 16 + + fromHexChar(uint8(ss[2 * i + 1])) + ); + } + return r; + } + + function toBytes32(bytes memory f) internal pure returns (bytes16 fc) { + assembly { + fc := mload(add(f, 32)) + } + return fc; + } + + // Convert an hexadecimal character to their value + function fromHexChar(uint8 c) internal pure returns (uint8) { + if (bytes1(c) >= bytes1("0") && bytes1(c) <= bytes1("9")) { + return c - uint8(bytes1("0")); + } + if (bytes1(c) >= bytes1("a") && bytes1(c) <= bytes1("f")) { + return 10 + c - uint8(bytes1("a")); + } + if (bytes1(c) >= bytes1("A") && bytes1(c) <= bytes1("F")) { + return 10 + c - uint8(bytes1("A")); + } + revert("Failed to encode hex char"); + } +} + + \ No newline at end of file diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index 81793b1b..1e2a75db 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -15,7 +15,6 @@ contract MockHomeConnector is Test { IMessageRecipient public home; uint32 immutable CENTRIFUGE_CHAIN_DOMAIN = 3000; - uint32 immutable NONCE = 1; enum Types { @@ -49,4 +48,9 @@ contract MockHomeConnector is Test { home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); } + function deposit(uint64 poolId, bytes16 trancheId, address user, uint256 amount) public { + bytes memory _message = ConnectorMessages.formatTransfer(poolId, trancheId, user, amount); + home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); + } + } From f32024b66291da4f7f248bf1d7e21d9b03abd355 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Thu, 4 Aug 2022 15:38:25 +0100 Subject: [PATCH 07/41] remove unused code --- src/routers/xcm/Router.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 35c967f4..b8da66bd 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -30,13 +30,9 @@ contract ConnectorXCMRouter is Router, Test { connector = ConnectorLike(connector_); centrifugeChainOrigin = centrifugeChainOrigin_; __Ownable_init(); - //_owner - // _owner = msg.sender; } - - modifier onlyCentrifugeChainOrigin() { require(msg.sender == address(centrifugeChainOrigin), "ConnectorXCMRouter/invalid-origin"); _; From ed9515fd6d17e929d9403043e8c14a8c19fc7429 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Tue, 9 Aug 2022 14:50:08 +0100 Subject: [PATCH 08/41] add xAppConnectionManager --- script/Connector-XCM.s.sol | 2 +- src/Connector.sol | 9 ++++++++- src/routers/xcm/Router.sol | 19 +++++++++++++++++-- src/token/restricted.sol | 1 + test/Connector.t.sol | 27 +++++++++++++++++++-------- test/mock/MockHomeConnector.sol | 10 ++++++++++ 6 files changed, 56 insertions(+), 12 deletions(-) diff --git a/script/Connector-XCM.s.sol b/script/Connector-XCM.s.sol index 0eac9078..4f107cf5 100644 --- a/script/Connector-XCM.s.sol +++ b/script/Connector-XCM.s.sol @@ -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)); } } diff --git a/src/Connector.sol b/src/Connector.sol index dd12d9b2..8b033b33 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -149,7 +149,7 @@ contract CentrifugeConnector { function withdraw( uint64 poolId, bytes16 trancheId, - address user, + address user, uint256 amount, string memory domainName ) public auth { @@ -163,5 +163,12 @@ contract CentrifugeConnector { token.burn(address(this), amount); router.sendMessage(domainId, poolId, trancheId, amount, user); } + + function tokenAddress(uint64 poolId, + bytes16 trancheId) public view returns(address) { + + return tranches[poolId][trancheId].token; + + } } diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index b8da66bd..05875aa5 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -26,10 +26,12 @@ 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); } @@ -72,10 +74,23 @@ contract ConnectorXCMRouter is Router, Test { function sendMessage(uint32 destinationDomain, uint64 poolId, bytes16 trancheId, uint256 amount, address user) external onlyConnector { bytes32 remoteAddress = _mustHaveRemote(destinationDomain); - Home(xAppConnectionManager.home()).dispatch( 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); + } + } diff --git a/src/token/restricted.sol b/src/token/restricted.sol index 9f5e65be..15524dbe 100644 --- a/src/token/restricted.sol +++ b/src/token/restricted.sol @@ -16,6 +16,7 @@ interface ERC20Like { 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 { diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 60aee366..cd6dce90 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -8,6 +8,8 @@ import { RestrictedTokenLike } from "src/token/restricted.sol"; import { MemberlistLike, Memberlist } from "src/token/memberlist.sol"; import { MockHomeConnector } from "./mock/MockHomeConnector.sol"; import { ConnectorXCMRouter } from "src/routers/xcm/Router.sol"; +import { Home } from "@nomad-xyz/contracts-core/contracts/Home.sol"; +import { XAppConnectionManager } from "@nomad-xyz/contracts-core/contracts/XAppConnectionManager.sol"; import "forge-std/Test.sol"; contract ConnectorTest is Test { @@ -23,9 +25,16 @@ contract ConnectorTest is Test { address memberlistFactory_ = address(new MemberlistFactory()); bridgedConnector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - // TODO: pass _xAppConnectionManager + + + // home = new Home(1000); homeConnector = new MockHomeConnector(); - bridgedRouter = new ConnectorXCMRouter(address(bridgedConnector), address(homeConnector)); + XAppConnectionManager connectionManager = new XAppConnectionManager(); + connectionManager.setHome(address(homeConnector)); + + + + bridgedRouter = new ConnectorXCMRouter(address(bridgedConnector), address(homeConnector), address(connectionManager)); homeConnector.setRouter(address(bridgedRouter)); bridgedConnector.file("router", address(bridgedRouter)); @@ -199,22 +208,24 @@ contract ConnectorTest is Test { // vm.assume(keccak256(abi.encodePacked(domainName)) == keccak256(abi.encodePacked(domainName))); uint32 domainId = 3000; - // add Centrifuge domain to router - bridgedRouter.enrollRemoteRouter(domainId, stringToBytes32("testAddress")); + // add Centrifuge domain to router + bridgedRouter.enrollRemoteRouter(domainId, stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132")); // add Centrifuge domain to connector assertEq(bridgedConnector.wards(address(this)), 1); bridgedConnector.file("domain", domainName, domainId); - // bridgedConnector.deny(address(this)); // revoke ward permissions to test publicfunctions - // address user = address(this); // test contract = user - + // bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions + + user = address(this); // set deployer as user to approve the cnnector to transfer funds // fund user homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); homeConnector.updateMember(poolId, trancheId, user, uint(-1)); homeConnector.deposit(poolId, trancheId, user, amount); - + // approve token + RestrictedTokenLike token = RestrictedTokenLike(bridgedConnector.tokenAddress(poolId, trancheId)); + token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token bridgedConnector.withdraw(poolId, trancheId, user, amount, domainName); } diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index 1e2a75db..407ebd25 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -53,4 +53,14 @@ contract MockHomeConnector is Test { home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); } + + function dispatch( + uint32 _destinationDomain, + bytes32 _recipientAddress, + bytes memory _messageBody + ) external { + + console.log("dispatch called"); + } + } From 2ed1b5afd7ba0364b86c6eacaf650bd30d167733 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Tue, 9 Aug 2022 19:02:05 +0100 Subject: [PATCH 09/41] add assertions for withdraw happu case --- src/Connector.sol | 10 +------ src/Messages.sol | 2 +- test/Connector.t.sol | 52 +++++++++++++++++++++------------ test/mock/MockHomeConnector.sol | 13 +++++++-- 4 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index 8b033b33..0de7ae72 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -152,7 +152,7 @@ contract CentrifugeConnector { address user, uint256 amount, string memory domainName - ) public auth { + ) public { uint32 domainId = domainLookup[keccak256(bytes(domainName))]; require(domainId > 0, "CentrifugeConnector/domain-does-not-exist"); @@ -163,12 +163,4 @@ contract CentrifugeConnector { token.burn(address(this), amount); router.sendMessage(domainId, poolId, trancheId, amount, user); } - - function tokenAddress(uint64 poolId, - bytes16 trancheId) public view returns(address) { - - return tranches[poolId][trancheId].token; - - } - } diff --git a/src/Messages.sol b/src/Messages.sol index dd682c06..fdab6fa6 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -136,7 +136,7 @@ library ConnectorMessages { } /** - * Deposit + * Transfer * * 0: call type (uint8 = 1 byte) * 1-8: poolId (uint64 = 8 bytes) diff --git a/test/Connector.t.sol b/test/Connector.t.sol index cd6dce90..9bc4bfee 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -8,8 +8,8 @@ import { RestrictedTokenLike } from "src/token/restricted.sol"; import { MemberlistLike, Memberlist } from "src/token/memberlist.sol"; import { MockHomeConnector } from "./mock/MockHomeConnector.sol"; import { ConnectorXCMRouter } from "src/routers/xcm/Router.sol"; -import { Home } from "@nomad-xyz/contracts-core/contracts/Home.sol"; import { XAppConnectionManager } from "@nomad-xyz/contracts-core/contracts/XAppConnectionManager.sol"; +import {ConnectorMessages} from "src/Messages.sol"; import "forge-std/Test.sol"; contract ConnectorTest is Test { @@ -25,15 +25,12 @@ contract ConnectorTest is Test { address memberlistFactory_ = address(new MemberlistFactory()); bridgedConnector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - // home = new Home(1000); homeConnector = new MockHomeConnector(); XAppConnectionManager connectionManager = new XAppConnectionManager(); connectionManager.setHome(address(homeConnector)); - - bridgedRouter = new ConnectorXCMRouter(address(bridgedConnector), address(homeConnector), address(connectionManager)); homeConnector.setRouter(address(bridgedRouter)); bridgedConnector.file("router", address(bridgedRouter)); @@ -97,7 +94,6 @@ contract ConnectorTest is Test { function testUpdatingMemberWorks(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); - // vm.assume(user != address(0)); -> not blocked by the memberlist contract homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); homeConnector.updateMember(poolId, trancheId, user, validUntil); @@ -112,7 +108,6 @@ contract ConnectorTest is Test { function testUpdatingMemberAsNonRouterFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { vm.assume(validUntil > block.timestamp); - // vm.assume(user != address(0)); vm.expectRevert(bytes("CentrifugeConnector/not-the-router")); bridgedConnector.updateMember(poolId, trancheId, user, validUntil); @@ -120,15 +115,13 @@ contract ConnectorTest is Test { function testUpdatingMemberForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { vm.assume(validUntil > block.timestamp); - // vm.assume(user != address(0)); bridgedConnector.file("router", address(this)); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); bridgedConnector.updateMember(poolId, trancheId, user, validUntil); } function testUpdatingMemberBeforeMinimumDelayFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { - vm.assume(validUntil < safeAdd(block.timestamp, minimumDelay)); - // vm.assume(user != address(0)); -> not blocked by the memberlist contract + vm.assume(validUntil < safeAdd(block.timestamp, minimumDelay)); homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); homeConnector.updateMember(poolId, trancheId, user, validUntil); @@ -138,7 +131,6 @@ contract ConnectorTest is Test { function testUpdatingMemberForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { vm.assume(validUntil > block.timestamp); - // vm.assume(user != address(0)); bridgedConnector.file("router", address(this)); bridgedConnector.addPool(poolId); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); @@ -190,7 +182,6 @@ contract ConnectorTest is Test { homeConnector.deposit(poolId, trancheId, user, amount); - assertEq(memberlist.members(user), validUntil); assertTrue(token.hasMember(user)); assertEq(token.balanceOf(user), amount); @@ -204,18 +195,17 @@ contract ConnectorTest is Test { function testWithdrawalWorks(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - string memory domainName = "Centrifuge"; - // vm.assume(keccak256(abi.encodePacked(domainName)) == keccak256(abi.encodePacked(domainName))); - + string memory domainName = "Centrifuge"; uint32 domainId = 3000; + bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); // add Centrifuge domain to router - bridgedRouter.enrollRemoteRouter(domainId, stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132")); - + bridgedRouter.enrollRemoteRouter(domainId, recipient); + // add Centrifuge domain to connector assertEq(bridgedConnector.wards(address(this)), 1); bridgedConnector.file("domain", domainName, domainId); - // bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - + bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions + user = address(this); // set deployer as user to approve the cnnector to transfer funds // fund user @@ -224,9 +214,26 @@ contract ConnectorTest is Test { homeConnector.updateMember(poolId, trancheId, user, uint(-1)); homeConnector.deposit(poolId, trancheId, user, amount); // approve token - RestrictedTokenLike token = RestrictedTokenLike(bridgedConnector.tokenAddress(poolId, trancheId)); + (address token_,,) = bridgedConnector.tranches(poolId, trancheId); + RestrictedTokenLike token = RestrictedTokenLike(token_); token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token + + uint userTokenBalanceBefore = token.balanceOf(user); + + // withdraw bridgedConnector.withdraw(poolId, trancheId, user, amount, domainName); + + assert(homeConnector.dispatchDomain() == domainId); + assertEq(token.balanceOf(user), (userTokenBalanceBefore - amount)); + assertEq(homeConnector.dispatchRecipient(), recipient); + assertEq(homeConnector.dispatchCalls(), 1); + + // TODO: fix assertions + //(uint64 poolIdDispatchCall, bytes16 trancheIdDispatchCall, address userDispatchCall, uint256 amountDispatchCall) = ConnectorMessages.parseTransfer(toBytes29(homeConnector.dispatchMessage())); + // assert(poolIdDispatchCall == poolId); + // assertEq(trancheIdDispatchCall,trancheId); + // assertEq(userDispatchCall, user); + // assertEq(amountDispatchCall, amount); } @@ -292,6 +299,13 @@ contract ConnectorTest is Test { return fc; } + function toBytes29(bytes memory f) internal pure returns (bytes29 fc) { + assembly { + fc := mload(add(f, 29)) + } + return fc; + } + // Convert an hexadecimal character to their value function fromHexChar(uint8 c) internal pure returns (uint8) { if (bytes1(c) >= bytes1("0") && bytes1(c) <= bytes1("9")) { diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index 407ebd25..d6054801 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -17,6 +17,13 @@ contract MockHomeConnector is Test { uint32 immutable CENTRIFUGE_CHAIN_DOMAIN = 3000; uint32 immutable NONCE = 1; + + uint32 public dispatchDomain; + bytes public dispatchMessage; + bytes32 public dispatchRecipient; + uint public dispatchCalls; + + enum Types { AddPool } @@ -59,8 +66,10 @@ contract MockHomeConnector is Test { bytes32 _recipientAddress, bytes memory _messageBody ) external { - - console.log("dispatch called"); + dispatchCalls++; + dispatchDomain = _destinationDomain; + dispatchMessage = _messageBody; + dispatchRecipient = _recipientAddress; } } From b1284e0e9683df4b59cb099c0b51411a460cb7a1 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Fri, 19 Aug 2022 14:31:55 +0100 Subject: [PATCH 10/41] add edges cases for transfer route --- src/Connector.sol | 3 +- src/routers/xcm/Router.sol | 4 +-- test/Connector.t.sol | 62 ++++++++++++++++++++++++++++----- test/mock/MockHomeConnector.sol | 2 +- 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index 0de7ae72..db239009 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -135,13 +135,14 @@ contract CentrifugeConnector { memberlist.updateMember(user, validUntil); } - function deposit( + function transfer( 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"); require(token.hasMember(user), "CentrifugeConnector/not-a-member"); token.mint(user, amount); } diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 05875aa5..d8290e0f 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -13,7 +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 deposit(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; + function transfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; } contract ConnectorXCMRouter is Router, Test { @@ -66,7 +66,7 @@ contract ConnectorXCMRouter is Router, Test { connector.updateTokenPrice(poolId, trancheId, price); } else if (ConnectorMessages.isTransfer(_msg) == true) { (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(_msg); - connector.deposit(poolId, trancheId, user, amount); + connector.transfer(poolId, trancheId, user, amount); } else { require(false, "invalid-message"); } diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 9bc4bfee..10be3f39 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -126,7 +126,7 @@ contract ConnectorTest is Test { homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); homeConnector.updateMember(poolId, trancheId, user, validUntil); - vm.expectRevert("invalid-validUntil"); + vm.expectRevert(bytes("invalid-validUntil")); } function testUpdatingMemberForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { @@ -168,7 +168,7 @@ contract ConnectorTest is Test { bridgedConnector.updateTokenPrice(poolId, trancheId, price); } - function testDepositWorks(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { + function testTransferWorks(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); homeConnector.addPool(poolId); @@ -180,7 +180,7 @@ contract ConnectorTest is Test { MemberlistLike memberlist = MemberlistLike(token.memberlist()); uint totalSupplyBefore = token.totalSupply(); - homeConnector.deposit(poolId, trancheId, user, amount); + homeConnector.transfer(poolId, trancheId, user, amount); assertEq(memberlist.members(user), validUntil); assertTrue(token.hasMember(user)); @@ -188,10 +188,56 @@ contract ConnectorTest is Test { assertEq(token.totalSupply(), safeAdd(totalSupplyBefore, amount)); } - // function testDepositForNonExistentPoolFails(uint64 poolId) public { } - // function testDepositForNonExistentTrancheFails(uint64 poolId) public { } - // function testDepositWithoutAllowanceFails(uint64 poolId) public { } - // function testDepositFromOtherOriginFails(uint64 poolId) public { } + function testTransferForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { + vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); + + // do not add pool + vm.expectRevert(bytes("CentrifugeConnector/invalid-pool")); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + + vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); + homeConnector.updateMember(poolId, trancheId, user, validUntil); + + vm.expectRevert(bytes("CentrifugeConnector/unknown-token")); + homeConnector.transfer(poolId, trancheId, user, amount); + } + + function testTransferForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { + vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); + + homeConnector.addPool(poolId); + //do not add tranche + vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); + homeConnector.updateMember(poolId, trancheId, user, validUntil); + + vm.expectRevert(bytes("CentrifugeConnector/unknown-token")); homeConnector.transfer(poolId, trancheId, user, amount); + + } + + function testTransferForNoMemberlistFails(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { + vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); + + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + + // do not add to Memberlist + vm.expectRevert(bytes("CentrifugeConnector/not-a-member")); + homeConnector.transfer(poolId, trancheId, user, amount); + } + + function testTransferFromOtherOriginFails(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { + + MockHomeConnector homeConnectorDifferentOrigin = new MockHomeConnector(); + homeConnectorDifferentOrigin.setRouter(address(bridgedRouter)); + vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); + + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, validUntil); + + vm.expectRevert(bytes("ConnectorXCMRouter/invalid-origin")); + homeConnectorDifferentOrigin.transfer(poolId, trancheId, user, amount); + } function testWithdrawalWorks(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { @@ -212,7 +258,7 @@ contract ConnectorTest is Test { homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); homeConnector.updateMember(poolId, trancheId, user, uint(-1)); - homeConnector.deposit(poolId, trancheId, user, amount); + homeConnector.transfer(poolId, trancheId, user, amount); // approve token (address token_,,) = bridgedConnector.tranches(poolId, trancheId); RestrictedTokenLike token = RestrictedTokenLike(token_); diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index d6054801..56edf946 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -55,7 +55,7 @@ contract MockHomeConnector is Test { home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); } - function deposit(uint64 poolId, bytes16 trancheId, address user, uint256 amount) public { + function transfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) public { bytes memory _message = ConnectorMessages.formatTransfer(poolId, trancheId, user, amount); home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); } From 47ec66f3cca52fe37fa3621017822fa54daf2ec2 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Fri, 19 Aug 2022 15:51:26 +0100 Subject: [PATCH 11/41] add edge case tests for transferTo --- src/Connector.sol | 2 +- test/Connector.t.sol | 248 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 198 insertions(+), 52 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index db239009..666076e6 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -147,7 +147,7 @@ contract CentrifugeConnector { token.mint(user, amount); } - function withdraw( + function transferTo( uint64 poolId, bytes16 trancheId, address user, diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 10be3f39..3aa2d17b 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -33,6 +33,7 @@ contract ConnectorTest is Test { bridgedRouter = new ConnectorXCMRouter(address(bridgedConnector), address(homeConnector), address(connectionManager)); homeConnector.setRouter(address(bridgedRouter)); + bridgedConnector.file("router", address(bridgedRouter)); minimumDelay = new Memberlist().minimumDelay(); @@ -124,9 +125,10 @@ contract ConnectorTest is Test { vm.assume(validUntil < safeAdd(block.timestamp, minimumDelay)); homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + vm.expectRevert(bytes("invalid-validUntil")); homeConnector.updateMember(poolId, trancheId, user, validUntil); - vm.expectRevert(bytes("invalid-validUntil")); + } function testUpdatingMemberForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { @@ -229,7 +231,7 @@ contract ConnectorTest is Test { MockHomeConnector homeConnectorDifferentOrigin = new MockHomeConnector(); homeConnectorDifferentOrigin.setRouter(address(bridgedRouter)); - vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); + vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); @@ -239,11 +241,12 @@ contract ConnectorTest is Test { homeConnectorDifferentOrigin.transfer(poolId, trancheId, user, amount); } - - function testWithdrawalWorks(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + function testTransferToWorks(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { string memory domainName = "Centrifuge"; uint32 domainId = 3000; bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); + user = address(this); // set deployer as user to approve the cnnector to transfer funds + // add Centrifuge domain to router bridgedRouter.enrollRemoteRouter(domainId, recipient); @@ -251,14 +254,13 @@ contract ConnectorTest is Test { assertEq(bridgedConnector.wards(address(this)), 1); bridgedConnector.file("domain", domainName, domainId); bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - - user = address(this); // set deployer as user to approve the cnnector to transfer funds - + // fund user homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); homeConnector.updateMember(poolId, trancheId, user, uint(-1)); homeConnector.transfer(poolId, trancheId, user, amount); + // approve token (address token_,,) = bridgedConnector.tranches(poolId, trancheId); RestrictedTokenLike token = RestrictedTokenLike(token_); @@ -266,31 +268,204 @@ contract ConnectorTest is Test { uint userTokenBalanceBefore = token.balanceOf(user); - // withdraw - bridgedConnector.withdraw(poolId, trancheId, user, amount, domainName); + // transferTo + bridgedConnector.transferTo(poolId, trancheId, user, amount, domainName); assert(homeConnector.dispatchDomain() == domainId); assertEq(token.balanceOf(user), (userTokenBalanceBefore - amount)); assertEq(homeConnector.dispatchRecipient(), recipient); assertEq(homeConnector.dispatchCalls(), 1); + } - // TODO: fix assertions + function testTransferToUnknownDomainNameFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + string memory domainName = "Centrifuge"; + uint32 domainId = 3000; + bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); + user = address(this); // set deployer as user to approve the cnnector to transfer funds + + // add Centrifuge domain to router + bridgedRouter.enrollRemoteRouter(domainId, recipient); + + // add Centrifuge domain to connector + assertEq(bridgedConnector.wards(address(this)), 1); + bridgedConnector.file("domain", domainName, domainId); + bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions + + // fund user + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, uint(-1)); + homeConnector.transfer(poolId, trancheId, user, amount); + + // approve token + (address token_,,) = bridgedConnector.tranches(poolId, trancheId); + RestrictedTokenLike token = RestrictedTokenLike(token_); + token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token + + // transferTo + vm.expectRevert(bytes("CentrifugeConnector/domain-does-not-exist")); + bridgedConnector.transferTo(poolId, trancheId, user, amount, "Unknown"); // use unknown domain name + } + + function testTransferToUnknownDomainIDFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + string memory domainName = "Centrifuge"; + uint32 domainId = 3000; + bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); + user = address(this); // set deployer as user to approve the cnnector to transfer funds + + // add Centrifuge domain to router + bridgedRouter.enrollRemoteRouter(domainId, recipient); + + // add Centrifuge domain to connector + assertEq(bridgedConnector.wards(address(this)), 1); + bridgedConnector.file("domain", domainName, 5000); // use wrong domainID + bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions + + // fund user + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, uint(-1)); + homeConnector.transfer(poolId, trancheId, user, amount); + + // approve token + (address token_,,) = bridgedConnector.tranches(poolId, trancheId); + RestrictedTokenLike token = RestrictedTokenLike(token_); + token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token + + // transferTo + vm.expectRevert(bytes("!remote")); + bridgedConnector.transferTo(poolId, trancheId, user, amount, "Centrifuge"); + } + + function testTransferToNotConnectorFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + + string memory domainName = "Centrifuge"; + uint32 domainId = 3000; + bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); + user = address(this); // set deployer as user to approve the cnnector to transfer funds + + // call from an address othe rthen bridged Connector + vm.expectRevert(bytes("ConnectorXCMRouter/only-connector-allowed-to-call")); + bridgedRouter.sendMessage(domainId, poolId, trancheId, amount, user); + } + + function testTransferToNotEnoughBalanceFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + vm.assume(amount > 0); + string memory domainName = "Centrifuge"; + uint32 domainId = 3000; + bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); + user = address(this); // set deployer as user to approve the cnnector to transfer funds + + // add Centrifuge domain to router + bridgedRouter.enrollRemoteRouter(domainId, recipient); + + // add Centrifuge domain to connector + assertEq(bridgedConnector.wards(address(this)), 1); + bridgedConnector.file("domain", domainName, domainId); // use wrong domainID + bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions + + // fund user + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, uint(-1)); + // do not fund user + + // approve token + (address token_,,) = bridgedConnector.tranches(poolId, trancheId); + RestrictedTokenLike token = RestrictedTokenLike(token_); + token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token + + // transferTo + vm.expectRevert(bytes("CentrifugeConnector/insufficient-balance")); + bridgedConnector.transferTo(poolId, trancheId, user, amount, "Centrifuge"); + } + + function testTransferToTokenDoesNotExistFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + string memory domainName = "Centrifuge"; + uint32 domainId = 3000; + bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); + user = address(this); // set deployer as user to approve the cnnector to transfer funds + + // add Centrifuge domain to router + bridgedRouter.enrollRemoteRouter(domainId, recipient); + + // add Centrifuge domain to connector + assertEq(bridgedConnector.wards(address(this)), 1); + bridgedConnector.file("domain", domainName, domainId); // use wrong domainID + bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions + + + // transferTo + vm.expectRevert(bytes("CentrifugeConnector/unknown-token")); + bridgedConnector.transferTo(poolId, trancheId, user, amount, "Centrifuge"); + } + + function testTransferToDomainNotEnrolledFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + string memory domainName = "Centrifuge"; + uint32 domainId = 3000; + bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); + user = address(this); // set deployer as user to approve the cnnector to transfer funds + + // do not enroll router + + // add Centrifuge domain to connector + assertEq(bridgedConnector.wards(address(this)), 1); + bridgedConnector.file("domain", domainName, domainId); + bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions + + // fund user + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, uint(-1)); + homeConnector.transfer(poolId, trancheId, user, amount); + + // approve token + (address token_,,) = bridgedConnector.tranches(poolId, trancheId); + RestrictedTokenLike token = RestrictedTokenLike(token_); + token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token + + // transferTo + vm.expectRevert(bytes("!remote")); + bridgedConnector.transferTo(poolId, trancheId, user, amount, domainName); // use unknown domain name + } + + function testTransferNoAllowanceFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + vm.assume(amount > 0); + string memory domainName = "Centrifuge"; + uint32 domainId = 3000; + bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); + user = address(this); // set deployer as user to approve the cnnector to transfer funds + + // add Centrifuge domain to router + bridgedRouter.enrollRemoteRouter(domainId, recipient); + + // add Centrifuge domain to connector + assertEq(bridgedConnector.wards(address(this)), 1); + bridgedConnector.file("domain", domainName, domainId); + bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions + + // fund user + homeConnector.addPool(poolId); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.updateMember(poolId, trancheId, user, uint(-1)); + homeConnector.transfer(poolId, trancheId, user, amount); + + // approve token + (address token_,,) = bridgedConnector.tranches(poolId, trancheId); + RestrictedTokenLike token = RestrictedTokenLike(token_); + //token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token + + // transferTo + vm.expectRevert(bytes("cent/insufficient-allowance")); + bridgedConnector.transferTo(poolId, trancheId, user, amount, domainName); // do not approve connector + } + + // TODO: fix & add assertions to transferTo tests - currently edge case that makes the assertion fail //(uint64 poolIdDispatchCall, bytes16 trancheIdDispatchCall, address userDispatchCall, uint256 amountDispatchCall) = ConnectorMessages.parseTransfer(toBytes29(homeConnector.dispatchMessage())); // assert(poolIdDispatchCall == poolId); // assertEq(trancheIdDispatchCall,trancheId); // assertEq(userDispatchCall, user); // assertEq(amountDispatchCall, amount); - } - - - // function testWithdrawalFailsUnknownDomainConnector(uint64 poolId) public { } - // function testWithdrawalFailsUnknownDomainRouter(uint64 poolId) public { } - // function testWithdrawalFailsUNoPermissions(uint64 poolId) public { } - // function testWithdrawalFailsNotConnector(uint64 poolId) public { } - // function testWithdrawalFailsNotEnoughBalance(uint64 poolId) public { } - // function testWithdrawalFailsNotEnoughBalance(uint64 poolId) public { } - // function testWithdrawalFailsPoolDoesNotExist(uint64 poolId) public { } - // function fails domain does not exist // helpers @@ -322,22 +497,6 @@ contract ConnectorTest is Test { return string(bytesArray); } - // Convert an hexadecimal string to raw bytes - function fromHex(string memory s) internal pure returns (bytes memory) { - bytes memory ss = bytes(s); - require(ss.length % 2 == 0); // length must be even - bytes memory r = new bytes(ss.length / 2); - - for (uint256 i = 0; i < ss.length / 2; ++i) { - r[i] = bytes1( - fromHexChar(uint8(ss[2 * i])) * - 16 + - fromHexChar(uint8(ss[2 * i + 1])) - ); - } - return r; - } - function toBytes32(bytes memory f) internal pure returns (bytes16 fc) { assembly { fc := mload(add(f, 32)) @@ -345,26 +504,13 @@ contract ConnectorTest is Test { return fc; } - function toBytes29(bytes memory f) internal pure returns (bytes29 fc) { + function toBytes29(bytes memory f) internal pure returns (bytes29 fc) { assembly { fc := mload(add(f, 29)) } return fc; } - // Convert an hexadecimal character to their value - function fromHexChar(uint8 c) internal pure returns (uint8) { - if (bytes1(c) >= bytes1("0") && bytes1(c) <= bytes1("9")) { - return c - uint8(bytes1("0")); - } - if (bytes1(c) >= bytes1("a") && bytes1(c) <= bytes1("f")) { - return 10 + c - uint8(bytes1("a")); - } - if (bytes1(c) >= bytes1("A") && bytes1(c) <= bytes1("F")) { - return 10 + c - uint8(bytes1("A")); - } - revert("Failed to encode hex char"); - } } \ No newline at end of file From fd2ecdea6c20a086101611379530b90b5e057374 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Fri, 19 Aug 2022 17:07:54 +0100 Subject: [PATCH 12/41] remove misleading messages --- test/Connector.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 78614bce..047cee95 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -363,7 +363,7 @@ contract ConnectorTest is Test { // add Centrifuge domain to connector assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, domainId); // use wrong domainID + bridgedConnector.file("domain", domainName, domainId); bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions // fund user From 335820c4ecca35298e158a391df802f855d95663 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Fri, 19 Aug 2022 17:08:40 +0100 Subject: [PATCH 13/41] remove misleading messages --- test/Connector.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 047cee95..884bf624 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -393,7 +393,7 @@ contract ConnectorTest is Test { // add Centrifuge domain to connector assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, domainId); // use wrong domainID + bridgedConnector.file("domain", domainName, domainId); bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions From d45f17e67c1e954f756e95786f65f42cb1fa253a Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Thu, 25 Aug 2022 16:14:12 -0300 Subject: [PATCH 14/41] update origin address --- script/Connector-XCM.s.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/Connector-XCM.s.sol b/script/Connector-XCM.s.sol index 5655fe4c..cf969fbd 100644 --- a/script/Connector-XCM.s.sol +++ b/script/Connector-XCM.s.sol @@ -17,8 +17,7 @@ contract ConnectorXCMScript is Script { address memberlistFactory_ = address(new MemberlistFactory()); CentrifugeConnector connector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - // TODO: add centrifugeChainOrigin_ arg. Using Kovan Admin Account in the meantime - ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0x0A735602a357802f553113F5831FE2fbf2F0E2e0)); + ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0x8c01f5aefdc2243742d312a29326ef44120ff965)); connector.file("router", address(router)); vm.stopBroadcast(); } From 71d553aa1241aebc47a1579eeccb3e24fb066520 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Thu, 25 Aug 2022 16:15:34 -0300 Subject: [PATCH 15/41] Use correct checksummed origin address --- script/Connector-XCM.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/Connector-XCM.s.sol b/script/Connector-XCM.s.sol index cf969fbd..b59a5e56 100644 --- a/script/Connector-XCM.s.sol +++ b/script/Connector-XCM.s.sol @@ -17,7 +17,7 @@ contract ConnectorXCMScript is Script { address memberlistFactory_ = address(new MemberlistFactory()); CentrifugeConnector connector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0x8c01f5aefdc2243742d312a29326ef44120ff965)); + ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0x8c01F5aefDc2243742d312a29326ef44120fF965)); connector.file("router", address(router)); vm.stopBroadcast(); } From e1cfc14489627b72ac2d455d2ed0af985a6d9af5 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Fri, 26 Aug 2022 15:31:49 -0300 Subject: [PATCH 16/41] Strip out nomad and testing dependencies from XCMRouter. Add ping function for testing. --- src/routers/xcm/Router.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 7fd0b5bb..2cea9789 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -3,9 +3,7 @@ pragma solidity ^0.7.6; pragma abicoder v2; import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol"; -import {Router} from "@nomad-xyz/contracts-router/contracts/Router.sol"; -import {ConnectorMessages} from "../..//Messages.sol"; -import "forge-std/Test.sol"; +import {ConnectorMessages} from "../../Messages.sol"; interface ConnectorLike { function addPool(uint64 poolId) external; @@ -14,7 +12,7 @@ interface ConnectorLike { function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) external; } -contract ConnectorXCMRouter is Router, Test { +contract ConnectorXCMRouter { using TypedMemView for bytes; // why bytes29? - https://github.com/summa-tx/memview-sol#why-bytes29 using TypedMemView for bytes29; @@ -35,11 +33,8 @@ contract ConnectorXCMRouter is Router, Test { } function handle( - uint32 _origin, - uint32 _nonce, - bytes32 _sender, bytes memory _message - ) external override onlyCentrifugeChainOrigin { + ) external onlyCentrifugeChainOrigin { bytes29 _msg = _message.ref(0); if (ConnectorMessages.isAddPool(_msg) == true) { uint64 poolId = ConnectorMessages.parseAddPool(_msg); @@ -57,4 +52,9 @@ contract ConnectorXCMRouter is Router, Test { require(false, "invalid-message"); } } + + // TODO: For testing purposes. Remove once router integration is complete + function ping() public pure returns (string memory){ + return "pong"; + } } From 2e4747096b5d1eec379978f71a713bb7400eade1 Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Mon, 29 Aug 2022 14:47:30 +0200 Subject: [PATCH 17/41] Update router --- src/routers/xcm/Router.sol | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 2cea9789..4a8d30b5 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -22,6 +22,8 @@ contract ConnectorXCMRouter { address immutable centrifugeChainOrigin; + event Pong(address indexed origin); + constructor(address connector_, address centrifugeChainOrigin_) { connector = ConnectorLike(connector_); centrifugeChainOrigin = centrifugeChainOrigin_; @@ -32,6 +34,28 @@ contract ConnectorXCMRouter { _; } + // TODO: re-add onlyCentrifugeChainOrigin + function handleUnauthenticated( + bytes memory _message + ) external { + bytes29 _msg = _message.ref(0); + if (ConnectorMessages.isAddPool(_msg) == true) { + uint64 poolId = ConnectorMessages.parseAddPool(_msg); + connector.addPool(poolId); + } else if (ConnectorMessages.isAddTranche(_msg) == true) { + (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); + } else if (ConnectorMessages.isUpdateTokenPrice(_msg) == true) { + (uint64 poolId, bytes16 trancheId, uint256 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); + connector.updateTokenPrice(poolId, trancheId, price); + } else { + require(false, "invalid-message"); + } + } + function handle( bytes memory _message ) external onlyCentrifugeChainOrigin { @@ -54,7 +78,12 @@ contract ConnectorXCMRouter { } // TODO: For testing purposes. Remove once router integration is complete - function ping() public pure returns (string memory){ - return "pong"; + function ping() external { + emit Pong(msg.sender); + } + + // TODO: For testing purposes. Remove once router integration is complete + function testThatAnErrorIsShown() external { + require(1 == 2, "explicit-error"); } } From 8338a3b16e73d0ac84b109d96a25cf6a44a59063 Mon Sep 17 00:00:00 2001 From: ilinzweilin Date: Tue, 30 Aug 2022 10:37:06 +0100 Subject: [PATCH 18/41] fix naming --- src/Connector.sol | 4 ++-- src/routers/xcm/Router.sol | 4 ++-- test/Connector.t.sol | 42 +++++++++++++++++++------------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index 666076e6..8620711a 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -135,7 +135,7 @@ contract CentrifugeConnector { memberlist.updateMember(user, validUntil); } - function transfer( + function handleTransfer( uint64 poolId, bytes16 trancheId, address user, @@ -147,7 +147,7 @@ contract CentrifugeConnector { token.mint(user, amount); } - function transferTo( + function transfer( uint64 poolId, bytes16 trancheId, address user, diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index d8290e0f..847d21f9 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -13,7 +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; + function handleTransfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; } contract ConnectorXCMRouter is Router, Test { @@ -66,7 +66,7 @@ contract ConnectorXCMRouter is Router, Test { connector.updateTokenPrice(poolId, trancheId, price); } else if (ConnectorMessages.isTransfer(_msg) == true) { (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(_msg); - connector.transfer(poolId, trancheId, user, amount); + connector.handleTransfer(poolId, trancheId, user, amount); } else { require(false, "invalid-message"); } diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 884bf624..9554cac5 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -243,7 +243,7 @@ contract ConnectorTest is Test { homeConnectorDifferentOrigin.transfer(poolId, trancheId, user, amount); } - function testTransferToWorks(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + function testTransferWorks(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { string memory domainName = "Centrifuge"; uint32 domainId = 3000; bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); @@ -270,8 +270,8 @@ contract ConnectorTest is Test { uint userTokenBalanceBefore = token.balanceOf(user); - // transferTo - bridgedConnector.transferTo(poolId, trancheId, user, amount, domainName); + // transfer + bridgedConnector.transfer(poolId, trancheId, user, amount, domainName); assert(homeConnector.dispatchDomain() == domainId); assertEq(token.balanceOf(user), (userTokenBalanceBefore - amount)); @@ -279,7 +279,7 @@ contract ConnectorTest is Test { assertEq(homeConnector.dispatchCalls(), 1); } - function testTransferToUnknownDomainNameFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + function testTransferUnknownDomainNameFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { string memory domainName = "Centrifuge"; uint32 domainId = 3000; bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); @@ -304,12 +304,12 @@ contract ConnectorTest is Test { RestrictedTokenLike token = RestrictedTokenLike(token_); token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - // transferTo + // transfer vm.expectRevert(bytes("CentrifugeConnector/domain-does-not-exist")); - bridgedConnector.transferTo(poolId, trancheId, user, amount, "Unknown"); // use unknown domain name + bridgedConnector.transfer(poolId, trancheId, user, amount, "Unknown"); // use unknown domain name } - function testTransferToUnknownDomainIDFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + function testTransferUnknownDomainIDFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { string memory domainName = "Centrifuge"; uint32 domainId = 3000; bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); @@ -334,12 +334,12 @@ contract ConnectorTest is Test { RestrictedTokenLike token = RestrictedTokenLike(token_); token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - // transferTo + // transfer vm.expectRevert(bytes("!remote")); - bridgedConnector.transferTo(poolId, trancheId, user, amount, "Centrifuge"); + bridgedConnector.transfer(poolId, trancheId, user, amount, "Centrifuge"); } - function testTransferToNotConnectorFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + function testTransferNotConnectorFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { string memory domainName = "Centrifuge"; uint32 domainId = 3000; @@ -351,7 +351,7 @@ contract ConnectorTest is Test { bridgedRouter.sendMessage(domainId, poolId, trancheId, amount, user); } - function testTransferToNotEnoughBalanceFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + function testTransferNotEnoughBalanceFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { vm.assume(amount > 0); string memory domainName = "Centrifuge"; uint32 domainId = 3000; @@ -377,12 +377,12 @@ contract ConnectorTest is Test { RestrictedTokenLike token = RestrictedTokenLike(token_); token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - // transferTo + // transfer vm.expectRevert(bytes("CentrifugeConnector/insufficient-balance")); - bridgedConnector.transferTo(poolId, trancheId, user, amount, "Centrifuge"); + bridgedConnector.transfer(poolId, trancheId, user, amount, "Centrifuge"); } - function testTransferToTokenDoesNotExistFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + function testTransferTokenDoesNotExistFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { string memory domainName = "Centrifuge"; uint32 domainId = 3000; bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); @@ -397,12 +397,12 @@ contract ConnectorTest is Test { bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - // transferTo + // transfer vm.expectRevert(bytes("CentrifugeConnector/unknown-token")); - bridgedConnector.transferTo(poolId, trancheId, user, amount, "Centrifuge"); + bridgedConnector.transfer(poolId, trancheId, user, amount, "Centrifuge"); } - function testTransferToDomainNotEnrolledFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { + function testTransferDomainNotEnrolledFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { string memory domainName = "Centrifuge"; uint32 domainId = 3000; bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); @@ -426,9 +426,9 @@ contract ConnectorTest is Test { RestrictedTokenLike token = RestrictedTokenLike(token_); token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - // transferTo + // transfer vm.expectRevert(bytes("!remote")); - bridgedConnector.transferTo(poolId, trancheId, user, amount, domainName); // use unknown domain name + bridgedConnector.transfer(poolId, trancheId, user, amount, domainName); // use unknown domain name } function testTransferNoAllowanceFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { @@ -457,9 +457,9 @@ contract ConnectorTest is Test { RestrictedTokenLike token = RestrictedTokenLike(token_); //token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - // transferTo + // transfer vm.expectRevert(bytes("cent/insufficient-allowance")); - bridgedConnector.transferTo(poolId, trancheId, user, amount, domainName); // do not approve connector + bridgedConnector.transfer(poolId, trancheId, user, amount, domainName); // do not approve connector } // TODO: fix & add assertions to transferTo tests - currently edge case that makes the assertion fail From 33a8191daf84513a270a1d07a23e3cc2a84dc635 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Thu, 29 Sep 2022 09:32:55 -0400 Subject: [PATCH 19/41] Update centrifuge origin address --- script/Connector-XCM.s.sol | 2 +- src/routers/xcm/Router.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/Connector-XCM.s.sol b/script/Connector-XCM.s.sol index b59a5e56..74475163 100644 --- a/script/Connector-XCM.s.sol +++ b/script/Connector-XCM.s.sol @@ -17,7 +17,7 @@ contract ConnectorXCMScript is Script { address memberlistFactory_ = address(new MemberlistFactory()); CentrifugeConnector connector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0x8c01F5aefDc2243742d312a29326ef44120fF965)); + ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0x7369626CEF070000000000000000000000000000)); connector.file("router", address(router)); vm.stopBroadcast(); } diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 4a8d30b5..678d0ade 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -37,7 +37,7 @@ contract ConnectorXCMRouter { // TODO: re-add onlyCentrifugeChainOrigin function handleUnauthenticated( bytes memory _message - ) external { + ) external { bytes29 _msg = _message.ref(0); if (ConnectorMessages.isAddPool(_msg) == true) { uint64 poolId = ConnectorMessages.parseAddPool(_msg); From 1c57fa7f1f99674a0ee8bc4c20b1100f4c3b54b9 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Wed, 5 Oct 2022 15:06:05 -0400 Subject: [PATCH 20/41] fix updateMember parsing --- src/Messages.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Messages.sol b/src/Messages.sol index 1f7d81cc..3544e4a7 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -110,7 +110,8 @@ library ConnectorMessages { poolId = uint64(_msg.indexUint(1, 8)); trancheId = bytes16(_msg.index(9, 16)); user = address(bytes20(_msg.index(25, 20))); - validUntil = uint256(_msg.index(45, 32)); + // skip 12 padded zeroes from address + validUntil = uint256(_msg.index(57, 32)); } /** From 65ed9bbdb77ea3299577bd27c046049e977e9945 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Thu, 17 Nov 2022 12:48:58 -0500 Subject: [PATCH 21/41] roll back updateMember change --- src/Messages.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Messages.sol b/src/Messages.sol index 3544e4a7..e48852d2 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -110,8 +110,8 @@ library ConnectorMessages { poolId = uint64(_msg.indexUint(1, 8)); trancheId = bytes16(_msg.index(9, 16)); user = address(bytes20(_msg.index(25, 20))); - // skip 12 padded zeroes from address - validUntil = uint256(_msg.index(57, 32)); + // TODO: skip 12 padded zeroes from address + validUntil = uint256(_msg.index(45, 32)); } /** From 6593be220fbdb03a5917ad7aac2872317ba55362 Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Fri, 18 Nov 2022 09:51:30 +0100 Subject: [PATCH 22/41] Clean up --- .gitmodules | 4 -- foundry.toml | 6 +-- lib/forge-std | 2 +- lib/monorepo | 1 - lib/openzeppelin-contracts-upgradeable | 2 +- script/Connector-Nomad.s.sol | 24 ---------- script/Connector-XCM.s.sol | 2 +- src/routers/nomad/Forwarder.sol | 0 src/routers/nomad/Router.sol | 63 -------------------------- src/routers/xcm/Router.sol | 32 ------------- test/Connector.t.sol | 7 ++- 11 files changed, 9 insertions(+), 134 deletions(-) delete mode 160000 lib/monorepo delete mode 100644 script/Connector-Nomad.s.sol delete mode 100644 src/routers/nomad/Forwarder.sol delete mode 100644 src/routers/nomad/Router.sol diff --git a/.gitmodules b/.gitmodules index 1fa31f34..178960bd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,10 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "lib/monorepo"] - path = lib/monorepo - branch = main - url = https://github.com/nomad-xyz/monorepo [submodule "lib/openzeppelin-contracts-upgradeable"] path = lib/openzeppelin-contracts-upgradeable url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/foundry.toml b/foundry.toml index 6146ab6b..bf40fbbd 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,4 +1,4 @@ -[default] +[profile.default] src = 'src' out = 'out' libs = ['lib'] @@ -9,10 +9,10 @@ optimizer = true # Enable or disable the solc optimizer optimizer_runs = 200 # The number of optimizer runs verbosity = 3 # The verbosity of tests -[pull_request] +[profile.pull_request] fuzz_runs = 1000 -[push_to_main] +[profile.push_to_main] fuzz_runs = 50000 fuzz_max_global_rejects = 262144 fuzz_max_local_rejects = 4096 diff --git a/lib/forge-std b/lib/forge-std index 0d0485bd..51a29d7f 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 0d0485bdea9f9455bd684acb0ba88548a104d99b +Subproject commit 51a29d7f15c574189518afd42680d16f367b7d59 diff --git a/lib/monorepo b/lib/monorepo deleted file mode 160000 index df21c2d1..00000000 --- a/lib/monorepo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit df21c2d16539ad98be062db43e95a0e7341a2afc diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index e0683346..54803be6 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit e0683346f70db930bd39551046b12449ea68f2dc +Subproject commit 54803be62207c2412e27d09325243f2f1452f7b9 diff --git a/script/Connector-Nomad.s.sol b/script/Connector-Nomad.s.sol deleted file mode 100644 index ead86ab6..00000000 --- a/script/Connector-Nomad.s.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.7.6; - -import { ConnectorNomadRouter } from "src/routers/nomad/Router.sol"; -import { CentrifugeConnector } from "src/Connector.sol"; -import { RestrictedTokenFactory, MemberlistFactory } from "src/token/factory.sol"; -import "forge-std/Script.sol"; - -// Script to deploy Connectors with a Nomad router. -contract ConnectorNomadScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - - address tokenFactory_ = address(new RestrictedTokenFactory()); - address memberlistFactory_ = address(new MemberlistFactory()); - CentrifugeConnector connector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - - // TODO: pass _xAppConnectionManager as 2nd argument - ConnectorNomadRouter router = new ConnectorNomadRouter(address(connector), address(0)); - connector.file("router", address(router)); - } -} diff --git a/script/Connector-XCM.s.sol b/script/Connector-XCM.s.sol index b59a5e56..41fdb511 100644 --- a/script/Connector-XCM.s.sol +++ b/script/Connector-XCM.s.sol @@ -17,7 +17,7 @@ contract ConnectorXCMScript is Script { address memberlistFactory_ = address(new MemberlistFactory()); CentrifugeConnector connector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(0x8c01F5aefDc2243742d312a29326ef44120fF965)); + ConnectorXCMRouter router = new ConnectorXCMRouter(address(connector), address(vm.envAddress("CENTRIFUGE_CHAIN_ORIGIN"))); connector.file("router", address(router)); vm.stopBroadcast(); } diff --git a/src/routers/nomad/Forwarder.sol b/src/routers/nomad/Forwarder.sol deleted file mode 100644 index e69de29b..00000000 diff --git a/src/routers/nomad/Router.sol b/src/routers/nomad/Router.sol deleted file mode 100644 index 2bb1d4b9..00000000 --- a/src/routers/nomad/Router.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.7.6; -pragma abicoder v2; - -import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol"; -import {Router} from "@nomad-xyz/contracts-router/contracts/Router.sol"; -import {ConnectorMessages} from "../..//Messages.sol"; -import "forge-std/Test.sol"; - -interface ConnectorLike { - function addPool(uint64 poolId) external; - 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; -} - -contract ConnectorNomadRouter is Router, Test { - using TypedMemView for bytes; - using TypedMemView for bytes29; - using ConnectorMessages for bytes29; - - ConnectorLike public immutable connector; - - uint32 immutable CENTRIFUGE_CHAIN_DOMAIN = 3000; - - constructor(address connector_, address _xAppConnectionManager) { - connector = ConnectorLike(connector_); - __XAppConnectionClient_initialize(_xAppConnectionManager); - } - - function send(bytes memory message) internal { - (_home()).dispatch( - CENTRIFUGE_CHAIN_DOMAIN, - _mustHaveRemote(CENTRIFUGE_CHAIN_DOMAIN), - message - ); - } - - // TODO: onlyReplica onlyRemoteRouter(_origin, _sender) - function handle( - uint32 _origin, - uint32 _nonce, - bytes32 _sender, - bytes memory _message - ) external override { - bytes29 _msg = _message.ref(0); - if (ConnectorMessages.isAddPool(_msg) == true) { - uint64 poolId = ConnectorMessages.parseAddPool(_msg); - connector.addPool(poolId); - } else if (ConnectorMessages.isAddTranche(_msg) == true) { - (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); - } else if (ConnectorMessages.isUpdateTokenPrice(_msg) == true) { - (uint64 poolId, bytes16 trancheId, uint256 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); - connector.updateTokenPrice(poolId, trancheId, price); - } else { - require(false, "invalid-message"); - } - } -} diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 4a8d30b5..55c79308 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -34,28 +34,6 @@ contract ConnectorXCMRouter { _; } - // TODO: re-add onlyCentrifugeChainOrigin - function handleUnauthenticated( - bytes memory _message - ) external { - bytes29 _msg = _message.ref(0); - if (ConnectorMessages.isAddPool(_msg) == true) { - uint64 poolId = ConnectorMessages.parseAddPool(_msg); - connector.addPool(poolId); - } else if (ConnectorMessages.isAddTranche(_msg) == true) { - (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); - } else if (ConnectorMessages.isUpdateTokenPrice(_msg) == true) { - (uint64 poolId, bytes16 trancheId, uint256 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); - connector.updateTokenPrice(poolId, trancheId, price); - } else { - require(false, "invalid-message"); - } - } - function handle( bytes memory _message ) external onlyCentrifugeChainOrigin { @@ -76,14 +54,4 @@ contract ConnectorXCMRouter { require(false, "invalid-message"); } } - - // TODO: For testing purposes. Remove once router integration is complete - function ping() external { - emit Pong(msg.sender); - } - - // TODO: For testing purposes. Remove once router integration is complete - function testThatAnErrorIsShown() external { - require(1 == 2, "explicit-error"); - } } diff --git a/test/Connector.t.sol b/test/Connector.t.sol index a11d6155..a808b1cb 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -7,13 +7,13 @@ import { RestrictedTokenFactory, MemberlistFactory } from "src/token/factory.sol import { RestrictedTokenLike } from "src/token/restricted.sol"; import { MemberlistLike, Memberlist } from "src/token/memberlist.sol"; import { MockHomeConnector } from "./mock/MockHomeConnector.sol"; -import { ConnectorNomadRouter } from "src/routers/nomad/Router.sol"; +import { ConnectorXCMRouter } from "src/routers/xcm/Router.sol"; import "forge-std/Test.sol"; contract ConnectorTest is Test { CentrifugeConnector bridgedConnector; - ConnectorNomadRouter bridgedRouter; + ConnectorXCMRouter bridgedRouter; MockHomeConnector homeConnector; function setUp() public { @@ -21,8 +21,7 @@ contract ConnectorTest is Test { address memberlistFactory_ = address(new MemberlistFactory()); bridgedConnector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - // TODO: pass _xAppConnectionManager - bridgedRouter = new ConnectorNomadRouter(address(bridgedConnector), address(0)); + bridgedRouter = new ConnectorXCMRouter(address(bridgedConnector), address(0)); bridgedConnector.file("router", address(bridgedRouter)); homeConnector = new MockHomeConnector(address(bridgedRouter)); From 7da41b5608850a576d94c9d64c088f92896c79e1 Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Fri, 18 Nov 2022 09:51:38 +0100 Subject: [PATCH 23/41] forge install: memview-sol --- .gitmodules | 3 +++ lib/memview-sol | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/memview-sol diff --git a/.gitmodules b/.gitmodules index 178960bd..730015b1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/openzeppelin-contracts-upgradeable"] path = lib/openzeppelin-contracts-upgradeable url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "lib/memview-sol"] + path = lib/memview-sol + url = https://github.com/@summa-tx/memview-sol diff --git a/lib/memview-sol b/lib/memview-sol new file mode 160000 index 00000000..3071bb11 --- /dev/null +++ b/lib/memview-sol @@ -0,0 +1 @@ +Subproject commit 3071bb11a8f87dfaa65846f3f12bba2ddf16add8 From 84c7f39303ca6025a437d76236b1066a039ffdac Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Fri, 18 Nov 2022 10:03:09 +0100 Subject: [PATCH 24/41] Fix all the tests --- lib/forge-std | 2 +- lib/memview-sol | 2 +- lib/openzeppelin-contracts-upgradeable | 2 +- remappings.txt | 8 +++----- src/Messages.sol | 2 +- src/routers/xcm/Router.sol | 6 ++---- test/Connector.t.sol | 6 ++---- test/Messages.t.sol | 2 +- test/mock/MockHomeConnector.sol | 18 +++++++++--------- 9 files changed, 21 insertions(+), 27 deletions(-) diff --git a/lib/forge-std b/lib/forge-std index 51a29d7f..75129776 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 51a29d7f15c574189518afd42680d16f367b7d59 +Subproject commit 75129776235fe6ebf075b0c8d0fe412092a1ac96 diff --git a/lib/memview-sol b/lib/memview-sol index 3071bb11..98bb0c81 160000 --- a/lib/memview-sol +++ b/lib/memview-sol @@ -1 +1 @@ -Subproject commit 3071bb11a8f87dfaa65846f3f12bba2ddf16add8 +Subproject commit 98bb0c81aac9bf429d9044a3b31169281c118b51 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 54803be6..25aabd28 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 54803be62207c2412e27d09325243f2f1452f7b9 +Subproject commit 25aabd286e002a1526c345c8db259d57bdf0ad28 diff --git a/remappings.txt b/remappings.txt index ad990828..a47cfa4a 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,7 +1,5 @@ -@nomad-xyz/=lib/monorepo/packages/ -@summa-tx/=lib/monorepo/node_modules/@summa-tx/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ -monorepo/=lib/monorepo/ -openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ -@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ \ No newline at end of file +memview-sol/=lib/memview-sol/contracts/ +openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ \ No newline at end of file diff --git a/src/Messages.sol b/src/Messages.sol index 1f7d81cc..5c83e67c 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.7.6; -import "@summa-tx/memview-sol/contracts/TypedMemView.sol"; +import "memview-sol/TypedMemView.sol"; library ConnectorMessages { using TypedMemView for bytes; diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 55c79308..3133dbd6 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -2,7 +2,7 @@ pragma solidity ^0.7.6; pragma abicoder v2; -import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol"; +import {TypedMemView} from "memview-sol/TypedMemView.sol"; import {ConnectorMessages} from "../../Messages.sol"; interface ConnectorLike { @@ -20,9 +20,7 @@ contract ConnectorXCMRouter { ConnectorLike public immutable connector; - address immutable centrifugeChainOrigin; - - event Pong(address indexed origin); + address centrifugeChainOrigin; constructor(address connector_, address centrifugeChainOrigin_) { connector = ConnectorLike(connector_); diff --git a/test/Connector.t.sol b/test/Connector.t.sol index a808b1cb..cb4afb6d 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -21,10 +21,8 @@ contract ConnectorTest is Test { address memberlistFactory_ = address(new MemberlistFactory()); bridgedConnector = new CentrifugeConnector(tokenFactory_, memberlistFactory_); - bridgedRouter = new ConnectorXCMRouter(address(bridgedConnector), address(0)); - bridgedConnector.file("router", address(bridgedRouter)); - - homeConnector = new MockHomeConnector(address(bridgedRouter)); + homeConnector = new MockHomeConnector(address(bridgedConnector)); + bridgedConnector.file("router", address(homeConnector.router())); } function testAddingPoolWorks(uint64 poolId) public { diff --git a/test/Messages.t.sol b/test/Messages.t.sol index b9048496..14ced4a9 100644 --- a/test/Messages.t.sol +++ b/test/Messages.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.7.6; pragma abicoder v2; -import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol"; +import {TypedMemView} from "memview-sol/TypedMemView.sol"; import {ConnectorMessages} from "src/Messages.sol"; import "forge-std/Test.sol"; diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index f1c64efb..8998ceca 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -2,17 +2,17 @@ pragma solidity ^0.7.6; pragma abicoder v2; -import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol"; -import {IMessageRecipient} from "@nomad-xyz/contracts-core/contracts/interfaces/IMessageRecipient.sol"; +import {TypedMemView} from "memview-sol/TypedMemView.sol"; import {ConnectorMessages} from "src/Messages.sol"; import "forge-std/Test.sol"; +import { ConnectorXCMRouter } from "src/routers/xcm/Router.sol"; contract MockHomeConnector is Test { using TypedMemView for bytes; using TypedMemView for bytes29; using ConnectorMessages for bytes29; - IMessageRecipient public immutable home; + ConnectorXCMRouter public immutable router; uint32 immutable CENTRIFUGE_CHAIN_DOMAIN = 3000; @@ -22,28 +22,28 @@ contract MockHomeConnector is Test { AddPool } - constructor(address home_) { - home = IMessageRecipient(home_); + constructor(address bridgedConnector) { + router = new ConnectorXCMRouter(bridgedConnector, address(this)); } function addPool(uint64 poolId) public { bytes memory _message = ConnectorMessages.formatAddPool(poolId); - home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); + router.handle(_message); } function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) public { bytes memory _message = ConnectorMessages.formatAddTranche(poolId, trancheId, tokenName, tokenSymbol); - home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); + router.handle(_message); } function updateMember(uint64 poolId, bytes16 trancheId, address user, uint256 amount) public { bytes memory _message = ConnectorMessages.formatUpdateMember(poolId, trancheId, user, amount); - home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); + router.handle(_message); } function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) public { bytes memory _message = ConnectorMessages.formatUpdateTokenPrice(poolId, trancheId, price); - home.handle(CENTRIFUGE_CHAIN_DOMAIN, NONCE, "1", _message); + router.handle(_message); } } From 644dbb274d7d3ed634832495bcb89cddce0b4975 Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Fri, 18 Nov 2022 10:06:36 +0100 Subject: [PATCH 25/41] Fix deps --- .gitmodules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 730015b1..2b9f73b0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,5 @@ url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable [submodule "lib/memview-sol"] path = lib/memview-sol - url = https://github.com/@summa-tx/memview-sol + url = https://github.com/summa-tx/memview-sol + branch = v2.1.1 From 7f3870b87a6d7c58a7a74ae217dd131078c8cfb9 Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Fri, 18 Nov 2022 10:07:42 +0100 Subject: [PATCH 26/41] Remove yarn steps from workflows --- .github/workflows/pull-request.yml | 19 ------------------- .github/workflows/push-to-main.yml | 19 ------------------- 2 files changed, 38 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 5635cbbf..609e40d6 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -17,25 +17,6 @@ jobs: with: submodules: recursive - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2-beta - with: - node-version: '12' - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn config get cacheFolder)" - - - uses: actions/cache@v2 - id: yarn-cache - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - run: yarn install - working-directory: ./lib/monorepo - - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: diff --git a/.github/workflows/push-to-main.yml b/.github/workflows/push-to-main.yml index 17712a27..0a97bedc 100644 --- a/.github/workflows/push-to-main.yml +++ b/.github/workflows/push-to-main.yml @@ -20,25 +20,6 @@ jobs: with: submodules: recursive - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2-beta - with: - node-version: '12' - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn config get cacheFolder)" - - - uses: actions/cache@v2 - id: yarn-cache - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - run: yarn install - working-directory: ./lib/monorepo - - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: From 827797c93858ee3a71ef180ded818f4f46d163cf Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Fri, 18 Nov 2022 10:09:25 +0100 Subject: [PATCH 27/41] Remove deploy test --- test/Deploy.t.sol | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 test/Deploy.t.sol diff --git a/test/Deploy.t.sol b/test/Deploy.t.sol deleted file mode 100644 index fccfa37c..00000000 --- a/test/Deploy.t.sol +++ /dev/null @@ -1,12 +0,0 @@ -import {ConnectorXCMScript} from "../script/Connector-XCM.s.sol"; - -import "forge-std/Test.sol"; - -contract DeployTest is Test { - - function testXCMDeployWorks() public { - ConnectorXCMScript script = new ConnectorXCMScript(); - script.run(); - } - -} \ No newline at end of file From c8d6c3babb12079895b46d01851dbc4e3813abfd Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Mon, 16 Jan 2023 15:16:11 -0500 Subject: [PATCH 28/41] Remove nomad router's send message logic and tests pertaining to it --- src/routers/xcm/Router.sol | 8 +- test/Connector.t.sol | 297 ------------------------------------- 2 files changed, 1 insertion(+), 304 deletions(-) diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 050b30ea..4e530b1a 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -64,13 +64,7 @@ contract ConnectorXCMRouter { } function sendMessage(uint32 destinationDomain, uint64 poolId, bytes16 trancheId, uint256 amount, address user) external onlyConnector { - console.log("Destination domain: %d", destinationDomain); - console.log("Pool: %d", poolId); - // bytes32 remoteAddress = _mustHaveRemote(destinationDomain); - // Home(xAppConnectionManager.home()).dispatch( - // destinationDomain, - // remoteAddress, - // ConnectorMessages.formatTransfer(poolId, trancheId, user, amount)); + // TODO: implement } function bytes32ToString(bytes32 _bytes32) internal returns (string memory) { diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 1c8ba1f5..06498812 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -176,303 +176,6 @@ contract ConnectorTest is Test { vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); bridgedConnector.updateTokenPrice(poolId, trancheId, price); } - - function testTransferWorks(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { - vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); - - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - homeConnector.updateMember(poolId, trancheId, user, validUntil); - - (address token_,,) = bridgedConnector.tranches(poolId, trancheId); - RestrictedTokenLike token = RestrictedTokenLike(token_); - MemberlistLike memberlist = MemberlistLike(token.memberlist()); - uint totalSupplyBefore = token.totalSupply(); - - homeConnector.transfer(poolId, trancheId, user, amount); - - assertEq(memberlist.members(user), validUntil); - assertTrue(token.hasMember(user)); - assertEq(token.balanceOf(user), amount); - assertEq(token.totalSupply(), safeAdd(totalSupplyBefore, amount)); - } - - function testTransferForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { - vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); - - // do not add pool - vm.expectRevert(bytes("CentrifugeConnector/invalid-pool")); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - - vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); - homeConnector.updateMember(poolId, trancheId, user, validUntil); - - vm.expectRevert(bytes("CentrifugeConnector/unknown-token")); - homeConnector.transfer(poolId, trancheId, user, amount); - } - - function testTransferForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { - vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); - - homeConnector.addPool(poolId); - //do not add tranche - vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); - homeConnector.updateMember(poolId, trancheId, user, validUntil); - - vm.expectRevert(bytes("CentrifugeConnector/unknown-token")); homeConnector.transfer(poolId, trancheId, user, amount); - - } - - function testTransferForNoMemberlistFails(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { - vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); - - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - - // do not add to Memberlist - vm.expectRevert(bytes("CentrifugeConnector/not-a-member")); - homeConnector.transfer(poolId, trancheId, user, amount); - } - - function testTransferFromOtherOriginFails(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint256 validUntil) public { - - MockHomeConnector homeConnectorDifferentOrigin = new MockHomeConnector(address(bridgedConnector)); - vm.assume(validUntil > safeAdd(block.timestamp, minimumDelay)); - - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - homeConnector.updateMember(poolId, trancheId, user, validUntil); - - vm.expectRevert(bytes("ConnectorXCMRouter/invalid-origin")); - homeConnectorDifferentOrigin.transfer(poolId, trancheId, user, amount); - } - - function testTransferWorks(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - string memory domainName = "Centrifuge"; - uint32 domainId = 3000; - bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); - user = address(this); // set deployer as user to approve the cnnector to transfer funds - - // add Centrifuge domain to router - bridgedRouter.enrollRemoteRouter(domainId, recipient); - - // add Centrifuge domain to connector - assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, domainId); - bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - - // fund user - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - homeConnector.updateMember(poolId, trancheId, user, uint(-1)); - homeConnector.transfer(poolId, trancheId, user, amount); - - // approve token - (address token_,,) = bridgedConnector.tranches(poolId, trancheId); - RestrictedTokenLike token = RestrictedTokenLike(token_); - token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - - uint userTokenBalanceBefore = token.balanceOf(user); - - // transfer - bridgedConnector.transfer(poolId, trancheId, user, amount, domainName); - - assert(homeConnector.dispatchDomain() == domainId); - assertEq(token.balanceOf(user), (userTokenBalanceBefore - amount)); - assertEq(homeConnector.dispatchRecipient(), recipient); - assertEq(homeConnector.dispatchCalls(), 1); - } - - function testTransferUnknownDomainNameFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - string memory domainName = "Centrifuge"; - uint32 domainId = 3000; - bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); - user = address(this); // set deployer as user to approve the cnnector to transfer funds - - // add Centrifuge domain to router - bridgedRouter.enrollRemoteRouter(domainId, recipient); - - // add Centrifuge domain to connector - assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, domainId); - bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - - // fund user - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - homeConnector.updateMember(poolId, trancheId, user, uint(-1)); - homeConnector.transfer(poolId, trancheId, user, amount); - - // approve token - (address token_,,) = bridgedConnector.tranches(poolId, trancheId); - RestrictedTokenLike token = RestrictedTokenLike(token_); - token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - - // transfer - vm.expectRevert(bytes("CentrifugeConnector/domain-does-not-exist")); - bridgedConnector.transfer(poolId, trancheId, user, amount, "Unknown"); // use unknown domain name - } - - function testTransferUnknownDomainIDFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - string memory domainName = "Centrifuge"; - uint32 domainId = 3000; - bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); - user = address(this); // set deployer as user to approve the cnnector to transfer funds - - // add Centrifuge domain to router - bridgedRouter.enrollRemoteRouter(domainId, recipient); - - // add Centrifuge domain to connector - assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, 5000); // use wrong domainID - bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - - // fund user - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - homeConnector.updateMember(poolId, trancheId, user, uint(-1)); - homeConnector.transfer(poolId, trancheId, user, amount); - - // approve token - (address token_,,) = bridgedConnector.tranches(poolId, trancheId); - RestrictedTokenLike token = RestrictedTokenLike(token_); - token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - - // transfer - vm.expectRevert(bytes("!remote")); - bridgedConnector.transfer(poolId, trancheId, user, amount, "Centrifuge"); - } - - function testTransferNotConnectorFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - - string memory domainName = "Centrifuge"; - uint32 domainId = 3000; - bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); - user = address(this); // set deployer as user to approve the cnnector to transfer funds - - // call from an address othe rthen bridged Connector - vm.expectRevert(bytes("ConnectorXCMRouter/only-connector-allowed-to-call")); - bridgedRouter.sendMessage(domainId, poolId, trancheId, amount, user); - } - - function testTransferNotEnoughBalanceFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - vm.assume(amount > 0); - string memory domainName = "Centrifuge"; - uint32 domainId = 3000; - bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); - user = address(this); // set deployer as user to approve the cnnector to transfer funds - - // add Centrifuge domain to router - bridgedRouter.enrollRemoteRouter(domainId, recipient); - - // add Centrifuge domain to connector - assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, domainId); - bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - - // fund user - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - homeConnector.updateMember(poolId, trancheId, user, uint(-1)); - // do not fund user - - // approve token - (address token_,,) = bridgedConnector.tranches(poolId, trancheId); - RestrictedTokenLike token = RestrictedTokenLike(token_); - token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - - // transfer - vm.expectRevert(bytes("CentrifugeConnector/insufficient-balance")); - bridgedConnector.transfer(poolId, trancheId, user, amount, "Centrifuge"); - } - - function testTransferTokenDoesNotExistFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - string memory domainName = "Centrifuge"; - uint32 domainId = 3000; - bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); - user = address(this); // set deployer as user to approve the cnnector to transfer funds - - // add Centrifuge domain to router - bridgedRouter.enrollRemoteRouter(domainId, recipient); - - // add Centrifuge domain to connector - assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, domainId); - bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - - - // transfer - vm.expectRevert(bytes("CentrifugeConnector/unknown-token")); - bridgedConnector.transfer(poolId, trancheId, user, amount, "Centrifuge"); - } - - function testTransferDomainNotEnrolledFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - string memory domainName = "Centrifuge"; - uint32 domainId = 3000; - bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); - user = address(this); // set deployer as user to approve the cnnector to transfer funds - - // do not enroll router - - // add Centrifuge domain to connector - assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, domainId); - bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - - // fund user - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - homeConnector.updateMember(poolId, trancheId, user, uint(-1)); - homeConnector.transfer(poolId, trancheId, user, amount); - - // approve token - (address token_,,) = bridgedConnector.tranches(poolId, trancheId); - RestrictedTokenLike token = RestrictedTokenLike(token_); - token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - - // transfer - vm.expectRevert(bytes("!remote")); - bridgedConnector.transfer(poolId, trancheId, user, amount, domainName); // use unknown domain name - } - - function testTransferNoAllowanceFails(uint64 poolId, bytes16 trancheId, uint256 amount, address user) public { - vm.assume(amount > 0); - string memory domainName = "Centrifuge"; - uint32 domainId = 3000; - bytes32 recipient = stringToBytes32("0xefc56627233b02ea95bae7e19f648d7dcd5bb132"); - user = address(this); // set deployer as user to approve the cnnector to transfer funds - - // add Centrifuge domain to router - bridgedRouter.enrollRemoteRouter(domainId, recipient); - - // add Centrifuge domain to connector - assertEq(bridgedConnector.wards(address(this)), 1); - bridgedConnector.file("domain", domainName, domainId); - bridgedConnector.deny(address(this)); // revoke ward permissions to test public functions - - // fund user - homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); - homeConnector.updateMember(poolId, trancheId, user, uint(-1)); - homeConnector.transfer(poolId, trancheId, user, amount); - - // approve token - (address token_,,) = bridgedConnector.tranches(poolId, trancheId); - RestrictedTokenLike token = RestrictedTokenLike(token_); - //token.approve(address(bridgedConnector), uint(-1)); // approve connector to take token - - // transfer - vm.expectRevert(bytes("cent/insufficient-allowance")); - bridgedConnector.transfer(poolId, trancheId, user, amount, domainName); // do not approve connector - } - - // TODO: fix & add assertions to transferTo tests - currently edge case that makes the assertion fail - //(uint64 poolIdDispatchCall, bytes16 trancheIdDispatchCall, address userDispatchCall, uint256 amountDispatchCall) = ConnectorMessages.parseTransfer(toBytes29(homeConnector.dispatchMessage())); - // assert(poolIdDispatchCall == poolId); - // assertEq(trancheIdDispatchCall,trancheId); - // assertEq(userDispatchCall, user); - // assertEq(amountDispatchCall, amount); - // helpers function safeAdd(uint x, uint y) internal pure returns (uint z) { From 1a287d9618bcbc5669d1fd3e7bb46e45c652680d Mon Sep 17 00:00:00 2001 From: nuno Date: Thu, 26 Jan 2023 15:07:51 +0100 Subject: [PATCH 29/41] Fix memview-sol branch submodule not found --- .gitmodules | 2 +- lib/forge-std | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2b9f73b0..afd33bc4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,4 +7,4 @@ [submodule "lib/memview-sol"] path = lib/memview-sol url = https://github.com/summa-tx/memview-sol - branch = v2.1.1 + branch = main diff --git a/lib/forge-std b/lib/forge-std index 75129776..4a79aca8 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 75129776235fe6ebf075b0c8d0fe412092a1ac96 +Subproject commit 4a79aca83f8075f8b1b4fe9153945fef08375630 From f744dd272cc86a822543b28b40261034a2da36e2 Mon Sep 17 00:00:00 2001 From: nuno Date: Mon, 30 Jan 2023 15:21:04 +0100 Subject: [PATCH 30/41] Drop "== true" blocks --- src/routers/xcm/Router.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 4e530b1a..c907e5ae 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -43,19 +43,19 @@ contract ConnectorXCMRouter { bytes memory _message ) external onlyCentrifugeChainOrigin { bytes29 _msg = _message.ref(0); - if (ConnectorMessages.isAddPool(_msg) == true) { + if (ConnectorMessages.isAddPool(_msg)) { uint64 poolId = ConnectorMessages.parseAddPool(_msg); connector.addPool(poolId); - } else if (ConnectorMessages.isAddTranche(_msg) == true) { + } else if (ConnectorMessages.isAddTranche(_msg)) { (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) { + } else if (ConnectorMessages.isUpdateMember(_msg)) { (uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) = ConnectorMessages.parseUpdateMember(_msg); connector.updateMember(poolId, trancheId, user, validUntil); - } else if (ConnectorMessages.isUpdateTokenPrice(_msg) == true) { + } else if (ConnectorMessages.isUpdateTokenPrice(_msg)) { (uint64 poolId, bytes16 trancheId, uint256 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); connector.updateTokenPrice(poolId, trancheId, price); - } else if (ConnectorMessages.isTransfer(_msg) == true) { + } else if (ConnectorMessages.isTransfer(_msg)) { (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(_msg); connector.handleTransfer(poolId, trancheId, user, amount); } else { @@ -67,7 +67,7 @@ contract ConnectorXCMRouter { // TODO: implement } - function bytes32ToString(bytes32 _bytes32) internal returns (string memory) { + function bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) { uint8 i = 0; while (i < 32 && _bytes32[i] != 0) { i++; From e2832fe8ef35d4e0e6110df12064de36831dffd9 Mon Sep 17 00:00:00 2001 From: Nuno Alexandre Date: Mon, 30 Jan 2023 16:05:07 +0100 Subject: [PATCH 31/41] Fix `UpdateMember` encoding & decoding (#32) --- src/Connector.sol | 2 +- src/Messages.sol | 18 ++++++++++-------- src/routers/xcm/Router.sol | 4 ++-- test/Connector.t.sol | 16 +++++++--------- test/Messages.t.sol | 26 +++++++++++++------------- test/mock/MockHomeConnector.sol | 4 ++-- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index c71814d1..88364bcd 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -141,7 +141,7 @@ contract CentrifugeConnector { uint64 poolId, bytes16 trancheId, address user, - uint256 validUntil + uint64 validUntil ) public onlyRouter { Tranche storage tranche = tranches[poolId][trancheId]; require(tranche.latestPrice > 0, "CentrifugeConnector/invalid-pool-or-tranche"); diff --git a/src/Messages.sol b/src/Messages.sol index cfe2b15b..479af6a7 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -50,7 +50,7 @@ library ConnectorMessages { function formatAddTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) internal pure returns (bytes memory) { // TODO(nuno): Now, we encode `tokenName` as a 128-bytearray by first encoding `tokenName` // to bytes32 and then we encode three empty bytes32's, which sum up to a total of 128 bytes. - // Add support to actually encode `tokennName` fully as a 128 bytes string. + // Add support to actually encode `tokenName` fully as a 128 bytes string. return abi.encodePacked(uint8(Call.AddTranche), poolId, trancheId, stringToBytes32(tokenName), bytes32(""), bytes32(""), bytes32(""), stringToBytes32(tokenSymbol)); } @@ -96,25 +96,27 @@ library ConnectorMessages { * 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: validUntil (uint256 = 32 bytes) + * 25-45: user (Ethereum address, 20 bytes - Skip 12 bytes from 32-byte addresses) + * 57-65: validUntil (uint64 = 8 bytes) * * TODO: use bytes32 for user (for non-EVM compatibility) */ - function formatUpdateMember(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) internal pure returns (bytes memory) { - return abi.encodePacked(uint8(Call.UpdateMember), poolId, trancheId, user, validUntil); + function formatUpdateMember(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) internal pure returns (bytes memory) { + // NOTE: Since parseUpdateMember parses the first 20 bytes of `user` and skips the following 12 + // here we need to append 12 zeros to make it right. Drop once we support 32-byte addresses. + return abi.encodePacked(uint8(Call.UpdateMember), poolId, trancheId, user, bytes(hex"000000000000000000000000"), validUntil); } function isUpdateMember(bytes29 _msg) internal pure returns (bool) { return messageType(_msg) == Call.UpdateMember; } - function parseUpdateMember(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) { + + function parseUpdateMember(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) { poolId = uint64(_msg.indexUint(1, 8)); trancheId = bytes16(_msg.index(9, 16)); user = address(bytes20(_msg.index(25, 20))); - // TODO: skip 12 padded zeroes from address - validUntil = uint256(_msg.index(45, 32)); + validUntil = uint64(_msg.indexUint(57, 8)); } /** diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index c907e5ae..b32e19ab 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -8,7 +8,7 @@ import {ConnectorMessages} from "../../Messages.sol"; interface ConnectorLike { function addPool(uint64 poolId) external; 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 updateMember(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) external; function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) external; function handleTransfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; } @@ -50,7 +50,7 @@ contract ConnectorXCMRouter { (uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) = ConnectorMessages.parseAddTranche(_msg); connector.addTranche(poolId, trancheId, tokenName, tokenSymbol); } else if (ConnectorMessages.isUpdateMember(_msg)) { - (uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) = ConnectorMessages.parseUpdateMember(_msg); + (uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) = ConnectorMessages.parseUpdateMember(_msg); connector.updateMember(poolId, trancheId, user, validUntil); } else if (ConnectorMessages.isUpdateTokenPrice(_msg)) { (uint64 poolId, bytes16 trancheId, uint256 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 06498812..556bacf0 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -92,9 +92,8 @@ contract ConnectorTest is Test { homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol); } - function testUpdatingMemberWorks(uint64 poolId, bytes16 trancheId, address user, uint128 fuzzed_uint128) public { - vm.assume(fuzzed_uint128 > 0); - uint256 validUntil = safeAdd(fuzzed_uint128, safeAdd(block.timestamp, minimumDelay)); + function testUpdatingMemberWorks(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public { + vm.assume(validUntil >= safeAdd(block.timestamp, new Memberlist().minimumDelay())); vm.assume(user != address(0)); homeConnector.addPool(poolId); @@ -110,7 +109,7 @@ contract ConnectorTest is Test { assertEq(memberlist.members(user), validUntil); } - function testUpdatingMemberBeforeMinimumDelayFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { + function testUpdatingMemberBeforeMinimumDelayFails(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public { vm.assume(validUntil <= safeAdd(block.timestamp, new Memberlist().minimumDelay())); vm.assume(user != address(0)); @@ -121,16 +120,15 @@ contract ConnectorTest is Test { homeConnector.updateMember(poolId, trancheId, user, validUntil); } - function testUpdatingMemberAsNonRouterFails(uint64 poolId, bytes16 trancheId, address user, uint128 fuzzed_uint128) public { - vm.assume(fuzzed_uint128 > 0); - uint256 validUntil = safeAdd(fuzzed_uint128, safeAdd(block.timestamp, new Memberlist().minimumDelay())); + function testUpdatingMemberAsNonRouterFails(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public { + vm.assume(validUntil <= safeAdd(block.timestamp, new Memberlist().minimumDelay())); vm.assume(user != address(0)); vm.expectRevert(bytes("CentrifugeConnector/not-the-router")); bridgedConnector.updateMember(poolId, trancheId, user, validUntil); } - function testUpdatingMemberForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { + function testUpdatingMemberForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public { vm.assume(validUntil > block.timestamp); bridgedConnector.file("router", address(this)); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); @@ -138,7 +136,7 @@ contract ConnectorTest is Test { } - function testUpdatingMemberForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint256 validUntil) public { + function testUpdatingMemberForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public { vm.assume(validUntil > block.timestamp); bridgedConnector.file("router", address(this)); bridgedConnector.addPool(poolId); diff --git a/test/Messages.t.sol b/test/Messages.t.sol index d1b05fdf..fa0c8059 100644 --- a/test/Messages.t.sol +++ b/test/Messages.t.sol @@ -91,41 +91,41 @@ contract MessagesTest is Test { function testUpdateMemberEncoding() public { assertEq( - ConnectorMessages.formatUpdateMember(5, toBytes16(fromHex("010000000000000003")), 0x225ef95fa90f4F7938A5b34234d14768cB4263dd, 1657870537), - fromHex("04000000000000000500000000000000000000000000000009225ef95fa90f4f7938a5b34234d14768cb4263dd0000000000000000000000000000000000000000000000000000000062d118c9") - ); + ConnectorMessages.formatUpdateMember(2, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), 0x1231231231231231231231231231231231231231, 1706260138), + hex"040000000000000002811acd5b3f17c06841c7e41e9e04cb1b12312312312312312312312312312312312312310000000000000000000000000000000065b376aa" + ); } function testUpdateMemberDecoding() public { - (uint64 decodedPoolId, bytes16 decodedTrancheId, address decodedUser, uint256 decodedValidUntil) = ConnectorMessages.parseUpdateMember(fromHex("04000000000000000500000000000000000000000000000009225ef95fa90f4f7938a5b34234d14768cb4263dd0000000000000000000000000000000000000000000000000000000062d118c9").ref(0)); - assertEq(uint(decodedPoolId), uint(5)); - assertEq(decodedTrancheId, toBytes16(fromHex("010000000000000003"))); - assertEq(decodedUser, 0x225ef95fa90f4F7938A5b34234d14768cB4263dd); - assertEq(decodedValidUntil, uint(1657870537)); + (uint64 decodedPoolId, bytes16 decodedTrancheId, address decodedUser, uint64 decodedValidUntil) = ConnectorMessages.parseUpdateMember(fromHex("040000000000000002811acd5b3f17c06841c7e41e9e04cb1b12312312312312312312312312312312312312312312312312312312312312310000000065B376AA").ref(0)); + assertEq(uint(decodedPoolId), uint(2)); + assertEq(decodedTrancheId, hex"811acd5b3f17c06841c7e41e9e04cb1b"); + assertEq(decodedUser, 0x1231231231231231231231231231231231231231); + assertEq(decodedValidUntil, uint(1706260138)); } function testUpdateMemberEquivalence( uint64 poolId, bytes16 trancheId, address user, - uint256 amount + uint64 validUntil ) public { bytes memory _message = ConnectorMessages.formatUpdateMember( poolId, trancheId, user, - amount + validUntil ); ( uint64 decodedPoolId, bytes16 decodedTrancheId, address decodedUser, - uint256 decodedAmount + uint64 decodedValidUntil ) = ConnectorMessages.parseUpdateMember(_message.ref(0)); - assertEq(uint256(decodedPoolId), uint256(poolId)); + assertEq(uint(decodedPoolId), uint(poolId)); assertEq(decodedTrancheId, trancheId); assertEq(decodedUser, user); - assertEq(decodedAmount, amount); + assertEq(uint(decodedValidUntil), uint(validUntil)); } function testUpdateTokenPriceEncoding() public { diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index 42113905..864cd9dc 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -43,8 +43,8 @@ contract MockHomeConnector is Test { } - function updateMember(uint64 poolId, bytes16 trancheId, address user, uint256 amount) public { - bytes memory _message = ConnectorMessages.formatUpdateMember(poolId, trancheId, user, amount); + function updateMember(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public { + bytes memory _message = ConnectorMessages.formatUpdateMember(poolId, trancheId, user, validUntil); router.handle(_message); } From 2d917801d3a313c74aa7692fa0865d72a033a457 Mon Sep 17 00:00:00 2001 From: nuno Date: Mon, 30 Jan 2023 19:44:14 +0100 Subject: [PATCH 32/41] Fix 9-24: trancheId (16 bytes) docs --- src/Messages.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Messages.sol b/src/Messages.sol index 479af6a7..910e65f3 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -95,7 +95,7 @@ library ConnectorMessages { * * 0: call type (uint8 = 1 byte) * 1-8: poolId (uint64 = 8 bytes) - * 9-25: trancheId (16 bytes) + * 9-24: trancheId (16 bytes) * 25-45: user (Ethereum address, 20 bytes - Skip 12 bytes from 32-byte addresses) * 57-65: validUntil (uint64 = 8 bytes) * @@ -111,7 +111,6 @@ library ConnectorMessages { return messageType(_msg) == Call.UpdateMember; } - function parseUpdateMember(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) { poolId = uint64(_msg.indexUint(1, 8)); trancheId = bytes16(_msg.index(9, 16)); From afb740c62ed81f1da2a8d1d3bef3efc1e958d1ef Mon Sep 17 00:00:00 2001 From: Nuno Alexandre Date: Tue, 31 Jan 2023 15:07:33 +0100 Subject: [PATCH 33/41] feat: Add `price` field to `AddTranche` (#33) --- src/Connector.sol | 4 ++-- src/Messages.sol | 19 ++++++++++++++----- src/routers/xcm/Router.sol | 6 +++--- test/Connector.t.sol | 29 +++++++++++++++-------------- test/Messages.t.sol | 26 +++++++++++++++----------- test/mock/MockHomeConnector.sol | 4 ++-- 6 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index 88364bcd..ee9204ec 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -97,7 +97,7 @@ contract CentrifugeConnector { emit PoolAdded(poolId); } - function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) + function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) public onlyRouter { @@ -105,7 +105,7 @@ contract CentrifugeConnector { require(pool.createdAt > 0, "CentrifugeConnector/invalid-pool"); Tranche storage tranche = tranches[poolId][trancheId]; - tranche.latestPrice = 1*10**27; + tranche.latestPrice = uint256(price); tranche.tokenName = tokenName; tranche.tokenSymbol = tokenSymbol; diff --git a/src/Messages.sol b/src/Messages.sol index 910e65f3..5710b07f 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -43,26 +43,35 @@ library ConnectorMessages { * * 0: call type (uint8 = 1 byte) * 1-8: poolId (uint64 = 8 bytes) - * 9-25: trancheId (16 bytes) - * 26-154: tokenName (string = 128 bytes) + * 9-24: trancheId (16 bytes) + * 25-154: tokenName (string = 128 bytes) * 155-187: tokenSymbol (string = 32 bytes) + * 185-200: price (uint128 = 16 bytes) */ - function formatAddTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) internal pure returns (bytes memory) { + function formatAddTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) internal pure returns (bytes memory) { // TODO(nuno): Now, we encode `tokenName` as a 128-bytearray by first encoding `tokenName` // to bytes32 and then we encode three empty bytes32's, which sum up to a total of 128 bytes. // Add support to actually encode `tokenName` fully as a 128 bytes string. - return abi.encodePacked(uint8(Call.AddTranche), poolId, trancheId, stringToBytes32(tokenName), bytes32(""), bytes32(""), bytes32(""), stringToBytes32(tokenSymbol)); + return abi.encodePacked( + uint8(Call.AddTranche), + poolId, + trancheId, + stringToBytes32(tokenName), bytes32(""), bytes32(""), bytes32(""), + stringToBytes32(tokenSymbol), + price + ); } function isAddTranche(bytes29 _msg) internal pure returns (bool) { return messageType(_msg) == Call.AddTranche; } - function parseAddTranche(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) { + function parseAddTranche(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) { poolId = uint64(_msg.indexUint(1, 8)); trancheId = bytes16(_msg.index(9, 16)); tokenName = bytes32ToString(bytes32(_msg.index(25, 32))); tokenSymbol = bytes32ToString(bytes32(_msg.index(153, 32))); + price = uint128(_msg.indexUint(185, 16)); } // TODO: should be moved to a util contract diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index b32e19ab..12d9ae4e 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -7,7 +7,7 @@ import {ConnectorMessages} from "../../Messages.sol"; interface ConnectorLike { function addPool(uint64 poolId) external; - function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) external; + function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) external; function updateMember(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) external; function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) external; function handleTransfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; @@ -47,8 +47,8 @@ contract ConnectorXCMRouter { uint64 poolId = ConnectorMessages.parseAddPool(_msg); connector.addPool(poolId); } else if (ConnectorMessages.isAddTranche(_msg)) { - (uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) = ConnectorMessages.parseAddTranche(_msg); - connector.addTranche(poolId, trancheId, tokenName, tokenSymbol); + (uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) = ConnectorMessages.parseAddTranche(_msg); + connector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); } else if (ConnectorMessages.isUpdateMember(_msg)) { (uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) = ConnectorMessages.parseUpdateMember(_msg); connector.updateMember(poolId, trancheId, user, validUntil); diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 556bacf0..7504dc02 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -41,21 +41,21 @@ contract ConnectorTest is Test { bridgedConnector.addPool(poolId); } - function testAddingSingleTrancheWorks(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId) public { + function testAddingSingleTrancheWorks(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price) public { // 0. Add Pool homeConnector.addPool(poolId); (uint64 actualPoolId,) = bridgedConnector.pools(poolId); assertEq(uint256(actualPoolId), uint256(poolId)); // 1. Add the tranche - homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol); + homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); // 2. Then deploy the tranche bridgedConnector.deployTranche(poolId, trancheId); (address token_, uint256 latestPrice,,string memory actualTokenName, string memory actualTokenSymbol) = bridgedConnector.tranches(poolId, trancheId); assertTrue(token_ != address(0)); - assertTrue(latestPrice > 0); + assertEq(latestPrice, price); // Comparing raw input to output can erroneously fail when a byte string is given. // Intended behaviour is that byte strings will be treated as bytes and converted to strings @@ -69,27 +69,28 @@ contract ConnectorTest is Test { assertEq(token.symbol(), bytes32ToString(stringToBytes32(tokenSymbol))); } - function testAddingMultipleTranchesWorks(uint64 poolId, bytes16[] calldata trancheIds, string memory tokenName, string memory tokenSymbol) public { + function testAddingMultipleTranchesWorks(uint64 poolId, bytes16[] calldata trancheIds, string memory tokenName, string memory tokenSymbol, uint128 price) public { homeConnector.addPool(poolId); for (uint i = 0; i < trancheIds.length; i++) { - homeConnector.addTranche(poolId, trancheIds[i], tokenName, tokenSymbol); + uint128 tranchePrice = price + uint128(i); + homeConnector.addTranche(poolId, trancheIds[i], tokenName, tokenSymbol, tranchePrice); bridgedConnector.deployTranche(poolId, trancheIds[i]); (address token, uint256 latestPrice, , ,) = bridgedConnector.tranches(poolId, trancheIds[i]); - assertTrue(latestPrice > 0); + assertEq(latestPrice, tranchePrice); assertTrue(token != address(0)); } } - function testAddingTranchesAsNonRouterFails(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) public { + function testAddingTranchesAsNonRouterFails(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) public { homeConnector.addPool(poolId); vm.expectRevert(bytes("CentrifugeConnector/not-the-router")); - bridgedConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol); + bridgedConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); } - function testAddingTranchesForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) public { + function testAddingTranchesForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) public { vm.expectRevert(bytes("CentrifugeConnector/invalid-pool")); - homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol); + homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); } function testUpdatingMemberWorks(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public { @@ -97,7 +98,7 @@ contract ConnectorTest is Test { vm.assume(user != address(0)); homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL", 123); bridgedConnector.deployTranche(poolId, trancheId); homeConnector.updateMember(poolId, trancheId, user, validUntil); @@ -114,7 +115,7 @@ contract ConnectorTest is Test { vm.assume(user != address(0)); homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL", 123); bridgedConnector.deployTranche(poolId, trancheId); vm.expectRevert("invalid-validUntil"); homeConnector.updateMember(poolId, trancheId, user, validUntil); @@ -147,7 +148,7 @@ contract ConnectorTest is Test { function testUpdatingTokenPriceWorks(uint64 poolId, bytes16 trancheId, uint256 price) public { homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL", 123); homeConnector.updateTokenPrice(poolId, trancheId, price); (, uint256 latestPrice, uint256 lastPriceUpdate, ,) = bridgedConnector.tranches(poolId, trancheId); @@ -157,7 +158,7 @@ contract ConnectorTest is Test { function testUpdatingTokenPriceAsNonRouterFails(uint64 poolId, bytes16 trancheId, uint256 price) public { homeConnector.addPool(poolId); - homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL"); + homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL", 123); vm.expectRevert(bytes("CentrifugeConnector/not-the-router")); bridgedConnector.updateTokenPrice(poolId, trancheId, price); } diff --git a/test/Messages.t.sol b/test/Messages.t.sol index fa0c8059..ba314b4e 100644 --- a/test/Messages.t.sol +++ b/test/Messages.t.sol @@ -56,37 +56,41 @@ contract MessagesTest is Test { function testAddTrancheEncoding() public { assertEq( - ConnectorMessages.formatAddTranche(0, toBytes16(fromHex("010000000000000064")), "Some Name", "SYMBOL"), - fromHex("02000000000000000000000000000000000000000000000009536f6d65204e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053594d424f4c0000000000000000000000000000000000000000000000000000") + ConnectorMessages.formatAddTranche(12378532, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), "Some Name", "SYMBOL", 1), + hex"020000000000bce1a4811acd5b3f17c06841c7e41e9e04cb1b536f6d65204e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053594d424f4c000000000000000000000000000000000000000000000000000000000000000000000000000000000001" ); } function testAddTrancheDecoding() public { - (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol) = ConnectorMessages.parseAddTranche(fromHex("02000000000000000000000000000000000000000000000009536f6d65204e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053594d424f4c0000000000000000000000000000000000000000000000000000").ref(0)); - assertEq(uint(decodedPoolId), uint(0)); - assertEq(decodedTrancheId, toBytes16(fromHex("010000000000000064"))); - assertEq(decodedTokenName, "Some Name"); - assertEq(decodedTokenSymbol, "SYMBOL"); + (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint256 decodedPrice) = ConnectorMessages.parseAddTranche(fromHex("020000000000bce1a4000000000000000000000000000000010505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505060606060606060606060606060606060606060606060606060606060606060600000000033b2e3c9fd0803ce8000000").ref(0)); + assertEq(uint(decodedPoolId), uint(12378532)); + assertEq(decodedTrancheId, bytes16(hex"00000000000000000000000000000001")); + assertEq(decodedTokenName, bytes32ToString(bytes32(hex"0505050505050505050505050505050505050505050505050505050505050505"))); + assertEq(decodedTokenSymbol, bytes32ToString(hex"0606060606060606060606060606060606060606060606060606060606060606")); + assertEq(decodedPrice, uint(1000000000000000000000000000)); } - function testAddTrancheEquivalence(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) + function testAddTrancheEquivalence(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) public { bytes memory _message = ConnectorMessages.formatAddTranche( poolId, trancheId, tokenName, - tokenSymbol + tokenSymbol, + price ); - (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol) = ConnectorMessages + (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint256 decodedPrice) = ConnectorMessages .parseAddTranche(_message.ref(0)); assertEq(uint256(decodedPoolId), uint256(poolId)); assertEq(decodedTrancheId, trancheId); - // Comparing raw input to output can erroneously fail when a byte string is given. + // Comparing raw input to output can erroneously fail when a byte string is given. // Intended behaviour is that byte strings will be treated as bytes and converted to strings instead of treated as strings themselves. // This conversion from string to bytes32 to string is used to simulate this intended behaviour. assertEq(decodedTokenName, bytes32ToString(stringToBytes32(tokenName))); assertEq(decodedTokenSymbol, bytes32ToString(stringToBytes32(tokenSymbol))); + // TODO(nuno): fix this + assertEq(decodedPrice, uint256(price)); } function testUpdateMemberEncoding() public { diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index 864cd9dc..e8fc200d 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -37,8 +37,8 @@ contract MockHomeConnector is Test { router.handle(_message); } - function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol) public { - bytes memory _message = ConnectorMessages.formatAddTranche(poolId, trancheId, tokenName, tokenSymbol); + function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) public { + bytes memory _message = ConnectorMessages.formatAddTranche(poolId, trancheId, tokenName, tokenSymbol, price); router.handle(_message); } From 2ae621dc6646ca61ef09f27e2522c5e187d78b41 Mon Sep 17 00:00:00 2001 From: Nuno Alexandre Date: Tue, 31 Jan 2023 15:16:04 +0100 Subject: [PATCH 34/41] Make `UpdateTokenPrice.price` and `Tranche.latestPrice` `u128` (#34) --- src/Connector.sol | 6 +++--- src/Messages.sol | 10 +++++----- src/routers/xcm/Router.sol | 4 ++-- test/Connector.t.sol | 8 ++++---- test/Messages.t.sol | 22 +++++++++++----------- test/mock/MockHomeConnector.sol | 2 +- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index ee9204ec..f4313339 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -24,7 +24,7 @@ contract CentrifugeConnector { struct Tranche { address token; - uint256 latestPrice; // [ray] + uint128 latestPrice; // [ray] uint256 lastPriceUpdate; // TODO: the token name & symbol need to be stored because of the separation between adding and deploying tranches. // This leads to duplicate storage (also in the ERC20 contract), ideally we should refactor this somehow @@ -105,7 +105,7 @@ contract CentrifugeConnector { require(pool.createdAt > 0, "CentrifugeConnector/invalid-pool"); Tranche storage tranche = tranches[poolId][trancheId]; - tranche.latestPrice = uint256(price); + tranche.latestPrice = price; tranche.tokenName = tokenName; tranche.tokenSymbol = tokenSymbol; @@ -129,7 +129,7 @@ contract CentrifugeConnector { function updateTokenPrice( uint64 poolId, bytes16 trancheId, - uint256 price + uint128 price ) public onlyRouter { Tranche storage tranche = tranches[poolId][trancheId]; require(tranche.latestPrice > 0, "CentrifugeConnector/invalid-pool-or-tranche"); diff --git a/src/Messages.sol b/src/Messages.sol index 5710b07f..7346be16 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -132,10 +132,10 @@ library ConnectorMessages { * * 0: call type (uint8 = 1 byte) * 1-8: poolId (uint64 = 8 bytes) - * 9-25: trancheId (16 bytes) - * 26-58: price (uint256 = 32 bytes) + * 9-24: trancheId (16 bytes) + * 25-41: price (uint128 = 16 bytes) */ - function formatUpdateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) internal pure returns (bytes memory) { + function formatUpdateTokenPrice(uint64 poolId, bytes16 trancheId, uint128 price) internal pure returns (bytes memory) { return abi.encodePacked(uint8(Call.UpdateTokenPrice), poolId, trancheId, price); } @@ -143,10 +143,10 @@ library ConnectorMessages { return messageType(_msg) == Call.UpdateTokenPrice; } - function parseUpdateTokenPrice(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, uint256 price) { + function parseUpdateTokenPrice(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, uint128 price) { poolId = uint64(_msg.indexUint(1, 8)); trancheId = bytes16(_msg.index(9, 16)); - price = uint256(_msg.index(25, 32)); + price = uint128(_msg.indexUint(25, 16)); } /** diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index 12d9ae4e..ad46ca73 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -9,7 +9,7 @@ interface ConnectorLike { function addPool(uint64 poolId) external; function addTranche(uint64 poolId, bytes16 trancheId, string memory tokenName, string memory tokenSymbol, uint128 price) external; function updateMember(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) external; - function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) external; + function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint128 price) external; function handleTransfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) external; } @@ -53,7 +53,7 @@ contract ConnectorXCMRouter { (uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) = ConnectorMessages.parseUpdateMember(_msg); connector.updateMember(poolId, trancheId, user, validUntil); } else if (ConnectorMessages.isUpdateTokenPrice(_msg)) { - (uint64 poolId, bytes16 trancheId, uint256 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); + (uint64 poolId, bytes16 trancheId, uint128 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); connector.updateTokenPrice(poolId, trancheId, price); } else if (ConnectorMessages.isTransfer(_msg)) { (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(_msg); diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 7504dc02..27e80b1f 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -146,7 +146,7 @@ contract ConnectorTest is Test { } - function testUpdatingTokenPriceWorks(uint64 poolId, bytes16 trancheId, uint256 price) public { + function testUpdatingTokenPriceWorks(uint64 poolId, bytes16 trancheId, uint128 price) public { homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL", 123); homeConnector.updateTokenPrice(poolId, trancheId, price); @@ -156,20 +156,20 @@ contract ConnectorTest is Test { assertEq(lastPriceUpdate, block.timestamp); } - function testUpdatingTokenPriceAsNonRouterFails(uint64 poolId, bytes16 trancheId, uint256 price) public { + function testUpdatingTokenPriceAsNonRouterFails(uint64 poolId, bytes16 trancheId, uint128 price) public { homeConnector.addPool(poolId); homeConnector.addTranche(poolId, trancheId, "Some Name", "SYMBOL", 123); vm.expectRevert(bytes("CentrifugeConnector/not-the-router")); bridgedConnector.updateTokenPrice(poolId, trancheId, price); } - function testUpdatingTokenPriceForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, uint256 price) public { + function testUpdatingTokenPriceForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, uint128 price) public { bridgedConnector.file("router", address(this)); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); bridgedConnector.updateTokenPrice(poolId, trancheId, price); } - function testUpdatingTokenPriceForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, uint256 price) public { + function testUpdatingTokenPriceForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, uint128 price) public { bridgedConnector.file("router", address(this)); bridgedConnector.addPool(poolId); vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); diff --git a/test/Messages.t.sol b/test/Messages.t.sol index ba314b4e..fd8647a0 100644 --- a/test/Messages.t.sol +++ b/test/Messages.t.sol @@ -62,7 +62,7 @@ contract MessagesTest is Test { } function testAddTrancheDecoding() public { - (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint256 decodedPrice) = ConnectorMessages.parseAddTranche(fromHex("020000000000bce1a4000000000000000000000000000000010505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505060606060606060606060606060606060606060606060606060606060606060600000000033b2e3c9fd0803ce8000000").ref(0)); + (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint128 decodedPrice) = ConnectorMessages.parseAddTranche(fromHex("020000000000bce1a4000000000000000000000000000000010505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505060606060606060606060606060606060606060606060606060606060606060600000000033b2e3c9fd0803ce8000000").ref(0)); assertEq(uint(decodedPoolId), uint(12378532)); assertEq(decodedTrancheId, bytes16(hex"00000000000000000000000000000001")); assertEq(decodedTokenName, bytes32ToString(bytes32(hex"0505050505050505050505050505050505050505050505050505050505050505"))); @@ -80,7 +80,7 @@ contract MessagesTest is Test { tokenSymbol, price ); - (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint256 decodedPrice) = ConnectorMessages + (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint128 decodedPrice) = ConnectorMessages .parseAddTranche(_message.ref(0)); assertEq(uint256(decodedPoolId), uint256(poolId)); assertEq(decodedTrancheId, trancheId); @@ -134,22 +134,22 @@ contract MessagesTest is Test { function testUpdateTokenPriceEncoding() public { assertEq( - ConnectorMessages.formatUpdateTokenPrice(3, toBytes16(fromHex("010000000000000005")), 100), - fromHex("030000000000000003000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000064") + ConnectorMessages.formatUpdateTokenPrice(1, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), 1000000000000000000000000000), + fromHex("030000000000000001811acd5b3f17c06841c7e41e9e04cb1b00000000033b2e3c9fd0803ce8000000") ); } function testUpdateTokenPriceDecoding() public { - (uint64 decodedPoolId, bytes16 decodedTrancheId, uint256 decodedPrice) = ConnectorMessages.parseUpdateTokenPrice(fromHex("030000000000000003000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000064").ref(0)); - assertEq(uint(decodedPoolId), uint(3)); - assertEq(decodedTrancheId, toBytes16(fromHex("010000000000000005"))); - assertEq(decodedPrice, uint(100)); + (uint64 decodedPoolId, bytes16 decodedTrancheId, uint128 decodedPrice) = ConnectorMessages.parseUpdateTokenPrice(fromHex("030000000000000001811acd5b3f17c06841c7e41e9e04cb1b00000000033b2e3c9fd0803ce8000000").ref(0)); + assertEq(uint(decodedPoolId), uint(1)); + assertEq(decodedTrancheId, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b")); + assertEq(decodedPrice, uint(1000000000000000000000000000)); } function testUpdateTokenPriceEquivalence( uint64 poolId, bytes16 trancheId, - uint256 price + uint128 price ) public { bytes memory _message = ConnectorMessages.formatUpdateTokenPrice( poolId, @@ -159,11 +159,11 @@ contract MessagesTest is Test { ( uint64 decodedPoolId, bytes16 decodedTrancheId, - uint256 decodedPrice + uint128 decodedPrice ) = ConnectorMessages.parseUpdateTokenPrice(_message.ref(0)); assertEq(uint256(decodedPoolId), uint256(poolId)); assertEq(decodedTrancheId, trancheId); - assertEq(decodedPrice, price); + assertEq(uint(decodedPrice), uint(price)); } // Convert an hexadecimal character to their value diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index e8fc200d..4b004994 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -48,7 +48,7 @@ contract MockHomeConnector is Test { router.handle(_message); } - function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint256 price) public { + function updateTokenPrice(uint64 poolId, bytes16 trancheId, uint128 price) public { bytes memory _message = ConnectorMessages.formatUpdateTokenPrice(poolId, trancheId, price); router.handle(_message); } From d1898d8282799cec5609b7a5e6439f8a46605ce7 Mon Sep 17 00:00:00 2001 From: nuno Date: Tue, 31 Jan 2023 19:39:16 +0100 Subject: [PATCH 35/41] Use cent-chain AddTranche message Use a cent-chain generated message to test AddTranche encoding and decoding functions. --- test/Messages.t.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/Messages.t.sol b/test/Messages.t.sol index fd8647a0..214259a8 100644 --- a/test/Messages.t.sol +++ b/test/Messages.t.sol @@ -56,17 +56,17 @@ contract MessagesTest is Test { function testAddTrancheEncoding() public { assertEq( - ConnectorMessages.formatAddTranche(12378532, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), "Some Name", "SYMBOL", 1), - hex"020000000000bce1a4811acd5b3f17c06841c7e41e9e04cb1b536f6d65204e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053594d424f4c000000000000000000000000000000000000000000000000000000000000000000000000000000000001" + ConnectorMessages.formatAddTranche(12378532, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), "Some Name", "SYMBOL", 1000000000000000000000000000), + hex"020000000000bce1a4811acd5b3f17c06841c7e41e9e04cb1b536f6d65204e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053594d424f4c000000000000000000000000000000000000000000000000000000000000033b2e3c9fd0803ce8000000" ); } function testAddTrancheDecoding() public { - (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint128 decodedPrice) = ConnectorMessages.parseAddTranche(fromHex("020000000000bce1a4000000000000000000000000000000010505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505060606060606060606060606060606060606060606060606060606060606060600000000033b2e3c9fd0803ce8000000").ref(0)); + (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint128 decodedPrice) = ConnectorMessages.parseAddTranche(fromHex("020000000000bce1a4811acd5b3f17c06841c7e41e9e04cb1b536f6d65204e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053594d424f4c000000000000000000000000000000000000000000000000000000000000033b2e3c9fd0803ce8000000").ref(0)); assertEq(uint(decodedPoolId), uint(12378532)); - assertEq(decodedTrancheId, bytes16(hex"00000000000000000000000000000001")); - assertEq(decodedTokenName, bytes32ToString(bytes32(hex"0505050505050505050505050505050505050505050505050505050505050505"))); - assertEq(decodedTokenSymbol, bytes32ToString(hex"0606060606060606060606060606060606060606060606060606060606060606")); + assertEq(decodedTrancheId, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b")); + assertEq(decodedTokenName, bytes32ToString(bytes32("Some Name"))); + assertEq(decodedTokenSymbol, bytes32ToString(bytes32("SYMBOL"))); assertEq(decodedPrice, uint(1000000000000000000000000000)); } From aa41811d478bfbcd0f552a117354d1966b90d962 Mon Sep 17 00:00:00 2001 From: Nuno Alexandre Date: Thu, 2 Feb 2023 17:13:40 +0100 Subject: [PATCH 36/41] Align tests with cent chain (#36) * Use cent-chain generated UpdateMember for decoding * Drop toBytes16 --- test/Messages.t.sol | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/test/Messages.t.sol b/test/Messages.t.sol index 214259a8..cddb0156 100644 --- a/test/Messages.t.sol +++ b/test/Messages.t.sol @@ -82,17 +82,20 @@ contract MessagesTest is Test { ); (uint64 decodedPoolId, bytes16 decodedTrancheId, string memory decodedTokenName, string memory decodedTokenSymbol, uint128 decodedPrice) = ConnectorMessages .parseAddTranche(_message.ref(0)); - assertEq(uint256(decodedPoolId), uint256(poolId)); + assertEq(uint(decodedPoolId), uint(poolId)); assertEq(decodedTrancheId, trancheId); // Comparing raw input to output can erroneously fail when a byte string is given. - // Intended behaviour is that byte strings will be treated as bytes and converted to strings instead of treated as strings themselves. - // This conversion from string to bytes32 to string is used to simulate this intended behaviour. + // Intended behaviour is that byte strings will be treated as bytes and converted to strings instead + // of treated as strings themselves. This conversion from string to bytes32 to string is used to simulate + // this intended behaviour. assertEq(decodedTokenName, bytes32ToString(stringToBytes32(tokenName))); assertEq(decodedTokenSymbol, bytes32ToString(stringToBytes32(tokenSymbol))); - // TODO(nuno): fix this - assertEq(decodedPrice, uint256(price)); + assertEq(uint(decodedPrice), uint(price)); } + // Note: UpdateMember encodes differently in Solidity compared to the Rust counterpart because `user` is a 20-byte + // value in Solidity while it is 32-byte in Rust. However, UpdateMember messages coming from the cent-chain will + // be handled correctly as the last 12 bytes out of said 32 will be ignored. function testUpdateMemberEncoding() public { assertEq( ConnectorMessages.formatUpdateMember(2, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), 0x1231231231231231231231231231231231231231, 1706260138), @@ -100,8 +103,10 @@ contract MessagesTest is Test { ); } + // We use an UpdateMember encoded message generated in the cent-chain to + // verify we handle the 32 to 20 bytes address compatibility as expected. function testUpdateMemberDecoding() public { - (uint64 decodedPoolId, bytes16 decodedTrancheId, address decodedUser, uint64 decodedValidUntil) = ConnectorMessages.parseUpdateMember(fromHex("040000000000000002811acd5b3f17c06841c7e41e9e04cb1b12312312312312312312312312312312312312312312312312312312312312310000000065B376AA").ref(0)); + (uint64 decodedPoolId, bytes16 decodedTrancheId, address decodedUser, uint64 decodedValidUntil) = ConnectorMessages.parseUpdateMember(fromHex("040000000000000002811acd5b3f17c06841c7e41e9e04cb1b12312312312312312312312312312312312312312312312312312312312312310000000065b376aa").ref(0)); assertEq(uint(decodedPoolId), uint(2)); assertEq(decodedTrancheId, hex"811acd5b3f17c06841c7e41e9e04cb1b"); assertEq(decodedUser, 0x1231231231231231231231231231231231231231); @@ -196,13 +201,6 @@ contract MessagesTest is Test { return r; } - function toBytes16(bytes memory f) internal pure returns (bytes16 fc) { - assembly { - fc := mload(add(f, 16)) - } - return fc; - } - function stringToBytes32(string memory source) internal pure returns (bytes32 result) { bytes memory tempEmptyStringTest = bytes(source); if (tempEmptyStringTest.length == 0) { From 78edd87a11401688ec2c6084f955f20b6c46aa30 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Mon, 6 Feb 2023 10:18:36 -0500 Subject: [PATCH 37/41] Chore/add tests and optimizations (#38) * wip: add transfer test * move memberlist check from connectors to restricted token * fix tests * revert restricted.sol changes * revert unwanted changes to erc20 and memberlist * fix test --- lib/forge-std | 2 +- src/Connector.sol | 7 ++++--- test/Connector.t.sol | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/lib/forge-std b/lib/forge-std index 4a79aca8..75129776 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 4a79aca83f8075f8b1b4fe9153945fef08375630 +Subproject commit 75129776235fe6ebf075b0c8d0fe412092a1ac96 diff --git a/src/Connector.sol b/src/Connector.sol index f4313339..be382da3 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -106,6 +106,7 @@ contract CentrifugeConnector { Tranche storage tranche = tranches[poolId][trancheId]; tranche.latestPrice = price; + tranche.lastPriceUpdate = block.timestamp; tranche.tokenName = tokenName; tranche.tokenSymbol = tokenSymbol; @@ -132,7 +133,7 @@ contract CentrifugeConnector { uint128 price ) public onlyRouter { Tranche storage tranche = tranches[poolId][trancheId]; - require(tranche.latestPrice > 0, "CentrifugeConnector/invalid-pool-or-tranche"); + require(tranche.lastPriceUpdate > 0, "CentrifugeConnector/invalid-pool-or-tranche"); tranche.latestPrice = price; tranche.lastPriceUpdate = block.timestamp; } @@ -144,7 +145,7 @@ contract CentrifugeConnector { uint64 validUntil ) public onlyRouter { Tranche storage tranche = tranches[poolId][trancheId]; - require(tranche.latestPrice > 0, "CentrifugeConnector/invalid-pool-or-tranche"); + require(tranche.lastPriceUpdate > 0, "CentrifugeConnector/invalid-pool-or-tranche"); RestrictedTokenLike token = RestrictedTokenLike(tranche.token); MemberlistLike memberlist = MemberlistLike(token.memberlist()); memberlist.updateMember(user, validUntil); @@ -157,8 +158,8 @@ contract CentrifugeConnector { uint256 amount ) public onlyRouter { RestrictedTokenLike token = RestrictedTokenLike(tranches[poolId][trancheId].token); - require(address(token) != address(0), "CentrifugeConnector/unknown-token"); require(token.hasMember(user), "CentrifugeConnector/not-a-member"); + token.mint(user, amount); } diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 27e80b1f..0068d8d9 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -12,6 +12,10 @@ import {ConnectorMessages} from "src/Messages.sol"; import "forge-std/Test.sol"; import "../src/Connector.sol"; +interface ERC20Like { + function balanceOf(address) external view returns (uint); +} + contract ConnectorTest is Test { CentrifugeConnector bridgedConnector; @@ -176,6 +180,44 @@ contract ConnectorTest is Test { bridgedConnector.updateTokenPrice(poolId, trancheId, price); } + function testTransfer(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint32 destinationDomain, address user, uint256 amount, uint64 validUntil) public { + vm.assume(validUntil > block.timestamp + 7 days); + // 0. Add Pool + homeConnector.addPool(poolId); + + // 1. Add the tranche + homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); + + // 2. Then deploy the tranche + bridgedConnector.deployTranche(poolId, trancheId); + + // 3. Add member + homeConnector.updateMember(poolId, trancheId, user, validUntil); + + // 4. Transfer some tokens + homeConnector.transfer(poolId, trancheId, user, amount); + (address token,,,,) = bridgedConnector.tranches(poolId, trancheId); + assertEq(ERC20Like(token).balanceOf(user), amount); + } + + function testTransferWithoutMemberFails(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint32 destinationDomain, address user, uint256 amount, uint64 validUntil) public { + vm.assume(validUntil > block.timestamp + 7 days); + // 0. Add Pool + homeConnector.addPool(poolId); + + // 1. Add the tranche + homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); + + // 2. Then deploy the tranche + bridgedConnector.deployTranche(poolId, trancheId); + + // 3. Transfer some tokens and expect revert + vm.expectRevert(bytes("CentrifugeConnector/not-a-member")); + homeConnector.transfer(poolId, trancheId, user, amount); + (address token,,,,) = bridgedConnector.tranches(poolId, trancheId); + assertEq(ERC20Like(token).balanceOf(user), 0); + } + // helpers function safeAdd(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x, "math-add-overflow"); From 8a20e95f1dce53244d8a618f4b19e0478516be52 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Tue, 7 Feb 2023 10:52:13 -0500 Subject: [PATCH 38/41] Feat/domain update (#35) * remove domainLookup * require destination domain to be cent chain * fix tests * clean up * fix up * rename Domains to Domain * Add new domain lookup * update domain enum * move formatDomain to messages.sol * fix tests * update enum and domain encoding * update domain encoding to output bytes9 * Update src/Connector.sol * update domain encoding and add more tests --------- Co-authored-by: Jeroen Offerijns --- src/Connector.sol | 22 ++++------------- src/Messages.sol | 14 +++++++++-- src/routers/xcm/Router.sol | 2 +- test/Connector.t.sol | 32 +++++++++++++++++++++---- test/Messages.t.sol | 42 +++++++++++++++++++++++++++++++++ test/mock/MockHomeConnector.sol | 4 ++-- 6 files changed, 89 insertions(+), 27 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index be382da3..3f25914a 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -5,9 +5,10 @@ pragma abicoder v2; import { RestrictedTokenFactoryLike, MemberlistFactoryLike } from "./token/factory.sol"; import { RestrictedTokenLike } from "./token/restricted.sol"; import { MemberlistLike } from "./token/memberlist.sol"; +import { ConnectorMessages } from "src/Messages.sol"; interface RouterLike { - function sendMessage(uint32 destinationDomain, uint64 poolId, bytes16 trancheId, uint256 amount, address user) external; + function sendMessage(uint64 poolId, bytes16 trancheId, uint256 amount, address user) external; } contract CentrifugeConnector { @@ -35,14 +36,11 @@ contract CentrifugeConnector { mapping(uint64 => Pool) public pools; mapping(uint64 => mapping(bytes16 => Tranche)) public tranches; mapping(address => uint256) public wards; - mapping(bytes32 => uint32) public domainLookup; - // --- 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); event TrancheDeployed(uint256 indexed poolId, bytes16 indexed trancheId, address indexed token); @@ -81,14 +79,6 @@ contract CentrifugeConnector { emit File(what, data); } - function file(bytes32 name, string memory domainName, uint32 domainId) public auth { - 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]; @@ -168,16 +158,14 @@ contract CentrifugeConnector { bytes16 trancheId, address user, uint256 amount, - string memory domainName + ConnectorMessages.Domain domain ) public { - uint32 domainId = domainLookup[keccak256(bytes(domainName))]; - require(domainId > 0, "CentrifugeConnector/domain-does-not-exist"); - + require(domain == ConnectorMessages.Domain.Centrifuge, "CentrifugeConnector/invalid-domain"); 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); + router.sendMessage(poolId, trancheId, amount, user); } } diff --git a/src/Messages.sol b/src/Messages.sol index 7346be16..00fcc8e3 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -16,6 +16,8 @@ library ConnectorMessages { Transfer } + enum Domain { Centrifuge, EVM } + function messageType(bytes29 _msg) internal pure returns (Call _call) { _call = Call(uint8(_msg.indexUint(0, 1))); } @@ -159,8 +161,8 @@ library ConnectorMessages { * 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 formatTransfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount, bytes9 destinationDomain) internal pure returns (bytes memory) { + return abi.encodePacked(uint8(Call.Transfer), poolId, trancheId, user, amount, destinationDomain); } function isTransfer(bytes29 _msg) internal pure returns (bool) { @@ -174,4 +176,12 @@ library ConnectorMessages { amount = uint256(_msg.index(45, 32)); } + function formatDomain(Domain domain) public pure returns (bytes9) { + return bytes9(byte(uint8(domain))); + } + + function formatDomain(Domain domain, uint64 domainId) public pure returns (bytes9) { + return bytes9(abi.encodePacked(uint8(domain), domainId).ref(0).index(0, 9)); + } + } \ No newline at end of file diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index ad46ca73..dd535514 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -63,7 +63,7 @@ contract ConnectorXCMRouter { } } - function sendMessage(uint32 destinationDomain, uint64 poolId, bytes16 trancheId, uint256 amount, address user) external onlyConnector { + function sendMessage(uint64 poolId, bytes16 trancheId, uint256 amount, address user) external onlyConnector { // TODO: implement } diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 0068d8d9..a393384a 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -8,7 +8,6 @@ import { RestrictedTokenLike } from "src/token/restricted.sol"; import { MemberlistLike, Memberlist } from "src/token/memberlist.sol"; import { MockHomeConnector } from "./mock/MockHomeConnector.sol"; import { ConnectorXCMRouter } from "src/routers/xcm/Router.sol"; -import {ConnectorMessages} from "src/Messages.sol"; import "forge-std/Test.sol"; import "../src/Connector.sol"; @@ -180,7 +179,7 @@ contract ConnectorTest is Test { bridgedConnector.updateTokenPrice(poolId, trancheId, price); } - function testTransfer(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint32 destinationDomain, address user, uint256 amount, uint64 validUntil) public { + function testTransferCentrifuge(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, address user, uint256 amount, uint64 validUntil) public { vm.assume(validUntil > block.timestamp + 7 days); // 0. Add Pool homeConnector.addPool(poolId); @@ -195,12 +194,34 @@ contract ConnectorTest is Test { homeConnector.updateMember(poolId, trancheId, user, validUntil); // 4. Transfer some tokens - homeConnector.transfer(poolId, trancheId, user, amount); + bytes9 encodedDomain = ConnectorMessages.formatDomain(ConnectorMessages.Domain.Centrifuge); + homeConnector.transfer(poolId, trancheId, user, amount, encodedDomain); (address token,,,,) = bridgedConnector.tranches(poolId, trancheId); assertEq(ERC20Like(token).balanceOf(user), amount); } - function testTransferWithoutMemberFails(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint32 destinationDomain, address user, uint256 amount, uint64 validUntil) public { + function testTransferEVM(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint64 destinationChainId, address user, uint256 amount, uint64 validUntil) public { + vm.assume(validUntil > block.timestamp + 7 days); + // 0. Add Pool + homeConnector.addPool(poolId); + + // 1. Add the tranche + homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); + + // 2. Then deploy the tranche + bridgedConnector.deployTranche(poolId, trancheId); + + // 3. Add member + homeConnector.updateMember(poolId, trancheId, user, validUntil); + + // 4. Transfer some tokens + bytes9 encodedDomain = ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, destinationChainId); + homeConnector.transfer(poolId, trancheId, user, amount, encodedDomain); + (address token,,,,) = bridgedConnector.tranches(poolId, trancheId); + assertEq(ERC20Like(token).balanceOf(user), amount); + } + + function testTransferEVMWithoutMemberFails(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint64 destinationChainId, address user, uint256 amount, uint64 validUntil) public { vm.assume(validUntil > block.timestamp + 7 days); // 0. Add Pool homeConnector.addPool(poolId); @@ -212,8 +233,9 @@ contract ConnectorTest is Test { bridgedConnector.deployTranche(poolId, trancheId); // 3. Transfer some tokens and expect revert + bytes9 encodedDomain = ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, destinationChainId); vm.expectRevert(bytes("CentrifugeConnector/not-a-member")); - homeConnector.transfer(poolId, trancheId, user, amount); + homeConnector.transfer(poolId, trancheId, user, amount, encodedDomain); (address token,,,,) = bridgedConnector.tranches(poolId, trancheId); assertEq(ERC20Like(token).balanceOf(user), 0); } diff --git a/test/Messages.t.sol b/test/Messages.t.sol index cddb0156..029d8b04 100644 --- a/test/Messages.t.sol +++ b/test/Messages.t.sol @@ -171,6 +171,48 @@ contract MessagesTest is Test { assertEq(uint(decodedPrice), uint(price)); } + // TODO: replace payload with real msg from Cent chain + function testTransferEncoding() public { + assertEq( + ConnectorMessages.formatTransfer(1, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), 0x1231231231231231231231231231231231231231, 1000000000000000000000000000, bytes9(hex"000000000000000000")), + hex"050000000000000001811acd5b3f17c06841c7e41e9e04cb1b12312312312312312312312312312312312312310000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000000000000000000" + ); + } + + // TODO: replace payload with real msg from Cent chain + function testTransferDecoding() public { + (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(fromHex("050000000000000001811acd5b3f17c06841c7e41e9e04cb1b12312312312312312312312312312312312312310000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000000000000000000").ref(0)); + assertEq(uint(poolId), uint(1)); + assertEq(trancheId, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b")); + assertEq(user, 0x1231231231231231231231231231231231231231); + assertEq(amount, uint(1000000000000000000000000000)); + } + + function testTransferEquivalence(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint64 destinationChainId) public { + bytes memory _message = ConnectorMessages.formatTransfer(poolId, trancheId, user, amount, ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, destinationChainId)); + (uint64 decodedPoolId, bytes16 decodedTrancheId, address decodedUser, uint256 decodedAmount) = ConnectorMessages.parseTransfer(_message.ref(0)); + assertEq(uint(decodedPoolId), uint(poolId)); + assertEq(decodedTrancheId, trancheId); + assertEq(decodedUser, user); + assertEq(decodedAmount, amount); + } + + function testFormatDomainCentrifuge() public { + assertEq(ConnectorMessages.formatDomain(ConnectorMessages.Domain.Centrifuge), hex"000000000000000000"); + } + + function testFormatDomainMoonbeam() public { + assertEq(ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, 1284), hex"010000000000000504"); + } + + function testFormatDomainMoonbaseAlpha() public { + assertEq(ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, 1287), hex"010000000000000507"); + } + + function testFormatDomainAvalanche() public { + assertEq(ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, 43114), hex"01000000000000a86a"); + } + // Convert an hexadecimal character to their value function fromHexChar(uint8 c) internal pure returns (uint8) { if (bytes1(c) >= bytes1("0") && bytes1(c) <= bytes1("9")) { diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index 4b004994..08b13daa 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -53,8 +53,8 @@ contract MockHomeConnector is Test { router.handle(_message); } - function transfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount) public { - bytes memory _message = ConnectorMessages.formatTransfer(poolId, trancheId, user, amount); + function transfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount, bytes9 destinationDomain) public { + bytes memory _message = ConnectorMessages.formatTransfer(poolId, trancheId, user, amount, destinationDomain); router.handle(_message); } From 54dd4bb880df8fbe9215223e685929b1a92b8ade Mon Sep 17 00:00:00 2001 From: Nuno Alexandre Date: Tue, 7 Feb 2023 21:56:38 +0100 Subject: [PATCH 39/41] Fine-tune transfers encoding, decoding, and tests (#39) * Fix transfer encoding / decoding We need to encode address as 32 bytes and take in consideration when decoding. Also, amount should be encoded as a u128. * Fix transfer decoding * Cover transfer to centrifuge * Fix docs - thanks @AStox --- src/Messages.sol | 17 +++++++------- src/routers/xcm/Router.sol | 2 +- test/Connector.t.sol | 6 ++--- test/Messages.t.sol | 39 +++++++++++++++++++++++---------- test/mock/MockHomeConnector.sol | 2 +- 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/Messages.sol b/src/Messages.sol index 00fcc8e3..e3c3e4e7 100644 --- a/src/Messages.sol +++ b/src/Messages.sol @@ -156,24 +156,25 @@ library ConnectorMessages { * * 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) - * + * 9-24: trancheId (16 bytes) + * 25-56: user (Ethereum address, 20 bytes - Skip last 12 bytes for 32-byte address compatibility) + * 57-72: amount (uint128 = 16 bytes) + * 73-81: domain (Domain = 9 bytes) */ - function formatTransfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount, bytes9 destinationDomain) internal pure returns (bytes memory) { - return abi.encodePacked(uint8(Call.Transfer), poolId, trancheId, user, amount, destinationDomain); + function formatTransfer(uint64 poolId, bytes16 trancheId, address user, uint128 amount, bytes9 destinationDomain) internal pure returns (bytes memory) { + return abi.encodePacked(uint8(Call.Transfer), poolId, trancheId, user, bytes(hex"000000000000000000000000"), amount, destinationDomain); } 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) { + function parseTransfer(bytes29 _msg) internal pure returns (uint64 poolId, bytes16 trancheId, address user, uint128 amount, bytes9 encodedDomain) { 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)); + amount = uint128(_msg.indexUint(57, 16)); + encodedDomain = bytes9(_msg.index(73, 9)); } function formatDomain(Domain domain) public pure returns (bytes9) { diff --git a/src/routers/xcm/Router.sol b/src/routers/xcm/Router.sol index dd535514..435a2c4f 100644 --- a/src/routers/xcm/Router.sol +++ b/src/routers/xcm/Router.sol @@ -56,7 +56,7 @@ contract ConnectorXCMRouter { (uint64 poolId, bytes16 trancheId, uint128 price) = ConnectorMessages.parseUpdateTokenPrice(_msg); connector.updateTokenPrice(poolId, trancheId, price); } else if (ConnectorMessages.isTransfer(_msg)) { - (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(_msg); + (uint64 poolId, bytes16 trancheId, address user, uint256 amount, bytes9 _decodedDomain) = ConnectorMessages.parseTransfer(_msg); connector.handleTransfer(poolId, trancheId, user, amount); } else { require(false, "invalid-message"); diff --git a/test/Connector.t.sol b/test/Connector.t.sol index a393384a..848324e4 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -179,7 +179,7 @@ contract ConnectorTest is Test { bridgedConnector.updateTokenPrice(poolId, trancheId, price); } - function testTransferCentrifuge(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, address user, uint256 amount, uint64 validUntil) public { + function testTransferCentrifuge(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, address user, uint128 amount, uint64 validUntil) public { vm.assume(validUntil > block.timestamp + 7 days); // 0. Add Pool homeConnector.addPool(poolId); @@ -200,7 +200,7 @@ contract ConnectorTest is Test { assertEq(ERC20Like(token).balanceOf(user), amount); } - function testTransferEVM(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint64 destinationChainId, address user, uint256 amount, uint64 validUntil) public { + function testTransferEVM(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint64 destinationChainId, address user, uint128 amount, uint64 validUntil) public { vm.assume(validUntil > block.timestamp + 7 days); // 0. Add Pool homeConnector.addPool(poolId); @@ -221,7 +221,7 @@ contract ConnectorTest is Test { assertEq(ERC20Like(token).balanceOf(user), amount); } - function testTransferEVMWithoutMemberFails(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint64 destinationChainId, address user, uint256 amount, uint64 validUntil) public { + function testTransferEVMWithoutMemberFails(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price, uint64 destinationChainId, address user, uint128 amount, uint64 validUntil) public { vm.assume(validUntil > block.timestamp + 7 days); // 0. Add Pool homeConnector.addPool(poolId); diff --git a/test/Messages.t.sol b/test/Messages.t.sol index 029d8b04..2674af97 100644 --- a/test/Messages.t.sol +++ b/test/Messages.t.sol @@ -171,30 +171,47 @@ contract MessagesTest is Test { assertEq(uint(decodedPrice), uint(price)); } - // TODO: replace payload with real msg from Cent chain - function testTransferEncoding() public { + function testTransferToEvmDomainEncoding() public { assertEq( - ConnectorMessages.formatTransfer(1, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), 0x1231231231231231231231231231231231231231, 1000000000000000000000000000, bytes9(hex"000000000000000000")), - hex"050000000000000001811acd5b3f17c06841c7e41e9e04cb1b12312312312312312312312312312312312312310000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000000000000000000" - ); + ConnectorMessages.formatTransfer(1, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), 0x1231231231231231231231231231231231231231, 1000000000000000000000000000, ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, 1284)), + hex"050000000000000001811acd5b3f17c06841c7e41e9e04cb1b123123123123123123123123123123123123123100000000000000000000000000000000033b2e3c9fd0803ce8000000010000000000000504" + ); + } + + function testTransferToEvmDomainDecoding() public { + (uint64 poolId, bytes16 trancheId, address user, uint256 amount, bytes9 decodedDomain) = ConnectorMessages.parseTransfer(fromHex("050000000000000001811acd5b3f17c06841c7e41e9e04cb1b123123123123123123123123123123123123123100000000000000000000000000000000033b2e3c9fd0803ce8000000010000000000000504").ref(0)); + assertEq(uint(poolId), uint(1)); + assertEq(trancheId, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b")); + assertEq(user, 0x1231231231231231231231231231231231231231); + assertEq(amount, uint(1000000000000000000000000000)); + assertEq(decodedDomain, bytes9(hex"010000000000000504")); + } + + function testTransferToCentrifugeEncoding() public { + assertEq( + ConnectorMessages.formatTransfer(1, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b"), 0x1231231231231231231231231231231231231231, 1000000000000000000000000000, ConnectorMessages.formatDomain(ConnectorMessages.Domain.Centrifuge)), + hex"050000000000000001811acd5b3f17c06841c7e41e9e04cb1b123123123123123123123123123123123123123100000000000000000000000000000000033b2e3c9fd0803ce8000000000000000000000000" + ); } - // TODO: replace payload with real msg from Cent chain - function testTransferDecoding() public { - (uint64 poolId, bytes16 trancheId, address user, uint256 amount) = ConnectorMessages.parseTransfer(fromHex("050000000000000001811acd5b3f17c06841c7e41e9e04cb1b12312312312312312312312312312312312312310000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000000000000000000").ref(0)); + function testTransferToCentrifugeDecoding() public { + (uint64 poolId, bytes16 trancheId, address user, uint256 amount, bytes9 decodedDomain) = ConnectorMessages.parseTransfer(fromHex("050000000000000001811acd5b3f17c06841c7e41e9e04cb1b123123123123123123123123123123123123123100000000000000000000000000000000033b2e3c9fd0803ce8000000000000000000000000").ref(0)); assertEq(uint(poolId), uint(1)); assertEq(trancheId, bytes16(hex"811acd5b3f17c06841c7e41e9e04cb1b")); assertEq(user, 0x1231231231231231231231231231231231231231); assertEq(amount, uint(1000000000000000000000000000)); + assertEq(decodedDomain, bytes9(hex"000000000000000000")); } - function testTransferEquivalence(uint64 poolId, bytes16 trancheId, address user, uint256 amount, uint64 destinationChainId) public { - bytes memory _message = ConnectorMessages.formatTransfer(poolId, trancheId, user, amount, ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, destinationChainId)); - (uint64 decodedPoolId, bytes16 decodedTrancheId, address decodedUser, uint256 decodedAmount) = ConnectorMessages.parseTransfer(_message.ref(0)); + function testTransferEquivalence(uint64 poolId, bytes16 trancheId, address user, uint128 amount, uint64 destinationChainId) public { + bytes9 inputEncodedDomain = ConnectorMessages.formatDomain(ConnectorMessages.Domain.EVM, destinationChainId); + bytes memory _message = ConnectorMessages.formatTransfer(poolId, trancheId, user, amount, inputEncodedDomain); + (uint64 decodedPoolId, bytes16 decodedTrancheId, address decodedUser, uint256 decodedAmount, bytes9 encodedDomain) = ConnectorMessages.parseTransfer(_message.ref(0)); assertEq(uint(decodedPoolId), uint(poolId)); assertEq(decodedTrancheId, trancheId); assertEq(decodedUser, user); assertEq(decodedAmount, amount); + assertEq(encodedDomain, inputEncodedDomain); } function testFormatDomainCentrifuge() public { diff --git a/test/mock/MockHomeConnector.sol b/test/mock/MockHomeConnector.sol index 08b13daa..3cc74292 100644 --- a/test/mock/MockHomeConnector.sol +++ b/test/mock/MockHomeConnector.sol @@ -53,7 +53,7 @@ contract MockHomeConnector is Test { router.handle(_message); } - function transfer(uint64 poolId, bytes16 trancheId, address user, uint256 amount, bytes9 destinationDomain) public { + function transfer(uint64 poolId, bytes16 trancheId, address user, uint128 amount, bytes9 destinationDomain) public { bytes memory _message = ConnectorMessages.formatTransfer(poolId, trancheId, user, amount, destinationDomain); router.handle(_message); } From 695f1f08d11c15c05fc4d8cb60e879786a012172 Mon Sep 17 00:00:00 2001 From: Adam Stox Date: Wed, 8 Feb 2023 14:34:44 -0500 Subject: [PATCH 40/41] Add TODO and more tests --- src/Connector.sol | 6 +++--- test/Connector.t.sol | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Connector.sol b/src/Connector.sol index 3f25914a..deb21b31 100644 --- a/src/Connector.sol +++ b/src/Connector.sol @@ -5,6 +5,7 @@ pragma abicoder v2; import { RestrictedTokenFactoryLike, MemberlistFactoryLike } from "./token/factory.sol"; import { RestrictedTokenLike } from "./token/restricted.sol"; import { MemberlistLike } from "./token/memberlist.sol"; +// TODO: remove dependency on Messages.sol import { ConnectorMessages } from "src/Messages.sol"; interface RouterLike { @@ -104,10 +105,9 @@ contract CentrifugeConnector { } function deployTranche(uint64 poolId, bytes16 trancheId) public { - Pool storage pool = pools[poolId]; - require(pool.createdAt > 0, "CentrifugeConnector/invalid-pool"); - Tranche storage tranche = tranches[poolId][trancheId]; + require(tranche.lastPriceUpdate > 0, "CentrifugeConnector/invalid-pool-or-tranche"); + address token = tokenFactory.newRestrictedToken(tranche.tokenName, tranche.tokenSymbol); tranche.token = token; diff --git a/test/Connector.t.sol b/test/Connector.t.sol index 848324e4..e1e2f387 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -96,6 +96,34 @@ contract ConnectorTest is Test { homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); } + function testDeployingWrongTrancheFails(uint64 poolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, bytes16 wrongTrancheId, uint128 price) public { + vm.assume(trancheId != wrongTrancheId); + // 0. Add Pool + homeConnector.addPool(poolId); + (uint64 actualPoolId,) = bridgedConnector.pools(poolId); + assertEq(uint256(actualPoolId), uint256(poolId)); + + // 1. Add the tranche + homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); + // 2. Then deploy the tranche + vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); + bridgedConnector.deployTranche(poolId, wrongTrancheId); + } + + function testDeployingTrancheOnNonExistantPoolFails(uint64 poolId, uint64 wrongPoolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price) public { + vm.assume(poolId != wrongPoolId); + // 0. Add Pool + homeConnector.addPool(poolId); + (uint64 actualPoolId,) = bridgedConnector.pools(poolId); + assertEq(uint256(actualPoolId), uint256(poolId)); + + // 1. Add the tranche + homeConnector.addTranche(poolId, trancheId, tokenName, tokenSymbol, price); + // 2. Then deploy the tranche + vm.expectRevert(bytes("CentrifugeConnector/invalid-pool-or-tranche")); + bridgedConnector.deployTranche(wrongPoolId, trancheId); + } + function testUpdatingMemberWorks(uint64 poolId, bytes16 trancheId, address user, uint64 validUntil) public { vm.assume(validUntil >= safeAdd(block.timestamp, new Memberlist().minimumDelay())); vm.assume(user != address(0)); From 0b28d84e651346e0147353f21cdaab0c18b11ec3 Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Thu, 9 Feb 2023 10:20:06 +0100 Subject: [PATCH 41/41] Update test/Connector.t.sol --- test/Connector.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Connector.t.sol b/test/Connector.t.sol index e1e2f387..aa41f574 100644 --- a/test/Connector.t.sol +++ b/test/Connector.t.sol @@ -110,7 +110,7 @@ contract ConnectorTest is Test { bridgedConnector.deployTranche(poolId, wrongTrancheId); } - function testDeployingTrancheOnNonExistantPoolFails(uint64 poolId, uint64 wrongPoolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price) public { + function testDeployingTrancheOnNonExistentPoolFails(uint64 poolId, uint64 wrongPoolId, string memory tokenName, string memory tokenSymbol, bytes16 trancheId, uint128 price) public { vm.assume(poolId != wrongPoolId); // 0. Add Pool homeConnector.addPool(poolId);