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

Commit ed482a0

Browse files
committed
common: improve WalletConnect
* add gas limit * remove nonce (could cause issue) * don't transform `v` as it's legacy * improve signature extraction * add error for when WalletConnect API failed to sign Signed-off-by: xphoniex <dj.2dixx@gmail.com>
1 parent 4561ce1 commit ed482a0

File tree

2 files changed

+121
-15
lines changed

2 files changed

+121
-15
lines changed

common/src/ethereum.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ pub enum WalletError {
143143
#[error(transparent)]
144144
Local(#[from] ethers::signers::WalletError),
145145
#[error(transparent)]
146-
WalletConnect(#[from] ::walletconnect::client::CallError),
146+
WalletConnect(#[from] walletconnect::WalletError),
147147
#[error("no wallet specified")]
148148
NoWallet,
149149
}

common/src/ethereum/walletconnect.rs

Lines changed: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ pub struct WalletConnect {
1212
address: Address,
1313
}
1414

15+
#[derive(Debug, thiserror::Error)]
16+
pub enum WalletError {
17+
#[error(transparent)]
18+
CallError(#[from] CallError),
19+
#[error("failed to sign the tx")]
20+
FailedToSignTx,
21+
}
22+
1523
impl WalletConnect {
1624
pub fn new() -> Result<Self, Box<dyn Error>> {
1725
let client = Client::new(
@@ -61,14 +69,15 @@ impl WalletConnect {
6169
pub async fn sign_message<S: Send + Sync + AsRef<[u8]>>(
6270
&self,
6371
msg: S,
64-
) -> Result<Signature, CallError> {
72+
) -> Result<Signature, WalletError> {
6573
let msg = unsafe { std::str::from_utf8_unchecked(msg.as_ref()) };
6674
self.client
6775
.personal_sign(&[msg, &self.address_string()])
6876
.await
77+
.map_err(WalletError::from)
6978
}
7079

71-
pub async fn sign_transaction(&self, msg: &TypedTransaction) -> Result<Signature, CallError> {
80+
pub async fn sign_transaction(&self, msg: &TypedTransaction) -> Result<Signature, WalletError> {
7281
let to = if let Some(NameOrAddress::Address(address)) = msg.to() {
7382
Some(*address)
7483
} else {
@@ -77,28 +86,125 @@ impl WalletConnect {
7786
let tx = Transaction {
7887
from: *msg.from().unwrap(),
7988
to,
80-
gas_limit: None,
89+
gas_limit: msg.gas().cloned(),
8190
gas_price: msg.gas_price(),
8291
value: *msg.value().unwrap_or(&U256::from(0)),
8392
data: msg.data().unwrap().to_vec(),
84-
nonce: msg.nonce().copied(),
93+
nonce: None,
8594
};
8695

8796
let raw = self.client.sign_transaction(tx).await?.to_vec();
88-
assert_eq!(raw[raw.len() - 66], 160);
89-
assert_eq!(raw[raw.len() - 33], 160);
90-
91-
// Transform `v` according to:
92-
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#specification
93-
let mut v = raw[raw.len() - 67] as u64;
94-
if v == 27 || v == 28 {
95-
v += 2 * self.chain_id() + 8;
97+
let mut v_r_s = None;
98+
for offset in 0..7 {
99+
let mut head = raw.len() - 67 + offset;
100+
v_r_s = extract_v_r_s(&raw[head..]);
101+
if v_r_s.is_some() {
102+
break;
103+
}
104+
105+
if offset == 0 {
106+
continue;
107+
}
108+
head = raw.len() - 67 - offset;
109+
v_r_s = extract_v_r_s(&raw[head..]);
110+
if v_r_s.is_some() {
111+
break;
112+
}
96113
}
97114

115+
let (v, r, s) = v_r_s.ok_or(WalletError::FailedToSignTx)?;
98116
Ok(Signature {
99117
v,
100-
r: U256::from(&raw[raw.len() - 65..raw.len() - 33]),
101-
s: U256::from(&raw[raw.len() - 32..]),
118+
r: U256::from(r),
119+
s: U256::from(s),
102120
})
103121
}
104122
}
123+
124+
fn extract_v_r_s(tx: &[u8]) -> Option<(u64, &[u8], &[u8])> {
125+
let mut head = 0_usize;
126+
let v: u64 = tx[head].into();
127+
128+
head += 1;
129+
if tx[head] <= 0x80 {
130+
return None;
131+
}
132+
let len_r = (tx[head] - 0x80) as usize;
133+
if head + len_r >= tx.len() {
134+
return None;
135+
}
136+
let r = &tx[head + 1..head + 1 + len_r];
137+
138+
head += 1 + len_r;
139+
if tx[head] <= 0x80 {
140+
return None;
141+
}
142+
let len_s = (tx[head] - 0x80) as usize;
143+
if head + len_s >= tx.len() {
144+
return None;
145+
}
146+
let s = &tx[head + 1..head + 1 + len_s];
147+
148+
if 1 + r.len() + s.len() + 2 != tx.len() {
149+
return None;
150+
}
151+
152+
Some((v, r, s))
153+
}
154+
155+
#[cfg(test)]
156+
mod test {
157+
use super::*;
158+
159+
#[test]
160+
fn test_regular_sig() {
161+
let tx = [
162+
0x1c, 0xa0, 0x88, 0xff, 0x6c, 0xf0, 0xfe, 0xfd, 0x94, 0xdb, 0x46, 0x11, 0x11, 0x49,
163+
0xae, 0x4b, 0xfc, 0x17, 0x9e, 0x9b, 0x94, 0x72, 0x1f, 0xff, 0xd8, 0x21, 0xd3, 0x8d,
164+
0x16, 0x46, 0x4b, 0x3f, 0x71, 0xd0, 0xa0, 0x45, 0xe0, 0xaf, 0xf8, 0x00, 0x96, 0x1c,
165+
0xfc, 0xe8, 0x05, 0xda, 0xef, 0x70, 0x16, 0xb9, 0xb6, 0x75, 0xc1, 0x37, 0xa6, 0xa4,
166+
0x1a, 0x54, 0x8f, 0x7b, 0x60, 0xa3, 0x48, 0x4c, 0x06, 0xa3, 0x3a,
167+
];
168+
169+
let v_r_s = extract_v_r_s(&tx);
170+
assert!(v_r_s.is_some());
171+
let (v, r, s) = v_r_s.unwrap();
172+
173+
assert_eq!(v, 0x1c);
174+
assert_eq!(r, &tx[tx.len() - 65..tx.len() - 33]);
175+
assert_eq!(s, &tx[tx.len() - 32..]);
176+
}
177+
178+
#[test]
179+
fn test_variable_sig() {
180+
let tx = [
181+
0x2c, 0xa0, 0x09, 0x0c, 0x0a, 0x25, 0xaf, 0x16, 0x3b, 0x51, 0x86, 0xd5, 0x6f, 0x61,
182+
0xd2, 0xd1, 0xe7, 0xcf, 0xf1, 0x05, 0xb8, 0x9e, 0x24, 0xed, 0x48, 0x26, 0x7c, 0x43,
183+
0xa0, 0x22, 0x27, 0xd9, 0xf7, 0x14, 0x9f, 0x9b, 0xcc, 0xf7, 0x3a, 0xef, 0xa7, 0x7d,
184+
0x2c, 0xcb, 0x0b, 0x81, 0x59, 0x15, 0x04, 0xde, 0xcc, 0x07, 0xc1, 0x26, 0x92, 0xf9,
185+
0x0f, 0xfe, 0x47, 0xd0, 0xf0, 0xbd, 0xea, 0x99, 0xa6, 0x8d,
186+
];
187+
188+
let v_r_s = extract_v_r_s(&tx);
189+
assert!(v_r_s.is_some());
190+
let (v, r, s) = v_r_s.unwrap();
191+
192+
assert_eq!(v, 0x2c);
193+
assert_eq!(r, &tx[tx.len() - 64..tx.len() - 32]);
194+
assert_eq!(s, &tx[tx.len() - 31..]);
195+
}
196+
197+
#[test]
198+
fn test_malformed_sig() {
199+
let tx = [
200+
0x2c, 0xa0, 0x09, 0x0c, 0x0a, 0x25, 0xaf, 0x16, 0x3b, 0x51, 0x86, 0xd5, 0x6f, 0x61,
201+
0xd2, 0xd1, 0xe7, 0xcf, 0xf1, 0x05, 0xb8, 0x9e, 0x24, 0xed, 0x48, 0x26, 0x7c, 0x43,
202+
0xa0, 0x22, 0x27, 0xd9, 0xf7, 0x14, 0x81, 0x9b, 0xcc, 0xf7, 0x3a, 0xef, 0xa7, 0x7d,
203+
0x2c, 0xcb, 0x0b, 0x81, 0x59, 0x15, 0x04, 0xde, 0xcc, 0x07, 0xc1, 0x26, 0x92, 0xf9,
204+
0x0f, 0xfe, 0x47, 0xd0, 0xf0, 0xbd, 0xea, 0x99, 0xa6, 0x8d,
205+
];
206+
207+
let v_r_s = extract_v_r_s(&tx);
208+
assert!(v_r_s.is_none());
209+
}
210+
}

0 commit comments

Comments
 (0)