Skip to content

Commit ce43b56

Browse files
authored
Add teleportation unit tests to test/AnlogTokenV2.t.sol (#15)
* add test_TeleportOut_Below_ED() * add test_TeleportOut_Low_Value() * add test_TeleportOut() (rough match 4 GmpCreated) save use analog-gmp from branch save test_TeleportOut * add test_TeleportIn() * enhance test_TeleportOut(): exact match
1 parent 1df0476 commit ce43b56

File tree

3 files changed

+150
-1
lines changed

3 files changed

+150
-1
lines changed

.gitmodules

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
[submodule "lib/analog-gmp"]
1111
path = lib/analog-gmp
1212
url = https://github.com/Analog-Labs/analog-gmp
13+
branch = ag/fix

lib/analog-gmp

test/AnlogTokenV2.t.sol

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,30 @@ import {Test, console} from "forge-std/Test.sol";
55
import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol";
66
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
77
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
8+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
89
import {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.
1328
contract 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

Comments
 (0)