Skip to content

Commit ecc4f1f

Browse files
authored
Merge pull request open-wallet-standard#118 from open-wallet-standard/nj/sol
fix: Solana broadcast encoding, sign_transaction extraction, and compact-u16 parsing
2 parents f776fc6 + 0652827 commit ecc4f1f

11 files changed

Lines changed: 796 additions & 65 deletions

File tree

bindings/node/__test__/index.spec.mjs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,14 @@ describe('@open-wallet-standard/core', () => {
210210
it('signs transactions on all chains', () => {
211211
createWallet('tx-signer', undefined, 12, vaultDir);
212212
const txHex = 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef';
213+
// Solana extract_signable_bytes expects a valid wire format:
214+
// [compact-u16 sig count][64-byte sig slots...][message...]
215+
// Build a minimal tx with 1 sig slot (0x01) + 64 zero bytes + a message.
216+
const solTxHex = '01' + '00'.repeat(64) + 'deadbeefdeadbeef';
213217

214218
for (const chain of ['evm', 'solana', 'sui', 'bitcoin', 'cosmos', 'tron', 'ton', 'filecoin']) {
215-
const result = signTransaction('tx-signer', chain, txHex, undefined, undefined, vaultDir);
219+
const hex = chain === 'solana' ? solTxHex : txHex;
220+
const result = signTransaction('tx-signer', chain, hex, undefined, undefined, vaultDir);
216221
assert.ok(result.signature.length > 0, `signature should be non-empty for ${chain}`);
217222
}
218223

bindings/node/package-lock.json

Lines changed: 16 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ows/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ows/crates/ows-cli/src/commands/fund.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
11
use crate::CliError;
2+
use ows_lib::types::AccountInfo;
3+
4+
/// Returns the wallet account matching the target funding chain.
5+
fn find_account_for_chain<'a>(
6+
accounts: &'a [AccountInfo],
7+
chain: &str,
8+
) -> Result<&'a AccountInfo, CliError> {
9+
let chain_prefix = match chain {
10+
"solana" => "solana:",
11+
_ => "eip155:",
12+
};
13+
14+
accounts
15+
.iter()
16+
.find(|a| a.chain_id.starts_with(chain_prefix))
17+
.ok_or_else(|| {
18+
CliError::InvalidArgs(format!("wallet has no account for chain \"{chain}\""))
19+
})
20+
}
221

322
/// `ows fund buy --wallet <name> [--chain base] [--token USDC]`
423
///
524
/// Creates a MoonPay deposit that generates multi-chain deposit addresses.
625
/// Anyone can send crypto from any chain — it auto-converts to the target token.
726
pub fn run(wallet_name: &str, chain: Option<&str>, token: Option<&str>) -> Result<(), CliError> {
827
let wallet = ows_lib::get_wallet(wallet_name, None)?;
9-
let evm_account = wallet
10-
.accounts
11-
.iter()
12-
.find(|a| a.chain_id.starts_with("eip155:"))
13-
.ok_or_else(|| CliError::InvalidArgs("wallet has no EVM account".into()))?;
14-
15-
let address = &evm_account.address;
1628
let chain_name = chain.unwrap_or("base");
29+
30+
let account = find_account_for_chain(&wallet.accounts, chain_name)?;
31+
let address = &account.address;
1732
let token_name = token.unwrap_or("USDC");
1833

1934
eprintln!("Creating deposit for wallet \"{wallet_name}\" ({address})");
@@ -69,15 +84,11 @@ pub fn run(wallet_name: &str, chain: Option<&str>, token: Option<&str>) -> Resul
6984
/// Check token balances via MoonPay.
7085
pub fn balance(wallet_name: &str, chain: Option<&str>) -> Result<(), CliError> {
7186
let wallet = ows_lib::get_wallet(wallet_name, None)?;
72-
let evm_account = wallet
73-
.accounts
74-
.iter()
75-
.find(|a| a.chain_id.starts_with("eip155:"))
76-
.ok_or_else(|| CliError::InvalidArgs("wallet has no EVM account".into()))?;
77-
78-
let address = &evm_account.address;
7987
let chain_name = chain.unwrap_or("base");
8088

89+
let account = find_account_for_chain(&wallet.accounts, chain_name)?;
90+
let address = &account.address;
91+
8192
let rt =
8293
tokio::runtime::Runtime::new().map_err(|e| CliError::InvalidArgs(format!("tokio: {e}")))?;
8394

ows/crates/ows-cli/src/commands/sign_transaction.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ pub fn run(
3535
.map_err(|e| CliError::InvalidArgs(format!("invalid hex transaction: {e}")))?;
3636

3737
let signer = signer_for_chain(chain.chain_type);
38-
let output = signer.sign_transaction(key.expose(), &tx_bytes)?;
38+
let signable = signer.extract_signable_bytes(&tx_bytes)?;
39+
let output = signer.sign_transaction(key.expose(), signable)?;
3940

4041
print_result(
4142
&hex::encode(&output.signature),

ows/crates/ows-cli/src/commands/wallet.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ pub fn list() -> Result<(), CliError> {
174174
println!("Name: {}", w.name);
175175
println!("Secured: ✓ (encrypted)");
176176
for acct in &w.accounts {
177-
println!(" {} → {}", acct.chain_id, acct.address);
177+
let label = ows_core::parse_chain(&acct.chain_id)
178+
.map(|c| format!(" ({})", c.name))
179+
.unwrap_or_default();
180+
println!(" {}{} → {}", acct.chain_id, label, acct.address);
178181
}
179182
println!("Created: {}", w.created_at);
180183
println!();

ows/crates/ows-lib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ tempfile = "3"
3434
sha3 = "0.10"
3535
k256 = { version = "0.13", features = ["ecdsa"] }
3636
ed25519-dalek = "2"
37+
bs58 = "0.5"
3738
ows-signer = { path = "../ows-signer", version = "=1.0.0", features = ["fast-kdf"] }

ows/crates/ows-lib/src/key_ops.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,10 @@ pub fn sign_with_api_key(
136136
// 6. Decrypt mnemonic from key file using HKDF(token)
137137
let key = decrypt_key_from_api_key(&key_file, &wallet.id, token, chain.chain_type, index)?;
138138

139-
// 7. Sign
139+
// 7. Sign (extract signable portion first — e.g. strips Solana sig-slot headers)
140140
let signer = signer_for_chain(chain.chain_type);
141-
let output = signer.sign_transaction(key.expose(), tx_bytes)?;
141+
let signable = signer.extract_signable_bytes(tx_bytes)?;
142+
let output = signer.sign_transaction(key.expose(), signable)?;
142143

143144
Ok(crate::types::SignResult {
144145
signature: hex::encode(&output.signature),

0 commit comments

Comments
 (0)