contract.rs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218
  1. use crate::msg::WrappedRegistryResponse;
  2. use cosmwasm_std::{
  3. coin,
  4. entry_point,
  5. to_binary,
  6. BankMsg,
  7. Binary,
  8. CanonicalAddr,
  9. Coin,
  10. CosmosMsg,
  11. Deps,
  12. DepsMut,
  13. Empty,
  14. Env,
  15. MessageInfo,
  16. Order,
  17. QueryRequest,
  18. Reply,
  19. Response,
  20. StdError,
  21. StdResult,
  22. SubMsg,
  23. Uint128,
  24. WasmMsg,
  25. WasmQuery,
  26. };
  27. use crate::{
  28. msg::{
  29. ExecuteMsg,
  30. InstantiateMsg,
  31. MigrateMsg,
  32. QueryMsg,
  33. },
  34. state::{
  35. bridge_contracts,
  36. bridge_contracts_read,
  37. bridge_deposit,
  38. config,
  39. config_read,
  40. receive_native,
  41. send_native,
  42. wrapped_asset,
  43. wrapped_asset_address,
  44. wrapped_asset_address_read,
  45. wrapped_asset_read,
  46. wrapped_asset_seq,
  47. wrapped_asset_seq_read,
  48. wrapped_transfer_tmp,
  49. Action,
  50. AssetMeta,
  51. ConfigInfo,
  52. RegisterChain,
  53. TokenBridgeMessage,
  54. TransferInfo,
  55. TransferState,
  56. UpgradeContract,
  57. },
  58. };
  59. use wormhole::{
  60. byte_utils::{
  61. extend_address_to_32,
  62. extend_string_to_32,
  63. get_string_from_32,
  64. ByteUtils,
  65. },
  66. error::ContractError,
  67. };
  68. use cw20_base::msg::{
  69. ExecuteMsg as TokenMsg,
  70. QueryMsg as TokenQuery,
  71. };
  72. use wormhole::msg::{
  73. ExecuteMsg as WormholeExecuteMsg,
  74. QueryMsg as WormholeQueryMsg,
  75. };
  76. use wormhole::state::{
  77. vaa_archive_add,
  78. vaa_archive_check,
  79. GovernancePacket,
  80. ParsedVAA,
  81. };
  82. use cw20::{
  83. BalanceResponse,
  84. TokenInfoResponse,
  85. };
  86. use cw20_wrapped::msg::{
  87. ExecuteMsg as WrappedMsg,
  88. InitHook,
  89. InstantiateMsg as WrappedInit,
  90. QueryMsg as WrappedQuery,
  91. WrappedAssetInfoResponse,
  92. };
  93. use terraswap::asset::{
  94. Asset,
  95. AssetInfo,
  96. };
  97. use sha3::{
  98. Digest,
  99. Keccak256,
  100. };
  101. use std::{
  102. cmp::{
  103. max,
  104. min,
  105. },
  106. str::FromStr,
  107. };
  108. type HumanAddr = String;
  109. // Chain ID of Terra
  110. const CHAIN_ID: u16 = 3;
  111. const WRAPPED_ASSET_UPDATING: &str = "updating";
  112. #[cfg_attr(not(feature = "library"), entry_point)]
  113. pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
  114. let bucket = wrapped_asset_address(deps.storage);
  115. let mut messages = vec![];
  116. for item in bucket.range(None, None, Order::Ascending) {
  117. let contract_address = item?.0;
  118. messages.push(CosmosMsg::Wasm(WasmMsg::Migrate {
  119. contract_addr: deps
  120. .api
  121. .addr_humanize(&contract_address.into())?
  122. .to_string(),
  123. new_code_id: 767,
  124. msg: to_binary(&MigrateMsg {})?,
  125. }));
  126. }
  127. let count = messages.len();
  128. Ok(Response::new()
  129. .add_messages(messages)
  130. .add_attribute("migrate", "upgrade cw20 wrappers")
  131. .add_attribute("count", count.to_string()))
  132. }
  133. #[cfg_attr(not(feature = "library"), entry_point)]
  134. pub fn instantiate(
  135. deps: DepsMut,
  136. _env: Env,
  137. _info: MessageInfo,
  138. msg: InstantiateMsg,
  139. ) -> StdResult<Response> {
  140. // Save general wormhole info
  141. let state = ConfigInfo {
  142. gov_chain: msg.gov_chain,
  143. gov_address: msg.gov_address.as_slice().to_vec(),
  144. wormhole_contract: msg.wormhole_contract,
  145. wrapped_asset_code_id: msg.wrapped_asset_code_id,
  146. };
  147. config(deps.storage).save(&state)?;
  148. Ok(Response::default())
  149. }
  150. // When CW20 transfers complete, we need to verify the actual amount that is being transferred out
  151. // of the bridge. This is to handle fee tokens where the amount expected to be transferred may be
  152. // less due to burns, fees, etc.
  153. #[cfg_attr(not(feature = "library"), entry_point)]
  154. pub fn reply(deps: DepsMut, env: Env, _msg: Reply) -> StdResult<Response> {
  155. let cfg = config_read(deps.storage).load()?;
  156. let state = wrapped_transfer_tmp(deps.storage).load()?;
  157. let mut info = TransferInfo::deserialize(&state.message)?;
  158. // Fetch CW20 Balance post-transfer.
  159. let new_balance: BalanceResponse = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
  160. contract_addr: state.token_address.clone(),
  161. msg: to_binary(&TokenQuery::Balance {
  162. address: env.contract.address.to_string(),
  163. })?,
  164. }))?;
  165. // Actual amount should be the difference in balance of the CW20 account in question to account
  166. // for fee tokens.
  167. let multiplier = Uint128::from_str(&state.multiplier)?;
  168. let real_amount = new_balance.balance - Uint128::from_str(&state.previous_balance)?;
  169. let real_amount = real_amount / multiplier;
  170. // If the fee is too large the user would receive nothing.
  171. if info.fee.1 > real_amount.u128() {
  172. return Err(StdError::generic_err("fee greater than sent amount"));
  173. }
  174. // Update Wormhole message to correct amount.
  175. info.amount.1 = real_amount.u128();
  176. let token_bridge_message = TokenBridgeMessage {
  177. action: Action::TRANSFER,
  178. payload: info.serialize(),
  179. };
  180. // Post Wormhole Message
  181. let message = CosmosMsg::Wasm(WasmMsg::Execute {
  182. contract_addr: cfg.wormhole_contract,
  183. funds: vec![],
  184. msg: to_binary(&WormholeExecuteMsg::PostMessage {
  185. message: Binary::from(token_bridge_message.serialize()),
  186. nonce: state.nonce,
  187. })?,
  188. });
  189. send_native(deps.storage, &state.token_canonical, info.amount.1.into())?;
  190. Ok(Response::default()
  191. .add_message(message)
  192. .add_attribute("action", "reply_handler"))
  193. }
  194. pub fn coins_after_tax(deps: DepsMut, coins: Vec<Coin>) -> StdResult<Vec<Coin>> {
  195. let mut res = vec![];
  196. for coin in coins {
  197. let asset = Asset {
  198. amount: coin.amount.clone(),
  199. info: AssetInfo::NativeToken {
  200. denom: coin.denom.clone(),
  201. },
  202. };
  203. res.push(asset.deduct_tax(&deps.querier)?);
  204. }
  205. Ok(res)
  206. }
  207. pub fn parse_vaa(deps: DepsMut, block_time: u64, data: &Binary) -> StdResult<ParsedVAA> {
  208. let cfg = config_read(deps.storage).load()?;
  209. let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
  210. contract_addr: cfg.wormhole_contract.clone(),
  211. msg: to_binary(&WormholeQueryMsg::VerifyVAA {
  212. vaa: data.clone(),
  213. block_time,
  214. })?,
  215. }))?;
  216. Ok(vaa)
  217. }
  218. #[cfg_attr(not(feature = "library"), entry_point)]
  219. pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
  220. match msg {
  221. ExecuteMsg::RegisterAssetHook { asset_id } => {
  222. handle_register_asset(deps, env, info, &asset_id.as_slice())
  223. }
  224. ExecuteMsg::InitiateTransfer {
  225. asset,
  226. recipient_chain,
  227. recipient,
  228. fee,
  229. nonce,
  230. } => handle_initiate_transfer(
  231. deps,
  232. env,
  233. info,
  234. asset,
  235. recipient_chain,
  236. recipient.as_slice().to_vec(),
  237. fee,
  238. nonce,
  239. ),
  240. ExecuteMsg::DepositTokens {} => deposit_tokens(deps, env, info),
  241. ExecuteMsg::WithdrawTokens { asset } => withdraw_tokens(deps, env, info, asset),
  242. ExecuteMsg::SubmitVaa { data } => submit_vaa(deps, env, info, &data),
  243. ExecuteMsg::CreateAssetMeta { asset_info, nonce } => {
  244. handle_create_asset_meta(deps, env, info, asset_info, nonce)
  245. }
  246. }
  247. }
  248. fn deposit_tokens(deps: DepsMut, _env: Env, info: MessageInfo) -> StdResult<Response> {
  249. for coin in info.funds {
  250. let deposit_key = format!("{}:{}", info.sender, coin.denom);
  251. bridge_deposit(deps.storage).update(
  252. deposit_key.as_bytes(),
  253. |amount: Option<Uint128>| -> StdResult<Uint128> {
  254. Ok(amount.unwrap_or(Uint128::new(0)) + coin.amount)
  255. },
  256. )?;
  257. }
  258. Ok(Response::new().add_attribute("action", "deposit_tokens"))
  259. }
  260. fn withdraw_tokens(
  261. deps: DepsMut,
  262. _env: Env,
  263. info: MessageInfo,
  264. data: AssetInfo,
  265. ) -> StdResult<Response> {
  266. let mut messages: Vec<CosmosMsg> = vec![];
  267. if let AssetInfo::NativeToken { denom } = data {
  268. let deposit_key = format!("{}:{}", info.sender, denom);
  269. let mut deposited_amount: u128 = 0;
  270. bridge_deposit(deps.storage).update(
  271. deposit_key.as_bytes(),
  272. |current: Option<Uint128>| match current {
  273. Some(v) => {
  274. deposited_amount = v.u128();
  275. Ok(Uint128::new(0))
  276. }
  277. None => Err(StdError::generic_err("no deposit found to withdraw")),
  278. },
  279. )?;
  280. messages.push(CosmosMsg::Bank(BankMsg::Send {
  281. to_address: info.sender.to_string(),
  282. amount: coins_after_tax(deps, vec![coin(deposited_amount, &denom)])?,
  283. }));
  284. }
  285. Ok(Response::new()
  286. .add_messages(messages)
  287. .add_attribute("action", "withdraw_tokens"))
  288. }
  289. /// Handle wrapped asset registration messages
  290. fn handle_register_asset(
  291. deps: DepsMut,
  292. _env: Env,
  293. info: MessageInfo,
  294. asset_id: &[u8],
  295. ) -> StdResult<Response> {
  296. let mut bucket = wrapped_asset(deps.storage);
  297. let result = bucket.load(asset_id);
  298. let result = result.map_err(|_| ContractError::RegistrationForbidden.std())?;
  299. if result != HumanAddr::from(WRAPPED_ASSET_UPDATING) {
  300. return ContractError::AssetAlreadyRegistered.std_err();
  301. }
  302. bucket.save(asset_id, &info.sender.to_string())?;
  303. let contract_address: CanonicalAddr = deps.api.addr_canonicalize(&info.sender.as_str())?;
  304. wrapped_asset_address(deps.storage).save(contract_address.as_slice(), &asset_id.to_vec())?;
  305. Ok(Response::new()
  306. .add_attribute("action", "register_asset")
  307. .add_attribute("asset_id", format!("{:?}", asset_id))
  308. .add_attribute("contract_addr", info.sender))
  309. }
  310. fn handle_attest_meta(
  311. deps: DepsMut,
  312. env: Env,
  313. emitter_chain: u16,
  314. emitter_address: Vec<u8>,
  315. sequence: u64,
  316. data: &Vec<u8>,
  317. ) -> StdResult<Response> {
  318. let meta = AssetMeta::deserialize(data)?;
  319. let expected_contract =
  320. bridge_contracts_read(deps.storage).load(&emitter_chain.to_be_bytes())?;
  321. // must be sent by a registered token bridge contract
  322. if expected_contract != emitter_address {
  323. return Err(StdError::generic_err("invalid emitter"));
  324. }
  325. if CHAIN_ID == meta.token_chain {
  326. return Err(StdError::generic_err(
  327. "this asset is native to this chain and should not be attested",
  328. ));
  329. }
  330. let cfg = config_read(deps.storage).load()?;
  331. let asset_id = build_asset_id(meta.token_chain, &meta.token_address.as_slice());
  332. // If a CW20 wrapped already exists and this message has a newer sequence ID
  333. // we allow updating the metadata. If not, we create a brand new token.
  334. let message = if let Ok(contract) = wrapped_asset_read(deps.storage).load(&asset_id) {
  335. // Prevent anyone from re-attesting with old VAAs.
  336. if sequence <= wrapped_asset_seq_read(deps.storage).load(&asset_id)? {
  337. return Err(StdError::generic_err(
  338. "this asset has already been attested",
  339. ));
  340. }
  341. CosmosMsg::Wasm(WasmMsg::Execute {
  342. contract_addr: contract,
  343. msg: to_binary(&WrappedMsg::UpdateMetadata {
  344. name: get_string_from_32(&meta.name),
  345. symbol: get_string_from_32(&meta.symbol),
  346. })?,
  347. funds: vec![],
  348. })
  349. } else {
  350. wrapped_asset(deps.storage).save(&asset_id, &HumanAddr::from(WRAPPED_ASSET_UPDATING))?;
  351. CosmosMsg::Wasm(WasmMsg::Instantiate {
  352. admin: Some(env.contract.address.clone().into_string()),
  353. code_id: cfg.wrapped_asset_code_id,
  354. msg: to_binary(&WrappedInit {
  355. name: get_string_from_32(&meta.name),
  356. symbol: get_string_from_32(&meta.symbol),
  357. asset_chain: meta.token_chain,
  358. asset_address: meta.token_address.to_vec().into(),
  359. decimals: min(meta.decimals, 8u8),
  360. mint: None,
  361. init_hook: Some(InitHook {
  362. contract_addr: env.contract.address.to_string(),
  363. msg: to_binary(&ExecuteMsg::RegisterAssetHook {
  364. asset_id: asset_id.to_vec().into(),
  365. })?,
  366. }),
  367. })?,
  368. funds: vec![],
  369. label: String::new(),
  370. })
  371. };
  372. wrapped_asset_seq(deps.storage).save(&asset_id, &sequence)?;
  373. Ok(Response::new().add_message(message))
  374. }
  375. fn handle_create_asset_meta(
  376. deps: DepsMut,
  377. env: Env,
  378. info: MessageInfo,
  379. asset_info: AssetInfo,
  380. nonce: u32,
  381. ) -> StdResult<Response> {
  382. match asset_info {
  383. AssetInfo::Token { contract_addr } => {
  384. handle_create_asset_meta_token(deps, env, info, contract_addr, nonce)
  385. }
  386. AssetInfo::NativeToken { ref denom } => {
  387. handle_create_asset_meta_native_token(deps, env, info, denom.clone(), nonce)
  388. }
  389. }
  390. }
  391. fn handle_create_asset_meta_token(
  392. deps: DepsMut,
  393. env: Env,
  394. info: MessageInfo,
  395. asset_address: HumanAddr,
  396. nonce: u32,
  397. ) -> StdResult<Response> {
  398. let cfg = config_read(deps.storage).load()?;
  399. let request = QueryRequest::Wasm(WasmQuery::Smart {
  400. contract_addr: asset_address.clone(),
  401. msg: to_binary(&TokenQuery::TokenInfo {})?,
  402. });
  403. let asset_canonical = deps.api.addr_canonicalize(&asset_address)?;
  404. let token_info: TokenInfoResponse = deps.querier.query(&request)?;
  405. let meta: AssetMeta = AssetMeta {
  406. token_chain: CHAIN_ID,
  407. token_address: extend_address_to_32(&asset_canonical),
  408. decimals: token_info.decimals,
  409. symbol: extend_string_to_32(&token_info.symbol),
  410. name: extend_string_to_32(&token_info.name),
  411. };
  412. let token_bridge_message = TokenBridgeMessage {
  413. action: Action::ATTEST_META,
  414. payload: meta.serialize().to_vec(),
  415. };
  416. Ok(Response::new()
  417. .add_message(CosmosMsg::Wasm(WasmMsg::Execute {
  418. contract_addr: cfg.wormhole_contract,
  419. msg: to_binary(&WormholeExecuteMsg::PostMessage {
  420. message: Binary::from(token_bridge_message.serialize()),
  421. nonce,
  422. })?,
  423. // forward coins sent to this message
  424. funds: coins_after_tax(deps, info.funds.clone())?,
  425. }))
  426. .add_attribute("meta.token_chain", CHAIN_ID.to_string())
  427. .add_attribute("meta.token", asset_address)
  428. .add_attribute("meta.nonce", nonce.to_string())
  429. .add_attribute("meta.block_time", env.block.time.seconds().to_string()))
  430. }
  431. fn handle_create_asset_meta_native_token(
  432. deps: DepsMut,
  433. env: Env,
  434. info: MessageInfo,
  435. denom: String,
  436. nonce: u32,
  437. ) -> StdResult<Response> {
  438. let cfg = config_read(deps.storage).load()?;
  439. let mut asset_id = extend_address_to_32(&build_native_id(&denom).into());
  440. asset_id[0] = 1;
  441. let symbol = format_native_denom_symbol(&denom);
  442. let meta: AssetMeta = AssetMeta {
  443. token_chain: CHAIN_ID,
  444. token_address: asset_id.clone(),
  445. decimals: 6,
  446. symbol: extend_string_to_32(&symbol),
  447. name: extend_string_to_32(&symbol),
  448. };
  449. let token_bridge_message = TokenBridgeMessage {
  450. action: Action::ATTEST_META,
  451. payload: meta.serialize().to_vec(),
  452. };
  453. Ok(Response::new()
  454. .add_message(CosmosMsg::Wasm(WasmMsg::Execute {
  455. contract_addr: cfg.wormhole_contract,
  456. msg: to_binary(&WormholeExecuteMsg::PostMessage {
  457. message: Binary::from(token_bridge_message.serialize()),
  458. nonce,
  459. })?,
  460. // forward coins sent to this message
  461. funds: coins_after_tax(deps, info.funds.clone())?,
  462. }))
  463. .add_attribute("meta.token_chain", CHAIN_ID.to_string())
  464. .add_attribute("meta.symbol", symbol)
  465. .add_attribute("meta.asset_id", hex::encode(asset_id))
  466. .add_attribute("meta.nonce", nonce.to_string())
  467. .add_attribute("meta.block_time", env.block.time.seconds().to_string()))
  468. }
  469. fn submit_vaa(
  470. mut deps: DepsMut,
  471. env: Env,
  472. info: MessageInfo,
  473. data: &Binary,
  474. ) -> StdResult<Response> {
  475. let state = config_read(deps.storage).load()?;
  476. let vaa = parse_vaa(deps.branch(), env.block.time.seconds(), data)?;
  477. let data = vaa.payload;
  478. if vaa_archive_check(deps.storage, vaa.hash.as_slice()) {
  479. return ContractError::VaaAlreadyExecuted.std_err();
  480. }
  481. vaa_archive_add(deps.storage, vaa.hash.as_slice())?;
  482. // check if vaa is from governance
  483. if state.gov_chain == vaa.emitter_chain && state.gov_address == vaa.emitter_address {
  484. return handle_governance_payload(deps, env, &data);
  485. }
  486. let message = TokenBridgeMessage::deserialize(&data)?;
  487. match message.action {
  488. Action::TRANSFER => handle_complete_transfer(
  489. deps,
  490. env,
  491. info,
  492. vaa.emitter_chain,
  493. vaa.emitter_address,
  494. &message.payload,
  495. ),
  496. Action::ATTEST_META => handle_attest_meta(
  497. deps,
  498. env,
  499. vaa.emitter_chain,
  500. vaa.emitter_address,
  501. vaa.sequence,
  502. &message.payload,
  503. ),
  504. _ => ContractError::InvalidVAAAction.std_err(),
  505. }
  506. }
  507. fn handle_governance_payload(deps: DepsMut, env: Env, data: &Vec<u8>) -> StdResult<Response> {
  508. let gov_packet = GovernancePacket::deserialize(&data)?;
  509. let module = get_string_from_32(&gov_packet.module);
  510. if module != "TokenBridge" {
  511. return Err(StdError::generic_err("this is not a valid module"));
  512. }
  513. if gov_packet.chain != 0 && gov_packet.chain != CHAIN_ID {
  514. return Err(StdError::generic_err(
  515. "the governance VAA is for another chain",
  516. ));
  517. }
  518. match gov_packet.action {
  519. 1u8 => handle_register_chain(deps, env, &gov_packet.payload),
  520. 2u8 => handle_upgrade_contract(deps, env, &gov_packet.payload),
  521. _ => ContractError::InvalidVAAAction.std_err(),
  522. }
  523. }
  524. fn handle_upgrade_contract(_deps: DepsMut, env: Env, data: &Vec<u8>) -> StdResult<Response> {
  525. let UpgradeContract { new_contract } = UpgradeContract::deserialize(&data)?;
  526. Ok(Response::new()
  527. .add_message(CosmosMsg::Wasm(WasmMsg::Migrate {
  528. contract_addr: env.contract.address.to_string(),
  529. new_code_id: new_contract,
  530. msg: to_binary(&MigrateMsg {})?,
  531. }))
  532. .add_attribute("action", "contract_upgrade"))
  533. }
  534. fn handle_register_chain(deps: DepsMut, _env: Env, data: &Vec<u8>) -> StdResult<Response> {
  535. let RegisterChain {
  536. chain_id,
  537. chain_address,
  538. } = RegisterChain::deserialize(&data)?;
  539. let existing = bridge_contracts_read(deps.storage).load(&chain_id.to_be_bytes());
  540. if existing.is_ok() {
  541. return Err(StdError::generic_err(
  542. "bridge contract already exists for this chain",
  543. ));
  544. }
  545. let mut bucket = bridge_contracts(deps.storage);
  546. bucket.save(&chain_id.to_be_bytes(), &chain_address)?;
  547. Ok(Response::new()
  548. .add_attribute("chain_id", chain_id.to_string())
  549. .add_attribute("chain_address", hex::encode(chain_address)))
  550. }
  551. fn handle_complete_transfer(
  552. deps: DepsMut,
  553. env: Env,
  554. info: MessageInfo,
  555. emitter_chain: u16,
  556. emitter_address: Vec<u8>,
  557. data: &Vec<u8>,
  558. ) -> StdResult<Response> {
  559. let transfer_info = TransferInfo::deserialize(&data)?;
  560. match transfer_info.token_address.as_slice()[0] {
  561. 1 => handle_complete_transfer_token_native(
  562. deps,
  563. env,
  564. info,
  565. emitter_chain,
  566. emitter_address,
  567. data,
  568. ),
  569. _ => handle_complete_transfer_token(deps, env, info, emitter_chain, emitter_address, data),
  570. }
  571. }
  572. fn handle_complete_transfer_token(
  573. deps: DepsMut,
  574. _env: Env,
  575. info: MessageInfo,
  576. emitter_chain: u16,
  577. emitter_address: Vec<u8>,
  578. data: &Vec<u8>,
  579. ) -> StdResult<Response> {
  580. let transfer_info = TransferInfo::deserialize(&data)?;
  581. let expected_contract =
  582. bridge_contracts_read(deps.storage).load(&emitter_chain.to_be_bytes())?;
  583. // must be sent by a registered token bridge contract
  584. if expected_contract != emitter_address {
  585. return Err(StdError::generic_err("invalid emitter"));
  586. }
  587. if transfer_info.recipient_chain != CHAIN_ID {
  588. return Err(StdError::generic_err(
  589. "this transfer is not directed at this chain",
  590. ));
  591. }
  592. let token_chain = transfer_info.token_chain;
  593. let target_address = (&transfer_info.recipient.as_slice()).get_address(0);
  594. let (not_supported_amount, mut amount) = transfer_info.amount;
  595. let (not_supported_fee, mut fee) = transfer_info.fee;
  596. amount = amount.checked_sub(fee).unwrap();
  597. // Check high 128 bit of amount value to be empty
  598. if not_supported_amount != 0 || not_supported_fee != 0 {
  599. return ContractError::AmountTooHigh.std_err();
  600. }
  601. if token_chain != CHAIN_ID {
  602. let asset_address = transfer_info.token_address;
  603. let asset_id = build_asset_id(token_chain, &asset_address);
  604. // Check if this asset is already deployed
  605. let contract_addr = wrapped_asset_read(deps.storage).load(&asset_id).ok();
  606. return if let Some(contract_addr) = contract_addr {
  607. // Asset already deployed, just mint
  608. let recipient = deps
  609. .api
  610. .addr_humanize(&target_address)
  611. .or_else(|_| ContractError::WrongTargetAddressFormat.std_err())?;
  612. let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute {
  613. contract_addr: contract_addr.clone(),
  614. msg: to_binary(&WrappedMsg::Mint {
  615. recipient: recipient.to_string(),
  616. amount: Uint128::from(amount),
  617. })?,
  618. funds: vec![],
  619. })];
  620. if fee != 0 {
  621. messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
  622. contract_addr: contract_addr.clone(),
  623. msg: to_binary(&WrappedMsg::Mint {
  624. recipient: info.sender.to_string(),
  625. amount: Uint128::from(fee),
  626. })?,
  627. funds: vec![],
  628. }))
  629. }
  630. Ok(Response::new()
  631. .add_messages(messages)
  632. .add_attribute("action", "complete_transfer_wrapped")
  633. .add_attribute("contract", contract_addr)
  634. .add_attribute("recipient", recipient)
  635. .add_attribute("amount", amount.to_string()))
  636. } else {
  637. Err(StdError::generic_err("Wrapped asset not deployed. To deploy, invoke CreateWrapped with the associated AssetMeta"))
  638. };
  639. } else {
  640. let token_address = transfer_info.token_address.as_slice().get_address(0);
  641. let recipient = deps.api.addr_humanize(&target_address)?;
  642. let contract_addr = deps.api.addr_humanize(&token_address)?;
  643. // note -- here the amount is the amount the recipient will receive;
  644. // amount + fee is the total sent
  645. receive_native(deps.storage, &token_address, Uint128::new(amount + fee))?;
  646. // undo normalization to 8 decimals
  647. let token_info: TokenInfoResponse =
  648. deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
  649. contract_addr: contract_addr.to_string(),
  650. msg: to_binary(&TokenQuery::TokenInfo {})?,
  651. }))?;
  652. let decimals = token_info.decimals;
  653. let multiplier = 10u128.pow((max(decimals, 8u8) - 8u8) as u32);
  654. amount = amount.checked_mul(multiplier).unwrap();
  655. fee = fee.checked_mul(multiplier).unwrap();
  656. let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute {
  657. contract_addr: contract_addr.to_string(),
  658. msg: to_binary(&TokenMsg::Transfer {
  659. recipient: recipient.to_string(),
  660. amount: Uint128::from(amount),
  661. })?,
  662. funds: vec![],
  663. })];
  664. if fee != 0 {
  665. messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
  666. contract_addr: contract_addr.to_string(),
  667. msg: to_binary(&TokenMsg::Transfer {
  668. recipient: info.sender.to_string(),
  669. amount: Uint128::from(fee),
  670. })?,
  671. funds: vec![],
  672. }))
  673. }
  674. Ok(Response::new()
  675. .add_messages(messages)
  676. .add_attribute("action", "complete_transfer_native")
  677. .add_attribute("recipient", recipient)
  678. .add_attribute("contract", contract_addr)
  679. .add_attribute("amount", amount.to_string()))
  680. }
  681. }
  682. fn handle_complete_transfer_token_native(
  683. mut deps: DepsMut,
  684. _env: Env,
  685. _info: MessageInfo,
  686. emitter_chain: u16,
  687. emitter_address: Vec<u8>,
  688. data: &Vec<u8>,
  689. ) -> StdResult<Response> {
  690. let transfer_info = TransferInfo::deserialize(&data)?;
  691. let expected_contract =
  692. bridge_contracts_read(deps.storage).load(&emitter_chain.to_be_bytes())?;
  693. // must be sent by a registered token bridge contract
  694. if expected_contract != emitter_address {
  695. return Err(StdError::generic_err("invalid emitter"));
  696. }
  697. if transfer_info.recipient_chain != CHAIN_ID {
  698. return Err(StdError::generic_err(
  699. "this transfer is not directed at this chain",
  700. ));
  701. }
  702. let target_address = (&transfer_info.recipient.as_slice()).get_address(0);
  703. let (not_supported_amount, mut amount) = transfer_info.amount;
  704. let (not_supported_fee, fee) = transfer_info.fee;
  705. amount = amount.checked_sub(fee).unwrap();
  706. // Check high 128 bit of amount value to be empty
  707. if not_supported_amount != 0 || not_supported_fee != 0 {
  708. return ContractError::AmountTooHigh.std_err();
  709. }
  710. // Wipe the native byte marker and extract the serialized denom.
  711. let mut token_address = transfer_info.token_address.clone();
  712. let token_address = token_address.as_mut_slice();
  713. token_address[0] = 0;
  714. let mut denom = token_address.to_vec();
  715. denom.retain(|&c| c != 0);
  716. let denom = String::from_utf8(denom).unwrap();
  717. // note -- here the amount is the amount the recipient will receive;
  718. // amount + fee is the total sent
  719. let recipient = deps.api.addr_humanize(&target_address)?;
  720. let token_address = (&*token_address).get_address(0);
  721. receive_native(deps.storage, &token_address, Uint128::new(amount + fee))?;
  722. let mut messages = vec![CosmosMsg::Bank(BankMsg::Send {
  723. to_address: recipient.to_string(),
  724. amount: coins_after_tax(deps.branch(), vec![coin(amount, &denom)])?,
  725. })];
  726. if fee != 0 {
  727. messages.push(CosmosMsg::Bank(BankMsg::Send {
  728. to_address: recipient.to_string(),
  729. amount: coins_after_tax(deps, vec![coin(fee, &denom)])?,
  730. }));
  731. }
  732. Ok(Response::new()
  733. .add_messages(messages)
  734. .add_attribute("action", "complete_transfer_terra_native")
  735. .add_attribute("recipient", recipient)
  736. .add_attribute("denom", denom)
  737. .add_attribute("amount", amount.to_string()))
  738. }
  739. fn handle_initiate_transfer(
  740. deps: DepsMut,
  741. env: Env,
  742. info: MessageInfo,
  743. asset: Asset,
  744. recipient_chain: u16,
  745. recipient: Vec<u8>,
  746. fee: Uint128,
  747. nonce: u32,
  748. ) -> StdResult<Response> {
  749. match asset.info {
  750. AssetInfo::Token { contract_addr } => handle_initiate_transfer_token(
  751. deps,
  752. env,
  753. info,
  754. contract_addr,
  755. asset.amount,
  756. recipient_chain,
  757. recipient,
  758. fee,
  759. nonce,
  760. ),
  761. AssetInfo::NativeToken { ref denom } => handle_initiate_transfer_native_token(
  762. deps,
  763. env,
  764. info,
  765. denom.clone(),
  766. asset.amount,
  767. recipient_chain,
  768. recipient,
  769. fee,
  770. nonce,
  771. ),
  772. }
  773. }
  774. fn handle_initiate_transfer_token(
  775. mut deps: DepsMut,
  776. env: Env,
  777. info: MessageInfo,
  778. asset: HumanAddr,
  779. mut amount: Uint128,
  780. recipient_chain: u16,
  781. recipient: Vec<u8>,
  782. mut fee: Uint128,
  783. nonce: u32,
  784. ) -> StdResult<Response> {
  785. if recipient_chain == CHAIN_ID {
  786. return ContractError::SameSourceAndTarget.std_err();
  787. }
  788. if amount.is_zero() {
  789. return ContractError::AmountTooLow.std_err();
  790. }
  791. let asset_chain: u16;
  792. let asset_address: Vec<u8>;
  793. let cfg: ConfigInfo = config_read(deps.storage).load()?;
  794. let asset_canonical: CanonicalAddr = deps.api.addr_canonicalize(&asset)?;
  795. let mut messages: Vec<CosmosMsg> = vec![];
  796. let mut submessages: Vec<SubMsg> = vec![];
  797. match wrapped_asset_address_read(deps.storage).load(asset_canonical.as_slice()) {
  798. Ok(_) => {
  799. // If the fee is too large the user will receive nothing.
  800. if fee > amount {
  801. return Err(StdError::generic_err("fee greater than sent amount"));
  802. }
  803. // This is a deployed wrapped asset, burn it
  804. messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
  805. contract_addr: asset.clone(),
  806. msg: to_binary(&WrappedMsg::Burn {
  807. account: info.sender.to_string(),
  808. amount,
  809. })?,
  810. funds: vec![],
  811. }));
  812. let request = QueryRequest::<Empty>::Wasm(WasmQuery::Smart {
  813. contract_addr: asset,
  814. msg: to_binary(&WrappedQuery::WrappedAssetInfo {})?,
  815. });
  816. let wrapped_token_info: WrappedAssetInfoResponse =
  817. deps.querier.custom_query(&request)?;
  818. asset_chain = wrapped_token_info.asset_chain;
  819. asset_address = wrapped_token_info.asset_address.as_slice().to_vec();
  820. let transfer_info = TransferInfo {
  821. token_chain: asset_chain,
  822. token_address: asset_address.clone(),
  823. amount: (0, amount.u128()),
  824. recipient_chain,
  825. recipient: recipient.clone(),
  826. fee: (0, fee.u128()),
  827. };
  828. let token_bridge_message = TokenBridgeMessage {
  829. action: Action::TRANSFER,
  830. payload: transfer_info.serialize(),
  831. };
  832. messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
  833. contract_addr: cfg.wormhole_contract,
  834. msg: to_binary(&WormholeExecuteMsg::PostMessage {
  835. message: Binary::from(token_bridge_message.serialize()),
  836. nonce,
  837. })?,
  838. // forward coins sent to this message
  839. funds: coins_after_tax(deps.branch(), info.funds.clone())?,
  840. }));
  841. }
  842. Err(_) => {
  843. // normalize amount to 8 decimals when it sent over the wormhole
  844. let token_info: TokenInfoResponse =
  845. deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
  846. contract_addr: asset.clone(),
  847. msg: to_binary(&TokenQuery::TokenInfo {})?,
  848. }))?;
  849. let decimals = token_info.decimals;
  850. let multiplier = 10u128.pow((max(decimals, 8u8) - 8u8) as u32);
  851. // chop off dust
  852. amount = Uint128::new(
  853. amount
  854. .u128()
  855. .checked_sub(amount.u128().checked_rem(multiplier).unwrap())
  856. .unwrap(),
  857. );
  858. fee = Uint128::new(
  859. fee.u128()
  860. .checked_sub(fee.u128().checked_rem(multiplier).unwrap())
  861. .unwrap(),
  862. );
  863. // This is a regular asset, transfer its balance
  864. submessages.push(SubMsg::reply_on_success(
  865. CosmosMsg::Wasm(WasmMsg::Execute {
  866. contract_addr: asset.clone(),
  867. msg: to_binary(&TokenMsg::TransferFrom {
  868. owner: info.sender.to_string(),
  869. recipient: env.contract.address.to_string(),
  870. amount,
  871. })?,
  872. funds: vec![],
  873. }),
  874. 1,
  875. ));
  876. asset_address = extend_address_to_32(&asset_canonical);
  877. asset_chain = CHAIN_ID;
  878. // convert to normalized amounts before recording & posting vaa
  879. amount = Uint128::new(amount.u128().checked_div(multiplier).unwrap());
  880. fee = Uint128::new(fee.u128().checked_div(multiplier).unwrap());
  881. let transfer_info = TransferInfo {
  882. token_chain: asset_chain,
  883. token_address: asset_address.clone(),
  884. amount: (0, amount.u128()),
  885. recipient_chain,
  886. recipient: recipient.clone(),
  887. fee: (0, fee.u128()),
  888. };
  889. // Fetch current CW20 Balance pre-transfer.
  890. let balance: BalanceResponse =
  891. deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
  892. contract_addr: asset.to_string(),
  893. msg: to_binary(&TokenQuery::Balance {
  894. address: env.contract.address.to_string(),
  895. })?,
  896. }))?;
  897. // Wrap up state to be captured by the submessage reply.
  898. wrapped_transfer_tmp(deps.storage).save(&TransferState {
  899. previous_balance: balance.balance.to_string(),
  900. account: info.sender.to_string(),
  901. token_address: asset,
  902. token_canonical: asset_canonical.clone(),
  903. message: transfer_info.serialize(),
  904. multiplier: Uint128::new(multiplier).to_string(),
  905. nonce,
  906. })?;
  907. }
  908. };
  909. Ok(Response::new()
  910. .add_messages(messages)
  911. .add_submessages(submessages)
  912. .add_attribute("transfer.token_chain", asset_chain.to_string())
  913. .add_attribute("transfer.token", hex::encode(asset_address))
  914. .add_attribute(
  915. "transfer.sender",
  916. hex::encode(extend_address_to_32(
  917. &deps.api.addr_canonicalize(&info.sender.as_str())?,
  918. )),
  919. )
  920. .add_attribute("transfer.recipient_chain", recipient_chain.to_string())
  921. .add_attribute("transfer.recipient", hex::encode(recipient))
  922. .add_attribute("transfer.amount", amount.to_string())
  923. .add_attribute("transfer.nonce", nonce.to_string())
  924. .add_attribute("transfer.block_time", env.block.time.seconds().to_string()))
  925. }
  926. /// All ISO-4217 currency codes are 3 letters, so we can safely slice anything that is not ULUNA.
  927. /// https://www.xe.com/iso4217.php
  928. fn format_native_denom_symbol(denom: &str) -> String {
  929. if denom == "uluna" {
  930. return "LUNA".to_string();
  931. }
  932. // UUSD -> US -> UST
  933. denom.to_uppercase()[1..3].to_string() + "T"
  934. }
  935. fn handle_initiate_transfer_native_token(
  936. deps: DepsMut,
  937. env: Env,
  938. info: MessageInfo,
  939. denom: String,
  940. amount: Uint128,
  941. recipient_chain: u16,
  942. recipient: Vec<u8>,
  943. fee: Uint128,
  944. nonce: u32,
  945. ) -> StdResult<Response> {
  946. if recipient_chain == CHAIN_ID {
  947. return ContractError::SameSourceAndTarget.std_err();
  948. }
  949. if amount.is_zero() {
  950. return ContractError::AmountTooLow.std_err();
  951. }
  952. if fee > amount {
  953. return Err(StdError::generic_err("fee greater than sent amount"));
  954. }
  955. let deposit_key = format!("{}:{}", info.sender, denom);
  956. bridge_deposit(deps.storage).update(deposit_key.as_bytes(), |current: Option<Uint128>| {
  957. match current {
  958. Some(v) => Ok(v.checked_sub(amount)?),
  959. None => Err(StdError::generic_err("no deposit found to transfer")),
  960. }
  961. })?;
  962. let cfg: ConfigInfo = config_read(deps.storage).load()?;
  963. let mut messages: Vec<CosmosMsg> = vec![];
  964. let asset_chain: u16 = CHAIN_ID;
  965. let mut asset_address: Vec<u8> = build_native_id(&denom);
  966. send_native(deps.storage, &asset_address[..].into(), amount)?;
  967. // Mark the first byte of the address to distinguish it as native.
  968. asset_address = extend_address_to_32(&asset_address.into());
  969. asset_address[0] = 1;
  970. let transfer_info = TransferInfo {
  971. token_chain: asset_chain,
  972. token_address: asset_address.to_vec(),
  973. amount: (0, amount.u128()),
  974. recipient_chain,
  975. recipient: recipient.clone(),
  976. fee: (0, fee.u128()),
  977. };
  978. let token_bridge_message = TokenBridgeMessage {
  979. action: Action::TRANSFER,
  980. payload: transfer_info.serialize(),
  981. };
  982. let sender = deps.api.addr_canonicalize(&info.sender.as_str())?;
  983. messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
  984. contract_addr: cfg.wormhole_contract,
  985. msg: to_binary(&WormholeExecuteMsg::PostMessage {
  986. message: Binary::from(token_bridge_message.serialize()),
  987. nonce,
  988. })?,
  989. funds: coins_after_tax(deps, info.funds.clone())?,
  990. }));
  991. Ok(Response::new()
  992. .add_messages(messages)
  993. .add_attribute("transfer.token_chain", asset_chain.to_string())
  994. .add_attribute("transfer.token", hex::encode(asset_address))
  995. .add_attribute(
  996. "transfer.sender",
  997. hex::encode(extend_address_to_32(&sender)),
  998. )
  999. .add_attribute("transfer.recipient_chain", recipient_chain.to_string())
  1000. .add_attribute("transfer.recipient", hex::encode(recipient))
  1001. .add_attribute("transfer.amount", amount.to_string())
  1002. .add_attribute("transfer.nonce", nonce.to_string())
  1003. .add_attribute("transfer.block_time", env.block.time.seconds().to_string()))
  1004. }
  1005. #[cfg_attr(not(feature = "library"), entry_point)]
  1006. pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
  1007. match msg {
  1008. QueryMsg::WrappedRegistry { chain, address } => {
  1009. to_binary(&query_wrapped_registry(deps, chain, address.as_slice())?)
  1010. }
  1011. }
  1012. }
  1013. pub fn query_wrapped_registry(
  1014. deps: Deps,
  1015. chain: u16,
  1016. address: &[u8],
  1017. ) -> StdResult<WrappedRegistryResponse> {
  1018. let asset_id = build_asset_id(chain, address);
  1019. // Check if this asset is already deployed
  1020. match wrapped_asset_read(deps.storage).load(&asset_id) {
  1021. Ok(address) => Ok(WrappedRegistryResponse { address }),
  1022. Err(_) => ContractError::AssetNotFound.std_err(),
  1023. }
  1024. }
  1025. fn build_asset_id(chain: u16, address: &[u8]) -> Vec<u8> {
  1026. let mut asset_id: Vec<u8> = vec![];
  1027. asset_id.extend_from_slice(&chain.to_be_bytes());
  1028. asset_id.extend_from_slice(address);
  1029. let mut hasher = Keccak256::new();
  1030. hasher.update(asset_id);
  1031. hasher.finalize().to_vec()
  1032. }
  1033. // Produce a 20 byte asset "address" from a native terra denom.
  1034. fn build_native_id(denom: &str) -> Vec<u8> {
  1035. let mut asset_address: Vec<u8> = denom.clone().as_bytes().to_vec();
  1036. asset_address.reverse();
  1037. asset_address.extend(vec![0u8; 20 - denom.len()]);
  1038. asset_address.reverse();
  1039. assert_eq!(asset_address.len(), 20);
  1040. asset_address
  1041. }
  1042. #[cfg(test)]
  1043. mod tests {
  1044. use cosmwasm_std::{
  1045. to_binary,
  1046. Binary,
  1047. StdResult,
  1048. };
  1049. #[test]
  1050. fn test_me() -> StdResult<()> {
  1051. let x = vec![
  1052. 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 96u8, 180u8, 94u8, 195u8, 0u8, 0u8,
  1053. 0u8, 1u8, 0u8, 3u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 38u8,
  1054. 229u8, 4u8, 215u8, 149u8, 163u8, 42u8, 54u8, 156u8, 236u8, 173u8, 168u8, 72u8, 220u8,
  1055. 100u8, 90u8, 154u8, 159u8, 160u8, 215u8, 0u8, 91u8, 48u8, 44u8, 48u8, 44u8, 51u8, 44u8,
  1056. 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8,
  1057. 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 53u8, 55u8, 44u8, 52u8,
  1058. 54u8, 44u8, 50u8, 53u8, 53u8, 44u8, 53u8, 48u8, 44u8, 50u8, 52u8, 51u8, 44u8, 49u8,
  1059. 48u8, 54u8, 44u8, 49u8, 50u8, 50u8, 44u8, 49u8, 49u8, 48u8, 44u8, 49u8, 50u8, 53u8,
  1060. 44u8, 56u8, 56u8, 44u8, 55u8, 51u8, 44u8, 49u8, 56u8, 57u8, 44u8, 50u8, 48u8, 55u8,
  1061. 44u8, 49u8, 48u8, 52u8, 44u8, 56u8, 51u8, 44u8, 49u8, 49u8, 57u8, 44u8, 49u8, 50u8,
  1062. 55u8, 44u8, 49u8, 57u8, 50u8, 44u8, 49u8, 52u8, 55u8, 44u8, 56u8, 57u8, 44u8, 48u8,
  1063. 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
  1064. 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
  1065. 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
  1066. 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
  1067. 44u8, 48u8, 44u8, 51u8, 44u8, 50u8, 51u8, 50u8, 44u8, 48u8, 44u8, 51u8, 44u8, 48u8,
  1068. 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8,
  1069. 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 48u8, 44u8, 53u8, 51u8, 44u8, 49u8, 49u8,
  1070. 54u8, 44u8, 52u8, 56u8, 44u8, 49u8, 49u8, 54u8, 44u8, 49u8, 52u8, 57u8, 44u8, 49u8,
  1071. 48u8, 56u8, 44u8, 49u8, 49u8, 51u8, 44u8, 56u8, 44u8, 48u8, 44u8, 50u8, 51u8, 50u8,
  1072. 44u8, 52u8, 57u8, 44u8, 49u8, 53u8, 50u8, 44u8, 49u8, 44u8, 50u8, 56u8, 44u8, 50u8,
  1073. 48u8, 51u8, 44u8, 50u8, 49u8, 50u8, 44u8, 50u8, 50u8, 49u8, 44u8, 50u8, 52u8, 49u8,
  1074. 44u8, 56u8, 53u8, 44u8, 49u8, 48u8, 57u8, 93u8,
  1075. ];
  1076. let b = Binary::from(x.clone());
  1077. let y = b.as_slice().to_vec();
  1078. assert_eq!(x, y);
  1079. Ok(())
  1080. }
  1081. }