Code Monkey home page Code Monkey logo

subxt's Introduction

subxt · build Latest Version Documentation

Subxt is a library for interacting with Substrate based nodes in Rust and WebAssembly. It can:

  • Submit Extrinsics (this is where the name comes from).
  • Subscribe to blocks, reading the extrinsics and associated events from them.
  • Read and iterate over storage values.
  • Read constants and custom values from the metadata.
  • Call runtime APIs, returning the results.
  • Do all of the above via a safe, statically types interface or via a dynamic one when you need the flexibility.
  • Compile to WASM and run entirely in the browser.
  • Do a bunch of things in a #[no_std] environment via the subxt-core crate.
  • Use a built-in light client (smoldot) to interact with chains.

Usage

Take a look in the examples folder or the examples folder for various smaller or larger subxt usage examples, or read the guide to learn more.

Downloading metadata from a Substrate node

Use the subxt-cli tool to download the metadata for your target runtime from a node.

  1. Install:
cargo install subxt-cli
  1. Save the encoded metadata to a file:
subxt metadata -f bytes > metadata.scale

This defaults to querying the metadata of a locally running node on the default http://localhost:9933/. If querying a different node then the metadata command accepts a --url argument.

Subxt Documentation

For more details regarding utilizing subxt, please visit the documentation.

Integration Testing

Most tests require a running substrate node to communicate with. This is done by spawning an instance of the substrate node per test. It requires an up-to-date substrate executable on your path.

This can be installed from source via cargo:

cargo install --git https://github.com/paritytech/polkadot-sdk staging-node-cli --force

Real world usage

Please add your project to this list via a PR.

  • cargo-contract CLI for interacting with Wasm smart contracts.
  • xcm-cli CLI for submitting XCM messages.
  • phala-pherry The relayer between Phala blockchain and the off-chain Secure workers.
  • crunch CLI to claim staking rewards in batch every Era or X hours for substrate-based chains.
  • interbtc-clients Client implementations for the interBTC parachain; notably the Vault / Relayer and Oracle.
  • tidext Tidechain client with Stronghold signer.
  • staking-miner-v2 Submit NPos election solutions and get rewards.
  • polkadot-introspector Tools for monitoring Polkadot nodes.
  • ink! Smart contract language that uses subxt for allowing developers to conduct End-to-End testing of their contracts.
  • Chainflip A decentralised exchange for native cross-chain swaps.

Alternatives

substrate-api-client provides similar functionality.

License

The entire code within this repository is dual licensed under the GPL-3.0 or Apache-2.0 licenses. See the LICENSE file for more details.

Please contact us if you have questions about the licensing of our products.

subxt's People

Contributors

ascjones avatar c410-f3r avatar clearloop avatar cmichi avatar demi-marie avatar dependabot[bot] avatar dvc94ch avatar dvdplm avatar en avatar gregdhill avatar h4x3rotab avatar insipx avatar jsdw avatar koushiro avatar kylezs avatar lean-apple avatar lexnv avatar liuchengxu avatar muraca avatar niklasad1 avatar paritytech-subxt-pr-maker[bot] avatar paulormart avatar pkhry avatar pmikolajczyk41 avatar sander2 avatar sergejparity avatar simonsso avatar tadeohepperle avatar xanewok avatar yjhmelody avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

subxt's Issues

Create raw payload from rpc client

Hi,
I would like to get feedback on a proposed method for Client such that
client.create_raw_payload(AccountId, Call) returns the payload to be signed.

