rosetta_tx_polkadot/
lib.rs1use anyhow::{bail, Context, Result};
2use parity_scale_codec::{Compact, Decode, Encode};
3use rosetta_config_polkadot::{PolkadotMetadata, PolkadotMetadataParams};
4use rosetta_core::crypto::address::Address;
5use rosetta_core::crypto::SecretKey;
6use rosetta_core::{BlockchainConfig, TransactionBuilder};
7
8#[derive(Debug, Decode, Encode)]
9struct AccountId32([u8; 32]);
10
11#[derive(Debug, Decode, Encode)]
12enum MultiAddress {
13 Id(AccountId32),
14}
15
16#[derive(Encode)]
17enum MultiSignature {
18 #[allow(unused)]
19 Ed25519([u8; 64]),
20 Sr25519([u8; 64]),
21}
22
23#[derive(Encode)]
24enum Era {
25 Immortal,
26}
27
28fn parse_address(address: &Address) -> Result<AccountId32> {
29 const CHECKSUM_LEN: usize = 2;
30 let body_len = 32;
31
32 let data = bs58::decode(address.address()).into_vec()?;
33 if data.len() < 2 {
34 anyhow::bail!("ss58: bad length");
35 }
36 let (prefix_len, _ident) = match data[0] {
37 0..=63 => (1, data[0] as u16),
38 64..=127 => {
39 let lower = (data[0] << 2) | (data[1] >> 6);
45 let upper = data[1] & 0b00111111;
46 (2, (lower as u16) | ((upper as u16) << 8))
47 }
48 _ => anyhow::bail!("ss58: invalid prefix"),
49 };
50 if data.len() != prefix_len + body_len + CHECKSUM_LEN {
51 anyhow::bail!("ss58: bad length");
52 }
53 let hash = ss58hash(&data[0..body_len + prefix_len]);
59 let checksum = &hash.as_bytes()[0..CHECKSUM_LEN];
60 if data[body_len + prefix_len..body_len + prefix_len + CHECKSUM_LEN] != *checksum {
61 anyhow::bail!("invalid checksum");
63 }
64
65 let result = data[prefix_len..body_len + prefix_len]
66 .try_into()
67 .context("ss58: bad length")?;
68 Ok(AccountId32(result))
69}
70
71fn ss58hash(data: &[u8]) -> blake2_rfc::blake2b::Blake2bResult {
72 let mut context = blake2_rfc::blake2b::Blake2b::new(64);
73 context.update(b"SS58PRE");
74 context.update(data);
75 context.finalize()
76}
77
78#[derive(Default)]
79pub struct PolkadotTransactionBuilder;
80
81impl TransactionBuilder for PolkadotTransactionBuilder {
82 type MetadataParams = PolkadotMetadataParams;
83 type Metadata = PolkadotMetadata;
84
85 fn transfer(&self, address: &Address, amount: u128) -> Result<Self::MetadataParams> {
86 let address: AccountId32 = parse_address(address)?;
87 let dest = MultiAddress::Id(address);
88 #[derive(Debug, Decode, Encode)]
89 struct Transfer {
90 pub dest: MultiAddress,
91 #[codec(compact)]
92 pub amount: u128,
93 }
94 Ok(PolkadotMetadataParams {
95 pallet_name: "Balances".into(),
96 call_name: "transfer".into(),
97 call_args: Transfer { dest, amount }.encode(),
98 })
99 }
100
101 fn method_call(
102 &self,
103 _module: &str,
104 _method: &str,
105 _params: &[String],
106 _amount: u128,
107 ) -> Result<Self::MetadataParams> {
108 bail!("Not Implemented")
109 }
110
111 fn create_and_sign(
112 &self,
113 _config: &BlockchainConfig,
114 metadata_params: &Self::MetadataParams,
115 metadata: &Self::Metadata,
116 secret_key: &SecretKey,
117 ) -> Vec<u8> {
118 let address = AccountId32(secret_key.public_key().to_bytes().try_into().unwrap());
119 let address = MultiAddress::Id(address);
120 let extra_parameters = (
121 Era::Immortal,
122 Compact(metadata.nonce as u64),
123 Compact(0u128),
125 );
126 let additional_parameters = (
127 metadata.spec_version,
128 metadata.transaction_version,
129 metadata.genesis_hash,
130 metadata.genesis_hash,
131 );
132
133 let mut payload = vec![];
135 metadata.pallet_index.encode_to(&mut payload);
136 metadata.call_index.encode_to(&mut payload);
137 payload.extend(&metadata_params.call_args);
138 extra_parameters.encode_to(&mut payload);
139 additional_parameters.encode_to(&mut payload);
140
141 let signature = if payload.len() > 256 {
143 let hash = blake2_rfc::blake2b::blake2b(64, &[], &payload);
144 secret_key.sign(hash.as_bytes(), "substrate")
145 } else {
146 secret_key.sign(&payload, "substrate")
147 };
148 let signature =
149 MultiSignature::Sr25519(signature.to_bytes().as_slice().try_into().unwrap());
150
151 let mut encoded = vec![];
153 (0b10000000 + 4u8).encode_to(&mut encoded);
155 address.encode_to(&mut encoded);
157 signature.encode_to(&mut encoded);
159 extra_parameters.encode_to(&mut encoded);
161 metadata.pallet_index.encode_to(&mut encoded);
163 metadata.call_index.encode_to(&mut encoded);
164 encoded.extend(&metadata_params.call_args);
165
166 let len = Compact(encoded.len() as u32);
168 let mut transaction = vec![];
169 len.encode_to(&mut transaction);
170 transaction.extend(encoded);
171 transaction
172 }
173
174 fn deploy_contract(&self, _contract_binary: Vec<u8>) -> Result<Self::MetadataParams> {
175 bail!("Not Implemented")
176 }
177}