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

Commit cacb016

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 58e97e6 commit cacb016

16 files changed

Lines changed: 213 additions & 139 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: 60 additions & 5 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,18 @@ 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+
/// The DAI token contract on Ethereum mainnet.
30+
///
31+
/// Source: https://developer.makerdao.com/dai/1/api/
32+
static DAI_MAINNET: Lazy<ethereum::Address> =
33+
Lazy::new(|| parse_unchecked("0x6b175474e89094c44da98b954eedeac495271d0f"));
34+
35+
/// The DAI token contract on the Ethereum testnet "kovan".
36+
///
37+
/// Source: https://developer.makerdao.com/dai/1/api/
38+
static DAI_KOVAN: Lazy<ethereum::Address> =
39+
Lazy::new(|| parse_unchecked("0xc4375b7de8af5a38a93548eb8453a498222c4ff2"));
40+
2941
static COMIT_SOCKET: Lazy<Multiaddr> = Lazy::new(|| parse_unchecked("/ip4/0.0.0.0/tcp/9939"));
3042

3143
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
@@ -119,14 +131,16 @@ impl From<Bitcoin> for file::Bitcoin {
119131
pub struct Ethereum {
120132
pub chain_id: ChainId,
121133
pub geth: Geth,
134+
pub tokens: Tokens,
122135
}
123136

124137
impl Ethereum {
125-
fn new(chain_id: ChainId) -> Self {
126-
Self {
138+
fn new(chain_id: ChainId) -> Result<Ethereum> {
139+
Ok(Self {
127140
chain_id,
128141
geth: Geth::new(),
129-
}
142+
tokens: Tokens::from_config_or_chain_id(None, chain_id)?,
143+
})
130144
}
131145

132146
fn from_file(ethereum: file::Ethereum, comit_network: Option<comit::Network>) -> Result<Self> {
@@ -144,8 +158,16 @@ impl Ethereum {
144158

145159
let chain_id = ethereum.chain_id;
146160
let geth = ethereum.geth.unwrap_or_else(Geth::new);
161+
let tokens = ethereum.tokens.map_or_else(
162+
|| Tokens::from_config_or_chain_id(None, chain_id),
163+
|tokens| Tokens::from_config_or_chain_id(tokens.dai, chain_id),
164+
)?;
147165

148-
Ok(Ethereum { chain_id, geth })
166+
Ok(Ethereum {
167+
chain_id,
168+
geth,
169+
tokens,
170+
})
149171
}
150172
}
151173

@@ -154,6 +176,15 @@ impl From<Ethereum> for file::Ethereum {
154176
file::Ethereum {
155177
chain_id: ethereum.chain_id,
156178
geth: Some(ethereum.geth),
179+
tokens: Some(ethereum.tokens.into()),
180+
}
181+
}
182+
}
183+
184+
impl From<Tokens> for file::Tokens {
185+
fn from(tokens: Tokens) -> Self {
186+
file::Tokens {
187+
dai: Some(tokens.dai),
157188
}
158189
}
159190
}
@@ -171,6 +202,30 @@ impl Geth {
171202
}
172203
}
173204

205+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
206+
pub struct Tokens {
207+
pub dai: ethereum::Address,
208+
}
209+
210+
impl Tokens {
211+
fn from_config_or_chain_id(dai: Option<ethereum::Address>, id: ChainId) -> Result<Self> {
212+
let dai = dai.map_or_else(|| dai_address_from_chain_id(id), Ok)?;
213+
214+
Ok(Self { dai })
215+
}
216+
}
217+
218+
fn dai_address_from_chain_id(id: ChainId) -> Result<ethereum::Address> {
219+
Ok(match id {
220+
ChainId::MAINNET => *DAI_MAINNET,
221+
ChainId::KOVAN => *DAI_KOVAN,
222+
id => anyhow::bail!(
223+
"unable to infer DAI token contract from chain-ID {}",
224+
u32::from(id)
225+
),
226+
})
227+
}
228+
174229
#[derive(Clone, Debug, PartialEq, Serialize)]
175230
pub struct Lightning {
176231
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,
@@ -350,40 +367,67 @@ dir = "/foo/bar"
350367
fn ethereum_deserializes_correctly() {
351368
let file_contents = vec![
352369
r#"
353-
chain_id = 1337
370+
chain_id = 42
354371
[geth]
355372
node_url = "http://example.com:8545"
373+
[tokens]
374+
dai = "0xc4375b7de8af5a38a93548eb8453a498222c4ff2"
356375
"#,
357376
r#"
358377
chain_id = 3
359378
[geth]
360379
node_url = "http://example.com:8545"
380+
[tokens]
381+
dai = "0xaD6D458402F60fD3Bd25163575031ACDce07538D"
361382
"#,
362383
r#"
363384
chain_id = 1
364385
[geth]
365386
node_url = "http://example.com:8545"
387+
[tokens]
388+
dai = "0x6b175474e89094c44da98b954eedeac495271d0f"
366389
"#,
367390
];
368391

369392
let expected = vec![
370393
Ethereum {
371-
chain_id: ChainId::GETH_DEV,
394+
chain_id: ChainId::KOVAN,
372395
geth: Some(Geth {
373396
node_url: Url::parse("http://example.com:8545").unwrap(),
374397
}),
398+
tokens: Some(Tokens {
399+
dai: Some(
400+
"0xc4375b7de8af5a38a93548eb8453a498222c4ff2"
401+
.parse()
402+
.unwrap(),
403+
),
404+
}),
375405
},
376406
Ethereum {
377407
chain_id: ChainId::ROPSTEN,
378408
geth: Some(Geth {
379409
node_url: Url::parse("http://example.com:8545").unwrap(),
380410
}),
411+
tokens: Some(Tokens {
412+
dai: Some(
413+
"0xaD6D458402F60fD3Bd25163575031ACDce07538D"
414+
.parse()
415+
.unwrap(),
416+
),
417+
}),
381418
},
382419
Ethereum {
383420
chain_id: ChainId::MAINNET,
384421
geth: Some(Geth {
385422
node_url: Url::parse("http://example.com:8545").unwrap(),
386423
}),
424+
tokens: Some(Tokens {
425+
dai: Some(
426+
"0x6b175474e89094c44da98b954eedeac495271d0f"
427+
.parse()
428+
.unwrap(),
429+
),
430+
}),
387431
},
388432
];
389433

cnd/src/config/settings.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ impl Settings {
154154
|file| Bitcoin::from_file(file, comit_network),
155155
)?,
156156
ethereum: ethereum.map_or_else(
157-
|| Ok(Ethereum::new(comit_network.unwrap_or_default().into())),
157+
|| Ethereum::new(comit_network.unwrap_or_default().into()),
158158
|file| Ethereum::from_file(file, comit_network),
159159
)?,
160160
lightning: lightning.map_or_else(
@@ -169,7 +169,7 @@ impl Settings {
169169
mod tests {
170170
use super::*;
171171
use crate::{
172-
config::{file, Bitcoind, Geth, Lnd},
172+
config::{file, Bitcoind, Geth, Lnd, Tokens, DAI_MAINNET},
173173
ethereum::ChainId,
174174
};
175175
use spectral::prelude::*;
@@ -311,6 +311,7 @@ mod tests {
311311
geth: Geth {
312312
node_url: "http://localhost:8545".parse().unwrap(),
313313
},
314+
tokens: Tokens { dai: *DAI_MAINNET },
314315
})
315316
}
316317

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

0 commit comments

Comments
 (0)