The reason is so that the payload may be signed by other signers (maybe not in the XtBuilder and use this or other tools to create uncheckedExtrinsic.

transaction signer

transaction signing should be decoupled from the client

maybe XtBuilder should take a Sink<Item = (UnsignedTransaction, oneshot::Sender<Result<Transaction, AbortError>>)>

add a test runtime

should be useful to test things like #103 or #91

should have a plain, map, double map store item and some transactions to deterministically modify them. we should also have a transaction that always returns an error and one that always returns an event.

Allow for static event decoding

At the moment events from ExtrinsicSuccess are encoded in the RuntimeEvents struct. The RuntimeEvents struct has no relation to the runtime types. It would be preferable to instead decode the events as the real event type obtained from the runtime. More concretely

pub struct ExtrinsicSuccess<T: System> {
  // ...
  events: srml_system::EventRecord<T::Event, T::Hash>
}

This would require us to add the associated Event type to the System trait.

With this implementation we could do away with the dynamic event parsing which suffers from a couple of issues.

  • It needs to fetch the metadata
  • It requires substantial manual decoding logic for events
  • It requires registering type sizes for custom structs.

I’d be willing to give this a stab.

Signer should be asynchronous

Some signers require asynchronously interacting with external hardware, such as a hardware wallet. Therefore, Signer should return a Future.

An attempt to remove ModuleCalls in srml/mod.rs and relevant parts

In original implementation, there needs ModuleCalls to delegate the call of those srml module, I think this is owed to the problem of the project structure.

This design leads to a severe problem: if user of this library wants to introduce a new runtime module, he needs to impl ModuleCalls directly in his module file, and this is prevented by Rust for orphan rule.

So, I just do some changes for it. If we can agree on this approach, I will make a PR on it.

Thank you.

Ref: https://github.com/daogangtang/substrate-subxt/tree/master/src

`Treasury::Deposit` event can't be decoded

Treasury::Deposit can't be decoded, see log below:

[2020-06-22T09:16:51Z DEBUG substrate_subxt::events] received event 'Treasury::Deposit'
Error: Events(TypeSizeUnavailable("Balance"))

AFAIK, this could be fixed by register Balance in EventsDecoder::new. Why isn't it?

block_hash not completely usable?

@ascjones I could be just missing something obvious, but it seems the client's "async fn block_hash" is not completely usable by library users because "mod rpc" is private. The parameter is defined in mod rpc as pub type BlockNumber<T> = sp_rpc::number::NumberOrHex<<T as System>::BlockNumber>

Without reexporting sp_rpc::number::NumberOrHex or including rpc::BlockNumber in pub use, I'm not exactly sure how library users can get a specific block hash using a block number.

Incidentally, sp_rpc::number::NumberOrHex only implements std::convert::From for NumberOrHex<u64>, but kusama, node-template and basically all examples of substrate chains set System::BlockNumber to u32. Therefore,Some(9001u32.into()) does not work either.

Again, it is late and I might be missing something. If this is an oversight in the library, I can do a PR with some basic unit tests. I'm not sure if you would prefer to expose BlockNumber, reexport sp_rpc::number, or add more From's for different integer types in substrate/primitive/rpc.

Edit:

I was able to do sp_core::U256::from(x).into() to take advantage of the from Trait in sp_rpc::number. This works but seems to be a big circle to get to the right type.

Edit 2:

I just ran into the same issue with client.fetch for the StorageKey tuple struct. Can't call the method properly due to unexposed StorageKey. I'm importing my own substrate in Cargo.toml that is pinned to a specific git revision.

Subscriptions: add timeout if no more messages received?

Since converting to jsonrpsee, subscriptions are handled with while let msg = sub.next().await which is an infinite loop (see paritytech/jsonrpsee#86).

What if no more messages are received on a subscription (e.g. because connection has closed), or if the specific message we are waiting for doesn't arrive? Should we add a timeout? Otherwise we will wait indefinitely e.g. https://github.com/paritytech/substrate-subxt/blob/253a7d8b0b4ff33744aafc2f4aea6a6d14647681/src/rpc.rs#L286.

light-client needs a way to import block headers

To speed up initial sync most light clients distribute the block headers with the binary. The substrate cli supports exporting and importing blocks, but it seems like it exports/imports the entire block.

Multisignature decoding error in submit_and_watch example

I am unable to run the example with the following versions:

  1. using substrate node version 2.0.0-alpha.5-beff53804-x86_64-macos
  2. using release 0.5 of this repo
Error: Rpc(Request(Error { code: ServerError(1002), message: "Verification Error: Execution(ApiError(\"Could not convert parameter `tx` between node and runtime: No such variant in enum MultiSignature\"))", data: Some(String("RuntimeApi(\"Execution(ApiError(\\\"Could not convert parameter `tx` between node and runtime: No such variant in enum MultiSignature\\\"))\")")) }))

xt should return immediately

Currently when submitting an extrinsic, the future doesn't resolve until the extrinsic is included in a block. The future should return immediately and ExtrinsicSuccess should expose a wait_block and wait_final which return futures.

no_std support for subxt

Currently there isn't any no_std support for substrate-subxt making it really difficult to craft extrinsics from within a restricted environment. Is there any planned support for no_std / alloc environments?

Contracts::put_code: InvalidTransaction::BadProof

Since upgrading to latest signing #33 and Extrinsic version 4, the put_code test fails with Failed to submit extrinsic: Transaction pool error: Invalid transaction validity: InvalidTransaction::BadProof.

Oddly the transfer_balance test succeeds, so not sure why the signature is good for one and not the other.

Support `utility.batch`

Hi,

I know there's a module named utility that can send batch transactions.
So if subxt supports it, that would be great.

Misusing the proc macros gives poor error messages

Misusing the proc macros results in very unhelpful error messages. I have seen:

  • Unwrapping None
  • Undefined variables with no explanation
  • Probably others

Furthermore, the correct use of the proc macros is poorly documented. I had to figure it out by trial and error.

set_call in XtBuilder play two roles

Now in set_call, we do two things now:

  1. set_call
  2. build the XtBuilder instance

Should we split it into two methods: set_call() and build(), and use it as:

xt.foo().bar().set_call().build()

Unsigned extrinsics

At a glance this library seems to assume extrinsics are signed, it would be nice to support unsigned extrinsics.

Multiple metadata versions

Consider supporting multiple versions of substrate metadata (currently on v8).

This would allow the library to be used against chains running different versions of substrate.

Rel #25

subxt build error about rpc

Hi, I use subxt-v0.8.0 and subxt-v0.9.0, they both have some bugs as follows:

 Compiling substrate-subxt v0.9.0
error[E0107]: wrong number of type arguments: expected 0, found 1
  --> /Users/lawliet/.cargo/registry/src/crates.rustcc.cn-46a557d00542c05f/substrate-subxt-0.9.0/src/rpc.rs:89:26
   |
89 | impl<T> From<NumberOrHex<<T as System>::BlockNumber>> for BlockNumber<T>
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^ unexpected type argument

error[E0107]: wrong number of type arguments: expected 0, found 1
  --> /Users/lawliet/.cargo/registry/src/crates.rustcc.cn-46a557d00542c05f/substrate-subxt-0.9.0/src/rpc.rs:87:47
   |
87 | pub struct BlockNumber<T: System>(NumberOrHex<<T as System>::BlockNumber>);
   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ unexpected type argument

error[E0107]: wrong number of type arguments: expected 0, found 1
  --> /Users/lawliet/.cargo/registry/src/crates.rustcc.cn-46a557d00542c05f/substrate-subxt-0.9.0/src/rpc.rs:93:28
   |
93 |     fn from(x: NumberOrHex<<T as System>::BlockNumber>) -> Self {
   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ unexpected type argument

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0107`.
error: could not compile `substrate-subxt`.

My rust toolchain is nightly-2020-06-16

How to decode extrinsic for raw Vec<u8>

I want extract extrinsic from rpc result, but got invalid transaction version error, please help.

use substrate_subxt::{
    ClientBuilder,
    KusamaRuntime,
    runtimes::*,
    frame::system::*,
    frame::balances::*,
    extra::DefaultExtra
};
use sp_runtime::{
    generic::{
        UncheckedExtrinsic
    },
    MultiSignature,
    AnySignature
};
use codec::{Decode, Encode, EncodeLike, Input, Error};
use sp_core::crypto::Pair;

type Address = <KusamaRuntime as System>::Address;
type Call = Vec<u8>;
type Signature = AnySignature;
type Extra = DefaultExtra<KusamaRuntime>;

type UnEx = UncheckedExtrinsic<Address, Call, Signature, Extra>;


#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    env_logger::init();

    let client = ClientBuilder::<KusamaRuntime>::new()
        .set_url("wss://kusama-rpc.polkadot.io")
        .build()
        .await?;

    let block_number = 2852995;

    let block_hash = client.block_hash(Some(block_number.into())).await?;

    if let Some(hash) = block_hash {
        println!("Block hash for block number {}: {:?}", block_number, hash);

        let block = client.block(Some(hash)).await?.unwrap();

        println!("block = {:?}", block);

        let extrinsic: sp_runtime::OpaqueExtrinsic = block.block.clone().extrinsics[2].clone();

        let mut input = extrinsic.0.as_slice();
        println!("input  = {:?}", &input);
        // UncheckedExtrinsic<Address, Call, Signature, Extra>
        let uncheck_extrinsic = UnEx::decode(&mut input);

        println!("block extrinsics = {:?}", uncheck_extrinsic);  // Got  `Invalid transaction version` error

    } else {
        println!("Block number {} not found.", block_number);
    }

    Ok(())
}

Outputs:

Block hash for block number 2852995: 0xdbe3277db35554024f68d947b0f2a08bb2a2bb14b5723a60e44c930648dcbca2
block = SignedBlock { block: Block { header: Header { parent_hash: 0x9685770bfbc3b213b63e59dec3d518b6dab61849cb944b066644b56d94eda6c1, number: 2852995, state_root: 0x458ef43522540efc0b4a7be7f2aef470d229bdf4bad31f0bc38e6c67668f5a46, extrinsics_root: 0xc1d6f1f1216393b81bc155cf9f8163c11b51e3225df5a68febd6d1e9096f409b, digest: Digest { logs: [DigestItem::PreRuntime([66, 65, 66, 69], [2, 121, 0, 0, 0, 244, 178, 210, 15, 0, 0, 0, 0]), DigestItem::Seal([66, 65, 66, 69], [90, 217, 174, 195, 252, 86, 236, 170, 4, 204, 232, 209, 46, 172, 53, 117, 143, 194, 177, 184, 203, 42, 251, 157, 120, 136, 50, 141, 133, 137, 240, 90, 3, 229, 135, 210, 32, 181, 154, 216, 14, 61, 108, 141, 218, 110, 63, 172, 113, 128, 209, 228, 180, 66, 218, 161, 136, 38, 94, 73, 62, 160, 102, 137])] } }, extrinsics: [0402000bc03642da7201, 04140000, 84fe56be5933800b45a21ee8e9817eae9f49099fdf4a20076718497092ed43c62b019a7261d479c4b495cfd1fcffcf9daf35600c391e752e99c805e7951388fb17663a8ab4f9c2241835599c1df443630f0c218b3bdc3e83f5e0390cfced7e580b8ef50374000400a8070648f6e43b7d8a8f10418b696130d2046c10645f10085de80e6098b85f0902286bee] }, justification: None }
input  = [132, 254, 86, 190, 89, 51, 128, 11, 69, 162, 30, 232, 233, 129, 126, 174, 159, 73, 9, 159, 223, 74, 32, 7, 103, 24, 73, 112, 146, 237, 67, 198, 43, 1, 154, 114, 97, 212, 121, 196, 180, 149, 207, 209, 252, 255, 207, 157, 175, 53, 96, 12, 57, 30, 117, 46, 153, 200, 5, 231, 149, 19, 136, 251, 23, 102, 58, 138, 180, 249, 194, 36, 24, 53, 89, 156, 29, 244, 67, 99, 15, 12, 33, 139, 59, 220, 62, 131, 245, 224, 57, 12, 252, 237, 126, 88, 11, 142, 245, 3, 116, 0, 4, 0, 168, 7, 6, 72, 246, 228, 59, 125, 138, 143, 16, 65, 139, 105, 97, 48, 210, 4, 108, 16, 100, 95, 16, 8, 93, 232, 14, 96, 152, 184, 95, 9, 2, 40, 107, 238]
block extrinsics = Err(Error("Invalid transaction version"))

How to get the self-defined module state?

I do not know how to get the self-defined module state using this current version of subxt. I can only find the way of sending extrinsics. Will you consider adding this functionality of getting ledger state?

With the latest verision of Substrate, build failed

error[E0432]: unresolved import sp_runtime::traits::SimpleArithmetic
--> src/frame/balances.rs:30:5
|
30 | SimpleArithmetic,
| ^^^^^^^^^^^^^^^^ no SimpleArithmetic in traits

error[E0432]: unresolved import sp_runtime::traits::SimpleArithmetic
--> src/frame/system.rs:38:5
|
38 | SimpleArithmetic,
| ^^^^^^^^^^^^^^^^ no SimpleArithmetic in traits

I think this is due to the update of the substrate. Maybe we should add the rev of the substrate's github dependencies.

Export error::RuntimeError

The substrate_subxt::error::RuntimeError is not exported and error is private so it is impossible to match on RuntimeError. Exporting this error is probably a good idea.

Support multiple versions of Substrate

Currently, backwards-incompatible metadata changes cause a nasty problem for subxt, and thus Ledgeracio. The old version of subxt supports the old runtime, and the new version of subxt supports the new runtime. However, the on-chain runtime upgrade can happen at any time, so there is no way to know when to switch without race conditions.

To solve this, subxt needs to support multiple incompatible runtime versions. Support for a version will be added before it is proposed on-chain. Support for a version will only be removed when all runtimes have migrated off of it.

Setup CI

  • build
  • test
  • fmt

Integration Tests

If possible run integration tests against a recent substrate node instance (docker).

Generate the client from the metadata

Currently, all client implementations are written by hand. This works, but is very tedious and won’t scale to large runtimes.

A better option is to generate the client. This is already done by the TypeScript client implementation, so it should be quite doable for the Rust one.

module_with_calls is not public caused I failed to compose two calls

Hi there,

This issue might be quite the same with How could I send a sudo call.

I'm using subxt-0.6, which works fast and as expected. But I find out that this interface module_with_calls become private while I try to upgrade to 0.7. Here is code sample with subxt-0.6

let proposal = client.metadata().module_with_calls("MyModule")
		.and_then(|module| module.call("my_call", my_call_args))
		.map_err(|_| crate::Error::SubxtError("failed to compose a sudo call"))?;
let call = Call::new("Sudo", "sudo", proposal);

So now I cannot upgrade subxt to 0.7 or higher, can that interface change to be public? Or maybe you can give me a better solution instead of changing that interface.

Thanks!

tests now broken

Just pull the latest code, and open tests, run it, display below:

mike@DESKTOP-DC63SDI:~/works/substrate_works/substrate-subxt$ cargo test --release
   Compiling node-runtime v2.0.0 (https://github.com/paritytech/substrate/#337a0b22)
   Compiling substrate-subxt v0.1.0 (/home/mike/works/substrate_works/substrate-subxt)
    Finished release [optimized] target(s) in 1m 12s
     Running ../substrate/target/release/deps/substrate_subxt-ec0abb6820fe6c2b

running 6 tests
test tests::test_tx_contract_put_code ... FAILED
test tests::test_chain_read_metadata ... FAILED
test tests::test_tx_transfer_balance ... FAILED
test tests::test_chain_subscribe_blocks ... FAILED
test tests::test_chain_subscribe_finalized_blocks ... FAILED
test tests::test_state_read_free_balance ... FAILED

failures:

---- tests::test_tx_contract_put_code stdout ----
thread 'tests::test_tx_contract_put_code' panicked at 'called `Result::unwrap()` on an `Err` value: Rpc(ParseError("RuntimeVersion", Error("invalid type: string \"0xdf6acb689907609b\", expected an array of length 8", line: 0, column: 0)))', src/libcore/result.rs:999:5

---- tests::test_chain_read_metadata stdout ----
thread 'tests::test_chain_read_metadata' panicked at 'called `Result::unwrap()` on an `Err` value: Rpc(ParseError("RuntimeVersion", Error("invalid type: string \"0xdf6acb689907609b\", expected an array of length 8", line: 0, column: 0)))', src/libcore/result.rs:999:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

---- tests::test_tx_transfer_balance stdout ----
thread 'tests::test_tx_transfer_balance' panicked at 'called `Result::unwrap()` on an `Err` value: Rpc(ParseError("RuntimeVersion", Error("invalid type: string \"0xdf6acb689907609b\", expected an array of length 8", line: 0, column: 0)))', src/libcore/result.rs:999:5

---- tests::test_chain_subscribe_blocks stdout ----
thread 'tests::test_chain_subscribe_blocks' panicked at 'called `Result::unwrap()` on an `Err` value: Rpc(ParseError("RuntimeVersion", Error("invalid type: string \"0xdf6acb689907609b\", expected an array of length 8", line: 0, column: 0)))', src/libcore/result.rs:999:5

---- tests::test_chain_subscribe_finalized_blocks stdout ----
thread 'tests::test_chain_subscribe_finalized_blocks' panicked at 'called `Result::unwrap()` on an `Err` value: Rpc(ParseError("RuntimeVersion", Error("invalid type: string \"0xdf6acb689907609b\", expected an array of length 8", line: 0, column: 0)))', src/libcore/result.rs:999:5

---- tests::test_state_read_free_balance stdout ----
thread 'tests::test_state_read_free_balance' panicked at 'called `Result::unwrap()` on an `Err` value: Rpc(ParseError("RuntimeVersion", Error("invalid type: string \"0xdf6acb689907609b\", expected an array of length 8", line: 0, column: 0)))', src/libcore/result.rs:999:5


failures:
    tests::test_chain_read_metadata
    tests::test_chain_subscribe_blocks
    tests::test_chain_subscribe_finalized_blocks
    tests::test_state_read_free_balance
    tests::test_tx_contract_put_code
    tests::test_tx_transfer_balance

test result: FAILED. 0 passed; 6 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--lib'

How could I send a sudo call?

Hi there,

I just used this crate create a general call for my own module, and it worked as expected.
But while I tried to send a sudo call, it failed, the error message likes

DEBUG runtime  DispatchError
DEBUG runtime  Bad origin

And I checked examples and test cases, it looks there's no sample codes in it.
So my problem is how can I compose a sudo call and send it to a running node.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.