From bc83607b8c2f2c9a4ea43fa76c17e28f7ae41242 Mon Sep 17 00:00:00 2001 From: Richard Watts Date: Tue, 7 Jan 2025 16:30:48 +0000 Subject: [PATCH] (feat) Add a scale factor to the V4 token maanager --- .../TokenManagerUpgradeableV4.sol | 112 +++++++++++++++++- .../script/pol/deploy/06_routing.s.sol | 48 ++++---- 2 files changed, 134 insertions(+), 26 deletions(-) diff --git a/smart-contracts/contracts/periphery/TokenManagerV4/TokenManagerUpgradeableV4.sol b/smart-contracts/contracts/periphery/TokenManagerV4/TokenManagerUpgradeableV4.sol index aa5f929..600f357 100644 --- a/smart-contracts/contracts/periphery/TokenManagerV4/TokenManagerUpgradeableV4.sol +++ b/smart-contracts/contracts/periphery/TokenManagerV4/TokenManagerUpgradeableV4.sol @@ -9,15 +9,37 @@ import {ITokenManagerEvents, ITokenManagerStructs} from "contracts/periphery/Tok import {TokenManagerFees, ITokenManagerFees} from "contracts/periphery/TokenManagerV2/TokenManagerFees.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; +interface ITokenManagerV4Structs { + struct ScaledRemoteToken { + address token; + address tokenManager; + uint chainId; + int8 scale; + } +} + +interface ITokenManagerV4Events { + event TokenScaleChanged ( + address indexed token, + uint remoteChainId, + int8 remoteScale + ); +} + interface ITokenManager is ITokenManagerEvents, ITokenManagerStructs, - ITokenManagerFees + ITokenManagerFees, + ITokenManagerV4Structs, + ITokenManagerV4Events { error InvalidSourceChainId(); error InvalidTokenManager(); error NotGateway(); error InvalidTokenRouting(); + // the amount of local tokens we tried to send, the nearest scaled amount we could recieve on the target chain, and + // the amount of local tokens that would correspond to. + error InvalidTokenAmount(uint256 sent, uint256 adjusted, uint256 reconstructed); function getGateway() external view returns (address); @@ -73,6 +95,7 @@ abstract contract TokenManagerUpgradeableV4 is bytes32 private constant Token_Manager_Storage_Location = 0x4a6c2e6a7e6518c249bdcd1d934ea16ea5325bbae105af814eb678f5f49f3400; + function _getTokenManagerStorage() private pure @@ -83,6 +106,28 @@ abstract contract TokenManagerUpgradeableV4 is } } + struct TokenManagerV4Storage { + // This stores the scale for remote tokens, as remote_token.decimals-local_token.decimals. + // So, when sending a token with a +ve scale, we shift left. + // When sending a token with a -ve scale, we shift right. + // When receiving a token, we do nothing. + // This allows us to validate that the tokens can be exactly converted to the remote + // and revert the sending txn if not. + mapping(address => mapping(uint => int8)) scaleForRemoteTokens; + } + + // keccack256(abi.encode(uint256(keccak256("zilliqa.storage.TokenManagerV4"))-1))& ~bytes32(uint256(0xff)) + bytes32 private constant Token_ManagerV4_Storage_Location = + 0xe8b1c929e9ac4c16aaeb9d6494126adb9c7e3b297332aeb86accee6d41c67500; + + function _getTokenManagerV4Storage() + private pure returns (TokenManagerV4Storage storage $) + { + assembly { + $.slot := Token_ManagerV4_Storage_Location + } + } + function getGateway() public view returns (address) { TokenManagerStorage storage $ = _getTokenManagerStorage(); return $.gateway; @@ -96,6 +141,11 @@ abstract contract TokenManagerUpgradeableV4 is return $.remoteTokens[token][remoteChainId]; } + function getRemoteTokenScale(address token, uint remoteChainId) public view returns (int8) { + TokenManagerV4Storage storage $ = _getTokenManagerV4Storage(); + return $.scaleForRemoteTokens[token][remoteChainId]; + } + modifier onlyGateway() { if (_msgSender() != address(getGateway())) { revert NotGateway(); @@ -122,6 +172,8 @@ abstract contract TokenManagerUpgradeableV4 is function _removeToken(address localToken, uint remoteChainId) internal { TokenManagerStorage storage $ = _getTokenManagerStorage(); delete $.remoteTokens[localToken][remoteChainId]; + TokenManagerV4Storage storage $$ = _getTokenManagerV4Storage(); + delete $$.scaleForRemoteTokens[localToken][remoteChainId]; emit TokenRemoved(localToken, remoteChainId); } @@ -139,6 +191,49 @@ abstract contract TokenManagerUpgradeableV4 is ); } + function _setScaleForToken(address localToken, + uint remoteChainId, + int8 scale) internal { + TokenManagerV4Storage storage $ = _getTokenManagerV4Storage(); + $.scaleForRemoteTokens[localToken][remoteChainId] = scale; + emit TokenScaleChanged( localToken, + remoteChainId, + scale ); + } + + + function _getScaleForToken(address localToken, + uint remoteChainId) internal returns (int8) { + TokenManagerV4Storage storage $ = _getTokenManagerV4Storage(); + return $.scaleForRemoteTokens[localToken][remoteChainId]; + } + + function _scaleAmount(uint amount, + address localToken, + uint remoteChainId) internal returns (uint) + { + int8 scale = _getScaleForToken(localToken, remoteChainId); + uint adjusted; + uint reconstructed; + + if (scale < 0) { + // Must be < 0 since the condition above requires it. + uint divisor = uint(10)**uint(int256(-scale)); + adjusted = (amount / divisor); + reconstructed = amount * divisor; + } else if (scale > 0) { + // Must be > 0 since the condition above requires it. + uint multiplier = uint(10)**uint(int256(scale)); + adjusted = amount * multiplier; + reconstructed = amount / multiplier; + } + // If scale == 0, nothing is done, which is what is intended. + if (adjusted != reconstructed) { + revert InvalidTokenAmount(amount, adjusted, reconstructed); + } + return adjusted; + } + // Token Overrides function registerToken( address token, @@ -171,6 +266,13 @@ abstract contract TokenManagerUpgradeableV4 is _unpause(); } + // V4 new function + function setScaleForToken(address localToken, + uint remoteChainId, + int8 scale) external virtual onlyOwner { + _setScaleForToken(localToken, remoteChainId, scale); + } + // TO OVERRIDE – Incoming function _handleTransfer( address token, @@ -197,19 +299,25 @@ abstract contract TokenManagerUpgradeableV4 is revert InvalidTokenRouting(); } + // If this does not exactly correspond with amount, _scaleAmount() will revert. + uint scaledAmount = _scaleAmount(amount, token, remoteChainId); + + // We take the original amount. _handleTransfer(token, _msgSender(), amount); + // .. and send the scaled amount. IRelayer(getGateway()).relayWithMetadata( remoteToken.chainId, remoteToken.tokenManager, this.accept.selector, - abi.encode(AcceptArgs(remoteToken.token, remoteRecipient, amount)), + abi.encode(AcceptArgs(remoteToken.token, remoteRecipient, scaledAmount)), 1_000_000 ); } // Incoming // No pausing here because we want incoming txns to go through that have already initiated + // We cannot scale anything here, because there's no way to stop the tokens having been sent. function accept( CallMetadata calldata metadata, bytes calldata _args diff --git a/smart-contracts/script/pol/deploy/06_routing.s.sol b/smart-contracts/script/pol/deploy/06_routing.s.sol index cb37826..8df000b 100644 --- a/smart-contracts/script/pol/deploy/06_routing.s.sol +++ b/smart-contracts/script/pol/deploy/06_routing.s.sol @@ -11,31 +11,31 @@ import {LockProxyTokenManagerUpgradeable} from "contracts/periphery/LockProxyTok import {LockAndReleaseTokenManagerUpgradeable} from "contracts/periphery/LockAndReleaseTokenManagerUpgradeable.sol"; import {ITokenManagerStructs} from "contracts/periphery/TokenManagerUpgradeable.sol"; -contract Routing is Script, MainnetConfig { - function run() external { - -// Bridged: ['zil.1.6.52c256'] -// Native: ['matic.1.17.3254b4'] -// correspondent network: ['zilliqa'] -LockProxyTokenManagerUpgradeable polLockProxyTokenManager = LockProxyTokenManagerUpgradeable(polLockProxyTokenManager) -LockAndReleaseTokenManagerUpgradeable polLockAndReleaseOrNativeTokenManager = LockAndReleaseTokenManagerUpgradeable(polLockAndReleaseOrNativeTokenManagerUpgradeable) -// bridged to polygon: token zil.1.6.52c256 has zq_denom zil.1.18.1a4a06, name ZIL and is on zilliqa as 0x0000000000000000000000000000000000000000, zil.1.18.1a4a06 - - ITokenManagerStructs.RemoteToken memory ZILBridgedTokenRouting = ITokenManagerStructs.RemoteToken({ - token: address(0x0000000000000000000000000000000000000000), - tokenManager: address(zilLockAndReleaseOrNativeTokenManagerUpgradeable), - chainId: zqChainId }); - - polLockProxyTokenManager.registerToken(address(0xCc88D28f7d4B0D5AFACCC77F6102d88EE630fA17), ZILBridgedTokenRouting); +/* contract Routing is Script, MainnetConfig { */ +/* function run() external { */ + +/* // Bridged: ['zil.1.6.52c256'] */ +/* // Native: ['matic.1.17.3254b4'] */ +/* // correspondent network: ['zilliqa'] */ +/* LockProxyTokenManagerUpgradeable polLockProxyTokenManager = LockProxyTokenManagerUpgradeable(polLockProxyTokenManager); */ +/* LockAndReleaseTokenManagerUpgradeable polLockAndReleaseOrNativeTokenManager = LockAndReleaseTokenManagerUpgradeable(polLockAndReleaseOrNativeTokenManagerUpgradeable); */ +/* // bridged to polygon: token zil.1.6.52c256 has zq_denom zil.1.18.1a4a06, name ZIL and is on zilliqa as 0x0000000000000000000000000000000000000000, zil.1.18.1a4a06 */ + +/* ITokenManagerStructs.RemoteToken memory ZILBridgedTokenRouting = ITokenManagerStructs.RemoteToken({ */ +/* token: address(0x0000000000000000000000000000000000000000), */ +/* tokenManager: address(zilLockAndReleaseOrNativeTokenManagerUpgradeable), */ +/* chainId: zqChainId }); */ + +/* polLockProxyTokenManager.registerToken(address(0xCc88D28f7d4B0D5AFACCC77F6102d88EE630fA17), ZILBridgedTokenRouting); */ -// native on polygon: token matic.1.17.3254b4 has zq_denom zmatic.1.18.45185c, name zMATIC and is on zilliqa as 0xa1A172999AD3C5d457536c48736e30F53Bc260C9, zmatic.1.18.45185c +/* // native on polygon: token matic.1.17.3254b4 has zq_denom zmatic.1.18.45185c, name zMATIC and is on zilliqa as 0xa1A172999AD3C5d457536c48736e30F53Bc260C9, zmatic.1.18.45185c */ - ITokenManagerStructs.RemoteToken memory zMATICNativeTokenRouting = ITokenManagerStructs.RemoteToken({ - token: address(0xa1A172999AD3C5d457536c48736e30F53Bc260C9), - tokenManager: address(zilLockProxyTokenManager), - chainId: zqChainId }); - polLockAndReleaseOrNativeTokenManager.registerToken(address(0x0000000000000000000000000000000000000000), zMATICNativeTokenRouting); +/* ITokenManagerStructs.RemoteToken memory zMATICNativeTokenRouting = ITokenManagerStructs.RemoteToken({ */ +/* token: address(0xa1A172999AD3C5d457536c48736e30F53Bc260C9), */ +/* tokenManager: address(zilLockProxyTokenManager), */ +/* chainId: zqChainId }); */ +/* polLockAndReleaseOrNativeTokenManager.registerToken(address(0x0000000000000000000000000000000000000000), zMATICNativeTokenRouting); */ - } -} +/* } */ +/* } */