Skip to content
This repository was archived by the owner on Aug 1, 2023. It is now read-only.

Commit 7337a1c

Browse files
authored
Cob/account liquidity experiment (#279)
* account liqudity api experiment * add gateway namespace, add portfolio endpoint * playbook to update binary on full nodes ( update rpc ) * add exec trx request to repl
1 parent b0e4858 commit 7337a1c

File tree

10 files changed

+281
-7
lines changed

10 files changed

+281
-7
lines changed

chains/repl.js

+13
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,19 @@ function defineCommands(r, { api, keyring, types }, saddle, network, contracts)
179179
})
180180
});
181181

182+
r.defineCommand('exec', {
183+
help: 'Sign and send a trx request from saddle eth addr',
184+
action: defineAction(r, async (request) => {
185+
let user = saddle.account;// get saddle user and make chain account
186+
const nonce = await api.query.cash.nonces({eth: user});
187+
let req = `${nonce}:${request}`
188+
let sig = await saddle.web3.eth.sign(req, user)
189+
console.log("🎲", req)
190+
let tx = api.tx.cash.execTrxRequest(request, {'Eth': [user, sig]}, nonce)
191+
console.log("🏁", await tx.send())
192+
})
193+
});
194+
182195
r.defineCommand('eth_network', {
183196
help: 'Show given Ethereum network',
184197
action: defineAction(r, async () => {

node/src/api.rs

+137-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ use sp_runtime::{generic::BlockId, traits::Block as BlockT};
1111

1212
use pallet_cash::{
1313
chains::{ChainAccount, ChainAsset},
14+
portfolio::Portfolio,
1415
rates::APR,
1516
reason::Reason,
16-
types::{AssetAmount, AssetBalance, AssetInfo},
17+
types::{AssetAmount, AssetBalance, AssetInfo, InterestRateModel, Symbol},
1718
};
1819
use pallet_cash_runtime_api::CashApi as CashRuntimeApi;
1920
use pallet_oracle::types::AssetPrice;
@@ -43,13 +44,41 @@ pub struct ApiAssetData {
4344
price: String,
4445
}
4546

47+
#[derive(Deserialize, Serialize, Types)]
48+
pub enum ApiInterestRateModel {
49+
Kink {
50+
zero_rate: String,
51+
kink_rate: String,
52+
kink_utilization: String,
53+
full_rate: String,
54+
},
55+
}
56+
57+
#[derive(Deserialize, Serialize, Types)]
58+
pub struct ApiAssetInfo {
59+
asset: ChainAsset,
60+
decimals: u8,
61+
liquidity_factor: String,
62+
rate_model: ApiInterestRateModel,
63+
miner_shares: String,
64+
supply_cap: String,
65+
symbol: Symbol,
66+
ticker: String,
67+
}
68+
4669
#[derive(Deserialize, Serialize, Types)]
4770
pub struct ApiCashData {
4871
balance: String,
4972
cash_yield: String,
5073
price: String,
5174
}
5275

76+
#[derive(Deserialize, Serialize, Types)]
77+
pub struct ApiPortfolio {
78+
cash: String,
79+
positions: Vec<(ChainAsset, String)>,
80+
}
81+
5382
/// Converts a runtime trap into an RPC error.
5483
fn runtime_err(err: impl std::fmt::Debug) -> RpcError {
5584
RpcError {
@@ -93,6 +122,25 @@ pub trait GatewayRpcApi<BlockHash> {
93122

94123
#[rpc(name = "gateway_rates")]
95124
fn gateway_rates(&self, asset: ChainAsset, at: Option<BlockHash>) -> RpcResult<ApiRates>;
125+
126+
#[rpc(name = "gateway_assets")]
127+
fn gateway_assets(&self, at: Option<BlockHash>) -> RpcResult<Vec<ApiAssetInfo>>;
128+
129+
#[rpc(name = "gateway_chain_accounts")]
130+
fn chain_accounts(&self, at: Option<BlockHash>) -> RpcResult<Vec<ChainAccount>>;
131+
132+
#[rpc(name = "gateway_chain_account_liquidities")]
133+
fn chain_account_liquidities(
134+
&self,
135+
at: Option<BlockHash>,
136+
) -> RpcResult<Vec<(ChainAccount, String)>>;
137+
138+
#[rpc(name = "gateway_chain_account_portfolio")]
139+
fn chain_account_portfolio(
140+
&self,
141+
account: ChainAccount,
142+
at: Option<BlockHash>,
143+
) -> RpcResult<ApiPortfolio>;
96144
}
97145

98146
pub struct GatewayRpcHandler<C, B> {
@@ -226,4 +274,92 @@ where
226274
.map_err(chain_err)?;
227275
Ok((borrow_rate.0 as ApiAPR, supply_rate.0 as ApiAPR)) // XXX try_into?
228276
}
277+
278+
fn gateway_assets(&self, at: Option<<B as BlockT>::Hash>) -> RpcResult<Vec<ApiAssetInfo>> {
279+
let api = self.client.runtime_api();
280+
let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
281+
let assets = api
282+
.get_assets(&at)
283+
.map_err(runtime_err)?
284+
.map_err(chain_err)?;
285+
286+
fn api_rate_model(model: InterestRateModel) -> ApiInterestRateModel {
287+
match model {
288+
InterestRateModel::Kink {
289+
zero_rate,
290+
kink_rate,
291+
kink_utilization,
292+
full_rate,
293+
} => ApiInterestRateModel::Kink {
294+
zero_rate: format!("{:?}", zero_rate.0),
295+
kink_rate: format!("{:?}", kink_rate.0),
296+
kink_utilization: format!("{:?}", kink_utilization.0),
297+
full_rate: format!("{:?}", full_rate.0),
298+
},
299+
}
300+
}
301+
302+
let assets_lite = assets
303+
.iter()
304+
.map(|asset_info| ApiAssetInfo {
305+
asset: asset_info.asset,
306+
decimals: asset_info.decimals,
307+
liquidity_factor: format!("{}", asset_info.liquidity_factor.0),
308+
rate_model: api_rate_model(asset_info.rate_model),
309+
miner_shares: format!("{}", asset_info.miner_shares.0),
310+
supply_cap: format!("{}", asset_info.supply_cap),
311+
symbol: asset_info.symbol,
312+
ticker: String::from(asset_info.ticker),
313+
})
314+
.collect();
315+
316+
Ok(assets_lite) // XXX try_into?
317+
}
318+
319+
fn chain_accounts(&self, at: Option<<B as BlockT>::Hash>) -> RpcResult<Vec<ChainAccount>> {
320+
let api = self.client.runtime_api();
321+
let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
322+
let accounts = api
323+
.get_accounts(&at)
324+
.map_err(runtime_err)?
325+
.map_err(chain_err)?;
326+
Ok(accounts) // XXX try_into?
327+
}
328+
329+
fn chain_account_liquidities(
330+
&self,
331+
at: Option<<B as BlockT>::Hash>,
332+
) -> RpcResult<Vec<(ChainAccount, String)>> {
333+
let api = self.client.runtime_api();
334+
let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
335+
let accounts = api
336+
.get_accounts_liquidity(&at)
337+
.map_err(runtime_err)?
338+
.map_err(chain_err)?;
339+
Ok(accounts) // XXX try_into?
340+
}
341+
342+
fn chain_account_portfolio(
343+
&self,
344+
account: ChainAccount,
345+
at: Option<<B as BlockT>::Hash>,
346+
) -> RpcResult<ApiPortfolio> {
347+
let api = self.client.runtime_api();
348+
let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
349+
let result: Portfolio = api
350+
.get_portfolio(&at, account)
351+
.map_err(runtime_err)?
352+
.map_err(chain_err)?;
353+
354+
let positions_lite = result
355+
.positions
356+
.iter()
357+
.map(|p| (p.0.asset, format!("{}", p.1.value)))
358+
.collect();
359+
print!("{:?}", positions_lite);
360+
Ok(ApiPortfolio {
361+
cash: format!("{}", result.cash.value),
362+
positions: positions_lite,
363+
})
364+
}
229365
}

node/src/chain_spec.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ pub(crate) mod tests {
241241
#[test]
242242
fn test_extract_configuration_from_properties_happy_path() {
243243
let expected_starport = "hello starport";
244-
let expected_topic = "hello topic";
244+
let _expected_topic = "hello topic";
245245
let properties = serde_json::json!({ "eth_starport_address": expected_starport });
246246
let properties = properties.as_object().unwrap();
247247

node/src/rpc.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,9 @@ where
100100
let FullDeps {
101101
client,
102102
pool,
103-
select_chain, // XXX delete?
104-
chain_spec, // XXX delete?
105103
deny_unsafe,
106104
grandpa,
105+
..
107106
} = deps;
108107

109108
let GrandpaDeps {
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
- hosts:
2+
- full_node
3+
tasks:
4+
- name: stop gateway service
5+
become: true
6+
systemd:
7+
name: gateway.service
8+
state: stopped
9+
10+
- include_role:
11+
name: chain_node
12+
tasks_from: build-chain.yml
13+
14+
- name: start gateway service
15+
become: true
16+
systemd:
17+
name: gateway.service
18+
state: started
19+

pallets/cash/runtime-api/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use pallet_cash::{
22
chains::{ChainAccount, ChainAsset},
3+
portfolio::Portfolio,
34
rates::APR,
45
reason::Reason,
56
types::{AssetAmount, AssetBalance, AssetInfo},
@@ -17,5 +18,9 @@ sp_api::decl_runtime_apis! {
1718
fn get_price(ticker: String) -> Result<AssetPrice, Reason>;
1819
fn get_price_with_ticker(ticker: Ticker) -> Result<AssetPrice, Reason>;
1920
fn get_rates(asset: ChainAsset) -> Result<(APR, APR), Reason>;
21+
fn get_assets() -> Result<Vec<AssetInfo>, Reason>;
22+
fn get_accounts() -> Result<Vec<ChainAccount>, Reason>;
23+
fn get_accounts_liquidity() -> Result<Vec<(ChainAccount, String)>, Reason>;
24+
fn get_portfolio(account: ChainAccount) -> Result<Portfolio, Reason>;
2025
}
2126
}

pallets/cash/src/core.rs

+62
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ pub fn get_rates<T: Config>(asset: ChainAsset) -> Result<(APR, APR), Reason> {
108108
.get_rates(utilization, APR::ZERO, info.miner_shares)?)
109109
}
110110

111+
/// Return the current list of assets.
112+
pub fn get_assets<T: Config>() -> Result<Vec<AssetInfo>, Reason> {
113+
let info = SupportedAssets::iter()
114+
.map(|(_chain_asset, asset_info)| asset_info)
115+
.collect::<Vec<AssetInfo>>();
116+
Ok(info)
117+
}
118+
111119
/// Return the current total borrow and total supply balances for the asset.
112120
pub fn get_market_totals<T: Config>(
113121
asset: ChainAsset,
@@ -132,6 +140,28 @@ pub fn get_cash_yield<T: Config>() -> Result<APR, Reason> {
132140
Ok(CashYield::get())
133141
}
134142

143+
/// Return the current borrow and supply rates for the asset.
144+
pub fn get_accounts<T: Config>() -> Result<Vec<ChainAccount>, Reason> {
145+
let info: Vec<ChainAccount> = CashPrincipals::iter()
146+
.map(|p| p.0)
147+
.collect::<Vec<ChainAccount>>();
148+
Ok(info)
149+
}
150+
151+
/// Return the current borrow and supply rates for the asset.
152+
pub fn get_accounts_liquidity<T: Config>() -> Result<Vec<(ChainAccount, AssetBalance)>, Reason> {
153+
let mut info: Vec<(ChainAccount, AssetBalance)> = CashPrincipals::iter()
154+
.map(|a| (a.0.clone(), get_liquidity::<T>(a.0).unwrap().value))
155+
.collect::<Vec<(ChainAccount, AssetBalance)>>();
156+
info.sort_by(|(_a_account, a_balance), (_b_account, b_balance)| a_balance.cmp(b_balance));
157+
Ok(info)
158+
}
159+
160+
/// Return the portfolio of the chain account
161+
pub fn get_portfolio<T: Config>(account: ChainAccount) -> Result<Portfolio, Reason> {
162+
Ok(Portfolio::from_storage::<T>(account)?)
163+
}
164+
135165
// Internal helpers
136166

137167
pub fn passes_validation_threshold(
@@ -1492,6 +1522,38 @@ mod tests {
14921522
})
14931523
}
14941524

1525+
#[test]
1526+
fn test_get_assets() -> Result<(), Reason> {
1527+
new_test_ext().execute_with(|| {
1528+
initialize_storage();
1529+
let assets = vec![
1530+
AssetInfo {
1531+
ticker: FromStr::from_str("USD").unwrap(),
1532+
liquidity_factor: FromStr::from_str("7890").unwrap(),
1533+
..AssetInfo::minimal(
1534+
FromStr::from_str("eth:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
1535+
.unwrap(),
1536+
FromStr::from_str("USDC/6").unwrap(),
1537+
)
1538+
},
1539+
AssetInfo {
1540+
liquidity_factor: FromStr::from_str("7890").unwrap(),
1541+
..AssetInfo::minimal(
1542+
FromStr::from_str("eth:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE")
1543+
.unwrap(),
1544+
FromStr::from_str("ETH/18").unwrap(),
1545+
)
1546+
},
1547+
];
1548+
1549+
let found_assets = crate::core::get_assets::<Test>()?;
1550+
1551+
assert_eq!(assets, found_assets);
1552+
1553+
Ok(())
1554+
})
1555+
}
1556+
14951557
#[test]
14961558
fn test_has_liquidity_to_reduce_cash() -> Result<(), Reason> {
14971559
const BAT: Units = Units::from_ticker_str("BAT", 18);

pallets/cash/src/lib.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::{
1717
},
1818
events::{ChainLogEvent, ChainLogId, EventState},
1919
notices::{Notice, NoticeId, NoticeState},
20+
portfolio::Portfolio,
2021
types::{
2122
AssetAmount, AssetBalance, AssetIndex, AssetInfo, Bips, CashIndex, CashPrincipal,
2223
CashPrincipalAmount, CodeHash, EncodedNotice, GovernanceResult, InterestRateModel,
@@ -28,7 +29,7 @@ use codec::alloc::string::String;
2829
use frame_support::{
2930
decl_event, decl_module, decl_storage, dispatch,
3031
sp_runtime::traits::Convert,
31-
traits::{OnRuntimeUpgrade, StoredMap, UnfilteredDispatchable},
32+
traits::{StoredMap, UnfilteredDispatchable},
3233
weights::{DispatchClass, GetDispatchInfo, Pays, Weight},
3334
Parameter,
3435
};
@@ -591,7 +592,7 @@ decl_module! {
591592

592593
// Remove any notice holds if they have been executed
593594
#[weight = (1, DispatchClass::Normal, Pays::No)] // XXX
594-
pub fn cull_notices(origin) -> dispatch::DispatchResult {
595+
pub fn cull_notices(_origin) -> dispatch::DispatchResult {
595596
log!("Culling executed notices");
596597
NoticeHolds::iter().for_each(|(chain_id, notice_id)| {
597598
match NoticeStates::get(chain_id, notice_id) {
@@ -677,6 +678,29 @@ impl<T: Config> Module<T> {
677678
pub fn get_rates(asset: ChainAsset) -> Result<(APR, APR), Reason> {
678679
Ok(core::get_rates::<T>(asset)?)
679680
}
681+
682+
/// Get the list of assets
683+
pub fn get_assets() -> Result<Vec<AssetInfo>, Reason> {
684+
Ok(core::get_assets::<T>()?)
685+
}
686+
/// Get the rates for the given asset.
687+
pub fn get_accounts() -> Result<Vec<ChainAccount>, Reason> {
688+
Ok(core::get_accounts::<T>()?)
689+
}
690+
691+
/// Get the all liquidity
692+
pub fn get_accounts_liquidity() -> Result<Vec<(ChainAccount, String)>, Reason> {
693+
let accounts: Vec<(ChainAccount, String)> = core::get_accounts_liquidity::<T>()?
694+
.iter()
695+
.map(|(chain_account, bal)| (chain_account.clone(), format!("{}", bal)))
696+
.collect();
697+
Ok(accounts)
698+
}
699+
700+
/// Get the portfolio for the given chain account.
701+
pub fn get_portfolio(account: ChainAccount) -> Result<Portfolio, Reason> {
702+
Ok(core::get_portfolio::<T>(account)?)
703+
}
680704
}
681705

682706
impl<T: Config> frame_support::unsigned::ValidateUnsigned for Module<T> {

0 commit comments

Comments
 (0)