Compare commits
1 Commits
f461d945cb
...
fix-proto-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd46f8fb6e |
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
name: Widget decomposition and provider subscriptions
|
|
||||||
description: Prefer splitting screens into multiple focused files/widgets; each widget subscribes to its own relevant providers
|
|
||||||
type: feedback
|
|
||||||
---
|
|
||||||
|
|
||||||
Split screens into multiple smaller widgets across multiple files. Each widget should subscribe only to the providers it needs (`ref.watch` at lowest possible level), rather than having one large screen widget that watches everything and passes data down as parameters.
|
|
||||||
|
|
||||||
**Why:** Reduces unnecessary rebuilds; improves readability; each file has one clear responsibility.
|
|
||||||
|
|
||||||
**How to apply:** When building a new screen, identify which sub-widgets need their own provider subscriptions and extract them into separate files (e.g., `widgets/grant_card.dart` watches enrichment providers itself, rather than the screen doing it and passing resolved strings down).
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,4 +3,3 @@ scripts/__pycache__/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.cargo/config.toml
|
.cargo/config.toml
|
||||||
.vscode/
|
.vscode/
|
||||||
docs/
|
|
||||||
|
|||||||
@@ -132,22 +132,17 @@ message SdkClientConnectionCancel {
|
|||||||
bytes pubkey = 1;
|
bytes pubkey = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message WalletAccess {
|
|
||||||
int32 wallet_id = 1;
|
|
||||||
int32 sdk_client_id = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SdkClientWalletAccess {
|
message SdkClientWalletAccess {
|
||||||
int32 id = 1;
|
int32 client_id = 1;
|
||||||
WalletAccess access = 2;
|
int32 wallet_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SdkClientGrantWalletAccess {
|
message SdkClientGrantWalletAccess {
|
||||||
repeated WalletAccess accesses = 1;
|
repeated SdkClientWalletAccess accesses = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SdkClientRevokeWalletAccess {
|
message SdkClientRevokeWalletAccess {
|
||||||
repeated int32 accesses = 1;
|
repeated SdkClientWalletAccess accesses = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListWalletAccessResponse {
|
message ListWalletAccessResponse {
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||
use tonic_prost_build::configure;
|
use std::path::PathBuf;
|
||||||
|
use tonic_prost_build::{Config, configure};
|
||||||
|
|
||||||
static PROTOBUF_DIR: &str = "../../../protobufs";
|
static PROTOBUF_DIR: &str = "../../../protobufs";
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("cargo::rerun-if-changed={PROTOBUF_DIR}");
|
println!("cargo::rerun-if-changed={PROTOBUF_DIR}");
|
||||||
|
|
||||||
|
let protoc_path = protoc_bin_vendored::protoc_bin_path()?;
|
||||||
|
let protoc_include = protoc_bin_vendored::include_path()?;
|
||||||
|
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.protoc_executable(protoc_path);
|
||||||
|
|
||||||
|
let protos = [
|
||||||
|
PathBuf::from(format!("{}/arbiter.proto", PROTOBUF_DIR)),
|
||||||
|
PathBuf::from(format!("{}/user_agent.proto", PROTOBUF_DIR)),
|
||||||
|
PathBuf::from(format!("{}/client.proto", PROTOBUF_DIR)),
|
||||||
|
PathBuf::from(format!("{}/evm.proto", PROTOBUF_DIR)),
|
||||||
|
];
|
||||||
|
|
||||||
|
let includes = [PathBuf::from(PROTOBUF_DIR), protoc_include];
|
||||||
|
|
||||||
configure()
|
configure()
|
||||||
.message_attribute(".", "#[derive(::kameo::Reply)]")
|
.message_attribute(".", "#[derive(::kameo::Reply)]")
|
||||||
.compile_protos(
|
.compile_with_config(config, &protos, &includes)?;
|
||||||
&[
|
|
||||||
format!("{}/arbiter.proto", PROTOBUF_DIR),
|
|
||||||
format!("{}/user_agent.proto", PROTOBUF_DIR),
|
|
||||||
format!("{}/client.proto", PROTOBUF_DIR),
|
|
||||||
format!("{}/evm.proto", PROTOBUF_DIR),
|
|
||||||
],
|
|
||||||
&[PROTOBUF_DIR.to_string()],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ use rand::{SeedableRng, rng, rngs::StdRng};
|
|||||||
use crate::{
|
use crate::{
|
||||||
actors::keyholder::{CreateNew, Decrypt, KeyHolder},
|
actors::keyholder::{CreateNew, Decrypt, KeyHolder},
|
||||||
db::{
|
db::{
|
||||||
self, DatabaseError, DatabasePool,
|
self, DatabasePool,
|
||||||
models::{self, SqliteTimestamp},
|
models::{self, SqliteTimestamp},
|
||||||
schema,
|
schema,
|
||||||
},
|
},
|
||||||
evm::{
|
evm::{
|
||||||
self, RunKind,
|
self, ListGrantsError, RunKind,
|
||||||
policies::{
|
policies::{
|
||||||
FullGrant, Grant, SharedGrantSettings, SpecificGrant, SpecificMeaning,
|
FullGrant, Grant, SharedGrantSettings, SpecificGrant, SpecificMeaning,
|
||||||
ether_transfer::EtherTransfer, token_transfers::TokenTransfer,
|
ether_transfer::EtherTransfer, token_transfers::TokenTransfer,
|
||||||
@@ -33,7 +33,11 @@ pub enum SignTransactionError {
|
|||||||
|
|
||||||
#[error("Database error: {0}")]
|
#[error("Database error: {0}")]
|
||||||
#[diagnostic(code(arbiter::evm::sign::database))]
|
#[diagnostic(code(arbiter::evm::sign::database))]
|
||||||
Database(#[from] DatabaseError),
|
Database(#[from] diesel::result::Error),
|
||||||
|
|
||||||
|
#[error("Database pool error: {0}")]
|
||||||
|
#[diagnostic(code(arbiter::evm::sign::pool))]
|
||||||
|
Pool(#[from] db::PoolError),
|
||||||
|
|
||||||
#[error("Keyholder error: {0}")]
|
#[error("Keyholder error: {0}")]
|
||||||
#[diagnostic(code(arbiter::evm::sign::keyholder))]
|
#[diagnostic(code(arbiter::evm::sign::keyholder))]
|
||||||
@@ -64,7 +68,15 @@ pub enum Error {
|
|||||||
|
|
||||||
#[error("Database error: {0}")]
|
#[error("Database error: {0}")]
|
||||||
#[diagnostic(code(arbiter::evm::database))]
|
#[diagnostic(code(arbiter::evm::database))]
|
||||||
Database(#[from] DatabaseError),
|
Database(#[from] diesel::result::Error),
|
||||||
|
|
||||||
|
#[error("Database pool error: {0}")]
|
||||||
|
#[diagnostic(code(arbiter::evm::database_pool))]
|
||||||
|
DatabasePool(#[from] db::PoolError),
|
||||||
|
|
||||||
|
#[error("Grant creation error: {0}")]
|
||||||
|
#[diagnostic(code(arbiter::evm::creation))]
|
||||||
|
Creation(#[from] evm::CreationError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Actor)]
|
#[derive(Actor)]
|
||||||
@@ -104,7 +116,7 @@ impl EvmActor {
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| Error::KeyholderSend)?;
|
.map_err(|_| Error::KeyholderSend)?;
|
||||||
|
|
||||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
let mut conn = self.db.get().await?;
|
||||||
let wallet_id = insert_into(schema::evm_wallet::table)
|
let wallet_id = insert_into(schema::evm_wallet::table)
|
||||||
.values(&models::NewEvmWallet {
|
.values(&models::NewEvmWallet {
|
||||||
address: address.as_slice().to_vec(),
|
address: address.as_slice().to_vec(),
|
||||||
@@ -112,20 +124,18 @@ impl EvmActor {
|
|||||||
})
|
})
|
||||||
.returning(schema::evm_wallet::id)
|
.returning(schema::evm_wallet::id)
|
||||||
.get_result(&mut conn)
|
.get_result(&mut conn)
|
||||||
.await
|
.await?;
|
||||||
.map_err(DatabaseError::from)?;
|
|
||||||
|
|
||||||
Ok((wallet_id, address))
|
Ok((wallet_id, address))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[message]
|
#[message]
|
||||||
pub async fn list_wallets(&self) -> Result<Vec<(i32, Address)>, Error> {
|
pub async fn list_wallets(&self) -> Result<Vec<(i32, Address)>, Error> {
|
||||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
let mut conn = self.db.get().await?;
|
||||||
let rows: Vec<models::EvmWallet> = schema::evm_wallet::table
|
let rows: Vec<models::EvmWallet> = schema::evm_wallet::table
|
||||||
.select(models::EvmWallet::as_select())
|
.select(models::EvmWallet::as_select())
|
||||||
.load(&mut conn)
|
.load(&mut conn)
|
||||||
.await
|
.await?;
|
||||||
.map_err(DatabaseError::from)?;
|
|
||||||
|
|
||||||
Ok(rows
|
Ok(rows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -141,7 +151,7 @@ impl EvmActor {
|
|||||||
&mut self,
|
&mut self,
|
||||||
basic: SharedGrantSettings,
|
basic: SharedGrantSettings,
|
||||||
grant: SpecificGrant,
|
grant: SpecificGrant,
|
||||||
) -> Result<i32, DatabaseError> {
|
) -> Result<i32, evm::CreationError> {
|
||||||
match grant {
|
match grant {
|
||||||
SpecificGrant::EtherTransfer(settings) => {
|
SpecificGrant::EtherTransfer(settings) => {
|
||||||
self.engine
|
self.engine
|
||||||
@@ -164,23 +174,22 @@ impl EvmActor {
|
|||||||
|
|
||||||
#[message]
|
#[message]
|
||||||
pub async fn useragent_delete_grant(&mut self, grant_id: i32) -> Result<(), Error> {
|
pub async fn useragent_delete_grant(&mut self, grant_id: i32) -> Result<(), Error> {
|
||||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
let mut conn = self.db.get().await?;
|
||||||
diesel::update(schema::evm_basic_grant::table)
|
diesel::update(schema::evm_basic_grant::table)
|
||||||
.filter(schema::evm_basic_grant::id.eq(grant_id))
|
.filter(schema::evm_basic_grant::id.eq(grant_id))
|
||||||
.set(schema::evm_basic_grant::revoked_at.eq(SqliteTimestamp::now()))
|
.set(schema::evm_basic_grant::revoked_at.eq(SqliteTimestamp::now()))
|
||||||
.execute(&mut conn)
|
.execute(&mut conn)
|
||||||
.await
|
.await?;
|
||||||
.map_err(DatabaseError::from)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[message]
|
#[message]
|
||||||
pub async fn useragent_list_grants(&mut self) -> Result<Vec<Grant<SpecificGrant>>, Error> {
|
pub async fn useragent_list_grants(&mut self) -> Result<Vec<Grant<SpecificGrant>>, Error> {
|
||||||
Ok(self
|
match self.engine.list_all_grants().await {
|
||||||
.engine
|
Ok(grants) => Ok(grants),
|
||||||
.list_all_grants()
|
Err(ListGrantsError::Database(db)) => Err(Error::Database(db)),
|
||||||
.await
|
Err(ListGrantsError::Pool(pool)) => Err(Error::DatabasePool(pool)),
|
||||||
.map_err(DatabaseError::from)?)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[message]
|
#[message]
|
||||||
@@ -190,14 +199,13 @@ impl EvmActor {
|
|||||||
wallet_address: Address,
|
wallet_address: Address,
|
||||||
transaction: TxEip1559,
|
transaction: TxEip1559,
|
||||||
) -> Result<SpecificMeaning, SignTransactionError> {
|
) -> Result<SpecificMeaning, SignTransactionError> {
|
||||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
let mut conn = self.db.get().await?;
|
||||||
let wallet = schema::evm_wallet::table
|
let wallet = schema::evm_wallet::table
|
||||||
.select(models::EvmWallet::as_select())
|
.select(models::EvmWallet::as_select())
|
||||||
.filter(schema::evm_wallet::address.eq(wallet_address.as_slice()))
|
.filter(schema::evm_wallet::address.eq(wallet_address.as_slice()))
|
||||||
.first(&mut conn)
|
.first(&mut conn)
|
||||||
.await
|
.await
|
||||||
.optional()
|
.optional()?
|
||||||
.map_err(DatabaseError::from)?
|
|
||||||
.ok_or(SignTransactionError::WalletNotFound)?;
|
.ok_or(SignTransactionError::WalletNotFound)?;
|
||||||
let wallet_access = schema::evm_wallet_access::table
|
let wallet_access = schema::evm_wallet_access::table
|
||||||
.select(models::EvmWalletAccess::as_select())
|
.select(models::EvmWalletAccess::as_select())
|
||||||
@@ -205,8 +213,7 @@ impl EvmActor {
|
|||||||
.filter(schema::evm_wallet_access::client_id.eq(client_id))
|
.filter(schema::evm_wallet_access::client_id.eq(client_id))
|
||||||
.first(&mut conn)
|
.first(&mut conn)
|
||||||
.await
|
.await
|
||||||
.optional()
|
.optional()?
|
||||||
.map_err(DatabaseError::from)?
|
|
||||||
.ok_or(SignTransactionError::WalletNotFound)?;
|
.ok_or(SignTransactionError::WalletNotFound)?;
|
||||||
drop(conn);
|
drop(conn);
|
||||||
|
|
||||||
@@ -225,14 +232,13 @@ impl EvmActor {
|
|||||||
wallet_address: Address,
|
wallet_address: Address,
|
||||||
mut transaction: TxEip1559,
|
mut transaction: TxEip1559,
|
||||||
) -> Result<Signature, SignTransactionError> {
|
) -> Result<Signature, SignTransactionError> {
|
||||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
let mut conn = self.db.get().await?;
|
||||||
let wallet = schema::evm_wallet::table
|
let wallet = schema::evm_wallet::table
|
||||||
.select(models::EvmWallet::as_select())
|
.select(models::EvmWallet::as_select())
|
||||||
.filter(schema::evm_wallet::address.eq(wallet_address.as_slice()))
|
.filter(schema::evm_wallet::address.eq(wallet_address.as_slice()))
|
||||||
.first(&mut conn)
|
.first(&mut conn)
|
||||||
.await
|
.await
|
||||||
.optional()
|
.optional()?
|
||||||
.map_err(DatabaseError::from)?
|
|
||||||
.ok_or(SignTransactionError::WalletNotFound)?;
|
.ok_or(SignTransactionError::WalletNotFound)?;
|
||||||
let wallet_access = schema::evm_wallet_access::table
|
let wallet_access = schema::evm_wallet_access::table
|
||||||
.select(models::EvmWalletAccess::as_select())
|
.select(models::EvmWalletAccess::as_select())
|
||||||
@@ -240,8 +246,7 @@ impl EvmActor {
|
|||||||
.filter(schema::evm_wallet_access::client_id.eq(client_id))
|
.filter(schema::evm_wallet_access::client_id.eq(client_id))
|
||||||
.first(&mut conn)
|
.first(&mut conn)
|
||||||
.await
|
.await
|
||||||
.optional()
|
.optional()?
|
||||||
.map_err(DatabaseError::from)?
|
|
||||||
.ok_or(SignTransactionError::WalletNotFound)?;
|
.ok_or(SignTransactionError::WalletNotFound)?;
|
||||||
drop(conn);
|
drop(conn);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ use crate::{
|
|||||||
db::{self, models::KeyType},
|
db::{self, models::KeyType},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub struct EvmAccessEntry {
|
||||||
|
pub wallet_id: i32,
|
||||||
|
pub sdk_client_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
/// Abstraction over Ed25519 / ECDSA-secp256k1 / RSA public keys used during the auth handshake.
|
/// Abstraction over Ed25519 / ECDSA-secp256k1 / RSA public keys used during the auth handshake.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum AuthPublicKey {
|
pub enum AuthPublicKey {
|
||||||
|
|||||||
@@ -13,10 +13,9 @@ use x25519_dalek::{EphemeralSecret, PublicKey};
|
|||||||
|
|
||||||
use crate::actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer;
|
use crate::actors::flow_coordinator::client_connect_approval::ClientApprovalAnswer;
|
||||||
use crate::actors::keyholder::KeyHolderState;
|
use crate::actors::keyholder::KeyHolderState;
|
||||||
|
use crate::actors::user_agent::EvmAccessEntry;
|
||||||
use crate::actors::user_agent::session::Error;
|
use crate::actors::user_agent::session::Error;
|
||||||
use crate::db::models::{
|
use crate::db::models::{ProgramClient, ProgramClientMetadata};
|
||||||
CoreEvmWalletAccess, EvmWalletAccess, NewEvmWalletAccess, ProgramClient, ProgramClientMetadata,
|
|
||||||
};
|
|
||||||
use crate::db::schema::evm_wallet_access;
|
use crate::db::schema::evm_wallet_access;
|
||||||
use crate::evm::policies::{Grant, SpecificGrant};
|
use crate::evm::policies::{Grant, SpecificGrant};
|
||||||
use crate::safe_cell::SafeCell;
|
use crate::safe_cell::SafeCell;
|
||||||
@@ -305,6 +304,8 @@ impl UserAgentSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[messages]
|
#[messages]
|
||||||
impl UserAgentSession {
|
impl UserAgentSession {
|
||||||
#[message]
|
#[message]
|
||||||
@@ -359,16 +360,20 @@ impl UserAgentSession {
|
|||||||
#[message]
|
#[message]
|
||||||
pub(crate) async fn handle_grant_evm_wallet_access(
|
pub(crate) async fn handle_grant_evm_wallet_access(
|
||||||
&mut self,
|
&mut self,
|
||||||
entries: Vec<NewEvmWalletAccess>,
|
entries: Vec<EvmAccessEntry>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut conn = self.props.db.get().await?;
|
let mut conn = self.props.db.get().await?;
|
||||||
conn.transaction(|conn| {
|
conn.transaction(|conn| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
use crate::db::models::NewEvmWalletAccess;
|
||||||
use crate::db::schema::evm_wallet_access;
|
use crate::db::schema::evm_wallet_access;
|
||||||
|
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
diesel::insert_into(evm_wallet_access::table)
|
diesel::insert_into(evm_wallet_access::table)
|
||||||
.values(&entry)
|
.values(&NewEvmWalletAccess {
|
||||||
|
wallet_id: entry.wallet_id,
|
||||||
|
client_id: entry.sdk_client_id,
|
||||||
|
})
|
||||||
.on_conflict_do_nothing()
|
.on_conflict_do_nothing()
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -384,7 +389,7 @@ impl UserAgentSession {
|
|||||||
#[message]
|
#[message]
|
||||||
pub(crate) async fn handle_revoke_evm_wallet_access(
|
pub(crate) async fn handle_revoke_evm_wallet_access(
|
||||||
&mut self,
|
&mut self,
|
||||||
entries: Vec<i32>,
|
entries: Vec<EvmAccessEntry>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut conn = self.props.db.get().await?;
|
let mut conn = self.props.db.get().await?;
|
||||||
conn.transaction(|conn| {
|
conn.transaction(|conn| {
|
||||||
@@ -392,7 +397,11 @@ impl UserAgentSession {
|
|||||||
use crate::db::schema::evm_wallet_access;
|
use crate::db::schema::evm_wallet_access;
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
diesel::delete(evm_wallet_access::table)
|
diesel::delete(evm_wallet_access::table)
|
||||||
.filter(evm_wallet_access::wallet_id.eq(entry))
|
.filter(
|
||||||
|
evm_wallet_access::wallet_id
|
||||||
|
.eq(entry.wallet_id)
|
||||||
|
.and(evm_wallet_access::client_id.eq(entry.sdk_client_id)),
|
||||||
|
)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@@ -405,15 +414,19 @@ impl UserAgentSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message]
|
#[message]
|
||||||
pub(crate) async fn handle_list_wallet_access(
|
pub(crate) async fn handle_list_wallet_access(&mut self) -> Result<Vec<EvmAccessEntry>, Error> {
|
||||||
&mut self,
|
|
||||||
) -> Result<Vec<EvmWalletAccess>, Error> {
|
|
||||||
let mut conn = self.props.db.get().await?;
|
let mut conn = self.props.db.get().await?;
|
||||||
use crate::db::schema::evm_wallet_access;
|
use crate::db::schema::evm_wallet_access;
|
||||||
let access_entries = evm_wallet_access::table
|
let access_entries = evm_wallet_access::table
|
||||||
.select(EvmWalletAccess::as_select())
|
.select((evm_wallet_access::wallet_id, evm_wallet_access::client_id))
|
||||||
.load::<_>(&mut conn)
|
.load::<(i32, i32)>(&mut conn)
|
||||||
.await?;
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|(wallet_id, sdk_client_id)| EvmAccessEntry {
|
||||||
|
wallet_id,
|
||||||
|
sdk_client_id,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
Ok(access_entries)
|
Ok(access_entries)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,12 +193,6 @@ pub struct EvmWallet {
|
|||||||
omit(id, created_at),
|
omit(id, created_at),
|
||||||
attributes_with = "deriveless"
|
attributes_with = "deriveless"
|
||||||
)]
|
)]
|
||||||
#[view(
|
|
||||||
CoreEvmWalletAccess,
|
|
||||||
derive(Insertable),
|
|
||||||
omit(created_at),
|
|
||||||
attributes_with = "deriveless"
|
|
||||||
)]
|
|
||||||
pub struct EvmWalletAccess {
|
pub struct EvmWalletAccess {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub wallet_id: i32,
|
pub wallet_id: i32,
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ use alloy::{
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use diesel::{ExpressionMethods as _, QueryDsl as _, QueryResult, insert_into, sqlite::Sqlite};
|
use diesel::{ExpressionMethods as _, QueryDsl as _, QueryResult, insert_into, sqlite::Sqlite};
|
||||||
use diesel_async::{AsyncConnection, RunQueryDsl};
|
use diesel_async::{AsyncConnection, RunQueryDsl};
|
||||||
use tracing_subscriber::registry::Data;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{
|
db::{
|
||||||
self, DatabaseError,
|
self,
|
||||||
models::{
|
models::{
|
||||||
EvmBasicGrant, EvmWalletAccess, NewEvmBasicGrant, NewEvmTransactionLog, SqliteTimestamp,
|
EvmBasicGrant, EvmWalletAccess, NewEvmBasicGrant, NewEvmTransactionLog, SqliteTimestamp,
|
||||||
},
|
},
|
||||||
@@ -31,8 +30,12 @@ mod utils;
|
|||||||
/// Errors that can only occur once the transaction meaning is known (during policy evaluation)
|
/// Errors that can only occur once the transaction meaning is known (during policy evaluation)
|
||||||
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||||
pub enum PolicyError {
|
pub enum PolicyError {
|
||||||
#[error("Database error")]
|
#[error("Database connection pool error")]
|
||||||
Error(#[from] crate::db::DatabaseError),
|
#[diagnostic(code(arbiter_server::evm::policy_error::pool))]
|
||||||
|
Pool(#[from] db::PoolError),
|
||||||
|
#[error("Database returned error")]
|
||||||
|
#[diagnostic(code(arbiter_server::evm::policy_error::database))]
|
||||||
|
Database(#[from] diesel::result::Error),
|
||||||
#[error("Transaction violates policy: {0:?}")]
|
#[error("Transaction violates policy: {0:?}")]
|
||||||
#[diagnostic(code(arbiter_server::evm::policy_error::violation))]
|
#[diagnostic(code(arbiter_server::evm::policy_error::violation))]
|
||||||
Violations(Vec<EvalViolation>),
|
Violations(Vec<EvalViolation>),
|
||||||
@@ -54,6 +57,16 @@ pub enum VetError {
|
|||||||
Evaluated(SpecificMeaning, #[source] PolicyError),
|
Evaluated(SpecificMeaning, #[source] PolicyError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||||
|
pub enum SignError {
|
||||||
|
#[error("Database connection pool error")]
|
||||||
|
#[diagnostic(code(arbiter_server::evm::database_error))]
|
||||||
|
Pool(#[from] db::PoolError),
|
||||||
|
#[error("Database returned error")]
|
||||||
|
#[diagnostic(code(arbiter_server::evm::database_error))]
|
||||||
|
Database(#[from] diesel::result::Error),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||||
pub enum AnalyzeError {
|
pub enum AnalyzeError {
|
||||||
#[error("Engine doesn't support granting permissions for contract creation")]
|
#[error("Engine doesn't support granting permissions for contract creation")]
|
||||||
@@ -65,6 +78,28 @@ pub enum AnalyzeError {
|
|||||||
UnsupportedTransactionType,
|
UnsupportedTransactionType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||||
|
pub enum CreationError {
|
||||||
|
#[error("Database connection pool error")]
|
||||||
|
#[diagnostic(code(arbiter_server::evm::creation_error::database_error))]
|
||||||
|
Pool(#[from] db::PoolError),
|
||||||
|
|
||||||
|
#[error("Database returned error")]
|
||||||
|
#[diagnostic(code(arbiter_server::evm::creation_error::database_error))]
|
||||||
|
Database(#[from] diesel::result::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
|
||||||
|
pub enum ListGrantsError {
|
||||||
|
#[error("Database connection pool error")]
|
||||||
|
#[diagnostic(code(arbiter_server::evm::list_grants_error::pool))]
|
||||||
|
Pool(#[from] db::PoolError),
|
||||||
|
|
||||||
|
#[error("Database returned error")]
|
||||||
|
#[diagnostic(code(arbiter_server::evm::list_grants_error::database))]
|
||||||
|
Database(#[from] diesel::result::Error),
|
||||||
|
}
|
||||||
|
|
||||||
/// Controls whether a transaction should be executed or only validated
|
/// Controls whether a transaction should be executed or only validated
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum RunKind {
|
pub enum RunKind {
|
||||||
@@ -132,22 +167,16 @@ impl Engine {
|
|||||||
meaning: &P::Meaning,
|
meaning: &P::Meaning,
|
||||||
run_kind: RunKind,
|
run_kind: RunKind,
|
||||||
) -> Result<(), PolicyError> {
|
) -> Result<(), PolicyError> {
|
||||||
let mut conn = self.db.get().await.map_err(DatabaseError::from)?;
|
let mut conn = self.db.get().await?;
|
||||||
|
|
||||||
let grant = P::try_find_grant(&context, &mut conn)
|
let grant = P::try_find_grant(&context, &mut conn)
|
||||||
.await
|
.await?
|
||||||
.map_err(DatabaseError::from)?
|
|
||||||
.ok_or(PolicyError::NoMatchingGrant)?;
|
.ok_or(PolicyError::NoMatchingGrant)?;
|
||||||
|
|
||||||
let mut violations =
|
let mut violations =
|
||||||
check_shared_constraints(&context, &grant.shared, grant.shared_grant_id, &mut conn)
|
check_shared_constraints(&context, &grant.shared, grant.shared_grant_id, &mut conn)
|
||||||
.await
|
.await?;
|
||||||
.map_err(DatabaseError::from)?;
|
violations.extend(P::evaluate(&context, meaning, &grant, &mut conn).await?);
|
||||||
violations.extend(
|
|
||||||
P::evaluate(&context, meaning, &grant, &mut conn)
|
|
||||||
.await
|
|
||||||
.map_err(DatabaseError::from)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !violations.is_empty() {
|
if !violations.is_empty() {
|
||||||
return Err(PolicyError::Violations(violations));
|
return Err(PolicyError::Violations(violations));
|
||||||
@@ -171,8 +200,7 @@ impl Engine {
|
|||||||
QueryResult::Ok(())
|
QueryResult::Ok(())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await?;
|
||||||
.map_err(DatabaseError::from)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -187,7 +215,7 @@ impl Engine {
|
|||||||
pub async fn create_grant<P: Policy>(
|
pub async fn create_grant<P: Policy>(
|
||||||
&self,
|
&self,
|
||||||
full_grant: FullGrant<P::Settings>,
|
full_grant: FullGrant<P::Settings>,
|
||||||
) -> Result<i32, DatabaseError> {
|
) -> Result<i32, CreationError> {
|
||||||
let mut conn = self.db.get().await?;
|
let mut conn = self.db.get().await?;
|
||||||
|
|
||||||
let id = conn
|
let id = conn
|
||||||
@@ -233,7 +261,7 @@ impl Engine {
|
|||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_all_grants(&self) -> Result<Vec<Grant<SpecificGrant>>, DatabaseError> {
|
pub async fn list_all_grants(&self) -> Result<Vec<Grant<SpecificGrant>>, ListGrantsError> {
|
||||||
let mut conn = self.db.get().await?;
|
let mut conn = self.db.get().await?;
|
||||||
|
|
||||||
let mut grants: Vec<Grant<SpecificGrant>> = Vec::new();
|
let mut grants: Vec<Grant<SpecificGrant>> = Vec::new();
|
||||||
|
|||||||
@@ -45,15 +45,10 @@ use crate::{
|
|||||||
user_agent::{
|
user_agent::{
|
||||||
OutOfBand, UserAgentConnection, UserAgentSession,
|
OutOfBand, UserAgentConnection, UserAgentSession,
|
||||||
session::connection::{
|
session::connection::{
|
||||||
BootstrapError, HandleBootstrapEncryptedKey, HandleEvmWalletCreate,
|
BootstrapError, HandleBootstrapEncryptedKey, HandleEvmWalletCreate, HandleEvmWalletList, HandleGrantCreate, HandleGrantDelete, HandleGrantEvmWalletAccess, HandleGrantList, HandleListWalletAccess, HandleNewClientApprove, HandleQueryVaultState, HandleRevokeEvmWalletAccess, HandleSdkClientList, HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError
|
||||||
HandleEvmWalletList, HandleGrantCreate, HandleGrantDelete,
|
|
||||||
HandleGrantEvmWalletAccess, HandleGrantList, HandleListWalletAccess,
|
|
||||||
HandleNewClientApprove, HandleQueryVaultState, HandleRevokeEvmWalletAccess,
|
|
||||||
HandleSdkClientList, HandleUnsealEncryptedKey, HandleUnsealRequest, UnsealError,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db::models::{CoreEvmWalletAccess, NewEvmWalletAccess},
|
|
||||||
grpc::{Convert, TryConvert, request_tracker::RequestTracker},
|
grpc::{Convert, TryConvert, request_tracker::RequestTracker},
|
||||||
};
|
};
|
||||||
mod auth;
|
mod auth;
|
||||||
@@ -388,8 +383,7 @@ async fn dispatch_inner(
|
|||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::GrantWalletAccess(SdkClientGrantWalletAccess { accesses }) => {
|
UserAgentRequestPayload::GrantWalletAccess(SdkClientGrantWalletAccess { accesses }) => {
|
||||||
let entries: Vec<NewEvmWalletAccess> =
|
let entries = accesses.try_convert()?;
|
||||||
accesses.into_iter().map(|a| a.convert()).collect();
|
|
||||||
|
|
||||||
match actor.ask(HandleGrantEvmWalletAccess { entries }).await {
|
match actor.ask(HandleGrantEvmWalletAccess { entries }).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
@@ -404,7 +398,9 @@ async fn dispatch_inner(
|
|||||||
}
|
}
|
||||||
|
|
||||||
UserAgentRequestPayload::RevokeWalletAccess(SdkClientRevokeWalletAccess { accesses }) => {
|
UserAgentRequestPayload::RevokeWalletAccess(SdkClientRevokeWalletAccess { accesses }) => {
|
||||||
match actor.ask(HandleRevokeEvmWalletAccess { entries: accesses }).await {
|
let entries = accesses.try_convert()?;
|
||||||
|
|
||||||
|
match actor.ask(HandleRevokeEvmWalletAccess { entries }).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
info!("Successfully revoked wallet access");
|
info!("Successfully revoked wallet access");
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
use alloy::primitives::{Address, U256};
|
|
||||||
use arbiter_proto::proto::evm::{
|
use arbiter_proto::proto::evm::{
|
||||||
EtherTransferSettings as ProtoEtherTransferSettings, SharedSettings as ProtoSharedSettings,
|
EtherTransferSettings as ProtoEtherTransferSettings,
|
||||||
SpecificGrant as ProtoSpecificGrant, TokenTransferSettings as ProtoTokenTransferSettings,
|
SharedSettings as ProtoSharedSettings,
|
||||||
TransactionRateLimit as ProtoTransactionRateLimit, VolumeRateLimit as ProtoVolumeRateLimit,
|
SpecificGrant as ProtoSpecificGrant,
|
||||||
|
TokenTransferSettings as ProtoTokenTransferSettings,
|
||||||
|
TransactionRateLimit as ProtoTransactionRateLimit,
|
||||||
|
VolumeRateLimit as ProtoVolumeRateLimit,
|
||||||
specific_grant::Grant as ProtoSpecificGrantType,
|
specific_grant::Grant as ProtoSpecificGrantType,
|
||||||
};
|
};
|
||||||
use arbiter_proto::proto::user_agent::{SdkClientWalletAccess, WalletAccess};
|
use arbiter_proto::proto::user_agent::SdkClientWalletAccess;
|
||||||
|
use alloy::primitives::{Address, U256};
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
use prost_types::Timestamp as ProtoTimestamp;
|
use prost_types::Timestamp as ProtoTimestamp;
|
||||||
use tonic::Status;
|
use tonic::Status;
|
||||||
|
|
||||||
use crate::db::models::{CoreEvmWalletAccess, NewEvmWallet, NewEvmWalletAccess};
|
use crate::actors::user_agent::EvmAccessEntry;
|
||||||
use crate::grpc::Convert;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
evm::policies::{
|
evm::policies::{
|
||||||
SharedGrantSettings, SpecificGrant, TransactionRateLimit, VolumeRateLimit, ether_transfer,
|
SharedGrantSettings, SpecificGrant, TransactionRateLimit, VolumeRateLimit,
|
||||||
token_transfers,
|
ether_transfer, token_transfers,
|
||||||
},
|
},
|
||||||
grpc::TryConvert,
|
grpc::TryConvert,
|
||||||
};
|
};
|
||||||
@@ -77,14 +79,8 @@ impl TryConvert for ProtoSharedSettings {
|
|||||||
Ok(SharedGrantSettings {
|
Ok(SharedGrantSettings {
|
||||||
wallet_access_id: self.wallet_access_id,
|
wallet_access_id: self.wallet_access_id,
|
||||||
chain: self.chain_id,
|
chain: self.chain_id,
|
||||||
valid_from: self
|
valid_from: self.valid_from.map(ProtoTimestamp::try_convert).transpose()?,
|
||||||
.valid_from
|
valid_until: self.valid_until.map(ProtoTimestamp::try_convert).transpose()?,
|
||||||
.map(ProtoTimestamp::try_convert)
|
|
||||||
.transpose()?,
|
|
||||||
valid_until: self
|
|
||||||
.valid_until
|
|
||||||
.map(ProtoTimestamp::try_convert)
|
|
||||||
.transpose()?,
|
|
||||||
max_gas_fee_per_gas: self
|
max_gas_fee_per_gas: self
|
||||||
.max_gas_fee_per_gas
|
.max_gas_fee_per_gas
|
||||||
.as_deref()
|
.as_deref()
|
||||||
@@ -140,29 +136,17 @@ impl TryConvert for ProtoSpecificGrant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Convert for WalletAccess {
|
impl TryConvert for Vec<SdkClientWalletAccess> {
|
||||||
type Output = NewEvmWalletAccess;
|
type Output = Vec<EvmAccessEntry>;
|
||||||
|
|
||||||
fn convert(self) -> Self::Output {
|
|
||||||
NewEvmWalletAccess {
|
|
||||||
wallet_id: self.wallet_id,
|
|
||||||
client_id: self.sdk_client_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryConvert for SdkClientWalletAccess {
|
|
||||||
type Output = CoreEvmWalletAccess;
|
|
||||||
type Error = Status;
|
type Error = Status;
|
||||||
|
|
||||||
fn try_convert(self) -> Result<CoreEvmWalletAccess, Status> {
|
fn try_convert(self) -> Result<Vec<EvmAccessEntry>, Status> {
|
||||||
let Some(access) = self.access else {
|
Ok(self
|
||||||
return Err(Status::invalid_argument("Missing wallet access entry"));
|
.into_iter()
|
||||||
};
|
.map(|SdkClientWalletAccess { client_id, wallet_id }| EvmAccessEntry {
|
||||||
Ok(CoreEvmWalletAccess {
|
wallet_id,
|
||||||
wallet_id: access.wallet_id,
|
sdk_client_id: client_id,
|
||||||
client_id: access.sdk_client_id,
|
})
|
||||||
id: self.id,
|
.collect())
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ use arbiter_proto::proto::{
|
|||||||
TransactionRateLimit as ProtoTransactionRateLimit, VolumeRateLimit as ProtoVolumeRateLimit,
|
TransactionRateLimit as ProtoTransactionRateLimit, VolumeRateLimit as ProtoVolumeRateLimit,
|
||||||
specific_grant::Grant as ProtoSpecificGrantType,
|
specific_grant::Grant as ProtoSpecificGrantType,
|
||||||
},
|
},
|
||||||
user_agent::{SdkClientWalletAccess as ProtoSdkClientWalletAccess, WalletAccess},
|
user_agent::SdkClientWalletAccess as ProtoSdkClientWalletAccess,
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use prost_types::Timestamp as ProtoTimestamp;
|
use prost_types::Timestamp as ProtoTimestamp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::models::EvmWalletAccess,
|
actors::user_agent::EvmAccessEntry,
|
||||||
evm::policies::{SharedGrantSettings, SpecificGrant, TransactionRateLimit, VolumeRateLimit},
|
evm::policies::{SharedGrantSettings, SpecificGrant, TransactionRateLimit, VolumeRateLimit},
|
||||||
grpc::Convert,
|
grpc::Convert,
|
||||||
};
|
};
|
||||||
@@ -96,16 +96,13 @@ impl Convert for SpecificGrant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Convert for EvmWalletAccess {
|
impl Convert for EvmAccessEntry {
|
||||||
type Output = ProtoSdkClientWalletAccess;
|
type Output = ProtoSdkClientWalletAccess;
|
||||||
|
|
||||||
fn convert(self) -> Self::Output {
|
fn convert(self) -> Self::Output {
|
||||||
Self::Output {
|
ProtoSdkClientWalletAccess {
|
||||||
id: self.id,
|
client_id: self.sdk_client_id,
|
||||||
access: Some(WalletAccess {
|
wallet_id: self.wallet_id,
|
||||||
wallet_id: self.wallet_id,
|
|
||||||
sdk_client_id: self.client_id,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,27 +29,17 @@ Future<List<GrantEntry>> listEvmGrants(Connection connection) async {
|
|||||||
|
|
||||||
Future<int> createEvmGrant(
|
Future<int> createEvmGrant(
|
||||||
Connection connection, {
|
Connection connection, {
|
||||||
required SharedSettings sharedSettings,
|
required int clientId,
|
||||||
|
required int walletId,
|
||||||
|
required Int64 chainId,
|
||||||
|
DateTime? validFrom,
|
||||||
|
DateTime? validUntil,
|
||||||
|
List<int>? maxGasFeePerGas,
|
||||||
|
List<int>? maxPriorityFeePerGas,
|
||||||
|
TransactionRateLimit? rateLimit,
|
||||||
required SpecificGrant specific,
|
required SpecificGrant specific,
|
||||||
}) async {
|
}) async {
|
||||||
final request = UserAgentRequest(
|
throw UnimplementedError('EVM grant creation is not yet implemented.');
|
||||||
evmGrantCreate: EvmGrantCreateRequest(
|
|
||||||
shared: sharedSettings,
|
|
||||||
specific: specific,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final resp = await connection.ask(request);
|
|
||||||
|
|
||||||
if (!resp.hasEvmGrantCreate()) {
|
|
||||||
throw Exception(
|
|
||||||
'Expected EVM grant create response, got ${resp.whichPayload()}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = resp.evmGrantCreate;
|
|
||||||
|
|
||||||
return result.grantId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteEvmGrant(Connection connection, int grantId) async {
|
Future<void> deleteEvmGrant(Connection connection, int grantId) async {
|
||||||
|
|||||||
@@ -15,25 +15,11 @@ Future<Set<int>> readClientWalletAccess(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
for (final entry in response.listWalletAccessResponse.accesses)
|
for (final access in response.listWalletAccessResponse.accesses)
|
||||||
if (entry.access.sdkClientId == clientId) entry.access.walletId,
|
if (access.clientId == clientId) access.walletId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SdkClientWalletAccess>> listAllWalletAccesses(
|
|
||||||
Connection connection,
|
|
||||||
) async {
|
|
||||||
final response = await connection.ask(
|
|
||||||
UserAgentRequest(listWalletAccess: Empty()),
|
|
||||||
);
|
|
||||||
if (!response.hasListWalletAccessResponse()) {
|
|
||||||
throw Exception(
|
|
||||||
'Expected list wallet access response, got ${response.whichPayload()}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return response.listWalletAccessResponse.accesses.toList(growable: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> writeClientWalletAccess(
|
Future<void> writeClientWalletAccess(
|
||||||
Connection connection, {
|
Connection connection, {
|
||||||
required int clientId,
|
required int clientId,
|
||||||
@@ -50,7 +36,7 @@ Future<void> writeClientWalletAccess(
|
|||||||
grantWalletAccess: SdkClientGrantWalletAccess(
|
grantWalletAccess: SdkClientGrantWalletAccess(
|
||||||
accesses: [
|
accesses: [
|
||||||
for (final walletId in toGrant)
|
for (final walletId in toGrant)
|
||||||
WalletAccess(sdkClientId: clientId, walletId: walletId),
|
SdkClientWalletAccess(clientId: clientId, walletId: walletId),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -63,7 +49,7 @@ Future<void> writeClientWalletAccess(
|
|||||||
revokeWalletAccess: SdkClientRevokeWalletAccess(
|
revokeWalletAccess: SdkClientRevokeWalletAccess(
|
||||||
accesses: [
|
accesses: [
|
||||||
for (final walletId in toRevoke)
|
for (final walletId in toRevoke)
|
||||||
walletId
|
SdkClientWalletAccess(clientId: clientId, walletId: walletId),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1072,81 +1072,14 @@ class SdkClientConnectionCancel extends $pb.GeneratedMessage {
|
|||||||
void clearPubkey() => $_clearField(1);
|
void clearPubkey() => $_clearField(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WalletAccess extends $pb.GeneratedMessage {
|
|
||||||
factory WalletAccess({
|
|
||||||
$core.int? walletId,
|
|
||||||
$core.int? sdkClientId,
|
|
||||||
}) {
|
|
||||||
final result = create();
|
|
||||||
if (walletId != null) result.walletId = walletId;
|
|
||||||
if (sdkClientId != null) result.sdkClientId = sdkClientId;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
WalletAccess._();
|
|
||||||
|
|
||||||
factory WalletAccess.fromBuffer($core.List<$core.int> data,
|
|
||||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
|
||||||
create()..mergeFromBuffer(data, registry);
|
|
||||||
factory WalletAccess.fromJson($core.String json,
|
|
||||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
|
||||||
create()..mergeFromJson(json, registry);
|
|
||||||
|
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
|
||||||
_omitMessageNames ? '' : 'WalletAccess',
|
|
||||||
package:
|
|
||||||
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.user_agent'),
|
|
||||||
createEmptyInstance: create)
|
|
||||||
..aI(1, _omitFieldNames ? '' : 'walletId')
|
|
||||||
..aI(2, _omitFieldNames ? '' : 'sdkClientId')
|
|
||||||
..hasRequiredFields = false;
|
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
|
||||||
WalletAccess clone() => deepCopy();
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
|
||||||
WalletAccess copyWith(void Function(WalletAccess) updates) =>
|
|
||||||
super.copyWith((message) => updates(message as WalletAccess))
|
|
||||||
as WalletAccess;
|
|
||||||
|
|
||||||
@$core.override
|
|
||||||
$pb.BuilderInfo get info_ => _i;
|
|
||||||
|
|
||||||
@$core.pragma('dart2js:noInline')
|
|
||||||
static WalletAccess create() => WalletAccess._();
|
|
||||||
@$core.override
|
|
||||||
WalletAccess createEmptyInstance() => create();
|
|
||||||
@$core.pragma('dart2js:noInline')
|
|
||||||
static WalletAccess getDefault() => _defaultInstance ??=
|
|
||||||
$pb.GeneratedMessage.$_defaultFor<WalletAccess>(create);
|
|
||||||
static WalletAccess? _defaultInstance;
|
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
|
||||||
$core.int get walletId => $_getIZ(0);
|
|
||||||
@$pb.TagNumber(1)
|
|
||||||
set walletId($core.int value) => $_setSignedInt32(0, value);
|
|
||||||
@$pb.TagNumber(1)
|
|
||||||
$core.bool hasWalletId() => $_has(0);
|
|
||||||
@$pb.TagNumber(1)
|
|
||||||
void clearWalletId() => $_clearField(1);
|
|
||||||
|
|
||||||
@$pb.TagNumber(2)
|
|
||||||
$core.int get sdkClientId => $_getIZ(1);
|
|
||||||
@$pb.TagNumber(2)
|
|
||||||
set sdkClientId($core.int value) => $_setSignedInt32(1, value);
|
|
||||||
@$pb.TagNumber(2)
|
|
||||||
$core.bool hasSdkClientId() => $_has(1);
|
|
||||||
@$pb.TagNumber(2)
|
|
||||||
void clearSdkClientId() => $_clearField(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SdkClientWalletAccess extends $pb.GeneratedMessage {
|
class SdkClientWalletAccess extends $pb.GeneratedMessage {
|
||||||
factory SdkClientWalletAccess({
|
factory SdkClientWalletAccess({
|
||||||
$core.int? id,
|
$core.int? clientId,
|
||||||
WalletAccess? access,
|
$core.int? walletId,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (id != null) result.id = id;
|
if (clientId != null) result.clientId = clientId;
|
||||||
if (access != null) result.access = access;
|
if (walletId != null) result.walletId = walletId;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1164,9 +1097,8 @@ class SdkClientWalletAccess extends $pb.GeneratedMessage {
|
|||||||
package:
|
package:
|
||||||
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.user_agent'),
|
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.user_agent'),
|
||||||
createEmptyInstance: create)
|
createEmptyInstance: create)
|
||||||
..aI(1, _omitFieldNames ? '' : 'id')
|
..aI(1, _omitFieldNames ? '' : 'clientId')
|
||||||
..aOM<WalletAccess>(2, _omitFieldNames ? '' : 'access',
|
..aI(2, _omitFieldNames ? '' : 'walletId')
|
||||||
subBuilder: WalletAccess.create)
|
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
@@ -1190,29 +1122,27 @@ class SdkClientWalletAccess extends $pb.GeneratedMessage {
|
|||||||
static SdkClientWalletAccess? _defaultInstance;
|
static SdkClientWalletAccess? _defaultInstance;
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
$core.int get id => $_getIZ(0);
|
$core.int get clientId => $_getIZ(0);
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
set id($core.int value) => $_setSignedInt32(0, value);
|
set clientId($core.int value) => $_setSignedInt32(0, value);
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
$core.bool hasId() => $_has(0);
|
$core.bool hasClientId() => $_has(0);
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
void clearId() => $_clearField(1);
|
void clearClientId() => $_clearField(1);
|
||||||
|
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
WalletAccess get access => $_getN(1);
|
$core.int get walletId => $_getIZ(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
set access(WalletAccess value) => $_setField(2, value);
|
set walletId($core.int value) => $_setSignedInt32(1, value);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
$core.bool hasAccess() => $_has(1);
|
$core.bool hasWalletId() => $_has(1);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
void clearAccess() => $_clearField(2);
|
void clearWalletId() => $_clearField(2);
|
||||||
@$pb.TagNumber(2)
|
|
||||||
WalletAccess ensureAccess() => $_ensure(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SdkClientGrantWalletAccess extends $pb.GeneratedMessage {
|
class SdkClientGrantWalletAccess extends $pb.GeneratedMessage {
|
||||||
factory SdkClientGrantWalletAccess({
|
factory SdkClientGrantWalletAccess({
|
||||||
$core.Iterable<WalletAccess>? accesses,
|
$core.Iterable<SdkClientWalletAccess>? accesses,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (accesses != null) result.accesses.addAll(accesses);
|
if (accesses != null) result.accesses.addAll(accesses);
|
||||||
@@ -1233,8 +1163,8 @@ class SdkClientGrantWalletAccess extends $pb.GeneratedMessage {
|
|||||||
package:
|
package:
|
||||||
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.user_agent'),
|
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.user_agent'),
|
||||||
createEmptyInstance: create)
|
createEmptyInstance: create)
|
||||||
..pPM<WalletAccess>(1, _omitFieldNames ? '' : 'accesses',
|
..pPM<SdkClientWalletAccess>(1, _omitFieldNames ? '' : 'accesses',
|
||||||
subBuilder: WalletAccess.create)
|
subBuilder: SdkClientWalletAccess.create)
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
@@ -1259,12 +1189,12 @@ class SdkClientGrantWalletAccess extends $pb.GeneratedMessage {
|
|||||||
static SdkClientGrantWalletAccess? _defaultInstance;
|
static SdkClientGrantWalletAccess? _defaultInstance;
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
$pb.PbList<WalletAccess> get accesses => $_getList(0);
|
$pb.PbList<SdkClientWalletAccess> get accesses => $_getList(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SdkClientRevokeWalletAccess extends $pb.GeneratedMessage {
|
class SdkClientRevokeWalletAccess extends $pb.GeneratedMessage {
|
||||||
factory SdkClientRevokeWalletAccess({
|
factory SdkClientRevokeWalletAccess({
|
||||||
$core.Iterable<$core.int>? accesses,
|
$core.Iterable<SdkClientWalletAccess>? accesses,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (accesses != null) result.accesses.addAll(accesses);
|
if (accesses != null) result.accesses.addAll(accesses);
|
||||||
@@ -1285,7 +1215,8 @@ class SdkClientRevokeWalletAccess extends $pb.GeneratedMessage {
|
|||||||
package:
|
package:
|
||||||
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.user_agent'),
|
const $pb.PackageName(_omitMessageNames ? '' : 'arbiter.user_agent'),
|
||||||
createEmptyInstance: create)
|
createEmptyInstance: create)
|
||||||
..p<$core.int>(1, _omitFieldNames ? '' : 'accesses', $pb.PbFieldType.K3)
|
..pPM<SdkClientWalletAccess>(1, _omitFieldNames ? '' : 'accesses',
|
||||||
|
subBuilder: SdkClientWalletAccess.create)
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
@@ -1311,7 +1242,7 @@ class SdkClientRevokeWalletAccess extends $pb.GeneratedMessage {
|
|||||||
static SdkClientRevokeWalletAccess? _defaultInstance;
|
static SdkClientRevokeWalletAccess? _defaultInstance;
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
$pb.PbList<$core.int> get accesses => $_getList(0);
|
$pb.PbList<SdkClientWalletAccess> get accesses => $_getList(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListWalletAccessResponse extends $pb.GeneratedMessage {
|
class ListWalletAccessResponse extends $pb.GeneratedMessage {
|
||||||
|
|||||||
@@ -418,40 +418,19 @@ final $typed_data.Uint8List sdkClientConnectionCancelDescriptor =
|
|||||||
$convert.base64Decode(
|
$convert.base64Decode(
|
||||||
'ChlTZGtDbGllbnRDb25uZWN0aW9uQ2FuY2VsEhYKBnB1YmtleRgBIAEoDFIGcHVia2V5');
|
'ChlTZGtDbGllbnRDb25uZWN0aW9uQ2FuY2VsEhYKBnB1YmtleRgBIAEoDFIGcHVia2V5');
|
||||||
|
|
||||||
@$core.Deprecated('Use walletAccessDescriptor instead')
|
|
||||||
const WalletAccess$json = {
|
|
||||||
'1': 'WalletAccess',
|
|
||||||
'2': [
|
|
||||||
{'1': 'wallet_id', '3': 1, '4': 1, '5': 5, '10': 'walletId'},
|
|
||||||
{'1': 'sdk_client_id', '3': 2, '4': 1, '5': 5, '10': 'sdkClientId'},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Descriptor for `WalletAccess`. Decode as a `google.protobuf.DescriptorProto`.
|
|
||||||
final $typed_data.Uint8List walletAccessDescriptor = $convert.base64Decode(
|
|
||||||
'CgxXYWxsZXRBY2Nlc3MSGwoJd2FsbGV0X2lkGAEgASgFUgh3YWxsZXRJZBIiCg1zZGtfY2xpZW'
|
|
||||||
'50X2lkGAIgASgFUgtzZGtDbGllbnRJZA==');
|
|
||||||
|
|
||||||
@$core.Deprecated('Use sdkClientWalletAccessDescriptor instead')
|
@$core.Deprecated('Use sdkClientWalletAccessDescriptor instead')
|
||||||
const SdkClientWalletAccess$json = {
|
const SdkClientWalletAccess$json = {
|
||||||
'1': 'SdkClientWalletAccess',
|
'1': 'SdkClientWalletAccess',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'id', '3': 1, '4': 1, '5': 5, '10': 'id'},
|
{'1': 'client_id', '3': 1, '4': 1, '5': 5, '10': 'clientId'},
|
||||||
{
|
{'1': 'wallet_id', '3': 2, '4': 1, '5': 5, '10': 'walletId'},
|
||||||
'1': 'access',
|
|
||||||
'3': 2,
|
|
||||||
'4': 1,
|
|
||||||
'5': 11,
|
|
||||||
'6': '.arbiter.user_agent.WalletAccess',
|
|
||||||
'10': 'access'
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `SdkClientWalletAccess`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `SdkClientWalletAccess`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List sdkClientWalletAccessDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List sdkClientWalletAccessDescriptor = $convert.base64Decode(
|
||||||
'ChVTZGtDbGllbnRXYWxsZXRBY2Nlc3MSDgoCaWQYASABKAVSAmlkEjgKBmFjY2VzcxgCIAEoCz'
|
'ChVTZGtDbGllbnRXYWxsZXRBY2Nlc3MSGwoJY2xpZW50X2lkGAEgASgFUghjbGllbnRJZBIbCg'
|
||||||
'IgLmFyYml0ZXIudXNlcl9hZ2VudC5XYWxsZXRBY2Nlc3NSBmFjY2Vzcw==');
|
'l3YWxsZXRfaWQYAiABKAVSCHdhbGxldElk');
|
||||||
|
|
||||||
@$core.Deprecated('Use sdkClientGrantWalletAccessDescriptor instead')
|
@$core.Deprecated('Use sdkClientGrantWalletAccessDescriptor instead')
|
||||||
const SdkClientGrantWalletAccess$json = {
|
const SdkClientGrantWalletAccess$json = {
|
||||||
@@ -462,7 +441,7 @@ const SdkClientGrantWalletAccess$json = {
|
|||||||
'3': 1,
|
'3': 1,
|
||||||
'4': 3,
|
'4': 3,
|
||||||
'5': 11,
|
'5': 11,
|
||||||
'6': '.arbiter.user_agent.WalletAccess',
|
'6': '.arbiter.user_agent.SdkClientWalletAccess',
|
||||||
'10': 'accesses'
|
'10': 'accesses'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -471,22 +450,29 @@ const SdkClientGrantWalletAccess$json = {
|
|||||||
/// Descriptor for `SdkClientGrantWalletAccess`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `SdkClientGrantWalletAccess`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List sdkClientGrantWalletAccessDescriptor =
|
final $typed_data.Uint8List sdkClientGrantWalletAccessDescriptor =
|
||||||
$convert.base64Decode(
|
$convert.base64Decode(
|
||||||
'ChpTZGtDbGllbnRHcmFudFdhbGxldEFjY2VzcxI8CghhY2Nlc3NlcxgBIAMoCzIgLmFyYml0ZX'
|
'ChpTZGtDbGllbnRHcmFudFdhbGxldEFjY2VzcxJFCghhY2Nlc3NlcxgBIAMoCzIpLmFyYml0ZX'
|
||||||
'IudXNlcl9hZ2VudC5XYWxsZXRBY2Nlc3NSCGFjY2Vzc2Vz');
|
'IudXNlcl9hZ2VudC5TZGtDbGllbnRXYWxsZXRBY2Nlc3NSCGFjY2Vzc2Vz');
|
||||||
|
|
||||||
@$core.Deprecated('Use sdkClientRevokeWalletAccessDescriptor instead')
|
@$core.Deprecated('Use sdkClientRevokeWalletAccessDescriptor instead')
|
||||||
const SdkClientRevokeWalletAccess$json = {
|
const SdkClientRevokeWalletAccess$json = {
|
||||||
'1': 'SdkClientRevokeWalletAccess',
|
'1': 'SdkClientRevokeWalletAccess',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'accesses', '3': 1, '4': 3, '5': 5, '10': 'accesses'},
|
{
|
||||||
|
'1': 'accesses',
|
||||||
|
'3': 1,
|
||||||
|
'4': 3,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.arbiter.user_agent.SdkClientWalletAccess',
|
||||||
|
'10': 'accesses'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `SdkClientRevokeWalletAccess`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `SdkClientRevokeWalletAccess`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List sdkClientRevokeWalletAccessDescriptor =
|
final $typed_data.Uint8List sdkClientRevokeWalletAccessDescriptor =
|
||||||
$convert.base64Decode(
|
$convert.base64Decode(
|
||||||
'ChtTZGtDbGllbnRSZXZva2VXYWxsZXRBY2Nlc3MSGgoIYWNjZXNzZXMYASADKAVSCGFjY2Vzc2'
|
'ChtTZGtDbGllbnRSZXZva2VXYWxsZXRBY2Nlc3MSRQoIYWNjZXNzZXMYASADKAsyKS5hcmJpdG'
|
||||||
'Vz');
|
'VyLnVzZXJfYWdlbnQuU2RrQ2xpZW50V2FsbGV0QWNjZXNzUghhY2Nlc3Nlcw==');
|
||||||
|
|
||||||
@$core.Deprecated('Use listWalletAccessResponseDescriptor instead')
|
@$core.Deprecated('Use listWalletAccessResponseDescriptor instead')
|
||||||
const ListWalletAccessResponse$json = {
|
const ListWalletAccessResponse$json = {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:fixnum/fixnum.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:hooks_riverpod/experimental/mutation.dart';
|
import 'package:hooks_riverpod/experimental/mutation.dart';
|
||||||
import 'package:mtcore/markettakers.dart';
|
import 'package:mtcore/markettakers.dart';
|
||||||
import 'package:protobuf/well_known_types/google/protobuf/timestamp.pb.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
part 'evm_grants.freezed.dart';
|
part 'evm_grants.freezed.dart';
|
||||||
@@ -74,7 +73,14 @@ class EvmGrants extends _$EvmGrants {
|
|||||||
|
|
||||||
Future<int> executeCreateEvmGrant(
|
Future<int> executeCreateEvmGrant(
|
||||||
MutationTarget ref, {
|
MutationTarget ref, {
|
||||||
required SharedSettings sharedSettings,
|
required int clientId,
|
||||||
|
required int walletId,
|
||||||
|
required Int64 chainId,
|
||||||
|
DateTime? validFrom,
|
||||||
|
DateTime? validUntil,
|
||||||
|
List<int>? maxGasFeePerGas,
|
||||||
|
List<int>? maxPriorityFeePerGas,
|
||||||
|
TransactionRateLimit? rateLimit,
|
||||||
required SpecificGrant specific,
|
required SpecificGrant specific,
|
||||||
}) {
|
}) {
|
||||||
return createEvmGrantMutation.run(ref, (tsx) async {
|
return createEvmGrantMutation.run(ref, (tsx) async {
|
||||||
@@ -85,7 +91,14 @@ Future<int> executeCreateEvmGrant(
|
|||||||
|
|
||||||
final grantId = await createEvmGrant(
|
final grantId = await createEvmGrant(
|
||||||
connection,
|
connection,
|
||||||
sharedSettings: sharedSettings,
|
clientId: clientId,
|
||||||
|
walletId: walletId,
|
||||||
|
chainId: chainId,
|
||||||
|
validFrom: validFrom,
|
||||||
|
validUntil: validUntil,
|
||||||
|
maxGasFeePerGas: maxGasFeePerGas,
|
||||||
|
maxPriorityFeePerGas: maxPriorityFeePerGas,
|
||||||
|
rateLimit: rateLimit,
|
||||||
specific: specific,
|
specific: specific,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import 'package:arbiter/features/connection/evm/wallet_access.dart';
|
|
||||||
import 'package:arbiter/proto/user_agent.pb.dart';
|
|
||||||
import 'package:arbiter/providers/connection/connection_manager.dart';
|
|
||||||
import 'package:mtcore/markettakers.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
||||||
|
|
||||||
part 'wallet_access_list.g.dart';
|
|
||||||
|
|
||||||
@riverpod
|
|
||||||
Future<List<SdkClientWalletAccess>?> walletAccessList(Ref ref) async {
|
|
||||||
final connection = await ref.watch(connectionManagerProvider.future);
|
|
||||||
if (connection == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return await listAllWalletAccesses(connection);
|
|
||||||
} catch (e, st) {
|
|
||||||
talker.handle(e, st);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'wallet_access_list.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// RiverpodGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// ignore_for_file: type=lint, type=warning
|
|
||||||
|
|
||||||
@ProviderFor(walletAccessList)
|
|
||||||
final walletAccessListProvider = WalletAccessListProvider._();
|
|
||||||
|
|
||||||
final class WalletAccessListProvider
|
|
||||||
extends
|
|
||||||
$FunctionalProvider<
|
|
||||||
AsyncValue<List<SdkClientWalletAccess>?>,
|
|
||||||
List<SdkClientWalletAccess>?,
|
|
||||||
FutureOr<List<SdkClientWalletAccess>?>
|
|
||||||
>
|
|
||||||
with
|
|
||||||
$FutureModifier<List<SdkClientWalletAccess>?>,
|
|
||||||
$FutureProvider<List<SdkClientWalletAccess>?> {
|
|
||||||
WalletAccessListProvider._()
|
|
||||||
: super(
|
|
||||||
from: null,
|
|
||||||
argument: null,
|
|
||||||
retry: null,
|
|
||||||
name: r'walletAccessListProvider',
|
|
||||||
isAutoDispose: true,
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$walletAccessListHash();
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
$FutureProviderElement<List<SdkClientWalletAccess>?> $createElement(
|
|
||||||
$ProviderPointer pointer,
|
|
||||||
) => $FutureProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
FutureOr<List<SdkClientWalletAccess>?> create(Ref ref) {
|
|
||||||
return walletAccessList(ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _$walletAccessListHash() => r'c06006d6792ae463105a539723e9bb396192f96b';
|
|
||||||
@@ -19,7 +19,6 @@ class Router extends RootStackRouter {
|
|||||||
children: [
|
children: [
|
||||||
AutoRoute(page: EvmRoute.page, path: 'evm'),
|
AutoRoute(page: EvmRoute.page, path: 'evm'),
|
||||||
AutoRoute(page: ClientsRoute.page, path: 'clients'),
|
AutoRoute(page: ClientsRoute.page, path: 'clients'),
|
||||||
AutoRoute(page: EvmGrantsRoute.page, path: 'grants'),
|
|
||||||
AutoRoute(page: AboutRoute.page, path: 'about'),
|
AutoRoute(page: AboutRoute.page, path: 'about'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
|
|
||||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||||
import 'package:arbiter/proto/user_agent.pb.dart' as _i15;
|
import 'package:arbiter/proto/user_agent.pb.dart' as _i14;
|
||||||
import 'package:arbiter/screens/bootstrap.dart' as _i2;
|
import 'package:arbiter/screens/bootstrap.dart' as _i2;
|
||||||
import 'package:arbiter/screens/dashboard.dart' as _i7;
|
import 'package:arbiter/screens/dashboard.dart' as _i7;
|
||||||
import 'package:arbiter/screens/dashboard/about.dart' as _i1;
|
import 'package:arbiter/screens/dashboard/about.dart' as _i1;
|
||||||
@@ -17,24 +17,23 @@ import 'package:arbiter/screens/dashboard/clients/details.dart' as _i3;
|
|||||||
import 'package:arbiter/screens/dashboard/clients/details/client_details.dart'
|
import 'package:arbiter/screens/dashboard/clients/details/client_details.dart'
|
||||||
as _i4;
|
as _i4;
|
||||||
import 'package:arbiter/screens/dashboard/clients/table.dart' as _i5;
|
import 'package:arbiter/screens/dashboard/clients/table.dart' as _i5;
|
||||||
import 'package:arbiter/screens/dashboard/evm/evm.dart' as _i9;
|
import 'package:arbiter/screens/dashboard/evm/evm.dart' as _i8;
|
||||||
import 'package:arbiter/screens/dashboard/evm/grants/grant_create.dart' as _i6;
|
import 'package:arbiter/screens/dashboard/evm/grants/grant_create.dart' as _i6;
|
||||||
import 'package:arbiter/screens/dashboard/evm/grants/grants.dart' as _i8;
|
import 'package:arbiter/screens/server_connection.dart' as _i9;
|
||||||
import 'package:arbiter/screens/server_connection.dart' as _i10;
|
import 'package:arbiter/screens/server_info_setup.dart' as _i10;
|
||||||
import 'package:arbiter/screens/server_info_setup.dart' as _i11;
|
import 'package:arbiter/screens/vault_setup.dart' as _i11;
|
||||||
import 'package:arbiter/screens/vault_setup.dart' as _i12;
|
import 'package:auto_route/auto_route.dart' as _i12;
|
||||||
import 'package:auto_route/auto_route.dart' as _i13;
|
import 'package:flutter/material.dart' as _i13;
|
||||||
import 'package:flutter/material.dart' as _i14;
|
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i1.AboutScreen]
|
/// [_i1.AboutScreen]
|
||||||
class AboutRoute extends _i13.PageRouteInfo<void> {
|
class AboutRoute extends _i12.PageRouteInfo<void> {
|
||||||
const AboutRoute({List<_i13.PageRouteInfo>? children})
|
const AboutRoute({List<_i12.PageRouteInfo>? children})
|
||||||
: super(AboutRoute.name, initialChildren: children);
|
: super(AboutRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'AboutRoute';
|
static const String name = 'AboutRoute';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i1.AboutScreen();
|
return const _i1.AboutScreen();
|
||||||
@@ -44,13 +43,13 @@ class AboutRoute extends _i13.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i2.Bootstrap]
|
/// [_i2.Bootstrap]
|
||||||
class Bootstrap extends _i13.PageRouteInfo<void> {
|
class Bootstrap extends _i12.PageRouteInfo<void> {
|
||||||
const Bootstrap({List<_i13.PageRouteInfo>? children})
|
const Bootstrap({List<_i12.PageRouteInfo>? children})
|
||||||
: super(Bootstrap.name, initialChildren: children);
|
: super(Bootstrap.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'Bootstrap';
|
static const String name = 'Bootstrap';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i2.Bootstrap();
|
return const _i2.Bootstrap();
|
||||||
@@ -60,11 +59,11 @@ class Bootstrap extends _i13.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i3.ClientDetails]
|
/// [_i3.ClientDetails]
|
||||||
class ClientDetails extends _i13.PageRouteInfo<ClientDetailsArgs> {
|
class ClientDetails extends _i12.PageRouteInfo<ClientDetailsArgs> {
|
||||||
ClientDetails({
|
ClientDetails({
|
||||||
_i14.Key? key,
|
_i13.Key? key,
|
||||||
required _i15.SdkClientEntry client,
|
required _i14.SdkClientEntry client,
|
||||||
List<_i13.PageRouteInfo>? children,
|
List<_i12.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
ClientDetails.name,
|
ClientDetails.name,
|
||||||
args: ClientDetailsArgs(key: key, client: client),
|
args: ClientDetailsArgs(key: key, client: client),
|
||||||
@@ -73,7 +72,7 @@ class ClientDetails extends _i13.PageRouteInfo<ClientDetailsArgs> {
|
|||||||
|
|
||||||
static const String name = 'ClientDetails';
|
static const String name = 'ClientDetails';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
final args = data.argsAs<ClientDetailsArgs>();
|
final args = data.argsAs<ClientDetailsArgs>();
|
||||||
@@ -85,9 +84,9 @@ class ClientDetails extends _i13.PageRouteInfo<ClientDetailsArgs> {
|
|||||||
class ClientDetailsArgs {
|
class ClientDetailsArgs {
|
||||||
const ClientDetailsArgs({this.key, required this.client});
|
const ClientDetailsArgs({this.key, required this.client});
|
||||||
|
|
||||||
final _i14.Key? key;
|
final _i13.Key? key;
|
||||||
|
|
||||||
final _i15.SdkClientEntry client;
|
final _i14.SdkClientEntry client;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@@ -107,11 +106,11 @@ class ClientDetailsArgs {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i4.ClientDetailsScreen]
|
/// [_i4.ClientDetailsScreen]
|
||||||
class ClientDetailsRoute extends _i13.PageRouteInfo<ClientDetailsRouteArgs> {
|
class ClientDetailsRoute extends _i12.PageRouteInfo<ClientDetailsRouteArgs> {
|
||||||
ClientDetailsRoute({
|
ClientDetailsRoute({
|
||||||
_i14.Key? key,
|
_i13.Key? key,
|
||||||
required int clientId,
|
required int clientId,
|
||||||
List<_i13.PageRouteInfo>? children,
|
List<_i12.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
ClientDetailsRoute.name,
|
ClientDetailsRoute.name,
|
||||||
args: ClientDetailsRouteArgs(key: key, clientId: clientId),
|
args: ClientDetailsRouteArgs(key: key, clientId: clientId),
|
||||||
@@ -121,7 +120,7 @@ class ClientDetailsRoute extends _i13.PageRouteInfo<ClientDetailsRouteArgs> {
|
|||||||
|
|
||||||
static const String name = 'ClientDetailsRoute';
|
static const String name = 'ClientDetailsRoute';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
final pathParams = data.inheritedPathParams;
|
final pathParams = data.inheritedPathParams;
|
||||||
@@ -137,7 +136,7 @@ class ClientDetailsRoute extends _i13.PageRouteInfo<ClientDetailsRouteArgs> {
|
|||||||
class ClientDetailsRouteArgs {
|
class ClientDetailsRouteArgs {
|
||||||
const ClientDetailsRouteArgs({this.key, required this.clientId});
|
const ClientDetailsRouteArgs({this.key, required this.clientId});
|
||||||
|
|
||||||
final _i14.Key? key;
|
final _i13.Key? key;
|
||||||
|
|
||||||
final int clientId;
|
final int clientId;
|
||||||
|
|
||||||
@@ -159,13 +158,13 @@ class ClientDetailsRouteArgs {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i5.ClientsScreen]
|
/// [_i5.ClientsScreen]
|
||||||
class ClientsRoute extends _i13.PageRouteInfo<void> {
|
class ClientsRoute extends _i12.PageRouteInfo<void> {
|
||||||
const ClientsRoute({List<_i13.PageRouteInfo>? children})
|
const ClientsRoute({List<_i12.PageRouteInfo>? children})
|
||||||
: super(ClientsRoute.name, initialChildren: children);
|
: super(ClientsRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'ClientsRoute';
|
static const String name = 'ClientsRoute';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i5.ClientsScreen();
|
return const _i5.ClientsScreen();
|
||||||
@@ -175,13 +174,13 @@ class ClientsRoute extends _i13.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i6.CreateEvmGrantScreen]
|
/// [_i6.CreateEvmGrantScreen]
|
||||||
class CreateEvmGrantRoute extends _i13.PageRouteInfo<void> {
|
class CreateEvmGrantRoute extends _i12.PageRouteInfo<void> {
|
||||||
const CreateEvmGrantRoute({List<_i13.PageRouteInfo>? children})
|
const CreateEvmGrantRoute({List<_i12.PageRouteInfo>? children})
|
||||||
: super(CreateEvmGrantRoute.name, initialChildren: children);
|
: super(CreateEvmGrantRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'CreateEvmGrantRoute';
|
static const String name = 'CreateEvmGrantRoute';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i6.CreateEvmGrantScreen();
|
return const _i6.CreateEvmGrantScreen();
|
||||||
@@ -191,13 +190,13 @@ class CreateEvmGrantRoute extends _i13.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i7.DashboardRouter]
|
/// [_i7.DashboardRouter]
|
||||||
class DashboardRouter extends _i13.PageRouteInfo<void> {
|
class DashboardRouter extends _i12.PageRouteInfo<void> {
|
||||||
const DashboardRouter({List<_i13.PageRouteInfo>? children})
|
const DashboardRouter({List<_i12.PageRouteInfo>? children})
|
||||||
: super(DashboardRouter.name, initialChildren: children);
|
: super(DashboardRouter.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'DashboardRouter';
|
static const String name = 'DashboardRouter';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i7.DashboardRouter();
|
return const _i7.DashboardRouter();
|
||||||
@@ -206,45 +205,29 @@ class DashboardRouter extends _i13.PageRouteInfo<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i8.EvmGrantsScreen]
|
/// [_i8.EvmScreen]
|
||||||
class EvmGrantsRoute extends _i13.PageRouteInfo<void> {
|
class EvmRoute extends _i12.PageRouteInfo<void> {
|
||||||
const EvmGrantsRoute({List<_i13.PageRouteInfo>? children})
|
const EvmRoute({List<_i12.PageRouteInfo>? children})
|
||||||
: super(EvmGrantsRoute.name, initialChildren: children);
|
|
||||||
|
|
||||||
static const String name = 'EvmGrantsRoute';
|
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
|
||||||
name,
|
|
||||||
builder: (data) {
|
|
||||||
return const _i8.EvmGrantsScreen();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generated route for
|
|
||||||
/// [_i9.EvmScreen]
|
|
||||||
class EvmRoute extends _i13.PageRouteInfo<void> {
|
|
||||||
const EvmRoute({List<_i13.PageRouteInfo>? children})
|
|
||||||
: super(EvmRoute.name, initialChildren: children);
|
: super(EvmRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'EvmRoute';
|
static const String name = 'EvmRoute';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i9.EvmScreen();
|
return const _i8.EvmScreen();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i10.ServerConnectionScreen]
|
/// [_i9.ServerConnectionScreen]
|
||||||
class ServerConnectionRoute
|
class ServerConnectionRoute
|
||||||
extends _i13.PageRouteInfo<ServerConnectionRouteArgs> {
|
extends _i12.PageRouteInfo<ServerConnectionRouteArgs> {
|
||||||
ServerConnectionRoute({
|
ServerConnectionRoute({
|
||||||
_i14.Key? key,
|
_i13.Key? key,
|
||||||
String? arbiterUrl,
|
String? arbiterUrl,
|
||||||
List<_i13.PageRouteInfo>? children,
|
List<_i12.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
ServerConnectionRoute.name,
|
ServerConnectionRoute.name,
|
||||||
args: ServerConnectionRouteArgs(key: key, arbiterUrl: arbiterUrl),
|
args: ServerConnectionRouteArgs(key: key, arbiterUrl: arbiterUrl),
|
||||||
@@ -253,13 +236,13 @@ class ServerConnectionRoute
|
|||||||
|
|
||||||
static const String name = 'ServerConnectionRoute';
|
static const String name = 'ServerConnectionRoute';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
final args = data.argsAs<ServerConnectionRouteArgs>(
|
final args = data.argsAs<ServerConnectionRouteArgs>(
|
||||||
orElse: () => const ServerConnectionRouteArgs(),
|
orElse: () => const ServerConnectionRouteArgs(),
|
||||||
);
|
);
|
||||||
return _i10.ServerConnectionScreen(
|
return _i9.ServerConnectionScreen(
|
||||||
key: args.key,
|
key: args.key,
|
||||||
arbiterUrl: args.arbiterUrl,
|
arbiterUrl: args.arbiterUrl,
|
||||||
);
|
);
|
||||||
@@ -270,7 +253,7 @@ class ServerConnectionRoute
|
|||||||
class ServerConnectionRouteArgs {
|
class ServerConnectionRouteArgs {
|
||||||
const ServerConnectionRouteArgs({this.key, this.arbiterUrl});
|
const ServerConnectionRouteArgs({this.key, this.arbiterUrl});
|
||||||
|
|
||||||
final _i14.Key? key;
|
final _i13.Key? key;
|
||||||
|
|
||||||
final String? arbiterUrl;
|
final String? arbiterUrl;
|
||||||
|
|
||||||
@@ -291,33 +274,33 @@ class ServerConnectionRouteArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i11.ServerInfoSetupScreen]
|
/// [_i10.ServerInfoSetupScreen]
|
||||||
class ServerInfoSetupRoute extends _i13.PageRouteInfo<void> {
|
class ServerInfoSetupRoute extends _i12.PageRouteInfo<void> {
|
||||||
const ServerInfoSetupRoute({List<_i13.PageRouteInfo>? children})
|
const ServerInfoSetupRoute({List<_i12.PageRouteInfo>? children})
|
||||||
: super(ServerInfoSetupRoute.name, initialChildren: children);
|
: super(ServerInfoSetupRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'ServerInfoSetupRoute';
|
static const String name = 'ServerInfoSetupRoute';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i11.ServerInfoSetupScreen();
|
return const _i10.ServerInfoSetupScreen();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i12.VaultSetupScreen]
|
/// [_i11.VaultSetupScreen]
|
||||||
class VaultSetupRoute extends _i13.PageRouteInfo<void> {
|
class VaultSetupRoute extends _i12.PageRouteInfo<void> {
|
||||||
const VaultSetupRoute({List<_i13.PageRouteInfo>? children})
|
const VaultSetupRoute({List<_i12.PageRouteInfo>? children})
|
||||||
: super(VaultSetupRoute.name, initialChildren: children);
|
: super(VaultSetupRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'VaultSetupRoute';
|
static const String name = 'VaultSetupRoute';
|
||||||
|
|
||||||
static _i13.PageInfo page = _i13.PageInfo(
|
static _i12.PageInfo page = _i12.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i12.VaultSetupScreen();
|
return const _i11.VaultSetupScreen();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
|
|
||||||
const breakpoints = MaterialAdaptiveBreakpoints();
|
const breakpoints = MaterialAdaptiveBreakpoints();
|
||||||
|
|
||||||
final routes = [
|
final routes = [const EvmRoute(), const ClientsRoute(), const AboutRoute()];
|
||||||
const EvmRoute(),
|
|
||||||
const ClientsRoute(),
|
|
||||||
const EvmGrantsRoute(),
|
|
||||||
const AboutRoute(),
|
|
||||||
];
|
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class DashboardRouter extends StatelessWidget {
|
class DashboardRouter extends StatelessWidget {
|
||||||
@@ -43,11 +38,6 @@ class DashboardRouter extends StatelessWidget {
|
|||||||
selectedIcon: Icon(Icons.devices_other),
|
selectedIcon: Icon(Icons.devices_other),
|
||||||
label: "Clients",
|
label: "Clients",
|
||||||
),
|
),
|
||||||
NavigationDestination(
|
|
||||||
icon: Icon(Icons.policy_outlined),
|
|
||||||
selectedIcon: Icon(Icons.policy),
|
|
||||||
label: "Grants",
|
|
||||||
),
|
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: Icon(Icons.info_outline),
|
icon: Icon(Icons.info_outline),
|
||||||
selectedIcon: Icon(Icons.info),
|
selectedIcon: Icon(Icons.info),
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import 'package:arbiter/proto/evm.pb.dart';
|
import 'package:arbiter/proto/evm.pb.dart';
|
||||||
import 'package:arbiter/proto/user_agent.pb.dart';
|
|
||||||
import 'package:arbiter/providers/evm/evm.dart';
|
import 'package:arbiter/providers/evm/evm.dart';
|
||||||
import 'package:arbiter/providers/evm/evm_grants.dart';
|
import 'package:arbiter/providers/evm/evm_grants.dart';
|
||||||
import 'package:arbiter/providers/sdk_clients/list.dart';
|
|
||||||
import 'package:arbiter/providers/sdk_clients/wallet_access_list.dart';
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:hooks_riverpod/experimental/mutation.dart';
|
import 'package:hooks_riverpod/experimental/mutation.dart';
|
||||||
import 'package:protobuf/well_known_types/google/protobuf/timestamp.pb.dart';
|
|
||||||
import 'package:sizer/sizer.dart';
|
import 'package:sizer/sizer.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
@@ -19,10 +15,11 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final wallets = ref.watch(evmProvider).asData?.value ?? const <WalletEntry>[];
|
||||||
final createMutation = ref.watch(createEvmGrantMutation);
|
final createMutation = ref.watch(createEvmGrantMutation);
|
||||||
|
|
||||||
final selectedClientId = useState<int?>(null);
|
final selectedWalletIndex = useState<int?>(wallets.isEmpty ? null : 0);
|
||||||
final selectedWalletAccessId = useState<int?>(null);
|
final clientIdController = useTextEditingController();
|
||||||
final chainIdController = useTextEditingController(text: '1');
|
final chainIdController = useTextEditingController(text: '1');
|
||||||
final gasFeeController = useTextEditingController();
|
final gasFeeController = useTextEditingController();
|
||||||
final priorityFeeController = useTextEditingController();
|
final priorityFeeController = useTextEditingController();
|
||||||
@@ -43,13 +40,14 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
Future<void> submit() async {
|
Future<void> submit() async {
|
||||||
final accessId = selectedWalletAccessId.value;
|
final selectedWallet = selectedWalletIndex.value;
|
||||||
if (accessId == null) {
|
if (selectedWallet == null) {
|
||||||
_showCreateMessage(context, 'Select a client and wallet access.');
|
_showCreateMessage(context, 'At least one wallet is required.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final clientId = int.parse(clientIdController.text.trim());
|
||||||
final chainId = Int64.parseInt(chainIdController.text.trim());
|
final chainId = Int64.parseInt(chainIdController.text.trim());
|
||||||
final rateLimit = _buildRateLimit(
|
final rateLimit = _buildRateLimit(
|
||||||
txCountController.text,
|
txCountController.text,
|
||||||
@@ -85,25 +83,16 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
|||||||
_ => throw Exception('Unsupported grant type.'),
|
_ => throw Exception('Unsupported grant type.'),
|
||||||
};
|
};
|
||||||
|
|
||||||
final sharedSettings = SharedSettings(
|
|
||||||
walletAccessId: accessId,
|
|
||||||
chainId: chainId,
|
|
||||||
);
|
|
||||||
if (validFrom.value != null) {
|
|
||||||
sharedSettings.validFrom = _toTimestamp(validFrom.value!);
|
|
||||||
}
|
|
||||||
if (validUntil.value != null) {
|
|
||||||
sharedSettings.validUntil = _toTimestamp(validUntil.value!);
|
|
||||||
}
|
|
||||||
final gasBytes = _optionalBigIntBytes(gasFeeController.text);
|
|
||||||
if (gasBytes != null) sharedSettings.maxGasFeePerGas = gasBytes;
|
|
||||||
final priorityBytes = _optionalBigIntBytes(priorityFeeController.text);
|
|
||||||
if (priorityBytes != null) sharedSettings.maxPriorityFeePerGas = priorityBytes;
|
|
||||||
if (rateLimit != null) sharedSettings.rateLimit = rateLimit;
|
|
||||||
|
|
||||||
await executeCreateEvmGrant(
|
await executeCreateEvmGrant(
|
||||||
ref,
|
ref,
|
||||||
sharedSettings: sharedSettings,
|
clientId: clientId,
|
||||||
|
walletId: selectedWallet + 1,
|
||||||
|
chainId: chainId,
|
||||||
|
validFrom: validFrom.value,
|
||||||
|
validUntil: validUntil.value,
|
||||||
|
maxGasFeePerGas: _optionalBigIntBytes(gasFeeController.text),
|
||||||
|
maxPriorityFeePerGas: _optionalBigIntBytes(priorityFeeController.text),
|
||||||
|
rateLimit: rateLimit,
|
||||||
specific: specific,
|
specific: specific,
|
||||||
);
|
);
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
@@ -124,23 +113,22 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
padding: EdgeInsets.fromLTRB(2.4.w, 2.h, 2.4.w, 3.2.h),
|
padding: EdgeInsets.fromLTRB(2.4.w, 2.h, 2.4.w, 3.2.h),
|
||||||
children: [
|
children: [
|
||||||
const _CreateIntroCard(),
|
_CreateIntroCard(walletCount: wallets.length),
|
||||||
SizedBox(height: 1.8.h),
|
SizedBox(height: 1.8.h),
|
||||||
_CreateSection(
|
_CreateSection(
|
||||||
title: 'Shared grant options',
|
title: 'Shared grant options',
|
||||||
children: [
|
children: [
|
||||||
_ClientPickerField(
|
_WalletPickerField(
|
||||||
selectedClientId: selectedClientId.value,
|
wallets: wallets,
|
||||||
onChanged: (clientId) {
|
selectedIndex: selectedWalletIndex.value,
|
||||||
selectedClientId.value = clientId;
|
onChanged: (value) => selectedWalletIndex.value = value,
|
||||||
selectedWalletAccessId.value = null;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
_WalletAccessPickerField(
|
_NumberInputField(
|
||||||
selectedClientId: selectedClientId.value,
|
controller: clientIdController,
|
||||||
selectedAccessId: selectedWalletAccessId.value,
|
label: 'Client ID',
|
||||||
onChanged: (accessId) =>
|
hint: '42',
|
||||||
selectedWalletAccessId.value = accessId,
|
helper:
|
||||||
|
'Manual for now. The app does not yet expose a client picker.',
|
||||||
),
|
),
|
||||||
_NumberInputField(
|
_NumberInputField(
|
||||||
controller: chainIdController,
|
controller: chainIdController,
|
||||||
@@ -216,7 +204,9 @@ class CreateEvmGrantScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CreateIntroCard extends StatelessWidget {
|
class _CreateIntroCard extends StatelessWidget {
|
||||||
const _CreateIntroCard();
|
const _CreateIntroCard({required this.walletCount});
|
||||||
|
|
||||||
|
final int walletCount;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -232,7 +222,7 @@ class _CreateIntroCard extends StatelessWidget {
|
|||||||
border: Border.all(color: const Color(0x1A17324A)),
|
border: Border.all(color: const Color(0x1A17324A)),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Pick a client, then select one of the wallet accesses already granted to it. Compose shared constraints once, then switch between Ether and token transfer rules.',
|
'Compose shared constraints once, then switch between Ether and token transfer rules. $walletCount wallet${walletCount == 1 ? '' : 's'} currently available.',
|
||||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(height: 1.5),
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(height: 1.5),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -276,98 +266,37 @@ class _CreateSection extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ClientPickerField extends ConsumerWidget {
|
class _WalletPickerField extends StatelessWidget {
|
||||||
const _ClientPickerField({
|
const _WalletPickerField({
|
||||||
required this.selectedClientId,
|
required this.wallets,
|
||||||
|
required this.selectedIndex,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
});
|
});
|
||||||
|
|
||||||
final int? selectedClientId;
|
final List<WalletEntry> wallets;
|
||||||
|
final int? selectedIndex;
|
||||||
final ValueChanged<int?> onChanged;
|
final ValueChanged<int?> onChanged;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context) {
|
||||||
final clients =
|
|
||||||
ref.watch(sdkClientsProvider).asData?.value ?? const <SdkClientEntry>[];
|
|
||||||
|
|
||||||
return DropdownButtonFormField<int>(
|
return DropdownButtonFormField<int>(
|
||||||
value: clients.any((c) => c.id == selectedClientId)
|
initialValue: selectedIndex,
|
||||||
? selectedClientId
|
|
||||||
: null,
|
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Client',
|
labelText: 'Wallet',
|
||||||
|
helperText:
|
||||||
|
'Uses the current wallet order. The API still does not expose stable wallet IDs directly.',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
items: [
|
items: [
|
||||||
for (final c in clients)
|
for (var i = 0; i < wallets.length; i++)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: c.id,
|
value: i,
|
||||||
child: Text(
|
child: Text(
|
||||||
c.info.name.isEmpty ? 'Client #${c.id}' : c.info.name,
|
'Wallet ${(i + 1).toString().padLeft(2, '0')} · ${_shortAddress(wallets[i].address)}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: clients.isEmpty ? null : onChanged,
|
onChanged: wallets.isEmpty ? null : onChanged,
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _WalletAccessPickerField extends ConsumerWidget {
|
|
||||||
const _WalletAccessPickerField({
|
|
||||||
required this.selectedClientId,
|
|
||||||
required this.selectedAccessId,
|
|
||||||
required this.onChanged,
|
|
||||||
});
|
|
||||||
|
|
||||||
final int? selectedClientId;
|
|
||||||
final int? selectedAccessId;
|
|
||||||
final ValueChanged<int?> onChanged;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final allAccesses =
|
|
||||||
ref.watch(walletAccessListProvider).asData?.value ??
|
|
||||||
const <SdkClientWalletAccess>[];
|
|
||||||
final wallets =
|
|
||||||
ref.watch(evmProvider).asData?.value ?? const <WalletEntry>[];
|
|
||||||
|
|
||||||
final walletById = <int, WalletEntry>{
|
|
||||||
for (final w in wallets) w.id: w,
|
|
||||||
};
|
|
||||||
|
|
||||||
final accesses = selectedClientId == null
|
|
||||||
? const <SdkClientWalletAccess>[]
|
|
||||||
: allAccesses
|
|
||||||
.where((a) => a.access.sdkClientId == selectedClientId)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final effectiveValue =
|
|
||||||
accesses.any((a) => a.id == selectedAccessId) ? selectedAccessId : null;
|
|
||||||
|
|
||||||
return DropdownButtonFormField<int>(
|
|
||||||
value: effectiveValue,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Wallet access',
|
|
||||||
helperText: selectedClientId == null
|
|
||||||
? 'Select a client first'
|
|
||||||
: accesses.isEmpty
|
|
||||||
? 'No wallet accesses for this client'
|
|
||||||
: null,
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
items: [
|
|
||||||
for (final a in accesses)
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: a.id,
|
|
||||||
child: Text(() {
|
|
||||||
final wallet = walletById[a.access.walletId];
|
|
||||||
return wallet != null
|
|
||||||
? _shortAddress(wallet.address)
|
|
||||||
: 'Wallet #${a.access.walletId}';
|
|
||||||
}()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: accesses.isEmpty ? null : onChanged,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -806,13 +735,6 @@ class _VolumeLimitValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamp _toTimestamp(DateTime value) {
|
|
||||||
final utc = value.toUtc();
|
|
||||||
return Timestamp()
|
|
||||||
..seconds = Int64(utc.millisecondsSinceEpoch ~/ 1000)
|
|
||||||
..nanos = (utc.microsecondsSinceEpoch % 1000000) * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
TransactionRateLimit? _buildRateLimit(String countText, String windowText) {
|
TransactionRateLimit? _buildRateLimit(String countText, String windowText) {
|
||||||
if (countText.trim().isEmpty || windowText.trim().isEmpty) {
|
if (countText.trim().isEmpty || windowText.trim().isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,231 +0,0 @@
|
|||||||
import 'package:arbiter/proto/evm.pb.dart';
|
|
||||||
import 'package:arbiter/providers/evm/evm_grants.dart';
|
|
||||||
import 'package:arbiter/providers/sdk_clients/wallet_access_list.dart';
|
|
||||||
import 'package:arbiter/router.gr.dart';
|
|
||||||
import 'package:arbiter/screens/dashboard/evm/grants/widgets/grant_card.dart';
|
|
||||||
import 'package:arbiter/theme/palette.dart';
|
|
||||||
import 'package:arbiter/widgets/page_header.dart';
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:sizer/sizer.dart';
|
|
||||||
|
|
||||||
String _formatError(Object error) {
|
|
||||||
final message = error.toString();
|
|
||||||
if (message.startsWith('Exception: ')) {
|
|
||||||
return message.substring('Exception: '.length);
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── State panel ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class _StatePanel extends StatelessWidget {
|
|
||||||
const _StatePanel({
|
|
||||||
required this.icon,
|
|
||||||
required this.title,
|
|
||||||
required this.body,
|
|
||||||
this.actionLabel,
|
|
||||||
this.onAction,
|
|
||||||
this.busy = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
final IconData icon;
|
|
||||||
final String title;
|
|
||||||
final String body;
|
|
||||||
final String? actionLabel;
|
|
||||||
final Future<void> Function()? onAction;
|
|
||||||
final bool busy;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(24),
|
|
||||||
color: Palette.cream.withValues(alpha: 0.92),
|
|
||||||
border: Border.all(color: Palette.line),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(2.8.h),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (busy)
|
|
||||||
SizedBox(
|
|
||||||
width: 2.8.h,
|
|
||||||
height: 2.8.h,
|
|
||||||
child: const CircularProgressIndicator(strokeWidth: 2.5),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Icon(icon, size: 34, color: Palette.coral),
|
|
||||||
SizedBox(height: 1.8.h),
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: theme.textTheme.headlineSmall?.copyWith(
|
|
||||||
color: Palette.ink,
|
|
||||||
fontWeight: FontWeight.w800,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 1.h),
|
|
||||||
Text(
|
|
||||||
body,
|
|
||||||
style: theme.textTheme.bodyLarge?.copyWith(
|
|
||||||
color: Palette.ink.withValues(alpha: 0.72),
|
|
||||||
height: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (actionLabel != null && onAction != null) ...[
|
|
||||||
SizedBox(height: 2.h),
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: () => onAction!(),
|
|
||||||
icon: const Icon(Icons.refresh),
|
|
||||||
label: Text(actionLabel!),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Grant list ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
class _GrantList extends StatelessWidget {
|
|
||||||
const _GrantList({required this.grants});
|
|
||||||
|
|
||||||
final List<GrantEntry> grants;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
for (var i = 0; i < grants.length; i++)
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: i == grants.length - 1 ? 0 : 1.8.h,
|
|
||||||
),
|
|
||||||
child: GrantCard(grant: grants[i]),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Screen ───────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@RoutePage()
|
|
||||||
class EvmGrantsScreen extends ConsumerWidget {
|
|
||||||
const EvmGrantsScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
// Screen watches only the grant list for top-level state decisions
|
|
||||||
final grantsAsync = ref.watch(evmGrantsProvider);
|
|
||||||
|
|
||||||
Future<void> refresh() async {
|
|
||||||
ref.invalidate(walletAccessListProvider);
|
|
||||||
ref.invalidate(evmGrantsProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showMessage(String message) {
|
|
||||||
if (!context.mounted) return;
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text(message), behavior: SnackBarBehavior.floating),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> safeRefresh() async {
|
|
||||||
try {
|
|
||||||
await refresh();
|
|
||||||
} catch (e) {
|
|
||||||
showMessage(_formatError(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final grantsState = grantsAsync.asData?.value;
|
|
||||||
final grants = grantsState?.grants;
|
|
||||||
|
|
||||||
final content = switch (grantsAsync) {
|
|
||||||
AsyncLoading() when grantsState == null => const _StatePanel(
|
|
||||||
icon: Icons.hourglass_top,
|
|
||||||
title: 'Loading grants',
|
|
||||||
body: 'Pulling grant registry from Arbiter.',
|
|
||||||
busy: true,
|
|
||||||
),
|
|
||||||
AsyncError(:final error) => _StatePanel(
|
|
||||||
icon: Icons.sync_problem,
|
|
||||||
title: 'Grant registry unavailable',
|
|
||||||
body: _formatError(error),
|
|
||||||
actionLabel: 'Retry',
|
|
||||||
onAction: safeRefresh,
|
|
||||||
),
|
|
||||||
AsyncData(:final value) when value == null => _StatePanel(
|
|
||||||
icon: Icons.portable_wifi_off,
|
|
||||||
title: 'No active server connection',
|
|
||||||
body: 'Reconnect to Arbiter to list EVM grants.',
|
|
||||||
actionLabel: 'Refresh',
|
|
||||||
onAction: safeRefresh,
|
|
||||||
),
|
|
||||||
_ when grants != null && grants.isEmpty => _StatePanel(
|
|
||||||
icon: Icons.policy_outlined,
|
|
||||||
title: 'No grants yet',
|
|
||||||
body: 'Create a grant to allow SDK clients to sign transactions.',
|
|
||||||
actionLabel: 'Create grant',
|
|
||||||
onAction: () async => context.router.push(const CreateEvmGrantRoute()),
|
|
||||||
),
|
|
||||||
_ => _GrantList(grants: grants ?? const []),
|
|
||||||
};
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: SafeArea(
|
|
||||||
child: RefreshIndicator.adaptive(
|
|
||||||
color: Palette.ink,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
onRefresh: safeRefresh,
|
|
||||||
child: ListView(
|
|
||||||
physics: const BouncingScrollPhysics(
|
|
||||||
parent: AlwaysScrollableScrollPhysics(),
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.fromLTRB(2.4.w, 2.4.h, 2.4.w, 3.2.h),
|
|
||||||
children: [
|
|
||||||
PageHeader(
|
|
||||||
title: 'EVM Grants',
|
|
||||||
isBusy: grantsAsync.isLoading,
|
|
||||||
actions: [
|
|
||||||
FilledButton.icon(
|
|
||||||
onPressed: () =>
|
|
||||||
context.router.push(const CreateEvmGrantRoute()),
|
|
||||||
icon: const Icon(Icons.add_rounded),
|
|
||||||
label: const Text('Create grant'),
|
|
||||||
),
|
|
||||||
SizedBox(width: 1.w),
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: safeRefresh,
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
foregroundColor: Palette.ink,
|
|
||||||
side: BorderSide(color: Palette.line),
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 1.4.w,
|
|
||||||
vertical: 1.2.h,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(14),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
icon: const Icon(Icons.refresh, size: 18),
|
|
||||||
label: const Text('Refresh'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: 1.8.h),
|
|
||||||
content,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
import 'package:arbiter/proto/evm.pb.dart';
|
|
||||||
import 'package:arbiter/proto/user_agent.pb.dart';
|
|
||||||
import 'package:arbiter/providers/evm/evm.dart';
|
|
||||||
import 'package:arbiter/providers/evm/evm_grants.dart';
|
|
||||||
import 'package:arbiter/providers/sdk_clients/list.dart';
|
|
||||||
import 'package:arbiter/providers/sdk_clients/wallet_access_list.dart';
|
|
||||||
import 'package:arbiter/theme/palette.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/experimental/mutation.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:sizer/sizer.dart';
|
|
||||||
|
|
||||||
String _shortAddress(List<int> bytes) {
|
|
||||||
final hex = bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
|
|
||||||
return '0x${hex.substring(0, 6)}...${hex.substring(hex.length - 4)}';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatError(Object error) {
|
|
||||||
final message = error.toString();
|
|
||||||
if (message.startsWith('Exception: ')) {
|
|
||||||
return message.substring('Exception: '.length);
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GrantCard extends ConsumerWidget {
|
|
||||||
const GrantCard({super.key, required this.grant});
|
|
||||||
|
|
||||||
final GrantEntry grant;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
// Enrichment lookups — each watch scopes rebuilds to this card only
|
|
||||||
final walletAccesses =
|
|
||||||
ref.watch(walletAccessListProvider).asData?.value ?? const [];
|
|
||||||
final wallets = ref.watch(evmProvider).asData?.value ?? const [];
|
|
||||||
final clients = ref.watch(sdkClientsProvider).asData?.value ?? const [];
|
|
||||||
final revoking = ref.watch(revokeEvmGrantMutation) is MutationPending;
|
|
||||||
|
|
||||||
final isEther =
|
|
||||||
grant.specific.whichGrant() == SpecificGrant_Grant.etherTransfer;
|
|
||||||
final accent = isEther ? Palette.coral : Palette.token;
|
|
||||||
final typeLabel = isEther ? 'Ether' : 'Token';
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
final muted = Palette.ink.withValues(alpha: 0.62);
|
|
||||||
|
|
||||||
// Resolve wallet_access_id → wallet address + client name
|
|
||||||
final accessById = <int, SdkClientWalletAccess>{
|
|
||||||
for (final a in walletAccesses) a.id: a,
|
|
||||||
};
|
|
||||||
final walletById = <int, WalletEntry>{
|
|
||||||
for (final w in wallets) w.id: w,
|
|
||||||
};
|
|
||||||
final clientNameById = <int, String>{
|
|
||||||
for (final c in clients) c.id: c.info.name,
|
|
||||||
};
|
|
||||||
|
|
||||||
final accessId = grant.shared.walletAccessId;
|
|
||||||
final access = accessById[accessId];
|
|
||||||
final wallet = access != null ? walletById[access.access.walletId] : null;
|
|
||||||
|
|
||||||
final walletLabel = wallet != null
|
|
||||||
? _shortAddress(wallet.address)
|
|
||||||
: 'Access #$accessId';
|
|
||||||
|
|
||||||
final clientLabel = () {
|
|
||||||
if (access == null) return '';
|
|
||||||
final name = clientNameById[access.access.sdkClientId] ?? '';
|
|
||||||
return name.isEmpty ? 'Client #${access.access.sdkClientId}' : name;
|
|
||||||
}();
|
|
||||||
|
|
||||||
void showError(String message) {
|
|
||||||
if (!context.mounted) return;
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text(message), behavior: SnackBarBehavior.floating),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> revoke() async {
|
|
||||||
try {
|
|
||||||
await executeRevokeEvmGrant(ref, grantId: grant.id);
|
|
||||||
} catch (e) {
|
|
||||||
showError(_formatError(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(24),
|
|
||||||
color: Palette.cream.withValues(alpha: 0.92),
|
|
||||||
border: Border.all(color: Palette.line),
|
|
||||||
),
|
|
||||||
child: IntrinsicHeight(
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
// Accent strip
|
|
||||||
Container(
|
|
||||||
width: 0.8.w,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: accent,
|
|
||||||
borderRadius: const BorderRadius.horizontal(
|
|
||||||
left: Radius.circular(24),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Card body
|
|
||||||
Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 1.6.w,
|
|
||||||
vertical: 1.4.h,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Row 1: type badge · chain · spacer · revoke button
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 1.w,
|
|
||||||
vertical: 0.4.h,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: accent.withValues(alpha: 0.15),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
typeLabel,
|
|
||||||
style: theme.textTheme.labelSmall?.copyWith(
|
|
||||||
color: accent,
|
|
||||||
fontWeight: FontWeight.w800,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 1.w),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 1.w,
|
|
||||||
vertical: 0.4.h,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Palette.ink.withValues(alpha: 0.06),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Chain ${grant.shared.chainId}',
|
|
||||||
style: theme.textTheme.labelSmall?.copyWith(
|
|
||||||
color: muted,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
if (revoking)
|
|
||||||
SizedBox(
|
|
||||||
width: 1.8.h,
|
|
||||||
height: 1.8.h,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 2,
|
|
||||||
color: Palette.coral,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: revoke,
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
foregroundColor: Palette.coral,
|
|
||||||
side: BorderSide(
|
|
||||||
color: Palette.coral.withValues(alpha: 0.4),
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 1.w,
|
|
||||||
vertical: 0.6.h,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
icon: const Icon(Icons.block_rounded, size: 16),
|
|
||||||
label: const Text('Revoke'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: 0.8.h),
|
|
||||||
// Row 2: wallet address · client name
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
walletLabel,
|
|
||||||
style: theme.textTheme.bodySmall?.copyWith(
|
|
||||||
color: Palette.ink,
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 0.8.w),
|
|
||||||
child: Text(
|
|
||||||
'·',
|
|
||||||
style: theme.textTheme.bodySmall
|
|
||||||
?.copyWith(color: muted),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
clientLabel,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: theme.textTheme.bodySmall
|
|
||||||
?.copyWith(color: muted),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,5 +5,4 @@ class Palette {
|
|||||||
static const coral = Color(0xFFE26254);
|
static const coral = Color(0xFFE26254);
|
||||||
static const cream = Color(0xFFFFFAF4);
|
static const cream = Color(0xFFFFFAF4);
|
||||||
static const line = Color(0x1A15263C);
|
static const line = Color(0x1A15263C);
|
||||||
static const token = Color(0xFF5C6BC0);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user