1use crate::app::CosmosRouter;
2use crate::error::{bail, AnyResult};
3use crate::executor::AppResponse;
4use crate::module::Module;
5use crate::prefixed_storage::{prefixed, prefixed_read};
6use crate::queries::bank::BankRemoteQuerier;
7use crate::wasm_emulation::channel::RemoteChannel;
8use crate::wasm_emulation::input::BankStorage;
9use crate::wasm_emulation::query::AllBankQuerier;
10use cosmwasm_std::{
11 coin, to_json_binary, Addr, AllBalanceResponse, Api, BalanceResponse, BankMsg, BankQuery,
12 Binary, BlockInfo, Coin, Event, Order, Querier, Storage,
13};
14use cosmwasm_std::{StdResult, SupplyResponse, Uint128};
15use cw_storage_plus::Map;
16use cw_utils::NativeBalance;
17use itertools::Itertools;
18use schemars::JsonSchema;
19
20pub(crate) const BALANCES: Map<&Addr, NativeBalance> = Map::new("balances");
21
22pub const NAMESPACE_BANK: &[u8] = b"bank";
23
24#[derive(Clone, std::fmt::Debug, PartialEq, Eq, JsonSchema)]
25pub enum BankSudo {
26 Mint {
27 to_address: String,
28 amount: Vec<Coin>,
29 },
30}
31
32pub trait Bank:
33 Module<ExecT = BankMsg, QueryT = BankQuery, SudoT = BankSudo> + AllBankQuerier
34{
35}
36
37#[derive(Default)]
38pub struct BankKeeper {
39 remote: Option<RemoteChannel>,
40}
41
42impl BankKeeper {
43 pub fn new() -> Self {
44 BankKeeper::default()
45 }
46
47 pub fn with_remote(mut self, remote: RemoteChannel) -> Self {
48 self.remote = Some(remote);
49 self
50 }
51
52 pub fn init_balance(
54 &self,
55 storage: &mut dyn Storage,
56 account: &Addr,
57 amount: Vec<Coin>,
58 ) -> AnyResult<()> {
59 let mut bank_storage = prefixed(storage, NAMESPACE_BANK);
60 self.set_balance(&mut bank_storage, account, amount)
61 }
62
63 fn set_balance(
65 &self,
66 bank_storage: &mut dyn Storage,
67 account: &Addr,
68 amount: Vec<Coin>,
69 ) -> AnyResult<()> {
70 let mut balance = NativeBalance(amount);
71 balance.normalize();
72 BALANCES
73 .save(bank_storage, account, &balance)
74 .map_err(Into::into)
75 }
76
77 fn get_balance(&self, bank_storage: &dyn Storage, account: &Addr) -> AnyResult<Vec<Coin>> {
78 if let Some(val) = BALANCES.may_load(bank_storage, account)? {
80 Ok(val.into_vec())
81 } else {
82 BankRemoteQuerier::get_balance(self.remote.clone().unwrap(), account)
83 }
84 }
85
86 fn get_supply(&self, bank_storage: &dyn Storage, denom: String) -> AnyResult<Coin> {
87 let supply: Uint128 = BALANCES
88 .range(bank_storage, None, None, Order::Ascending)
89 .collect::<StdResult<Vec<_>>>()?
90 .into_iter()
91 .map(|a| a.1)
92 .fold(Uint128::zero(), |accum, item| {
93 let mut subtotal = Uint128::zero();
94 for coin in item.into_vec() {
95 if coin.denom == denom {
96 subtotal += coin.amount;
97 }
98 }
99 accum + subtotal
100 });
101 Ok(coin(supply.into(), denom))
102 }
103
104 fn send(
105 &self,
106 bank_storage: &mut dyn Storage,
107 from_address: Addr,
108 to_address: Addr,
109 amount: Vec<Coin>,
110 ) -> AnyResult<()> {
111 self.burn(bank_storage, from_address, amount.clone())?;
112 self.mint(bank_storage, to_address, amount)
113 }
114
115 fn mint(
116 &self,
117 bank_storage: &mut dyn Storage,
118 to_address: Addr,
119 amount: Vec<Coin>,
120 ) -> AnyResult<()> {
121 let amount = self.normalize_amount(amount)?;
122 let b = self.get_balance(bank_storage, &to_address)?;
123 let b = NativeBalance(b) + NativeBalance(amount);
124 self.set_balance(bank_storage, &to_address, b.into_vec())
125 }
126
127 fn burn(
128 &self,
129 bank_storage: &mut dyn Storage,
130 from_address: Addr,
131 amount: Vec<Coin>,
132 ) -> AnyResult<()> {
133 let amount = self.normalize_amount(amount)?;
134 let a = self.get_balance(bank_storage, &from_address)?;
135 let a = (NativeBalance(a) - amount)?;
136 self.set_balance(bank_storage, &from_address, a.into_vec())
137 }
138
139 fn normalize_amount(&self, amount: Vec<Coin>) -> AnyResult<Vec<Coin>> {
141 let res: Vec<_> = amount.into_iter().filter(|x| !x.amount.is_zero()).collect();
142 if res.is_empty() {
143 bail!("Cannot transfer empty coins amount")
144 } else {
145 Ok(res)
146 }
147 }
148}
149
150fn coins_to_string(coins: &[Coin]) -> String {
151 coins
152 .iter()
153 .map(|c| format!("{}{}", c.amount, c.denom))
154 .join(",")
155}
156
157impl Bank for BankKeeper {}
158
159impl Module for BankKeeper {
160 type ExecT = BankMsg;
161 type QueryT = BankQuery;
162 type SudoT = BankSudo;
163
164 fn execute<ExecC, QueryC>(
165 &self,
166 _api: &dyn Api,
167 storage: &mut dyn Storage,
168 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
169 _block: &BlockInfo,
170 sender: Addr,
171 msg: BankMsg,
172 ) -> AnyResult<AppResponse> {
173 let mut bank_storage = prefixed(storage, NAMESPACE_BANK);
174 match msg {
175 BankMsg::Send { to_address, amount } => {
176 let events = vec![Event::new("transfer")
178 .add_attribute("recipient", &to_address)
179 .add_attribute("sender", &sender)
180 .add_attribute("amount", coins_to_string(&amount))];
181 self.send(
182 &mut bank_storage,
183 sender,
184 Addr::unchecked(to_address),
185 amount,
186 )?;
187 Ok(AppResponse { events, data: None })
188 }
189 BankMsg::Burn { amount } => {
190 self.burn(&mut bank_storage, sender, amount)?;
192 Ok(AppResponse::default())
193 }
194 m => bail!("Unsupported bank message: {:?}", m),
195 }
196 }
197
198 fn sudo<ExecC, QueryC>(
199 &self,
200 api: &dyn Api,
201 storage: &mut dyn Storage,
202 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
203 _block: &BlockInfo,
204 msg: BankSudo,
205 ) -> AnyResult<AppResponse> {
206 let mut bank_storage = prefixed(storage, NAMESPACE_BANK);
207 match msg {
208 BankSudo::Mint { to_address, amount } => {
209 let to_address = api.addr_validate(&to_address)?;
210 self.mint(&mut bank_storage, to_address, amount)?;
211 Ok(AppResponse::default())
212 }
213 }
214 }
215
216 fn query(
217 &self,
218 api: &dyn Api,
219 storage: &dyn Storage,
220 _querier: &dyn Querier,
221 _block: &BlockInfo,
222 request: BankQuery,
223 ) -> AnyResult<Binary> {
224 let bank_storage = prefixed_read(storage, NAMESPACE_BANK);
225 match request {
226 BankQuery::AllBalances { address } => {
227 let address = api.addr_validate(&address)?;
228 let amount = self.get_balance(&bank_storage, &address)?;
229 let res = AllBalanceResponse::new(amount);
230 Ok(to_json_binary(&res)?)
231 }
232 BankQuery::Balance { address, denom } => {
233 let address = api.addr_validate(&address)?;
234 let all_amounts = self.get_balance(&bank_storage, &address)?;
235 let amount = all_amounts
236 .into_iter()
237 .find(|c| c.denom == denom)
238 .unwrap_or_else(|| coin(0, denom));
239 let res = BalanceResponse::new(amount);
240 Ok(to_json_binary(&res)?)
241 }
242 BankQuery::Supply { denom } => {
243 let amount = self.get_supply(&bank_storage, denom)?;
244 let res = SupplyResponse::new(amount);
245 Ok(to_json_binary(&res)?)
246 }
247 q => bail!("Unsupported bank query: {:?}", q),
248 }
249 }
250}
251
252impl AllBankQuerier for BankKeeper {
253 fn query_all(&self, storage: &dyn Storage) -> AnyResult<BankStorage> {
254 let bank_storage = prefixed_read(storage, NAMESPACE_BANK);
255 let balances: Result<Vec<_>, _> = BALANCES
256 .range(&bank_storage, None, None, Order::Ascending)
257 .collect();
258 Ok(BankStorage { storage: balances? })
259 }
260}