Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,28 @@ vesting and governance deposits) are read from the chain and never scaled.
> cap is too low to fund the ephemeral accounts, or if a selected phase needs more headroom
> than the cap leaves.

### NEAR (ML-DSA-65 accounts)

NEAR accepts ML-DSA-65 keys from protocol version 85, so a Quantus wallet can control a NEAR account directly. `quantus near show-key`, `create-account`, `keys`, `send` and `dao …` are documented in `quantus near --help`. Any other contract method goes through near-cli-rs's own `contract call-function` sentence, copied word for word so a script written for one runs on the other; `sign-with-wallet <WALLET>` stands where near-cli has `sign-with-keychain`:

```bash
# view method
quantus near contract call-function as-read-only dclv2.ref-labs.near get_metadata json-args '{}' network-config mainnet now

# change method, signed by the wallet that holds the account's ML-DSA-65 key
quantus near contract call-function as-transaction wrap.near near_deposit json-args '{}' \
prepaid-gas '30 Tgas' attached-deposit '0.1 NEAR' sign-as vault.alice.near \
network-config mainnet sign-with-wallet alice send

# same sentence ending in `display`: prints the transaction and the equivalent near-cli-rs
# command without loading the wallet or touching the network
quantus near contract call-function as-transaction wrap.near near_deposit json-args '{}' \
prepaid-gas '30 Tgas' attached-deposit '0.1 NEAR' sign-as vault.alice.near \
network-config mainnet sign-with-wallet alice display
```

Amounts use near-cli's unit strings (`'30 Tgas'`, `'0.5 NEAR'`, `'1 yoctoNEAR'`). `network-config` takes `mainnet` or `testnet` (both via FastNear RPC) or an RPC URL. `sign-with-wallet` accepts `--password-file` for scripting.

### Other Commands

| Command | Description |
Expand Down
1 change: 1 addition & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod metadata;
pub mod multisend;
pub mod multisig;
pub mod near;
pub mod near_contract;
pub mod preimage;
pub mod reversible;
pub mod runtime;
Expand Down
36 changes: 23 additions & 13 deletions src/cli/near.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
//! 5. `near dao ...` — act as a co-signer in a Sputnik DAO multisig (the contract behind Trezu):
//! propose transfers and vote, signed by the wallet. Sputnik authorizes by account id, so an
//! ML-DSA-65-controlled account is a full member with no DAO-side changes.
//! 6. `near contract call-function ...` — any contract method, in near-cli-rs's own sentence
//! grammar (see [`crate::cli::near_contract`]).
//!
//! ML-DSA-87 wallets are rejected: NEAR defined ML-DSA-65 only.

Expand Down Expand Up @@ -157,6 +159,12 @@ pub enum NearCommands {
#[command(subcommand)]
command: DaoCommands,
},

/// Use this for contract actions: call function (near-cli-rs grammar)
Contract {
#[command(subcommand)]
command: crate::cli::near_contract::ContractCommands,
},
}

/// Sputnik DAO subcommands
Expand Down Expand Up @@ -309,6 +317,8 @@ pub async fn handle_near_command(command: NearCommands) -> Result<()> {
handle_send(&wallet, &account, &to, &amount, &network, rpc_url, password, password_file)
.await,
NearCommands::Dao { command } => handle_dao_command(command).await,
NearCommands::Contract { command } =>
crate::cli::near_contract::handle_contract_command(command).await,
}
}

Expand Down Expand Up @@ -370,7 +380,7 @@ async fn handle_dao_command(command: DaoCommands) -> Result<()> {
}

/// Load a wallet and its key as a NEAR public key, refusing non-65 schemes.
fn load_ml_dsa_65_wallet(
pub(crate) fn load_ml_dsa_65_wallet(
wallet: &str,
password: Option<String>,
password_file: Option<String>,
Expand Down Expand Up @@ -412,7 +422,7 @@ fn explorer_tx_url(network: &str, tx_hash: &str) -> Option<String> {
}
}

fn report_outcome(network: &str, outcome: &serde_json::Value) {
pub(crate) fn report_outcome(network: &str, outcome: &serde_json::Value) {
if let Some(hash) = outcome.pointer("/transaction/hash").and_then(|h| h.as_str()) {
log_print!(" Transaction: {}", hash.bright_cyan());
if let Some(url) = explorer_tx_url(network, hash) {
Expand Down Expand Up @@ -651,17 +661,17 @@ fn policy_proposal_bond(policy: &serde_json::Value) -> Result<u128> {
})
}

/// Sign a single function call on the DAO with the member account's
/// ML-DSA-65 key and submit it.
/// Sign a single function call on `receiver` with the account's ML-DSA-65
/// key and submit it, returning the finalized outcome.
#[allow(clippy::too_many_arguments)]
async fn submit_dao_call(
pub(crate) async fn submit_function_call(
client: &NearRpcClient,
keypair: &QuantumKeyPair,
public: PublicKey,
account: &str,
dao: &str,
receiver: &str,
method_name: &str,
args: serde_json::Value,
args: Vec<u8>,
gas: u64,
deposit: u128,
network: &str,
Expand All @@ -674,11 +684,11 @@ async fn submit_dao_call(
signer_id: account.to_string(),
public_key: public,
nonce: access_key.nonce + 1,
receiver_id: dao.to_string(),
receiver_id: receiver.to_string(),
block_hash: access_key.block_hash,
actions: vec![Action::FunctionCall(FunctionCallAction {
method_name: method_name.to_string(),
args: args.to_string().into_bytes(),
args,
gas,
deposit,
})],
Expand Down Expand Up @@ -734,14 +744,14 @@ async fn handle_dao_propose_transfer(
);

let args = transfer_proposal_args(description, receiver, amount_yocto);
let outcome = submit_dao_call(
let outcome = submit_function_call(
&client,
&keypair,
public,
account,
dao,
"add_proposal",
args,
args.to_string().into_bytes(),
ADD_PROPOSAL_GAS,
bond_yocto,
network,
Expand Down Expand Up @@ -799,14 +809,14 @@ async fn handle_dao_vote(
);

let args = serde_json::json!({ "id": id, "action": action, "proposal": kind });
submit_dao_call(
submit_function_call(
&client,
&keypair,
public,
account,
dao,
"act_proposal",
args,
args.to_string().into_bytes(),
ACT_PROPOSAL_GAS,
0,
network,
Expand Down
Loading