@@ -5,12 +5,30 @@ import {Test, console} from "forge-std/Test.sol";
55import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol " ;
66import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol " ;
77import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol " ;
8+ import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
89import {AnlogTokenV2} from "../src/AnlogTokenV2.sol " ;
910
11+ import {
12+ IGateway,
13+ IExecutor,
14+ Gateway,
15+ Route,
16+ NetworkID,
17+ ERC1967 ,
18+ TssKey,
19+ GmpMessage,
20+ Signature
21+ } from "analog-gmp/src/Gateway.sol " ;
22+ import {GmpMessage, PrimitiveUtils, GmpSender, GmpStatus} from "analog-gmp/src/Primitives.sol " ;
23+ import {TestUtils, SigningKey, SigningUtils} from "analog-gmp/test/TestUtils.sol " ;
24+
1025/// @notice OZ ERC20 and its presets are covered with Hardhat tests.
1126/// Hence we keep these few basic tests here more as a boilerplate for
1227/// the future tests for custom added fetaures.
1328contract AnlogTokenV2Test is Test {
29+ using PrimitiveUtils for GmpMessage;
30+ using SigningUtils for SigningKey;
31+
1432 AnlogTokenV2 public token;
1533
1634 address constant MINTER = address (0 );
@@ -21,13 +39,23 @@ contract AnlogTokenV2Test is Test {
2139
2240 // Teleport-related
2341 address constant GATEWAY = 0xEb73D0D236DE8F8D09dc6A52916e5849ff1E8dfA ;
42+ // ERC-1967 storage slot for admin address:
43+ // 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
44+ // Can be queried with
45+ // cast storage 0xEb73D0D236DE8F8D09dc6A52916e5849ff1E8dfA 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103 -r $SEPOLIA_RPC_URL
46+ address constant GW_ADMIN = 0x38a78edA59AC73A95281Cb009A5EF986e320509F ;
2447 uint16 constant TIMECHAIN_ID = 1000 ;
2548 uint256 constant MIN_TELEPORT_VAL = 1000000000000 ;
2649
2750 // fork testing
2851 string SEPOLIA_RPC_URL = vm.envString ("SEPOLIA_RPC_URL " );
2952 uint256 sepoliaFork;
3053
54+ // Chronicle TSS Secret
55+ uint256 private constant SECRET = 0x42 ;
56+ address private constant SIGNER_ADDRESS = 0x2e234DAe75C793f67A35089C9d99245E1C58470b ;
57+ uint256 private constant SIGNING_NONCE = 0x69 ;
58+
3159 /// @notice deploys an UUPS proxy.
3260 /// Here we start with the V1 implementation right away.
3361 /// For V0->V1 upgrade see another test.
@@ -46,6 +74,29 @@ contract AnlogTokenV2Test is Test {
4674 token = AnlogTokenV2 (proxy);
4775 }
4876
77+ modifier setRoute () {
78+ Route memory route = Route (NetworkID.wrap (1000 ), 15_000_000 , 0 , bytes32 (bytes20 (address (42 ))), 1 , 1 );
79+ address payable gw = payable (GATEWAY);
80+
81+ vm.prank (GW_ADMIN);
82+ Gateway (gw).setRoute (route);
83+
84+ _;
85+ }
86+
87+ modifier setShard () {
88+ Route memory route = Route (NetworkID.wrap (1000 ), 15_000_000 , 0 , bytes32 (bytes20 (address (42 ))), 1 , 1 );
89+ address payable gw = payable (GATEWAY);
90+
91+ SigningKey memory signer = TestUtils.createSigner (SECRET);
92+ TssKey memory shardKey = TssKey ({yParity: signer.yParity () == 28 ? 3 : 2 , xCoord: signer.xCoord ()});
93+
94+ vm.prank (GW_ADMIN);
95+ Gateway (gw).setShard (shardKey);
96+
97+ _;
98+ }
99+
49100 modifier preMint (address to , uint256 amount ) {
50101 assertEq (token.totalSupply (), 0 );
51102 vm.prank (MINTER);
@@ -61,6 +112,13 @@ contract AnlogTokenV2Test is Test {
61112 _;
62113 }
63114
115+ function sign (GmpMessage memory gmp ) internal pure returns (Signature memory ) {
116+ bytes32 hash = gmp.eip712hash ();
117+ SigningKey memory signer = TestUtils.createSigner (SECRET);
118+ (uint256 e , uint256 s ) = signer.signPrehashed (hash, SIGNING_NONCE);
119+ return Signature ({xCoord: signer.xCoord (), e: e, s: s});
120+ }
121+
64122 function test_name_and_ticker () public view {
65123 assertEq (token.name (), "Wrapped Analog One Token " );
66124 assertEq (token.symbol (), "WANLOG " );
@@ -160,4 +218,94 @@ contract AnlogTokenV2Test is Test {
160218 );
161219 token.unpause ();
162220 }
221+
222+ function test_TeleportOut_Below_ED () public preMint (address (this ), MIN_TELEPORT_VAL - 1 ) {
223+ bytes32 dest = bytes32 (bytes20 (UPGRADER));
224+ vm.expectRevert ("value below minimum required " );
225+ token.teleport (dest, MIN_TELEPORT_VAL - 1 );
226+ }
227+
228+ function test_TeleportOut_Low_Value () public preMint (address (this ), MIN_TELEPORT_VAL) setRoute {
229+ bytes32 dest = bytes32 (bytes20 (uint160 (UPGRADER)));
230+
231+ vm.expectEmit (address (token));
232+ emit IERC20 .Transfer (address (this ), address (0 ), MIN_TELEPORT_VAL);
233+ vm.expectRevert ("insufficient tx value " );
234+ token.teleport (dest, MIN_TELEPORT_VAL);
235+ }
236+
237+ function test_TeleportOut () public preMint (address (this ), MIN_TELEPORT_VAL) setRoute {
238+ address payable gw = payable (GATEWAY);
239+ bytes32 dest = bytes32 (uint256 (uint160 (UPGRADER)));
240+ uint256 cost = token.estimateTeleportCost ();
241+
242+ GmpSender source = GmpSender.wrap (bytes32 (uint256 (uint160 (SIGNER_ADDRESS))));
243+
244+ AnlogTokenV2.OutboundTeleportCommand memory command =
245+ AnlogTokenV2.OutboundTeleportCommand (address (this ), dest, MIN_TELEPORT_VAL);
246+
247+ GmpMessage memory gmp = GmpMessage ({
248+ source: source,
249+ srcNetwork: Gateway (gw).networkId (),
250+ dest: address (0 ),
251+ destNetwork: TIMECHAIN_ID,
252+ gasLimit: 100_000 ,
253+ nonce: 0 ,
254+ data: abi.encode (command)
255+ });
256+
257+ bytes32 messageID = gmp.eip712hash ();
258+
259+ vm.expectEmit (address (token));
260+ emit IERC20 .Transfer (address (this ), address (0 ), MIN_TELEPORT_VAL);
261+
262+ vm.expectEmit (true , true , true , true , address (GATEWAY));
263+ emit IGateway.GmpCreated (
264+ messageID,
265+ GmpSender.unwrap (gmp.source),
266+ gmp.dest,
267+ gmp.destNetwork,
268+ gmp.gasLimit,
269+ 179835 ,
270+ gmp.nonce,
271+ gmp.data
272+ );
273+
274+ vm.expectEmit (true , true , true , true , address (token));
275+ emit AnlogTokenV2.OutboundTransfer (messageID, address (this ), dest, MIN_TELEPORT_VAL);
276+
277+ token.teleport {value: cost}(dest, MIN_TELEPORT_VAL);
278+ }
279+
280+ function test_TeleportIn () public setRoute setShard {
281+ address payable gw = payable (GATEWAY);
282+ GmpSender source = GmpSender.wrap (bytes32 (uint256 (uint160 (0 ))));
283+
284+ AnlogTokenV2.InboundTeleportCommand memory command =
285+ AnlogTokenV2.InboundTeleportCommand (GmpSender.unwrap (source), UPGRADER, MIN_TELEPORT_VAL);
286+
287+ GmpMessage memory gmp = GmpMessage ({
288+ source: source,
289+ srcNetwork: TIMECHAIN_ID,
290+ dest: address (token),
291+ destNetwork: Gateway (gw).networkId (),
292+ gasLimit: 100_000 ,
293+ nonce: 0 ,
294+ data: abi.encode (command)
295+ });
296+
297+ assertEq (token.totalSupply (), 0 );
298+
299+ Signature memory sig = sign (gmp);
300+ bytes32 messageID = gmp.eip712hash ();
301+
302+ vm.expectEmit (true , true , true , true , GATEWAY);
303+ emit IExecutor.GmpExecuted (messageID, gmp.source, gmp.dest, GmpStatus.SUCCESS, bytes32 (MIN_TELEPORT_VAL));
304+
305+ Gateway (gw).execute (sig, gmp);
306+ assertTrue (Gateway (gw).gmpInfo (messageID).status == GmpStatus.SUCCESS, "failed to execute GMP message " );
307+
308+ assertEq (token.balanceOf (UPGRADER), MIN_TELEPORT_VAL);
309+ assertEq (token.totalSupply (), MIN_TELEPORT_VAL);
310+ }
163311}
0 commit comments