create_wrapped.move 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. // SPDX-License-Identifier: Apache 2
  2. /// This module implements methods that create a specific coin type reflecting a
  3. /// wrapped (foreign) asset, whose metadata is encoded in a VAA sent from
  4. /// another network.
  5. ///
  6. /// Wrapped assets are created in two steps.
  7. /// 1. `prepare_registration`: This method creates a new `TreasuryCap` for a
  8. /// given coin type and wraps an encoded asset metadata VAA. We require a
  9. /// one-time witness (OTW) to throw an explicit error (even though it is
  10. /// redundant with what `create_currency` requires). This coin will
  11. /// be published using this method, meaning the `init` method in that
  12. /// untrusted package will have the asset's decimals hard-coded for its
  13. /// coin metadata. A `WrappedAssetSetup` object is transferred to the
  14. /// transaction sender.
  15. /// 2. `complete_registration`: This method destroys the `WrappedAssetSetup`
  16. /// object by unpacking its `TreasuryCap`, which will be warehoused in the
  17. /// `TokenRegistry`. The shared coin metadata object will be updated to
  18. /// reflect the contents of the encoded asset metadata payload.
  19. ///
  20. /// Wrapped asset metadata can also be updated with a new asset metadata VAA.
  21. /// By calling `update_attestation`, Token Bridge verifies that the specific
  22. /// coin type is registered and agrees with the encoded asset metadata's
  23. /// canonical token info. `ForeignInfo` and the coin's metadata will be updated
  24. /// based on the encoded asset metadata payload.
  25. ///
  26. /// See `state` and `wrapped_asset` modules for more details.
  27. ///
  28. /// References:
  29. /// https://examples.sui.io/basics/one-time-witness.html
  30. module token_bridge::create_wrapped {
  31. use std::ascii::{Self};
  32. use std::option::{Self};
  33. use std::type_name::{Self};
  34. use sui::coin::{Self, TreasuryCap, CoinMetadata};
  35. use sui::object::{Self, UID};
  36. use sui::package::{UpgradeCap};
  37. use sui::transfer::{Self};
  38. use sui::tx_context::{TxContext};
  39. use token_bridge::asset_meta::{Self};
  40. use token_bridge::normalized_amount::{max_decimals};
  41. use token_bridge::state::{Self, State};
  42. use token_bridge::token_registry::{Self};
  43. use token_bridge::vaa::{Self, TokenBridgeMessage};
  44. use token_bridge::wrapped_asset::{Self};
  45. #[test_only]
  46. use token_bridge::version_control::{Self, V__0_2_0 as V__CURRENT};
  47. /// Failed one-time witness verification.
  48. const E_BAD_WITNESS: u64 = 0;
  49. /// Coin witness does not equal "COIN".
  50. const E_INVALID_COIN_MODULE_NAME: u64 = 1;
  51. /// Decimals value exceeds `MAX_DECIMALS` from `normalized_amount`.
  52. const E_DECIMALS_EXCEED_WRAPPED_MAX: u64 = 2;
  53. /// A.K.A. "coin".
  54. const COIN_MODULE_NAME: vector<u8> = b"coin";
  55. /// Container holding new coin type's `TreasuryCap` and encoded asset metadata
  56. /// VAA, which are required to complete this asset's registration.
  57. struct WrappedAssetSetup<phantom CoinType, phantom Version> has key, store {
  58. id: UID,
  59. treasury_cap: TreasuryCap<CoinType>
  60. }
  61. /// This method is executed within the `init` method of an untrusted module,
  62. /// which defines a one-time witness (OTW) type (`CoinType`). OTW is
  63. /// required to ensure that only one `TreasuryCap` exists for `CoinType`. This
  64. /// is similar to how a `TreasuryCap` is created in `coin::create_currency`.
  65. ///
  66. /// Because this method is stateless (i.e. no dependency on Token Bridge's
  67. /// `State` object), the contract defers VAA verification to
  68. /// `complete_registration` after this method has been executed.
  69. public fun prepare_registration<CoinType: drop, Version>(
  70. witness: CoinType,
  71. decimals: u8,
  72. ctx: &mut TxContext
  73. ): WrappedAssetSetup<CoinType, Version> {
  74. let setup = prepare_registration_internal(witness, decimals, ctx);
  75. // Also make sure that this witness module name is literally "coin".
  76. let module_name = type_name::get_module(&type_name::get<CoinType>());
  77. assert!(
  78. ascii::into_bytes(module_name) == COIN_MODULE_NAME,
  79. E_INVALID_COIN_MODULE_NAME
  80. );
  81. setup
  82. }
  83. #[allow(lint(share_owned))]
  84. /// This function performs the bulk of `prepare_registration`, except
  85. /// checking the module name. This separation is useful for testing.
  86. fun prepare_registration_internal<CoinType: drop, Version>(
  87. witness: CoinType,
  88. decimals: u8,
  89. ctx: &mut TxContext
  90. ): WrappedAssetSetup<CoinType, Version> {
  91. // Make sure there's only one instance of the type `CoinType`. This
  92. // resembles the same check for `coin::create_currency`.
  93. // Technically this check is redundant as it's performed by
  94. // `coin::create_currency` below, but it doesn't hurt.
  95. assert!(sui::types::is_one_time_witness(&witness), E_BAD_WITNESS);
  96. // Ensure that the decimals passed into this method do not exceed max
  97. // decimals (see `normalized_amount` module).
  98. assert!(decimals <= max_decimals(), E_DECIMALS_EXCEED_WRAPPED_MAX);
  99. // We initialise the currency with empty metadata. Later on, in the
  100. // `complete_registration` call, when `CoinType` gets associated with a
  101. // VAA, we update these fields.
  102. let no_symbol = b"";
  103. let no_name = b"";
  104. let no_description = b"";
  105. let no_icon_url = option::none();
  106. let (treasury_cap, coin_meta) =
  107. coin::create_currency(
  108. witness,
  109. decimals,
  110. no_symbol,
  111. no_name,
  112. no_description,
  113. no_icon_url,
  114. ctx
  115. );
  116. // The CoinMetadata is turned into a shared object so that other
  117. // functions (and wallets) can easily grab references to it. This is
  118. // safe to do, as the metadata setters require a `TreasuryCap` for the
  119. // coin too, which is held by the token bridge.
  120. transfer::public_share_object(coin_meta);
  121. // Create `WrappedAssetSetup` object and transfer to transaction sender.
  122. // The owner of this object will call `complete_registration` to destroy
  123. // it.
  124. WrappedAssetSetup {
  125. id: object::new(ctx),
  126. treasury_cap
  127. }
  128. }
  129. /// After executing `prepare_registration`, owner of `WrappedAssetSetup`
  130. /// executes this method to complete this wrapped asset's registration.
  131. ///
  132. /// This method destroys `WrappedAssetSetup`, unpacking the `TreasuryCap` and
  133. /// encoded asset metadata VAA. The deserialized asset metadata VAA is used
  134. /// to update the associated `CoinMetadata`.
  135. public fun complete_registration<CoinType: drop, Version>(
  136. token_bridge_state: &mut State,
  137. coin_meta: &mut CoinMetadata<CoinType>,
  138. setup: WrappedAssetSetup<CoinType, Version>,
  139. coin_upgrade_cap: UpgradeCap,
  140. msg: TokenBridgeMessage
  141. ) {
  142. // This capability ensures that the current build version is used. This
  143. // call performs an additional check of whether `WrappedAssetSetup` was
  144. // created using the current package.
  145. let latest_only =
  146. state::assert_latest_only_specified<Version>(token_bridge_state);
  147. let WrappedAssetSetup {
  148. id,
  149. treasury_cap
  150. } = setup;
  151. // Finally destroy the object.
  152. object::delete(id);
  153. // Deserialize to `AssetMeta`.
  154. let token_meta = asset_meta::deserialize(vaa::take_payload(msg));
  155. // `register_wrapped_asset` uses `token_registry::add_new_wrapped`,
  156. // which will check whether the asset has already been registered and if
  157. // the token chain ID is not Sui's.
  158. //
  159. // If both of these conditions are met, `register_wrapped_asset` will
  160. // succeed and the new wrapped coin will be registered.
  161. token_registry::add_new_wrapped(
  162. state::borrow_mut_token_registry(&latest_only, token_bridge_state),
  163. token_meta,
  164. coin_meta,
  165. treasury_cap,
  166. coin_upgrade_cap
  167. );
  168. }
  169. /// For registered wrapped assets, we can update `ForeignInfo` for a
  170. /// given `CoinType` with a new asset meta VAA emitted from another network.
  171. public fun update_attestation<CoinType>(
  172. token_bridge_state: &mut State,
  173. coin_meta: &mut CoinMetadata<CoinType>,
  174. msg: TokenBridgeMessage
  175. ) {
  176. // This capability ensures that the current build version is used.
  177. let latest_only = state::assert_latest_only(token_bridge_state);
  178. // Deserialize to `AssetMeta`.
  179. let token_meta = asset_meta::deserialize(vaa::take_payload(msg));
  180. // This asset must exist in the registry.
  181. let registry =
  182. state::borrow_mut_token_registry(&latest_only, token_bridge_state);
  183. token_registry::assert_has<CoinType>(registry);
  184. // Now update wrapped.
  185. wrapped_asset::update_metadata(
  186. token_registry::borrow_mut_wrapped<CoinType>(registry),
  187. coin_meta,
  188. token_meta
  189. );
  190. }
  191. public fun incomplete_metadata<CoinType>(
  192. coin_meta: &CoinMetadata<CoinType>
  193. ): bool {
  194. use std::string::{bytes};
  195. use std::vector::{is_empty};
  196. (
  197. is_empty(ascii::as_bytes(&coin::get_symbol(coin_meta))) &&
  198. is_empty(bytes(&coin::get_name(coin_meta))) &&
  199. is_empty(bytes(&coin::get_description(coin_meta))) &&
  200. std::option::is_none(&coin::get_icon_url(coin_meta))
  201. )
  202. }
  203. #[test_only]
  204. public fun new_setup_test_only<CoinType: drop, Version: drop>(
  205. _version: Version,
  206. witness: CoinType,
  207. decimals: u8,
  208. ctx: &mut TxContext
  209. ): (WrappedAssetSetup<CoinType, Version>, UpgradeCap) {
  210. let setup =
  211. prepare_registration_internal(
  212. witness,
  213. decimals,
  214. ctx
  215. );
  216. let upgrade_cap =
  217. sui::package::test_publish(
  218. object::id_from_address(@token_bridge),
  219. ctx
  220. );
  221. (setup, upgrade_cap)
  222. }
  223. #[test_only]
  224. public fun new_setup_current<CoinType: drop>(
  225. witness: CoinType,
  226. decimals: u8,
  227. ctx: &mut TxContext
  228. ): (WrappedAssetSetup<CoinType, V__CURRENT>, UpgradeCap) {
  229. new_setup_test_only(
  230. version_control::current_version_test_only(),
  231. witness,
  232. decimals,
  233. ctx
  234. )
  235. }
  236. #[test_only]
  237. public fun take_treasury_cap<CoinType>(
  238. setup: WrappedAssetSetup<CoinType, V__CURRENT>
  239. ): TreasuryCap<CoinType> {
  240. let WrappedAssetSetup {
  241. id,
  242. treasury_cap
  243. } = setup;
  244. object::delete(id);
  245. treasury_cap
  246. }
  247. }
  248. #[test_only]
  249. module token_bridge::create_wrapped_tests {
  250. use sui::coin::{Self};
  251. use sui::test_scenario::{Self};
  252. use sui::test_utils::{Self};
  253. use sui::tx_context::{Self};
  254. use wormhole::wormhole_scenario::{parse_and_verify_vaa};
  255. use token_bridge::asset_meta::{Self};
  256. use token_bridge::coin_wrapped_12::{Self};
  257. use token_bridge::coin_wrapped_7::{Self};
  258. use token_bridge::create_wrapped::{Self};
  259. use token_bridge::state::{Self};
  260. use token_bridge::string_utils::{Self};
  261. use token_bridge::token_bridge_scenario::{
  262. register_dummy_emitter,
  263. return_state,
  264. set_up_wormhole_and_token_bridge,
  265. take_state,
  266. two_people
  267. };
  268. use token_bridge::token_registry::{Self};
  269. use token_bridge::vaa::{Self};
  270. use token_bridge::version_control::{V__0_2_0 as V__CURRENT};
  271. use token_bridge::wrapped_asset::{Self};
  272. struct NOT_A_WITNESS has drop {}
  273. struct CREATE_WRAPPED_TESTS has drop {}
  274. #[test]
  275. #[expected_failure(abort_code = create_wrapped::E_BAD_WITNESS)]
  276. fun test_cannot_prepare_registration_bad_witness() {
  277. let ctx = &mut tx_context::dummy();
  278. // You shall not pass!
  279. let wrapped_asset_setup =
  280. create_wrapped::prepare_registration<NOT_A_WITNESS, V__CURRENT>(
  281. NOT_A_WITNESS {},
  282. 3,
  283. ctx
  284. );
  285. // Clean up.
  286. test_utils::destroy(wrapped_asset_setup);
  287. abort 42
  288. }
  289. #[test]
  290. #[expected_failure(abort_code = create_wrapped::E_INVALID_COIN_MODULE_NAME)]
  291. fun test_cannot_prepare_registration_invalid_coin_module_name() {
  292. let ctx = &mut tx_context::dummy();
  293. // You shall not pass!
  294. let wrapped_asset_setup =
  295. create_wrapped::prepare_registration<
  296. CREATE_WRAPPED_TESTS,
  297. V__CURRENT
  298. >(
  299. CREATE_WRAPPED_TESTS {},
  300. 3,
  301. ctx
  302. );
  303. // Clean up.
  304. test_utils::destroy(wrapped_asset_setup);
  305. abort 42
  306. }
  307. #[test]
  308. fun test_complete_and_update_attestation() {
  309. let (caller, coin_deployer) = two_people();
  310. let my_scenario = test_scenario::begin(caller);
  311. let scenario = &mut my_scenario;
  312. // Set up contracts.
  313. let wormhole_fee = 350;
  314. set_up_wormhole_and_token_bridge(scenario, wormhole_fee);
  315. // Register foreign emitter on chain ID == 2.
  316. let expected_source_chain = 2;
  317. register_dummy_emitter(scenario, expected_source_chain);
  318. // Ignore effects. Make sure `coin_deployer` receives
  319. // `WrappedAssetSetup`.
  320. test_scenario::next_tx(scenario, coin_deployer);
  321. // Publish coin.
  322. let (
  323. wrapped_asset_setup,
  324. upgrade_cap
  325. ) =
  326. create_wrapped::new_setup_current(
  327. CREATE_WRAPPED_TESTS {},
  328. 8,
  329. test_scenario::ctx(scenario)
  330. );
  331. let token_bridge_state = take_state(scenario);
  332. let verified_vaa =
  333. parse_and_verify_vaa(scenario, coin_wrapped_12::encoded_vaa());
  334. let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa);
  335. let coin_meta = test_scenario::take_shared(scenario);
  336. create_wrapped::complete_registration(
  337. &mut token_bridge_state,
  338. &mut coin_meta,
  339. wrapped_asset_setup,
  340. upgrade_cap,
  341. msg
  342. );
  343. let (
  344. token_address,
  345. token_chain,
  346. native_decimals,
  347. symbol,
  348. name
  349. ) = asset_meta::unpack_test_only(coin_wrapped_12::token_meta());
  350. // Check registry.
  351. {
  352. let registry = state::borrow_token_registry(&token_bridge_state);
  353. let verified =
  354. token_registry::verified_asset<CREATE_WRAPPED_TESTS>(registry);
  355. assert!(token_registry::is_wrapped(&verified), 0);
  356. let asset =
  357. token_registry::borrow_wrapped<CREATE_WRAPPED_TESTS>(registry);
  358. assert!(wrapped_asset::total_supply(asset) == 0, 0);
  359. // Decimals are capped for this wrapped asset.
  360. assert!(coin::get_decimals(&coin_meta) == 8, 0);
  361. // Check metadata against asset metadata.
  362. let info = wrapped_asset::info(asset);
  363. assert!(wrapped_asset::token_chain(info) == token_chain, 0);
  364. assert!(wrapped_asset::token_address(info) == token_address, 0);
  365. assert!(
  366. wrapped_asset::native_decimals(info) == native_decimals,
  367. 0
  368. );
  369. assert!(coin::get_symbol(&coin_meta) == string_utils::to_ascii(&symbol), 0);
  370. assert!(coin::get_name(&coin_meta) == name, 0);
  371. };
  372. // Now update metadata.
  373. let verified_vaa =
  374. parse_and_verify_vaa(
  375. scenario,
  376. coin_wrapped_12::encoded_updated_vaa()
  377. );
  378. let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa);
  379. create_wrapped::update_attestation<CREATE_WRAPPED_TESTS>(
  380. &mut token_bridge_state,
  381. &mut coin_meta,
  382. msg
  383. );
  384. // Check updated name and symbol.
  385. let (
  386. _,
  387. _,
  388. _,
  389. new_symbol,
  390. new_name
  391. ) = asset_meta::unpack_test_only(coin_wrapped_12::updated_token_meta());
  392. assert!(symbol != new_symbol, 0);
  393. assert!(coin::get_symbol(&coin_meta) == string_utils::to_ascii(&new_symbol), 0);
  394. assert!(name != new_name, 0);
  395. assert!(coin::get_name(&coin_meta) == new_name, 0);
  396. test_scenario::return_shared(coin_meta);
  397. // Clean up.
  398. return_state(token_bridge_state);
  399. // Done.
  400. test_scenario::end(my_scenario);
  401. }
  402. #[test]
  403. #[expected_failure(abort_code = wrapped_asset::E_ASSET_META_MISMATCH)]
  404. fun test_cannot_update_attestation_wrong_canonical_info() {
  405. let (caller, coin_deployer) = two_people();
  406. let my_scenario = test_scenario::begin(caller);
  407. let scenario = &mut my_scenario;
  408. // Set up contracts.
  409. let wormhole_fee = 350;
  410. set_up_wormhole_and_token_bridge(scenario, wormhole_fee);
  411. // Register foreign emitter on chain ID == 2.
  412. let expected_source_chain = 2;
  413. register_dummy_emitter(scenario, expected_source_chain);
  414. // Ignore effects. Make sure `coin_deployer` receives
  415. // `WrappedAssetSetup`.
  416. test_scenario::next_tx(scenario, coin_deployer);
  417. // Publish coin.
  418. let (
  419. wrapped_asset_setup,
  420. upgrade_cap
  421. ) =
  422. create_wrapped::new_setup_current(
  423. CREATE_WRAPPED_TESTS {},
  424. 8,
  425. test_scenario::ctx(scenario)
  426. );
  427. let token_bridge_state = take_state(scenario);
  428. let verified_vaa =
  429. parse_and_verify_vaa(scenario, coin_wrapped_12::encoded_vaa());
  430. let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa);
  431. let coin_meta = test_scenario::take_shared(scenario);
  432. create_wrapped::complete_registration(
  433. &mut token_bridge_state,
  434. &mut coin_meta,
  435. wrapped_asset_setup,
  436. upgrade_cap,
  437. msg
  438. );
  439. // This VAA is for COIN_WRAPPED_7 metadata, which disagrees with
  440. // COIN_WRAPPED_12.
  441. let invalid_asset_meta_vaa = coin_wrapped_7::encoded_vaa();
  442. let verified_vaa =
  443. parse_and_verify_vaa(scenario, invalid_asset_meta_vaa);
  444. let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa);
  445. // You shall not pass!
  446. create_wrapped::update_attestation<CREATE_WRAPPED_TESTS>(
  447. &mut token_bridge_state,
  448. &mut coin_meta,
  449. msg
  450. );
  451. abort 42
  452. }
  453. #[test]
  454. #[expected_failure(abort_code = state::E_VERSION_MISMATCH)]
  455. fun test_cannot_complete_registration_version_mismatch() {
  456. let (caller, coin_deployer) = two_people();
  457. let my_scenario = test_scenario::begin(caller);
  458. let scenario = &mut my_scenario;
  459. // Set up contracts.
  460. let wormhole_fee = 350;
  461. set_up_wormhole_and_token_bridge(scenario, wormhole_fee);
  462. // Register foreign emitter on chain ID == 2.
  463. let expected_source_chain = 2;
  464. register_dummy_emitter(scenario, expected_source_chain);
  465. // Ignore effects. Make sure `coin_deployer` receives
  466. // `WrappedAssetSetup`.
  467. test_scenario::next_tx(scenario, coin_deployer);
  468. // Publish coin.
  469. let (
  470. wrapped_asset_setup,
  471. upgrade_cap
  472. ) =
  473. create_wrapped::new_setup_test_only(
  474. token_bridge::version_control::dummy(),
  475. CREATE_WRAPPED_TESTS {},
  476. 8,
  477. test_scenario::ctx(scenario)
  478. );
  479. let token_bridge_state = take_state(scenario);
  480. let verified_vaa =
  481. parse_and_verify_vaa(scenario, coin_wrapped_12::encoded_vaa());
  482. let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa);
  483. let coin_meta = test_scenario::take_shared(scenario);
  484. create_wrapped::complete_registration(
  485. &mut token_bridge_state,
  486. &mut coin_meta,
  487. wrapped_asset_setup,
  488. upgrade_cap,
  489. msg
  490. );
  491. abort 42
  492. }
  493. #[test]
  494. #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)]
  495. fun test_cannot_complete_registration_outdated_version() {
  496. let (caller, coin_deployer) = two_people();
  497. let my_scenario = test_scenario::begin(caller);
  498. let scenario = &mut my_scenario;
  499. // Set up contracts.
  500. let wormhole_fee = 350;
  501. set_up_wormhole_and_token_bridge(scenario, wormhole_fee);
  502. // Register foreign emitter on chain ID == 2.
  503. let expected_source_chain = 2;
  504. register_dummy_emitter(scenario, expected_source_chain);
  505. // Ignore effects. Make sure `coin_deployer` receives
  506. // `WrappedAssetSetup`.
  507. test_scenario::next_tx(scenario, coin_deployer);
  508. // Publish coin.
  509. let (
  510. wrapped_asset_setup,
  511. upgrade_cap
  512. ) =
  513. create_wrapped::new_setup_current(
  514. CREATE_WRAPPED_TESTS {},
  515. 8,
  516. test_scenario::ctx(scenario)
  517. );
  518. let token_bridge_state = take_state(scenario);
  519. let verified_vaa =
  520. parse_and_verify_vaa(scenario, coin_wrapped_12::encoded_vaa());
  521. let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa);
  522. let coin_meta = test_scenario::take_shared(scenario);
  523. // Conveniently roll version back.
  524. state::reverse_migrate_version(&mut token_bridge_state);
  525. // Simulate executing with an outdated build by upticking the minimum
  526. // required version for `publish_message` to something greater than
  527. // this build.
  528. state::migrate_version_test_only(
  529. &mut token_bridge_state,
  530. token_bridge::version_control::previous_version_test_only(),
  531. token_bridge::version_control::next_version()
  532. );
  533. // You shall not pass!
  534. create_wrapped::complete_registration(
  535. &mut token_bridge_state,
  536. &mut coin_meta,
  537. wrapped_asset_setup,
  538. upgrade_cap,
  539. msg
  540. );
  541. abort 42
  542. }
  543. }