use oasis_contract_sdk_types::{
env::{AccountsQuery, AccountsResponse, QueryRequest, QueryResponse},
InstanceId,
};
use oasis_runtime_sdk::{context::Context, modules::accounts::API as _, Runtime};
use super::{memory::Region, OasisV1};
use crate::{
abi::{gas, ExecutionContext},
types::Instance,
Config, Error,
};
impl<Cfg: Config> OasisV1<Cfg> {
pub fn link_env<C: Context>(
instance: &mut wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
) -> Result<(), Error> {
let _ = instance.link_function(
"env",
"query",
|ctx, query: (u32, u32)| -> Result<u32, wasm3::Trap> {
let ec = ctx.context.ok_or(wasm3::Trap::Abort)?;
gas::use_gas(ctx.instance, ec.params.gas_costs.wasm_env_query_base)?;
let request: QueryRequest = ctx.instance.runtime().try_with_memory(
|memory| -> Result<_, wasm3::Trap> {
let query = Region::from_arg(query).as_slice(&memory)?;
if query.len() > ec.params.max_query_size_bytes as usize {
return Err(wasm3::Trap::Abort);
}
cbor::from_slice(query).map_err(|_| wasm3::Trap::Abort)
},
)??;
let result = dispatch_query::<C>(ec.tx_context, request);
Self::serialize_and_allocate_as_ptr(ctx.instance, result).map_err(|err| err.into())
},
);
let _ = instance.link_function(
"env",
"address_for_instance",
|ctx, request: (u64, (u32, u32))| -> Result<(), wasm3::Trap> {
let ec = ctx.context.ok_or(wasm3::Trap::Abort)?;
gas::use_gas(ctx.instance, ec.params.gas_costs.wasm_env_query_base)?;
ctx.instance
.runtime()
.try_with_memory(|mut memory| -> Result<_, wasm3::Trap> {
let instance_id: InstanceId = request.0.into();
let dst = Region::from_arg(request.1).as_slice_mut(&mut memory)?;
let address = Instance::address_for(instance_id);
dst.copy_from_slice(address.as_ref());
Ok(())
})?
},
);
#[cfg(feature = "debug-utils")]
let _ = instance.link_function(
"env",
"debug_print",
|ctx, request: (u32, u32)| -> Result<(), wasm3::Trap> {
ctx.instance
.runtime()
.try_with_memory(|memory| -> Result<_, wasm3::Trap> {
let msg_bytes = Region::from_arg(request).as_slice(&memory)?;
if let Ok(msg) = std::str::from_utf8(msg_bytes) {
eprintln!("{msg}");
}
Ok(())
})?
},
);
Ok(())
}
}
fn dispatch_query<C: Context>(ctx: &C, query: QueryRequest) -> QueryResponse {
match query {
QueryRequest::BlockInfo => QueryResponse::BlockInfo {
round: ctx.runtime_header().round,
epoch: ctx.epoch(),
timestamp: ctx.runtime_header().timestamp,
},
QueryRequest::Accounts(query) => dispatch_accounts_query::<C>(ctx, query),
_ => QueryResponse::Error {
module: "".to_string(),
code: 1,
message: "query not supported".to_string(),
},
}
}
fn dispatch_accounts_query<C: Context>(_ctx: &C, query: AccountsQuery) -> QueryResponse {
match query {
AccountsQuery::Balance {
address,
denomination,
} => {
let balance =
<C::Runtime as Runtime>::Accounts::get_balance(address.into(), denomination.into())
.unwrap_or_default();
AccountsResponse::Balance { balance }.into()
}
_ => QueryResponse::Error {
module: "".to_string(),
code: 1,
message: "query not supported".to_string(),
},
}
}