Skip to content
This repository was archived by the owner on Mar 23, 2021. It is now read-only.

Commit b90d9d6

Browse files
Infer DAI token from Ethereum chain ID
This introduces the ethereum::Address type into our config::File struct. Unfortunately, this opens a can of worms with a bug in the serde-hex library. See [0] for more details. We work around this by removing the dependency on serde-hex from our Ethereum deserialization code and instead hand-roll everything for our usecase. This also has the advantage that we can now again just use our structs directly in the route handlers of warp instead of serde_json::Value. See [1] for more on this. [0]: fspmarshall/serde-hex#8 [1]: #2405
1 parent 3ec3cec commit b90d9d6

16 files changed

Lines changed: 202 additions & 135 deletions

File tree

Cargo.lock

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

api_tests/src/config.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,8 @@ export class E2ETestActorConfig {
6565
};
6666
}
6767

68-
private createLedgerConnectors(
69-
ledgerConfig: LedgerConfig
70-
): LedgerConnectors {
71-
const config: LedgerConnectors = {};
68+
private createLedgerConnectors(ledgerConfig: LedgerConfig): LedgerConfigs {
69+
const config: LedgerConfigs = {};
7270

7371
if (ledgerConfig.bitcoin) {
7472
config.bitcoin = bitcoinConnector(ledgerConfig.bitcoin);
@@ -106,26 +104,31 @@ export class E2ETestActorConfig {
106104
}
107105
}
108106

109-
interface LedgerConnectors {
110-
bitcoin?: BitcoinConnector;
111-
ethereum?: EthereumConnector;
112-
lightning?: LightningConnector;
107+
interface LedgerConfigs {
108+
bitcoin?: BitcoinConfig;
109+
ethereum?: EthereumConfig;
110+
lightning?: LightningConfig;
113111
}
114112

115113
interface Geth {
116114
node_url: string;
117115
}
118116

119-
interface EthereumConnector {
117+
interface EthereumConfig {
120118
chain_id: number;
121119
geth: Geth;
120+
tokens: Tokens;
121+
}
122+
123+
interface Tokens {
124+
dai: string;
122125
}
123126

124127
interface Bitcoind {
125128
node_url: string;
126129
}
127130

128-
interface BitcoinConnector {
131+
interface BitcoinConfig {
129132
network: string;
130133
bitcoind: Bitcoind;
131134
}
@@ -135,12 +138,12 @@ interface Lnd {
135138
dir: string;
136139
}
137140

138-
interface LightningConnector {
141+
interface LightningConfig {
139142
network: string;
140143
lnd: Lnd;
141144
}
142145

143-
function bitcoinConnector(nodeConfig: BitcoinNodeConfig): BitcoinConnector {
146+
function bitcoinConnector(nodeConfig: BitcoinNodeConfig): BitcoinConfig {
144147
return {
145148
bitcoind: {
146149
node_url: nodeConfig.rpcUrl,
@@ -149,18 +152,19 @@ function bitcoinConnector(nodeConfig: BitcoinNodeConfig): BitcoinConnector {
149152
};
150153
}
151154

152-
function ethereumConnector(nodeConfig: EthereumNodeConfig): EthereumConnector {
155+
function ethereumConnector(nodeConfig: EthereumNodeConfig): EthereumConfig {
153156
return {
154157
chain_id: nodeConfig.chain_id,
155158
geth: {
156159
node_url: nodeConfig.rpc_url,
157160
},
161+
tokens: {
162+
dai: nodeConfig.tokenContract,
163+
},
158164
};
159165
}
160166

161-
function lightningConnector(
162-
nodeConfig: LightningNodeConfig
163-
): LightningConnector {
167+
function lightningConnector(nodeConfig: LightningNodeConfig): LightningConfig {
164168
return {
165169
network: "regtest",
166170
lnd: {

cnd/src/config.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod serde_bitcoin_network;
33
mod settings;
44
mod validation;
55

6-
use crate::ethereum::ChainId;
6+
use crate::{ethereum, ethereum::ChainId};
77
use anyhow::{Context, Result};
88
use conquer_once::Lazy;
99
use libp2p::Multiaddr;
@@ -26,6 +26,11 @@ static LND_URL: Lazy<Url> = Lazy::new(|| parse_unchecked("https://localhost:8080
2626

2727
static WEB3_URL: Lazy<Url> = Lazy::new(|| parse_unchecked("http://localhost:8545"));
2828

29+
static DAI_MAINNET: Lazy<ethereum::Address> =
30+
Lazy::new(|| parse_unchecked("0x6b175474e89094c44da98b954eedeac495271d0f"));
31+
static DAI_KOVAN: Lazy<ethereum::Address> =
32+
Lazy::new(|| parse_unchecked("0xc4375b7de8af5a38a93548eb8453a498222c4ff2"));
33+
2934
static COMIT_SOCKET: Lazy<Multiaddr> = Lazy::new(|| parse_unchecked("/ip4/0.0.0.0/tcp/9939"));
3035

3136
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
@@ -109,6 +114,7 @@ impl From<file::Bitcoin> for Bitcoin {
109114
pub struct Ethereum {
110115
pub chain_id: ChainId,
111116
pub geth: Geth,
117+
pub tokens: Tokens,
112118
}
113119

114120
impl Ethereum {
@@ -117,8 +123,16 @@ impl Ethereum {
117123
let geth = ethereum.geth.unwrap_or_else(|| Geth {
118124
node_url: WEB3_URL.clone(),
119125
});
126+
let tokens = ethereum.tokens.map_or_else(
127+
|| Tokens::from_config_or_chain_id(None, chain_id),
128+
|tokens| Tokens::from_config_or_chain_id(tokens.dai, chain_id),
129+
)?;
120130

121-
Ok(Ethereum { chain_id, geth })
131+
Ok(Ethereum {
132+
chain_id,
133+
geth,
134+
tokens,
135+
})
122136
}
123137
}
124138

@@ -127,6 +141,15 @@ impl From<Ethereum> for file::Ethereum {
127141
file::Ethereum {
128142
chain_id: ethereum.chain_id,
129143
geth: Some(ethereum.geth),
144+
tokens: Some(ethereum.tokens.into()),
145+
}
146+
}
147+
}
148+
149+
impl From<Tokens> for file::Tokens {
150+
fn from(tokens: Tokens) -> Self {
151+
file::Tokens {
152+
dai: Some(tokens.dai),
130153
}
131154
}
132155
}
@@ -138,6 +161,7 @@ impl Default for Ethereum {
138161
geth: Geth {
139162
node_url: WEB3_URL.clone(),
140163
},
164+
tokens: Tokens { dai: *DAI_MAINNET },
141165
}
142166
}
143167
}
@@ -147,6 +171,30 @@ pub struct Geth {
147171
pub node_url: Url,
148172
}
149173

174+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
175+
pub struct Tokens {
176+
pub dai: ethereum::Address,
177+
}
178+
179+
impl Tokens {
180+
fn from_config_or_chain_id(dai: Option<ethereum::Address>, id: ChainId) -> Result<Self> {
181+
let dai = dai.map_or_else(|| dai_address_from_chain_id(id), Ok)?;
182+
183+
Ok(Self { dai })
184+
}
185+
}
186+
187+
fn dai_address_from_chain_id(id: ChainId) -> Result<ethereum::Address> {
188+
Ok(match id {
189+
ChainId::MAINNET => *DAI_MAINNET,
190+
ChainId::KOVAN => *DAI_KOVAN,
191+
id => anyhow::bail!(
192+
"unable to infer DAI token contract from chain-ID {}",
193+
u32::from(id)
194+
),
195+
})
196+
}
197+
150198
#[derive(Clone, Debug, PartialEq, Serialize)]
151199
pub struct Lightning {
152200
pub network: bitcoin::Network,

cnd/src/config/file.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
22
config::{Bitcoind, Data, Geth, Network},
3+
ethereum,
34
ethereum::ChainId,
45
};
56
use log::LevelFilter;
@@ -37,6 +38,12 @@ pub struct Bitcoin {
3738
pub struct Ethereum {
3839
pub chain_id: ChainId,
3940
pub geth: Option<Geth>,
41+
pub tokens: Option<Tokens>,
42+
}
43+
44+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
45+
pub struct Tokens {
46+
pub dai: Option<ethereum::Address>,
4047
}
4148

4249
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
@@ -230,6 +237,9 @@ chain_id = 1337
230237
[ethereum.geth]
231238
node_url = "http://localhost:8545/"
232239
240+
[ethereum.tokens]
241+
dai = "0x6b175474e89094c44da98b954eedeac495271d0f"
242+
233243
[lightning]
234244
network = "regtest"
235245
@@ -264,6 +274,13 @@ dir = "/foo/bar"
264274
geth: Some(Geth {
265275
node_url: "http://localhost:8545".parse().unwrap(),
266276
}),
277+
tokens: Some(Tokens {
278+
dai: Some(
279+
"0x6b175474e89094c44da98b954eedeac495271d0f"
280+
.parse()
281+
.unwrap(),
282+
),
283+
}),
267284
}),
268285
lightning: Some(Lightning {
269286
network: bitcoin::Network::Regtest,
@@ -349,40 +366,67 @@ dir = "/foo/bar"
349366
fn ethereum_deserializes_correctly() {
350367
let file_contents = vec![
351368
r#"
352-
chain_id = 1337
369+
chain_id = 42
353370
[geth]
354371
node_url = "http://example.com:8545"
372+
[tokens]
373+
dai = "0xc4375b7de8af5a38a93548eb8453a498222c4ff2"
355374
"#,
356375
r#"
357376
chain_id = 3
358377
[geth]
359378
node_url = "http://example.com:8545"
379+
[tokens]
380+
dai = "0xaD6D458402F60fD3Bd25163575031ACDce07538D"
360381
"#,
361382
r#"
362383
chain_id = 1
363384
[geth]
364385
node_url = "http://example.com:8545"
386+
[tokens]
387+
dai = "0x6b175474e89094c44da98b954eedeac495271d0f"
365388
"#,
366389
];
367390

368391
let expected = vec![
369392
Ethereum {
370-
chain_id: ChainId::REGTEST,
393+
chain_id: ChainId::KOVAN,
371394
geth: Some(Geth {
372395
node_url: Url::parse("http://example.com:8545").unwrap(),
373396
}),
397+
tokens: Some(Tokens {
398+
dai: Some(
399+
"0xc4375b7de8af5a38a93548eb8453a498222c4ff2"
400+
.parse()
401+
.unwrap(),
402+
),
403+
}),
374404
},
375405
Ethereum {
376406
chain_id: ChainId::ROPSTEN,
377407
geth: Some(Geth {
378408
node_url: Url::parse("http://example.com:8545").unwrap(),
379409
}),
410+
tokens: Some(Tokens {
411+
dai: Some(
412+
"0xaD6D458402F60fD3Bd25163575031ACDce07538D"
413+
.parse()
414+
.unwrap(),
415+
),
416+
}),
380417
},
381418
Ethereum {
382419
chain_id: ChainId::MAINNET,
383420
geth: Some(Geth {
384421
node_url: Url::parse("http://example.com:8545").unwrap(),
385422
}),
423+
tokens: Some(Tokens {
424+
dai: Some(
425+
"0x6b175474e89094c44da98b954eedeac495271d0f"
426+
.parse()
427+
.unwrap(),
428+
),
429+
}),
386430
},
387431
];
388432

cnd/src/config/settings.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ impl Settings {
156156
mod tests {
157157
use super::*;
158158
use crate::{
159-
config::{file, Bitcoind, Geth, Lnd},
159+
config::{file, Bitcoind, Geth, Lnd, Tokens, DAI_MAINNET},
160160
ethereum::ChainId,
161161
};
162162
use spectral::prelude::*;
@@ -298,6 +298,7 @@ mod tests {
298298
geth: Geth {
299299
node_url: "http://localhost:8545".parse().unwrap(),
300300
},
301+
tokens: Tokens { dai: *DAI_MAINNET },
301302
})
302303
}
303304

cnd/src/http_api/halbit_herc20.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@ use crate::{
88
storage::Save,
99
Facade, LocalSwapId,
1010
};
11-
use serde::Deserialize;
1211
use warp::{http::StatusCode, Rejection, Reply};
1312

1413
#[allow(clippy::needless_pass_by_value)]
15-
pub async fn post_swap(body: serde_json::Value, facade: Facade) -> Result<impl Reply, Rejection> {
16-
let body = PostBody::<Halbit, Herc20>::deserialize(&body)
17-
.map_err(anyhow::Error::new)
18-
.map_err(problem::from_anyhow)
19-
.map_err(warp::reject::custom)?;
20-
14+
pub async fn post_swap(
15+
body: PostBody<Halbit, Herc20>,
16+
facade: Facade,
17+
) -> Result<impl Reply, Rejection> {
2118
let swap_id = LocalSwapId::default();
2219
let reply = warp::reply::reply();
2320

cnd/src/http_api/hbit_herc20.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@ use crate::{
88
storage::Save,
99
Facade, LocalSwapId, Side,
1010
};
11-
use serde::Deserialize;
1211
use warp::{http::StatusCode, Rejection, Reply};
1312

1413
#[allow(clippy::needless_pass_by_value)]
15-
pub async fn post_swap(body: serde_json::Value, facade: Facade) -> Result<impl Reply, Rejection> {
16-
let body = PostBody::<Hbit, Herc20>::deserialize(&body)
17-
.map_err(anyhow::Error::new)
18-
.map_err(problem::from_anyhow)
19-
.map_err(warp::reject::custom)?;
20-
14+
pub async fn post_swap(
15+
body: PostBody<Hbit, Herc20>,
16+
facade: Facade,
17+
) -> Result<impl Reply, Rejection> {
2118
let swap_id = LocalSwapId::default();
2219
let reply = warp::reply::reply();
2320

cnd/src/http_api/herc20_halbit.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,12 @@ use crate::{
88
storage::Save,
99
Facade, LocalSwapId,
1010
};
11-
use serde::Deserialize;
1211
use warp::{http::StatusCode, Rejection, Reply};
1312

14-
pub async fn post_swap(body: serde_json::Value, facade: Facade) -> Result<impl Reply, Rejection> {
15-
let body = PostBody::<Herc20, Halbit>::deserialize(&body)
16-
.map_err(anyhow::Error::new)
17-
.map_err(problem::from_anyhow)
18-
.map_err(warp::reject::custom)?;
19-
13+
pub async fn post_swap(
14+
body: PostBody<Herc20, Halbit>,
15+
facade: Facade,
16+
) -> Result<impl Reply, Rejection> {
2017
let swap_id = LocalSwapId::default();
2118
let reply = warp::reply::reply();
2219

0 commit comments

Comments
 (0)