Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add usage examples to the guide #920

Open
jsdw opened this issue Apr 21, 2023 · 8 comments
Open

Add usage examples to the guide #920

jsdw opened this issue Apr 21, 2023 · 8 comments

Comments

@jsdw
Copy link
Collaborator

jsdw commented Apr 21, 2023

This is a follow-up to #898, because I wanted to break the work down a little.

Add something a bit like this to the end of the main page of the guide:

## Advanced

Follow links from the below sections for information on some more advanced use cases:

- Building a WASM app with Subxt.
- Ditching the statically generated interface.
- Integrating with Substrate.
- Working offline.
- Interacting with a parachain

(actual use cases we want to show need to be thought through)

And then add the corresponding sections and examples etc as needed.

@boyswan
Copy link
Contributor

boyswan commented Apr 23, 2023

Another really useful example (and something I'm currently struggling with) is showing how to sign partial extrinsics with something like polkadot.js in the browser

@jsdw
Copy link
Collaborator Author

jsdw commented Apr 24, 2023

Noted, thanks! The new guide will actually have one example of building a partial extrinsic, getting the signer payload and then getting an extrinsic ready to submit with the final signature. It doesn't look at how to sign the payload though and assumes some external thing will do it. A full e2e example of this is something that's on my mind :)

@jsdw
Copy link
Collaborator Author

jsdw commented May 2, 2023

Also added #942; let's make sure we cover configuring and using Subxt for some parachain (this might be: how to create custom config (ie look in metadata / runtime definition of chain and see if types/extra params line up for instance).

@jsdw
Copy link
Collaborator Author

jsdw commented Jul 19, 2023

@boyswan just FYI, we did eventually work out how to use a browser extension to sign partial extrinsics produced by Subxt; we're working on the example at #1067 incase it helps!

It's somewhat tedious because browser extensions expect a SignerPayloadJSON type that has a bunch of things in, and don't accept a raw signer payload at the moment. Over time I hope that browser extensions will improve this interface since metadata is available to help decoding etc nowadays.

@boyswan
Copy link
Contributor

boyswan commented Jul 24, 2023

Thanks for this! I actually ended up going down a similar route and constructing the payload in JS, but it's great to see a more rust-y example

@jsdw
Copy link
Collaborator Author

jsdw commented Jul 24, 2023

Ooh, I'd love to see what approach you took; do you have any code you can link to?

@boyswan
Copy link
Contributor

boyswan commented Jul 24, 2023

This is pretty much the approach I took:

...
let nonce = client.rpc().system_account_next_index(&account_id).await?;
let js_signature = sign_message(self.account.into(), call_bytes, nonce).await;
let ext_bytes: Vec<u8> = serde_wasm_bindgen::from_value(js_signature).unwrap_or_default();
let extrinsic = SubmittableExtrinsic::from_bytes(client, ext_bytes);

let mut tx = extrinsic.submit_and_watch().await?;

Then calling via wasm-bindgen:

export let sign_message = async (address: string, method_u8: Uint8Array, nonce_u32: number) => {
  return new Promise(async (res, rej) => {

    let injector = await web3FromAddress(address);

    let api = await ApiPromise.create({
      provider: new WsProvider('ws://127.0.0.1:9944'),
      signer: injector.signer,
    });

    let genesisHash = api.genesisHash;
    let lastHeader = await api.rpc.chain.getHeader();
    let blockHash = await api.rpc.chain.getBlockHash();
    let method = u8aToHex(method_u8);
    let nonce = api.createType("u32", nonce_u32);
    let specVersion = api.createType("u32", api.runtimeVersion.specVersion);
    let blocknumber = api.createType("BlockNumber", lastHeader.number);
    let transactionVersion = api.createType("u32", api.runtimeVersion.transactionVersion)
    let era = api.createType("ExtrinsicEra", {
      current: lastHeader.number.toNumber(),
      period: ONE_SECOND * VALIDITY_PERIOD,
    });

    if (!!injector.signer.signPayload) {
      let payload = {
        address,
        blockHash: blockHash.toHex(),
        blockNumber: blocknumber.toHex(),
        era: era.toHex(),
        genesisHash: genesisHash.toHex(),
        method,
        nonce: nonce.toHex(),
        specVersion: specVersion.toHex(),
        transactionVersion: transactionVersion.toHex(),
        tip: "0x0",
        signedExtensions: api.registry.signedExtensions,
        version: 4,
      };

      let { signature } = await injector.signer.signPayload(payload);
      let extrinsic = api.registry.createType(
        'Extrinsic',
        { method: payload.method },
        { version: payload.version }
      );

      extrinsic.addSignature(payload.address, signature, payload);
    
      res(extrinsic.toU8a())
    } else {
      rej()
    }
  })
}

Trying to figure out the magic combination of types via polkadot.js was... painful!

@jsdw
Copy link
Collaborator Author

jsdw commented Jul 24, 2023

Ah brilliant, thankyou! Yeah, we tried that sort of approach first, though we were trying to get PJS to decode the entire signer payload and it sortof half worked but didn't. Your approach of encoding the specific values is def cleaner :)

Maybe there's a way we can show both approaches in an example; will have to think about that! I think depending on what you're trying to do, either could be good to know about!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants