use thruput_xdr::contract::{Contract, ContractEntrypointID};
use thruput_xdr::transaction::Transaction;
use thruput_xdr::types::{ContractAddress, SHA256Hash};
use xdr_common::VecM;
use crate::{
BuildCallTransaction, BuildDeployTransaction, PubkeyAccount, SigningError,
build_call_transaction, build_deploy_transaction, wallet_contract_hash,
};
pub const DEFAULT_WALLET_EXECUTE_ENTRYPOINT: ContractEntrypointID =
ContractEntrypointID::new(0xc3ed_b11f_dd6d_5d58_u64.to_be_bytes());
pub const DEFAULT_WALLET_DEPLOY_ENTRYPOINT: ContractEntrypointID =
ContractEntrypointID::new(0x6be0_cb0f_6de0_917a_u64.to_be_bytes());
#[derive(Debug, Clone, Copy)]
pub struct WalletInitCall<'a> {
pub entrypoint: ContractEntrypointID,
pub gas_limit: u64,
pub calldata: &'a [u8],
}
#[derive(Debug, Clone, Copy)]
pub struct BuildDefaultWalletCallTransaction<'a> {
pub account: &'a PubkeyAccount,
pub target_address: ContractAddress,
pub target_entrypoint: ContractEntrypointID,
pub target_calldata: &'a [u8],
pub target_gas_limit: u64,
pub gas_limit: u64,
pub block_expiry_height: i64,
pub nonce: u64,
}
pub fn build_default_wallet_call_transaction(
args: BuildDefaultWalletCallTransaction<'_>,
) -> Result<Transaction, SigningError> {
let call = (
args.target_address,
args.target_entrypoint,
args.target_gas_limit,
args.target_calldata,
);
let calldata = xdrs::cxs::encode(&[call].as_slice())
.map_err(|error| SigningError::XdrEncode(error.to_string()))?;
Ok(build_call_transaction(BuildCallTransaction {
invoked_address: args.account.address(),
entrypoint: DEFAULT_WALLET_EXECUTE_ENTRYPOINT,
calldata,
gas_limit: args.gas_limit,
block_expiry_height: args.block_expiry_height,
nonce: args.nonce,
auth_claim: args.account.auth_claim(),
}))
}
#[derive(Debug, Clone)]
pub enum DeploymentCode {
Attached(Contract),
Registered(SHA256Hash),
}
#[derive(Debug, Clone)]
pub struct BuildDefaultWalletDeployTransaction<'a> {
pub account: &'a PubkeyAccount,
pub deployment_nonce: SHA256Hash,
pub code: DeploymentCode,
pub amount: u128,
pub initializer: Option<WalletInitCall<'a>>,
pub gas_limit: u64,
pub block_expiry_height: i64,
pub nonce: u64,
}
pub fn build_default_wallet_deploy_transaction(
args: BuildDefaultWalletDeployTransaction<'_>,
) -> Result<Transaction, SigningError> {
let (hash, contracts_to_deploy) = match args.code {
DeploymentCode::Attached(contract) => {
(wallet_contract_hash(&contract)?, VecM(vec![contract]))
}
DeploymentCode::Registered(hash) => (hash, VecM(vec![])),
};
let initializer = args
.initializer
.map(|call| (call.entrypoint, call.gas_limit, call.calldata));
let calldata = xdrs::cxs::encode(&(args.deployment_nonce, hash, args.amount, initializer))
.map_err(|error| SigningError::XdrEncode(error.to_string()))?;
Ok(build_deploy_transaction(BuildDeployTransaction {
invoked_address: args.account.address(),
entrypoint: DEFAULT_WALLET_DEPLOY_ENTRYPOINT,
calldata,
gas_limit: args.gas_limit,
block_expiry_height: args.block_expiry_height,
nonce: args.nonce,
contracts_to_deploy,
auth_claim: args.account.auth_claim(),
}))
}
const DEFAULT_WALLET_RETURN_DECODE_LIMIT: usize = 1024;
pub fn decode_default_wallet_call_return(data: &[u8]) -> Result<Vec<u8>, SigningError> {
if data.len() > DEFAULT_WALLET_RETURN_DECODE_LIMIT {
return Err(SigningError::XdrDecode(
"native wallet return exceeds decoder buffer limit".into(),
));
}
let mut returns: Vec<Vec<u8>> =
xdrs::cxs::decode(data).map_err(|error| SigningError::XdrDecode(error.to_string()))?;
if returns.len() != 1 {
return Err(SigningError::UnexpectedWalletReturnCount(returns.len()));
}
Ok(returns.remove(0))
}