@@ -10,9 +10,6 @@ import {ERC20PausableUpgradeable} from
1010 "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PausableUpgradeable.sol " ;
1111import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol " ;
1212import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol " ;
13- import {Math} from "@openzeppelin/contracts/utils/math/Math.sol " ;
14- import {IGmpReceiver} from "@analog-gmp/interfaces/IGmpReceiver.sol " ;
15- import {IGateway} from "@analog-gmp/interfaces/IGateway.sol " ;
1613
1714/// @notice V1: Roles Model implementation of upgradable ERC20 token.
1815/// This to be used as the initial implementation of UUPS proxy.
@@ -24,99 +21,15 @@ contract AnlogTokenV1 is
2421 ERC20BurnableUpgradeable ,
2522 ERC20PausableUpgradeable ,
2623 AccessControlUpgradeable ,
27- UUPSUpgradeable ,
28- IGmpReceiver
24+ UUPSUpgradeable
2925{
3026 bytes32 public constant MINTER_ROLE = keccak256 ("MINTER_ROLE " );
3127 bytes32 public constant UPGRADER_ROLE = keccak256 ("UPGRADER_ROLE " );
3228 bytes32 public constant PAUSER_ROLE = keccak256 ("PAUSER_ROLE " );
3329 bytes32 public constant UNPAUSER_ROLE = keccak256 ("UNPAUSER_ROLE " );
3430
35- /**
36- * @dev Length of `OutboundTeleportCommand` struct encoded in bytes.
37- * ```
38- * uint256 messageLength = abi.encode(OutboundTeleportCommand({from: address(0), to: bytes32(0), amount: 0})).length;
39- * ```
40- */
41- uint256 public constant TELEPORT_COMMAND_ENCODED_LEN = 96 ;
42-
43- /**
44- * @dev Minimun gas limit necessary to execute the `onGmpReceived` method defined in this contract.
45- */
46- uint256 public constant INBOUND_TRANSFER_GAS_LIMIT = 100_000 ;
47-
48- /**
49- * @dev Address of Analog Gateway deployed in the local network, work as "broker" to exchange messages,
50- * between this contract and the Timechain.
51- *
52- * References:
53- * - Protocol Overview: https://docs.analog.one/documentation/developers/analog-gmp
54- * - Gateway source-code: https://github.com/Analog-Labs/analog-gmp
55- */
56- /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
57- IGateway public immutable GATEWAY;
58-
59- /**
60- * @dev Timechain's Route ID, this is the unique identifier of Timechain's network.
61- */
62- /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
63- uint16 public immutable TIMECHAIN_ROUTE_ID;
64-
65- /**
66- * @dev Minimal quantity of tokens allowed per teleport.
67- *
68- * IMPORTANT: This value MUST be equal or greater than the timechain's existential deposit.
69- * see: https://github.com/paritytech/polkadot-sdk/blob/polkadot-v1.17.1/substrate/frame/balances/README.md?plain=1#L24-L29
70- */
71- /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
72- uint256 public immutable MINIMAL_TELEPORT_VALUE;
73-
74- /**
75- * @dev Emitted when `amount` tokens are teleported from `source` account in the local network to `recipient` in Timechain.
76- */
77- event OutboundTransfer (bytes32 indexed id , address indexed source , bytes32 indexed recipient , uint256 amount );
78-
79- /**
80- * @dev @dev Emitted when `amount` tokens are teleported from `source` in Timechain to `recipient` in the local network.
81- */
82- event InboundTransfer (bytes32 indexed id , bytes32 indexed source , address indexed recipient , uint256 amount );
83-
84- /**
85- * @dev One or more preconditions of `onGmpReceived` method failed.
86- */
87- error Unauthorized ();
88-
89- /**
90- * @dev Command encoded in the `data` field on the `onGmpReceived` method, representing a teleport from Timechain to the local network.
91- * @param from Timechain's account teleporting the tokens.
92- * @param to Local account receing the tokens.
93- * @param amount The amount of tokens teleported.
94- */
95- struct InboundTeleportCommand {
96- bytes32 from;
97- address to;
98- uint256 amount;
99- }
100-
101- /**
102- * @dev Command that that teleports tokens from the local network to the Timechain.
103- * @param from Account in the local network teleporting the tokens.
104- * @param to Account in Timechain receing the tokens.
105- * @param amount The amount of tokens to teleport.
106- */
107- struct OutboundTeleportCommand {
108- address from;
109- bytes32 to;
110- uint256 amount;
111- }
112-
11331 /// @custom:oz-upgrades-unsafe-allow constructor
114- constructor (address gateway , uint16 timechainId , uint256 minimalTeleport ) {
115- require (gateway.code.length > 0 , "Gateway address is not a contract " );
116- require (IGateway (gateway).networkId () != timechainId, "local network and Timechain must be different networks " );
117- GATEWAY = IGateway (gateway);
118- TIMECHAIN_ROUTE_ID = timechainId;
119- MINIMAL_TELEPORT_VALUE = minimalTeleport;
32+ constructor () {
12033 _disableInitializers ();
12134 }
12235
@@ -150,142 +63,14 @@ contract AnlogTokenV1 is
15063 _mint (to, amount);
15164 }
15265
153- /**
154- * @dev The following functions are overrides required by Solidity.
155- */
15666 function _authorizeUpgrade (address newImplementation ) internal override onlyRole (UPGRADER_ROLE) {}
15767
68+ // The following functions are overrides required by Solidity.
69+
15870 function _update (address from , address to , uint256 value )
15971 internal
16072 override (ERC20Upgradeable , ERC20PausableUpgradeable )
16173 {
16274 super ._update (from, to, value);
16375 }
164-
165- /**
166- * @dev Workaround for EVM compatibility, in some chains like `Astar` where `address(this).balance` can
167- * be less than `msg.value` if this contract has no previous existential deposit.
168- * Reference:
169- * - https://github.com/polkadot-evm/frontier/blob/polkadot-v1.11.0/ts-tests/tests/test-balance.ts#L41
170- */
171- function _msgValue () private view returns (uint256 ) {
172- return Math.min (msg .value , address (this ).balance);
173- }
174-
175- /**
176- * @dev Teleport a `value` amount of tokens from the caller's account in the local chain to `to`
177- * account in the Timechain.
178- *
179- * Returns the GMP message identifier.
180- *
181- * Requirements:
182- * - `to` cannot be the zero address.
183- * - `value` must be equal or greater than `MINIMAL_TELEPORT_VALUE`.
184- * - the caller must have a balance of at least `value`.
185- *
186- * Emits a {OutboundTransfer} event.
187- */
188- function teleport (bytes32 to , uint256 value ) external payable returns (bytes32 messageID ) {
189- return _teleportFrom (_msgSender (), to, value);
190- }
191-
192- /**
193- * @dev Teleports a `value` amount of tokens from `from` account in the local chain to `to` account
194- * in the Timechain using the allowance mechanism. `value` is then deducted from the caller's
195- * allowance.
196- *
197- * Returns the GMP message identifier.
198- *
199- * NOTE: Does not update the allowance if the current allowance
200- * is the maximum `uint256`.
201- *
202- * Requirements:
203- * - `from` and `to` cannot be the zero address.
204- * - `from` must have a balance of at least `value`.
205- * - `value` must be equal or greater than `MINIMAL_TELEPORT_VALUE`.
206- * - the caller must have allowance for ``from``'s tokens of at least
207- * `value`.
208- *
209- * Emits a {OutboundTransfer} event.
210- */
211- function teleportFrom (address from , bytes32 to , uint256 value ) external payable returns (bytes32 messageID ) {
212- address spender = _msgSender ();
213- _spendAllowance (from, spender, value);
214- return _teleportFrom (from, to, value);
215- }
216-
217- /**
218- * @dev Teleports a `value` amount of tokens from `from` account in the local chain to `to` account
219- * in the Timechain.
220- *
221- * Requirements:
222- * - `from` and `to` cannot be the zero address.
223- * - `from` must have a balance of at least `value`.
224- * - `value` must be equal or greater than `MINIMAL_TELEPORT_VALUE`.
225- *
226- * Emits a {OutboundTransfer} event.
227- */
228- function _teleportFrom (address from , bytes32 to , uint256 value ) private returns (bytes32 messageID ) {
229- if (from == address (0 )) {
230- revert ERC20InvalidSender (address (0 ));
231- }
232- if (to == bytes32 (bytes20 (address (0 )))) {
233- revert ERC20InvalidReceiver (address (0 ));
234- }
235- require (value >= MINIMAL_TELEPORT_VALUE, "value below minimum required " );
236- _burn (from, value);
237- bytes memory message = abi.encode (OutboundTeleportCommand ({from: from, to: to, amount: value}));
238- messageID = GATEWAY.submitMessage {value: _msgValue ()}(
239- address (0 ), TIMECHAIN_ROUTE_ID, INBOUND_TRANSFER_GAS_LIMIT, message
240- );
241- emit OutboundTransfer (messageID, from, to, value);
242- }
243-
244- /**
245- * @dev Estimate the teleport cost in native tokens, the returned is the amount of ether to send to `teleport` method.
246- */
247- function estimateTeleportCost () public view returns (uint256 ) {
248- return GATEWAY.estimateMessageCost (TIMECHAIN_ROUTE_ID, TELEPORT_COMMAND_ENCODED_LEN, INBOUND_TRANSFER_GAS_LIMIT);
249- }
250-
251- /**
252- * @dev Handles the receipt of a single GMP message.
253- * The contract must verify the msg.sender, it must be the Gateway Contract address.
254- *
255- * @param id The global unique identifier of the message.
256- * @param network The unique identifier of the source chain who send the message
257- * @param payload The message payload with no specified format
258- * @return 32 byte result which will be stored together with GMP message
259- *
260- * * Requirements:
261- * - the caller must be the `GATEWAY` contract.
262- * - `network` must be the `TIMECHAIN_ROUTE_ID`.
263- * - `source` must be the `REMOTE_ADDRESS`.
264- * - `payload` must be the struct `InboundTeleportCommand` encoded.
265- *
266- * Emits a {InboundTransfer} event.
267- */
268- function onGmpReceived (bytes32 id , uint128 network , bytes32 , bytes calldata payload )
269- external
270- payable
271- returns (bytes32 )
272- {
273- // Check preconditions
274- require (msg .sender == address (GATEWAY), Unauthorized ());
275- require (network == TIMECHAIN_ROUTE_ID, Unauthorized ());
276-
277- // Decode the command
278- InboundTeleportCommand memory command = abi.decode (payload, (InboundTeleportCommand));
279-
280- // Mint the tokens to the recipient account
281- if (command.to != address (0 ) && command.amount > 0 ) {
282- _mint (command.to, command.amount);
283- }
284- emit InboundTransfer (id, command.from, command.to, command.amount);
285-
286- // Returns the current total supply as result, the result is included in the `GmpExecuted` event
287- // emitted by the gateway. It allows the Timechain to verify if the amount of tokens locked matches
288- // the total supply of this contract.
289- return bytes32 (totalSupply ());
290- }
29176}
0 commit comments