I want to send cis2 multi token from wallet to smart contract and then contract to wallet but unfortunately unable to send through smart contract following is my function code
#[receive(
contract = "name",
name = "recieve_token",
mutable,
parameter = "TokenParam",
error = "VestingError"
)]
fn recieve_token<S: HasStateApi>(
_ctx: &impl HasReceiveContext,
host: &mut impl HasHost<State, StateApiType = S>,
) -> VestingResult<()> {
let params: TokenParam = _ctx
.parameter_cursor()
.get()
.map_err(|_e: ParseError| VestingError::ParseParams)?;
Cis2Client::transfer(
host,
params.token.id.clone(),
params.token.address,
params.token_amount,
_ctx.sender(),
Receiver::Contract(
_ctx.self_address(),
OwnedEntrypointName::new_unchecked("onReceivingCIS2".to_string()),
),
)?;
Ok(())
}
this is my parameter.json file
{
"token": {
"id": "01",
"address": {
"index": 5112,
"subindex": 0
}
},
"token_amount": "2000"
}
and here is the cis2client file
use std::vec;
use concordium_cis2::*;
use concordium_std::*;
use crate::{errors::VestingError, state::State};
pub const SUPPORTS_ENTRYPOINT_NAME: &str = "supports";
pub const OPERATOR_OF_ENTRYPOINT_NAME: &str = "operatorOf";
pub const BALANCE_OF_ENTRYPOINT_NAME: &str = "balanceOf";
pub const TRANSFER_ENTRYPOINT_NAME: &str = "transfer";
pub struct Cis2Client;
impl Cis2Client {
pub(crate) fn supports_cis2<S: HasStateApi>(
host: &mut impl HasHost<State, StateApiType = S>,
token_contract_address: &ContractAddress,
) -> Result<bool, VestingError> {
let params = SupportsQueryParams {
queries: vec![StandardIdentifierOwned::new_unchecked("CIS-2".to_string())],
};
let parsed_res: SupportsQueryResponse = Cis2Client::invoke_contract_read_only(
host,
token_contract_address,
SUPPORTS_ENTRYPOINT_NAME,
¶ms,
)?;
let supports_cis2: bool = {
let f = parsed_res
.results
.first()
.ok_or(VestingError::InvokeVestingError)?;
match f {
SupportResult::NoSupport => false,
SupportResult::Support => true,
SupportResult::SupportBy(_) => false,
}
};
Ok(supports_cis2)
}
pub(crate) fn is_operator_of<S: HasStateApi>(
host: &mut impl HasHost<State, StateApiType = S>,
owner: Address,
current_contract_address: ContractAddress,
token_contract_address: &ContractAddress,
) -> Result<bool, VestingError> {
let params = &OperatorOfQueryParams {
queries: vec![OperatorOfQuery {
owner,
address: Address::Contract(current_contract_address),
}],
};
let parsed_res: OperatorOfQueryResponse = Cis2Client::invoke_contract_read_only(
host,
token_contract_address,
OPERATOR_OF_ENTRYPOINT_NAME,
params,
)?;
let is_operator = parsed_res
.0
.first()
.ok_or(VestingError::InvokeVestingError)?
.to_owned();
Ok(is_operator)
}
pub(crate) fn get_balance<
S,
T: IsTokenId + Clone,
A: Default + IsTokenAmount + Clone + Copy + ops::Sub<Output = A>,
>(
host: &mut impl HasHost<State, StateApiType = S>,
token_id: T,
token_contract_address: &ContractAddress,
owner: Address,
) -> Result<A, VestingError>
where
S: HasStateApi,
{
let params = BalanceOfQueryParams {
queries: vec![BalanceOfQuery {
token_id,
address: owner,
}],
};
let parsed_res: BalanceOfQueryResponse<A> = Cis2Client::invoke_contract_read_only(
host,
token_contract_address,
BALANCE_OF_ENTRYPOINT_NAME,
¶ms,
)?;
let ret = parsed_res.0.first().map_or(A::default(), |f| *f);
Ok(ret)
}
pub(crate) fn transfer<
S,
T: IsTokenId + Clone,
A: IsTokenAmount + Clone + Copy + ops::Sub<Output = A>,
>(
host: &mut impl HasHost<State, StateApiType = S>,
token_id: T,
token_contract_address: ContractAddress,
amount: A,
from: Address,
to: Receiver,
) -> Result<bool, VestingError>
where
S: HasStateApi,
A: IsTokenAmount,
{
let params: TransferParams<T, A> = TransferParams(vec![Transfer {
token_id,
amount,
from,
data: AdditionalData::empty(),
to,
}]);
Cis2Client::invoke_contract_read_only(
host,
&token_contract_address,
TRANSFER_ENTRYPOINT_NAME,
¶ms,
)?;
Ok(true)
}
fn invoke_contract_read_only<S: HasStateApi, R: Deserial, P: Serial>(
host: &mut impl HasHost<State, StateApiType = S>,
contract_address: &ContractAddress,
entrypoint_name: &str,
params: &P,
) -> Result<R, VestingError> {
let invoke_contract_result = host
.invoke_contract_read_only(
contract_address,
params,
EntrypointName::new(entrypoint_name).unwrap_abort(),
Amount::from_ccd(0),
)
.map_err(|_| VestingError::InvokeContractNoResult)?;
let mut invoke_contract_res = match invoke_contract_result {
Some(s) => s,
None => return Err(VestingError::InvokeContractNoResult),
};
let parsed_res =
R::deserial(&mut invoke_contract_res).map_err(|_e| VestingError::ParseResult)?;
Ok(parsed_res)
}
}