Replace substrate/client's use of Payload with usage of RuntimeCall

Gains explicit typing.
This commit is contained in:
Luke Parker 2023-11-22 11:23:00 -05:00
parent fcfdadc791
commit 08e6669403
No known key found for this signature in database
9 changed files with 93 additions and 105 deletions

View file

@ -5,9 +5,7 @@ use serai_runtime::{
pub use coins::primitives;
use primitives::OutInstructionWithBalance;
use subxt::tx::Payload;
use crate::{TemporalSerai, SeraiError, Composite, scale_value, scale_composite};
use crate::{TemporalSerai, SeraiError, scale_value};
const PALLET: &str = "Coins";
@ -56,23 +54,22 @@ impl<'a> SeraiCoins<'a> {
))
}
pub fn transfer(to: SeraiAddress, balance: Balance) -> Payload<Composite<()>> {
Payload::new(
PALLET,
"transfer",
scale_composite(serai_runtime::coins::Call::<Runtime>::transfer { to: to.into(), balance }),
)
pub fn transfer(to: SeraiAddress, balance: Balance) -> serai_runtime::RuntimeCall {
serai_runtime::RuntimeCall::Coins(serai_runtime::coins::Call::<Runtime>::transfer {
to: to.into(),
balance,
})
}
pub fn burn(balance: Balance) -> Payload<Composite<()>> {
Payload::new(PALLET, "burn", scale_composite(coins::Call::<Runtime>::burn { balance }))
pub fn burn(balance: Balance) -> serai_runtime::RuntimeCall {
serai_runtime::RuntimeCall::Coins(serai_runtime::coins::Call::<Runtime>::burn { balance })
}
pub fn burn_with_instruction(instruction: OutInstructionWithBalance) -> Payload<Composite<()>> {
Payload::new(
PALLET,
"burn_with_instruction",
scale_composite(coins::Call::<Runtime>::burn_with_instruction { instruction }),
pub fn burn_with_instruction(
instruction: OutInstructionWithBalance,
) -> serai_runtime::RuntimeCall {
serai_runtime::RuntimeCall::Coins(
serai_runtime::coins::Call::<Runtime>::burn_with_instruction { instruction },
)
}
}

View file

@ -4,11 +4,7 @@ use serai_runtime::{
dex, Dex, Runtime,
};
use subxt::tx::Payload;
use crate::{SeraiError, Composite, TemporalSerai, scale_composite};
const PALLET: &str = "Dex";
use crate::{SeraiError, TemporalSerai};
pub type DexEvent = dex::Event<Runtime>;
@ -26,19 +22,15 @@ impl<'a> SeraiDex<'a> {
min_coin_amount: Amount,
min_sri_amount: Amount,
address: SeraiAddress,
) -> Payload<Composite<()>> {
Payload::new(
PALLET,
"add_liquidity",
scale_composite(dex::Call::<Runtime>::add_liquidity {
coin,
coin_desired: coin_amount.0,
sri_desired: sri_amount.0,
coin_min: min_coin_amount.0,
sri_min: min_sri_amount.0,
mint_to: address.into(),
}),
)
) -> serai_runtime::RuntimeCall {
serai_runtime::RuntimeCall::Dex(dex::Call::<Runtime>::add_liquidity {
coin,
coin_desired: coin_amount.0,
sri_desired: sri_amount.0,
coin_min: min_coin_amount.0,
sri_min: min_sri_amount.0,
mint_to: address.into(),
})
}
pub fn swap(
@ -47,7 +39,7 @@ impl<'a> SeraiDex<'a> {
amount_in: Amount,
amount_out_min: Amount,
address: SeraiAddress,
) -> Payload<Composite<()>> {
) -> serai_runtime::RuntimeCall {
let path = if to_coin.is_native() {
BoundedVec::try_from(vec![from_coin, Coin::Serai]).unwrap()
} else if from_coin.is_native() {
@ -56,15 +48,11 @@ impl<'a> SeraiDex<'a> {
BoundedVec::try_from(vec![from_coin, Coin::Serai, to_coin]).unwrap()
};
Payload::new(
PALLET,
"swap_exact_tokens_for_tokens",
scale_composite(dex::Call::<Runtime>::swap_exact_tokens_for_tokens {
path,
amount_in: amount_in.0,
amount_out_min: amount_out_min.0,
send_to: address.into(),
}),
)
serai_runtime::RuntimeCall::Dex(dex::Call::<Runtime>::swap_exact_tokens_for_tokens {
path,
amount_in: amount_in.0,
amount_out_min: amount_out_min.0,
send_to: address.into(),
})
}
}

View file

@ -2,8 +2,6 @@ use serai_runtime::{in_instructions, InInstructions, Runtime};
pub use in_instructions::primitives;
use primitives::SignedBatch;
use subxt::utils::Encoded;
use crate::{
primitives::{BlockHash, NetworkId},
SeraiError, Serai, TemporalSerai, scale_value,
@ -41,7 +39,7 @@ impl<'a> SeraiInInstructions<'a> {
.await
}
pub fn execute_batch(batch: SignedBatch) -> Encoded {
pub fn execute_batch(batch: SignedBatch) -> Vec<u8> {
Serai::unsigned::<InInstructions, _>(&in_instructions::Call::<Runtime>::execute_batch { batch })
}
}

View file

@ -4,7 +4,7 @@ use futures::stream::{Stream, StreamExt};
use scale::{Encode, Decode, Compact};
mod scale_value;
pub(crate) use scale_value::{Value, Composite, scale_value, scale_composite};
pub(crate) use scale_value::{Value, scale_value};
pub use sp_core::{
Pair as PairTrait,
@ -14,13 +14,12 @@ pub use sp_core::{
pub use subxt;
use subxt::{
error::Error as SubxtError,
utils::Encoded,
config::{
Header as HeaderTrait,
substrate::{BlakeTwo256, SubstrateHeader},
extrinsic_params::{BaseExtrinsicParams, BaseExtrinsicParamsBuilder},
extrinsic_params::BaseExtrinsicParams,
},
tx::{Signer, Payload, TxClient},
tx::Signer,
rpc::types::{ChainBlock, ChainBlockExtrinsic},
Config as SubxtConfig, OnlineClient,
};
@ -149,7 +148,7 @@ impl Serai {
Ok(Serai(OnlineClient::<SeraiConfig>::from_url(url).await.map_err(SeraiError::RpcError)?))
}
fn unsigned<P: 'static, C: Encode>(call: &C) -> Encoded {
fn unsigned<P: 'static, C: Encode>(call: &C) -> Vec<u8> {
// TODO: Should Serai purge the old transaction code AND set this to 0/1?
const TRANSACTION_VERSION: u8 = 4;
@ -162,30 +161,54 @@ impl Serai {
bytes.extend(call.encode());
// Prefix the length
let mut complete_bytes = scale::Compact(u32::try_from(bytes.len()).unwrap()).encode();
let mut complete_bytes = Compact(u32::try_from(bytes.len()).unwrap()).encode();
complete_bytes.extend(bytes);
Encoded(complete_bytes)
complete_bytes
}
pub fn sign<S: Send + Sync + Signer<SeraiConfig>>(
&self,
signer: &S,
payload: &Payload<Composite<()>>,
call: &serai_runtime::RuntimeCall,
nonce: u32,
params: BaseExtrinsicParamsBuilder<SeraiConfig, Tip>,
) -> Result<Encoded, SeraiError> {
TxClient::new(self.0.offline())
.create_signed_with_nonce(payload, signer, nonce, params)
.map(|tx| Encoded(tx.into_encoded()))
// TODO: Don't have this potentially return an error (requires modifying the Payload type)
.map_err(|_| SeraiError::InvalidRuntime)
tip: Tip,
) -> Vec<u8> {
const SPEC_VERSION: u32 = 100;
const IMPL_VERSION: u32 = 1;
const TRANSACTION_VERSION: u8 = 4;
let era = subxt::config::substrate::Era::Immortal;
let extra = (era, Compact(nonce), tip);
let genesis = self.0.genesis_hash();
let mortality_checkpoint = genesis;
let mut signature_payload =
(call, extra, SPEC_VERSION, IMPL_VERSION, genesis, mortality_checkpoint).encode();
if signature_payload.len() > 256 {
use subxt::config::Hasher;
signature_payload = BlakeTwo256::hash(&signature_payload).0.to_vec();
}
let signature = signer.sign(&signature_payload);
let signed = 1 << 7;
let extrinsic =
(signed + TRANSACTION_VERSION, signer.address(), signature, extra, call).encode();
let mut res = Compact(u32::try_from(extrinsic.len()).unwrap()).encode();
res.extend(&extrinsic);
res
}
pub async fn publish(&self, tx: &Encoded) -> Result<(), SeraiError> {
// Drop the hash, which is the hash of the raw TX, as TXs are allowed to share hashes and this
// hash is practically useless/unsafe
// If we are to return something, it should be block included in and position within block
self.0.rpc().submit_extrinsic(tx).await.map(|_| ()).map_err(SeraiError::RpcError)
pub async fn publish(&self, tx: &[u8]) -> Result<(), SeraiError> {
self
.0
.rpc()
.deref()
.request::<[u8; 32]>("author_submitExtrinsic", subxt::rpc::rpc_params![tx])
.await
// Drop the hash, which is the hash of the raw extrinsic, as extrinsics are allowed to share
// hashes and this hash is accordingly useless/unsafe
// If we are to return something, it should be block included in and position within block
.map(|_| ())
.map_err(SeraiError::RpcError)
}
pub async fn latest_block_hash(&self) -> Result<[u8; 32], SeraiError> {

View file

@ -2,8 +2,8 @@ use ::scale::Encode;
use scale_info::{MetaType, TypeInfo, Registry, PortableRegistry};
use subxt::ext::scale_value;
pub(crate) use scale_value::{Composite, Value};
use scale_value::{ValueDef, scale};
pub(crate) use scale_value::Value;
use scale_value::scale;
pub(crate) fn scale_value<V: 'static + Encode + TypeInfo>(value: V) -> Value {
let mut registry = Registry::new();
@ -11,11 +11,3 @@ pub(crate) fn scale_value<V: 'static + Encode + TypeInfo>(value: V) -> Value {
let registry = PortableRegistry::from(registry);
scale::decode_as_type(&mut value.encode().as_ref(), id, &registry).unwrap().remove_context()
}
pub(crate) fn scale_composite<V: 'static + Encode + TypeInfo>(value: V) -> Composite<()> {
match scale_value(value).value {
ValueDef::Composite(composite) => composite,
ValueDef::Variant(variant) => variant.values,
_ => panic!("not composite"),
}
}

View file

@ -4,8 +4,6 @@ use serai_runtime::{primitives::Amount, validator_sets, ValidatorSets, Runtime};
pub use validator_sets::primitives;
use primitives::{Session, ValidatorSet, KeyPair};
use subxt::utils::Encoded;
use crate::{primitives::NetworkId, Serai, TemporalSerai, SeraiError, scale_value};
const PALLET: &str = "ValidatorSets";
@ -82,7 +80,7 @@ impl<'a> SeraiValidatorSets<'a> {
self.0.storage(PALLET, "Keys", Some(vec![scale_value(set)])).await
}
pub fn set_keys(network: NetworkId, key_pair: KeyPair, signature: Signature) -> Encoded {
pub fn set_keys(network: NetworkId, key_pair: KeyPair, signature: Signature) -> Vec<u8> {
Serai::unsigned::<ValidatorSets, _>(&validator_sets::Call::<Runtime>::set_keys {
network,
key_pair,

View file

@ -11,7 +11,6 @@ use serai_runtime::coins::primitives::OutInstructionWithBalance;
use sp_core::Pair;
use serai_client::{
subxt::config::extrinsic_params::BaseExtrinsicParamsBuilder,
primitives::{
Amount, NetworkId, Coin, Balance, BlockHash, SeraiAddress, Data, ExternalAddress,
insecure_pair_from_name,
@ -95,9 +94,8 @@ serai_test!(
&PairSigner::new(pair),
&SeraiCoins::burn_with_instruction(instruction.clone()),
0,
BaseExtrinsicParamsBuilder::new(),
Default::default(),
)
.unwrap(),
)
.await;

View file

@ -3,8 +3,6 @@ use serai_runtime::primitives::{Coin, Amount};
use serai_client::{Serai, SeraiDex, PairSigner};
use sp_core::{sr25519::Pair, Pair as PairTrait};
use subxt::config::extrinsic_params::BaseExtrinsicParamsBuilder;
use crate::common::tx::publish_tx;
#[allow(dead_code)]
@ -18,14 +16,12 @@ pub async fn add_liquidity(
) -> [u8; 32] {
let address = pair.public();
let tx = serai
.sign(
&PairSigner::new(pair),
&SeraiDex::add_liquidity(coin, coin_amount, sri_amount, Amount(1), Amount(1), address.into()),
nonce,
BaseExtrinsicParamsBuilder::new(),
)
.unwrap();
let tx = serai.sign(
&PairSigner::new(pair),
&SeraiDex::add_liquidity(coin, coin_amount, sri_amount, Amount(1), Amount(1), address.into()),
nonce,
Default::default(),
);
publish_tx(serai, &tx).await
}
@ -42,14 +38,12 @@ pub async fn swap(
) -> [u8; 32] {
let address = pair.public();
let tx = serai
.sign(
&PairSigner::new(pair),
&SeraiDex::swap(from_coin, to_coin, amount_in, amount_out_min, address.into()),
nonce,
BaseExtrinsicParamsBuilder::new(),
)
.unwrap();
let tx = serai.sign(
&PairSigner::new(pair),
&SeraiDex::swap(from_coin, to_coin, amount_in, amount_out_min, address.into()),
nonce,
Default::default(),
);
publish_tx(serai, &tx).await
}

View file

@ -2,10 +2,10 @@ use core::time::Duration;
use tokio::time::sleep;
use serai_client::{subxt::utils::Encoded, Serai};
use serai_client::Serai;
#[allow(dead_code)]
pub async fn publish_tx(serai: &Serai, tx: &Encoded) -> [u8; 32] {
pub async fn publish_tx(serai: &Serai, tx: &[u8]) -> [u8; 32] {
let mut latest =
serai.block(serai.latest_block_hash().await.unwrap()).await.unwrap().unwrap().number();
@ -34,7 +34,7 @@ pub async fn publish_tx(serai: &Serai, tx: &Encoded) -> [u8; 32] {
};
for transaction in block.transactions() {
if transaction.0 == tx.0[2 ..] {
if transaction.0 == tx[2 ..] {
return block.hash();
}
}