1"use strict";(self.webpackChunkcosmwasm_documentation=self.webpackChunkcosmwasm_documentation||[]).push([["2341"],{7255(e,n,t){t.r(n),t.d(n,{metadata:()=>s,default:()=>h,frontMatter:()=>d,contentTitle:()=>c,toc:()=>u,assets:()=>l});var s=JSON.parse('{"id":"tutorial/platform/cross-module","title":"First cross-module integration","description":"Send a message to another Cosmos module, break the CosmWasm barrier.","source":"@site/docs/tutorial/platform/15-cross-module.md","sourceDirName":"tutorial/platform","slug":"/tutorial/platform/cross-module","permalink":"/tutorial/platform/cross-module","draft":false,"unlisted":false,"tags":[],"version":"current","lastUpdatedAt":1763724834000,"sidebarPosition":15,"frontMatter":{"title":"First cross-module integration","description":"Send a message to another Cosmos module, break the CosmWasm barrier."},"sidebar":"tutorialSidebar","previous":{"title":"First contract reply integration","permalink":"/tutorial/platform/contract-reply"},"next":{"title":"Proper funds handling","permalink":"/tutorial/platform/fund-handling"}}'),a=t(4848),r=t(8453),o=t(773),i=t(7250);let d={title:"First cross-module integration",description:"Send a message to another Cosmos module, break the CosmWasm barrier."},c="First cross-module integration",l={},u=[{value:"The use-case",id:"the-use-case",level:2},{value:"Add a storage library",id:"add-a-storage-library",level:2},{value:"New elements",id:"new-elements",level:2},{value:"Forward funds in <code>execute</code>",id:"forward-funds-in-execute",level:2},{value:"Update the unit tests",id:"update-the-unit-tests",level:2},{value:"Update the mocked-app tests",id:"update-the-mocked-app-tests",level:2},{value:"Add a mocked app test with tokens",id:"add-a-mocked-app-test-with-tokens",level:2},{value:"Conclusion",id:"conclusion",level:2}];function m(e){let n={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",ul:"ul",...(0,r.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"first-cross-module-integration",children:"First cross-module integration"})}),"\n",(0,a.jsxs)(n.p,{children:["In the previous sections, you made your ",(0,a.jsx)(n.em,{children:"collection manager"})," smart contract sends funds onwards\nto the ",(0,a.jsx)(n.em,{children:"NFT collection"})," smart contract. That's convenient from the point of view of the manager,\nalthough this kicks the can down to the NFT collection."]}),"\n",(0,a.jsx)(n.p,{children:":::info Exercise progression"}),"\n",(0,a.jsx)(n.p,{children:"If you skipped the previous section, you can just switch:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["The ",(0,a.jsx)(n.code,{children:"my-nameservice"})," project to its ",(0,a.jsx)(n.a,{href:"https://github.com/b9lab/cw-my-nameservice/tree/execute-return-data",children:(0,a.jsx)(n.code,{children:"execute-return-data"})})," branch."]}),"\n",(0,a.jsxs)(n.li,{children:["The ",(0,a.jsx)(n.code,{children:"my-collection-manager"})," project to its ",(0,a.jsx)(n.a,{href:"https://github.com/b9lab/cw-my-collection-manager/tree/reply-from-execute",children:(0,a.jsx)(n.code,{children:"reply-from-execute"})})," branch."]}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"And take it from there."}),"\n",(0,a.jsx)(n.p,{children:":::"}),"\n",(0,a.jsx)(n.h2,{id:"the-use-case",children:"The use-case"}),"\n",(0,a.jsx)(n.p,{children:"You foresee your collection manager smart contract becoming a marketplace where owners sell their registered names.\nFor this to happen, your smart contract needs to handle funds properly and therefore to be able to send appropriate\nmessages to the app-chain's bank module."}),"\n",(0,a.jsx)(n.p,{children:"As a first step, you change your manager contract so that it sends the received funds to a beneficiary address,\ninstead of sending them to the NFT collection."}),"\n",(0,a.jsx)(n.h2,{id:"add-a-storage-library",children:"Add a storage library"}),"\n",(0,a.jsxs)(n.p,{children:["The address of the beneficiary of these funds is information that needs to be stored in storage\nand so set at instantiation. Add the ",(0,a.jsx)(n.code,{children:"cw-storage-plus"})," library:"]}),"\n",(0,a.jsxs)(o.A,{groupId:"local-docker",children:[(0,a.jsx)(i.A,{value:"Local",default:!0,children:(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-shell",children:"cargo add [email protected]\n"})})}),(0,a.jsx)(i.A,{value:"Docker",children:(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-shell",children:"docker run --rm -it \\\n -v $(pwd):/root/ -w /root \\\n rust:1.80.1 \\\n cargo add [email protected]\n"})})})]}),"\n",(0,a.jsx)(n.h2,{id:"new-elements",children:"New elements"}),"\n",(0,a.jsxs)(n.p,{children:["The beneficiary is an address that is valid for the whole contract.\nAs you keep an eye on future expansion, it is
1worth making a small structure.\nUpdate ",(0,a.jsx)(n.code,{children:"src/msg.rs"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",metastring:'title="src/msg.rs"',children:" use cosmwasm_schema::cw_serde;\n// diff-del\n- use cosmwasm_std::Empty;\n// diff-add\n+ use cosmwasm_std::{Addr, Empty};\n use cw721::msg::{Cw721ExecuteMsg, Cw721QueryMsg};\n\n #[cw_serde]\n pub struct InstantiateMsg {\n// diff-add\n+ pub payment_params: PaymentParams,\n }\n\n// diff-add-start\n+ #[cw_serde]\n+ pub struct PaymentParams {\n+ pub beneficiary: Addr,\n+ }\n+\n// diff-add-end\n pub type CollectionExecuteMsg = Cw721ExecuteMsg<Option<Empty>, Option<Empty>, Empty>;\n ...\n"})}),"\n",(0,a.jsxs)(n.p,{children:["And define its storage location. Create a new ",(0,a.jsx)(n.code,{children:"src/state.rs"})," with a single stored item in it:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",metastring:'title="src/state.rs"',children:'use cw_storage_plus::Item;\n\nuse crate::msg::PaymentParams;\n\npub const PAYMENT_PARAMS: Item<PaymentParams> = Item::new("payment_params");\n'})}),"\n",(0,a.jsx)(n.p,{children:"Then tie it back into the library:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",metastring:'title="src/lib.rs"',children:" pub mod contract;\n mod error;\n pub mod msg;\n// diff-add\n+ mod state;\n"})}),"\n",(0,a.jsxs)(n.p,{children:["And of course, update your ",(0,a.jsx)(n.code,{children:"instantiate"})," method to use it:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",metastring:'title="src/contract.rs"',children:" use crate::{\n ...\n state::PAYMENT_PARAMS,\n }\n ...\n pub fn instantiate(\n// diff-del\n- _deps_: DepsMut,\n// diff-add\n+ deps: DepsMut,\n _: Env,\n _: MessageInfo,\n msg: InstantiateMsg,\n ) -> ContractResult {\n// diff-add\n+ PAYMENT_PARAMS.save(deps.storage, &msg.payment_params)?;\n Ok(Response::default())\n }\n"})}),"\n",(0,a.jsxs)(n.h2,{id:"forward-funds-in-execute",children:["Forward funds in ",(0,a.jsx)(n.code,{children:"execute"})]}),"\n",(0,a.jsxs)(n.p,{children:["With the straightforward stuff taken care of, it is time to properly handle fund forwarding in the ",(0,a.jsx)(n.code,{children:"execute"})," method:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",metastring:'title="src/contract.rs"',children:" ...\n use cosmwasm_std::{\n// diff-del-start\n- from_json, to_json_binary, CosmosMsg, DepsMut, Empty, Env, Event, MessageInfo, QueryRequest,\n- Reply, ReplyOn, Response, StdError, SubMsg, WasmMsg, WasmQuery,\n// diff-del-end\n// diff-add-start\n+ from_json, to_json_binary, BankMsg, CosmosMsg, DepsMut, Empty, Env, Event, MessageInfo,\n+ QueryRequest, Reply, ReplyOn, Response, StdError, SubMsg, WasmMsg, WasmQuery,\n// diff-add-end\n }\n ...\n fn execute_pass_through(\n ...\n ) -> ContractResult {\n// diff-add-start\n+ let response = Response::default();\n+ let response = if !info.funds.is_empty() {\n+ let payment_params = PAYMENT_PARAMS.load(deps.storage)?;\n+ let forward_funds_msg = BankMsg::Send {\n+ to_address: payment_params.beneficiary.to_string(),\n+ amount: info.funds,\n+ };\n+ response.add_message(forward_funds_msg)\n+ } else {\n+ response\n+ };\n// diff-add-end\n let onward_exec_msg = WasmMsg::Execute {\n contract_addr: collection.to_owned(),\n msg: to_json_binary(&message)?,\n// diff-del\n- funds: info.funds,\n// diff-add\n+ funds: vec![],\n };\n ...\n// diff-del\n- Ok(Response::default()\n// diff-add\n+ Ok(response\n .add_submessage(onward_sub_msg)\n .add_event(token_count_event))\n }\n"})}),"\n",(0,a.jsx)(n.p,{children:"Note how:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["The ",(0,a.jsx)(n.code,{children:"WasmMsg::Execute"})," message now no longer forwards any funds to the NFT collection, but otherwise stays the same."]}),"\n",(0,a.jsx)(n.li,{children:"It only loads the parameters from storage if there are funds to forward. This is to reduce unnecessary gas costs."}),"\n",(0,a.jsxs)(n.li,{children:["Sending a ",(0,a.jsx)(n.a,{href:"https://github.com/CosmWasm/cosmwasm/blob/v1.5.8/packages/std/src/results/cosmos_msg.rs#L56",children:(0,a.jsx)(n.code,{children:"BankMsg"})}),"\nis how you talk to the bank module."]}),"\n",(0,a.jsxs)(n.li,{children:["The ",(0,a.jsx)(n.code,{children:".add_message"})," takes an ",(0,a.jsx)(n.a,{href:"https://github.com/CosmWasm/cosmwasm/blob/v1.5.8/packages/std/src/results/response.rs#L114",children:(0,a.jsx)(n.code,{children:"msg: impl Into<CosmosMsg<T>>"})}),"."]}),"\n",(0,a.jsxs)(n.li,{children:["Thankfully, ",(0,a.jsx)(n.code,{children:"CosmosMsg"}),"'s ",(0,a.jsxs)(n.a,{href:"https://github.com/CosmWasm/cosmwasm/blob/v1.5.8/packages/std/src/results/cosmos_msg.rs#L363-L367",children:["implements ",(0,a.jsx)(n.code,{children:"From<BankMsg>"})]}),",\nso the ",(0,a.jsx)(n.code,{children:"BankMsg"})," is converted into a",(0,a.jsx)(n.a,{href:"https://github.com/CosmWasm/cosmwasm/blob/v1.5.8/packages/std/src/results/cosmos_msg.rs#L28",children:(0,a.jsx)(n.code,{children:"CosmosMsg::Bank(BankMsg)"})}),"."]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"update-the-unit-tests",children:"Update the unit tests"}),"\n",(0,a.jsx)(n.p,{children:"Now that there needs to be something in storage, it is better to call instantiate as part of the tests,\nand to update the expectations on the response:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",metastring:'title="src/contract.rs"',children:' ...\n mod tests {\n use crate::{\n ...\n msg::{\n// diff-del\n- CollectionExecuteMsg, CollectionQueryMsg, ExecuteMsg, NameServiceExecuteMsgResponse,\n// diff-add-start\n+ CollectionExecuteMsg, CollectionQueryMsg, ExecuteMsg, InstantiateMsg,\n+ NameServiceExecuteMsgResponse, msg::PaymentParam
1s,\n// diff-add-end\n },\n }\n use cosmwasm_std::{\n from_json,\n testing::{self, MockApi, MockQuerier, MockStorage},\n// diff-del-start\n- to_json_binary, Addr, Binary, Coin, ContractResult, CosmosMsg, Empty, Event, OwnedDeps,\n- Querier, QuerierResult, QueryRequest, Reply, ReplyOn, Response, SubMsg, SubMsgResponse,\n- SubMsgResult, SystemError, SystemResult, Uint128, WasmMsg, WasmQuery,\n// diff-del-end\n// diff-add-start\n+ to_json_binary, Addr, BankMsg, Binary, Coin, ContractResult, CosmosMsg, Empty, Event,\n+ OwnedDeps, Querier, QuerierResult, QueryRequest, Reply, ReplyOn, Response, SubMsg,\n+ SubMsgResponse, SubMsgResult, SystemError, SystemResult, Uint128, WasmMsg, WasmQuery,\n// diff-add-end\n }\n ...\n fn test_pass_through() {\n ...\n let mocked_env = testing::mock_env();\n// diff-add-start\n+ let deployer = Addr::unchecked("deployer");\n+ let mocked_msg_info = testing::mock_info(deployer.as_ref(), &[]);\n+ let instantiate_msg = InstantiateMsg {\n+ payment_params: PaymentParams { beneficiary: deployer.to_owned() },\n+ };\n+ let _ = super::instantiate(\n+ mocked_deps_mut.as_mut(),\n+ mocked_env.to_owned(),\n+ mocked_msg_info,\n+ instantiate_msg,\n+ )\n+ .expect("Failed to instantiate manager");\n// diff-add-end\n let executer = Addr::unchecked("executer");\n ...\n let expected_response = Response::default()\n// diff-add-start\n+ .add_message(BankMsg::Send {\n+ to_address: executer.to_string(),\n+ amount: vec![fund_sent],\n+ })\n// diff-add-end\n .add_submessage(SubMsg {\n ...\n msg: CosmosMsg::<Empty>::Wasm(WasmMsg::Execute {\n ...\n// diff-del\n- funds: vec![fund_sent],\n// diff-add\n+ funds: vec![],\n }),\n })\n ...\n }\n }\n'})}),"\n",(0,a.jsx)(n.p,{children:"Note that:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["You removed the funds expectation on the ",(0,a.jsx)(n.code,{children:"WasmMsg::Execute"})," message."]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"update-the-mocked-app-tests",children:"Update the mocked-app tests"}),"\n",(0,a.jsx)(n.p,{children:"Your current test with the mocked app does not send coins along with the mint call.\nAnd indeed, the mocked addresses you use have no balances of their own to send from.\nBefore you make some serious changes with balances, you can update your test that does not handle coins.\nOther than modifying the way you instantiate your collection manager, there is no change in the rest of the test."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",metastring:'title="tests/contract.rs"',children:'// diff-del\n- use cosmwasm_std::{to_json_binary, Addr, Empty, Event};\n// diff-add\n+ use cosmwasm_std::{to_json_binary, Addr, Coin, Empty, Event, Uint128};\n use cw721::msg::{Cw721ExecuteMsg, Cw721QueryMsg, OwnerOfResponse};\n// diff-del\n- use cw_multi_test::{App, ContractWrapper, Executor};\n// diff-add\n+ use cw_multi_test::{App, AppBuilder, ContractWrapper, Executor};\n use cw_my_collection_manager::{\n contract::{execute, instantiate, reply},\n// diff-del\n- msg::{ExecuteMsg, InstantiateMsg},\n// diff-add\n+ msg::{ExecuteMsg, InstantiateMsg, PaymentParams},\n };\n ...\n fn instantiate_collection_manager(\n mock_app: &mut App,\n// diff-add\n+ payment_params: PaymentParams,\n ) -> (u64, Addr) {\n ...\n return (\n manager_code_id,\n mock_app\n .instantiate_contract(\n manager_code_id,\n Addr::unchecked("deployer-manager"),\n// diff-del\n- &InstantiateMsg {},\n// diff-add\n+ &InstantiateMsg { payment_params },\n &[],\n "my-collection-manager",\n None,\n )\n .expect("Failed to instantiate collection manager"),\n );\n }
1\n ...\n fn test_mint_through() {\n // Arrange\n let mut mock_app = App::default();\n// diff-add\n+ let beneficiary_addr = Addr::unchecked("beneficiary");\n let (_, addr_manager) = instantiate_collection_manager(\n &mut mock_app,\n// diff-add-start\n+ PaymentParams {\n+ beneficiary: beneficiary_addr.to_owned(),\n+ },\n// diff-add-end\n );\n ...\n }\n ...\n fn test_mint_num_tokens() {\n // Arrange\n let mut mock_app = App::default();\n// diff-add\n+ let beneficiary_addr = Addr::unchecked("beneficiary");\n let (_, addr_manager) = instantiate_collection_manager(\n &mut mock_app,\n// diff-add-start\n+ PaymentParams {\n+ beneficiary: beneficiary_addr.to_owned(),\n+ },\n// diff-add-end\n );\n ...\n }\n'})}),"\n",(0,a.jsx)(n.p,{children:"Note that:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Only the instantiation changes."}),"\n",(0,a.jsx)(n.li,{children:"It includes imports used below here."}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"add-a-mocked-app-test-with-tokens",children:"Add a mocked app test with tokens"}),"\n",(0,a.jsxs)(n.p,{children:["Let's make it interesting and have the sender send tokens along with the pass-through mint transaction.\nTo be able to send tokens with the mocked app, you have to set some balances when mocking.\nThat's where the ",(0,a.jsx)(n.a,{href:"https://github.com/CosmWasm/cw-multi-test/blob/v1.2.0/src/app_builder.rs",children:(0,a.jsx)(n.code,{children:"AppBuilder"})})," comes in."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",metastring:'title="tests/contract.rs"',children:'#[test]\nfn test_paid_mint_through() {\n // Arrange\n let sender_addr = Addr::unchecked("sender");\n let extra_fund_sent = Coin {\n denom: "gold".to_owned(),\n amount: Uint128::from(335u128),\n };\n let mut mock_app = AppBuilder::default().build(|router, _api, storage| {\n router\n .bank\n .init_balance(\n storage,\n &sender_addr,\n vec![extra_fund_sent.to_owned()],\n )\n .expect("Failed to init bank balances");\n });\n let beneficiary = Addr::unchecked("beneficiary");\n let (_, addr_manager) = instantiate_collection_manager(\n &mut mock_app,\n PaymentParams {\n beneficiary: beneficiary.to_owned(),\n },\n );\n let (_, addr_collection) = instantiate_nameservice(&mut mock_app, addr_manager.to_string());\n let owner_addr = Addr::unchecked("owner");\n let name_alice = "alice".to_owned();\n let register_msg = ExecuteMsg::PassThrough {\n collection: addr_collection.to_string(),\n message: CollectionExecuteMsg::Mint {\n token_id: name_alice.clone(),\n owner: owner_addr.to_string(),\n token_uri: None,\n extension: None,\n },\n };\n\n // Act\n let result = mock_app.execute_contract(\n sender_addr.clone(),\n addr_manager.clone(),\n ®ister_msg,\n &[extra_fund_sent.to_owned()],\n );\n\n // Assert\n assert!(result.is_ok(), "Failed to pass through the message");\n let result = result.unwrap();\n let expected_beneficiary_bank_event = Event::new("transfer")\n .add_attribute("recipient", "beneficiary")\n .add_attribute("sender", "contract0")\n .add_attribute("amount", "335gold");\n result.assert_event(&expected_beneficiary_bank_event);\n assert_eq!(\n Vec::<Coin>::new(),\n mock_app\n .wrap()\n .query_all_balances(sender_addr)\n .expect("Failed to get sender balances")\n );\n assert_eq!(\n vec![extra_fund_sent],\n mock_app\n .wrap()\n .query_all_balances(beneficiary)\n .expect("Failed to get beneficiary balances")\n );\n assert_eq!(\n Vec::<Coin>::new(),\n mock_app\n .wrap()\n .query_all_balances(addr_manager)\n .expect("Failed to get manager balances")\n );\n assert_eq!(\n Vec::<Coin>::new(),\n mock_app\n .wrap()\n .query_all_balances(addr_collection)\n .expect("Failed to get collection balances")\n );\n}\n'})}),"\n",(0,a.jsx)(n.p,{children:"Note that:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["It is inside the ",(0,a.jsxs)(n.a,{href:"https://github.com/CosmWasm/cw-multi-test/blob/v1.2.0/src/app_builder.rs#L536",children:[(0,a.jsx)(n.code,{children:"build"})," function"]}),"\nthat you access the ",(0,a.jsx)(n.code,{children:"storage"})," element necessary to call up the mocked balances feature."]}),"\n",(0,a.jsxs)(n.li,{children:["The sender is only credicted with ",(0,a.jsx)(n.code,{children:"extra_fund_sent"})," so it has no remaining balance, which is asserted too."]}),"\n",(0,a.jsxs)(n.li,{children:["Only the bank message triggered an event, unlike the funds forwarded by the C
1osmWasm module when using the ",(0,a.jsx)(n.code,{children:"funds"})," feature."]}),"\n",(0,a.jsx)(n.li,{children:"Checking the balances of the smart contracts is just here as a belt-and-braces idea because that's akin to verifying\nthe mocked app has correctly implemented the conservation of funds."}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,a.jsx)(n.p,{children:"You smart contract now sends a message across the CosmWasm module barrier and into a Cosmos module, the bank.\nIt does so by forwarding all funds received. This is a rudimentary way of handling funds.\nIn particular, for a collection manager that plans on eventually being a marketplace."}),"\n",(0,a.jsx)(n.p,{children:":::info Exercise progression"}),"\n",(0,a.jsx)(n.p,{children:"At this stage:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["The ",(0,a.jsx)(n.code,{children:"my-nameservice"})," project should still have something similar to the\n",(0,a.jsx)(n.a,{href:"https://github.com/b9lab/cw-my-nameservice/tree/execute-return-data",children:(0,a.jsx)(n.code,{children:"execute-return-data"})})," branch."]}),"\n",(0,a.jsxs)(n.li,{children:["The ",(0,a.jsx)(n.code,{children:"my-collection-manager"})," project should have something similar to the\n",(0,a.jsx)(n.a,{href:"https://github.com/b9lab/cw-my-collection-manager/tree/cross-module-message",children:(0,a.jsx)(n.code,{children:"cross-module-message"})})," branch,\nwith ",(0,a.jsx)(n.a,{href:"https://github.com/b9lab/cw-my-collection-manager/compare/reply-from-execute..cross-module-message",children:"this"})," as the diff."]}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:":::"}),"\n",(0,a.jsx)(n.p,{children:"In the next section, you have your collection manager smart contract handle funds more elaborately."})]})}function h(e={}){let{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(m,{...e})}):m(e)}},7250(e,n,t){t.d(n,{A:()=>i});var s=t(4848);t(6540);var a=t(4164),r=t(7663);function o({children:e,className:n,hidden:t}){return(0,s.jsx)("div",{role:"tabpanel",className:(0,a.A)("tabItem_Ymn6",n),hidden:t,children:e})}function i({children:e,className:n,value:t}){let{selectedValue:a,lazy:d}=(0,r.uc)(),c=t===a;return!c&&d?null:(0,s.jsx)(o,{className:n,hidden:!c,children:e})}},773(e,n,t){t.d(n,{A:()=>m});var s=t(4848);t(6540);var a=t(4164),r=t(8287),o=t(7663),i=t(8584),d=t(9863);function c({className:e}){let{selectedValue:n,selectValue:t,tabValues:r,block:d}=(0,o.uc)(),l=[],{blockElementScrollPositionUntilNextRender:u}=(0,i.a_)(),m=e=>{let s=e.currentTarget,a=r[l.indexOf(s)].value;a!==n&&(u(s),t(a))},h=e=>{let n=null;switch(e.key){case"Enter":m(e);break;case"ArrowRight":{let t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{let t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1]}}n?.focus()};return(0,s.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":d},e),children:r.map(({value:e,label:t,attributes:r})=>(0,s.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{l.push(e)},onKeyDown:h,onClick:m,...r,className:(0,a.A)("tabs__item","tabItem_LNqP",r?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function l({children:e}){return(0,s.jsx)("div",{className:"margin-top--md",children:e})}function u({className:e,children:n}){return(0,s.jsxs)("div",{className:(0,a.A)(r.G.tabs.container,"tabs-container","tabList__CuJ"),children:[(0,s.jsx)(c,{className:e}),(0,s.jsx)(l,{children:n})]})}function m(e){let n=(0,d.A)(),t=(0,o.OC)(e);return(0,s.jsx)(o.O_,{value:t,children:(0,s.jsx)(u,{className:e.className,children:(0,o.vT)(e.children)})},String(n))}},7663(e,n,t){t.d(n,{OC:()=>m,O_:()=>f,uc:()=>p,vT:()=>l});var s=t(4848),a=t(6540),r=t(6347),o=t(9989),i=t(6629),d=t(618),c=t(1367);function l(e){return a.Children.toArray(e).filter(e=>"\n"!==e)}function u({value:e,tabValues:n}){return n.some(n=>n.value===e)}function m(e){let n,{defaultValue:t,queryString:s=!1,groupId:l}=e,m=function(e){let{values:n,children:t}=e;return(0,a.useMemo)(()=>{let e=n??a.Children.toArray(t).flatMap(e=>{if(!e)return[];if((0,a.isValidElement)(e)&&function(e){let{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];
1let n="string"==typeof e.type?e.type:e.type.name;throw Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop. 2If you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:s}})=>({value:e,label:n,attributes:t,default:s})),s=(0,d.XI)(e,(e,n)=>e.value===n.value);if(s.length>0)throw Error(`Docusaurus error: Duplicate values "${s.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`);return e},[n,t])}(e),[h,p]=(0,a.useState)(()=>(function({defaultValue:e,tabValues:n}){if(0===n.length)throw Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!u({value:e,tabValues:n}))throw Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}let t=n.find(e=>e.default)??n[0];if(!t)throw Error("Unexpected error: 0 tabValues");return t.value})({defaultValue:t,tabValues:m})),[f,g]=function({queryString:e=!1,groupId:n}){let t=(0,r.W6)(),s=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,i.aZ)(s),(0,a.useCallback)(e=>{if(!s)return;let n=new URLSearchParams(t.location.search);n.set(s,e),t.replace({...t.location,search:n.toString()})},[s,t])]}({queryString:s,groupId:l}),[_,x]=function({groupId:e}){let n=e?`docusaurus.tab.${e}`:null,[t,s]=(0,c.Dv)(n);return[t,(0,a.useCallback)(e=>{n&&s.set(e)},[n,s])]}({groupId:l}),b=u({value:n=f??_,tabValues:m})?n:null;return(0,o.A)(()=>{b&&p(b)},[b]),{selectedValue:h,selectValue:(0,a.useCallback)(e=>{if(!u({value:e,tabValues:m}))throw Error(`Can't select invalid tab value=${e}`);p(e),g(e),x(e)},[g,x,m]),tabValues:m,lazy:e.lazy??!1,block:e.block??!1}}let h=(0,a.createContext)(null);function p(){let e=a.useContext(h);if(!e)throw Error("useTabsContext() must be used within a Tabs component");return e}function f(e){return(0,s.jsx)(h.Provider,{value:e.value,children:e.children})}},8453(e,n,t){t.d(n,{R:()=>o,x:()=>i});var s=t(6540);let a={},r=s.createContext(a);function o(e){let n=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),s.createElement(r.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.