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

Commit 2ebdab3

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 9ddedf0 commit 2ebdab3

16 files changed

Lines changed: 221 additions & 141 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: 66 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, fs};
6+
use crate::{ethereum, ethereum::ChainId, fs};
77
use anyhow::{Context, Result};
88
use conquer_once::Lazy;
99
use libp2p::Multiaddr;
@@ -25,6 +25,18 @@ static LND_URL: Lazy<Url> = Lazy::new(|| parse_unchecked("https://localhost:8080
2525

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

28+
/// The DAI token contract on Ethereum mainnet.
29+
///
30+
/// Source: https://developer.makerdao.com/dai/1/api/
31+
static DAI_MAINNET: Lazy<ethereum::Address> =
32+
Lazy::new(|| parse_unchecked("0x6b175474e89094c44da98b954eedeac495271d0f"));
33+
34+
/// The DAI token contract on the Ethereum testnet "kovan".
35+
///
36+
/// Source: https://developer.makerdao.com/dai/1/api/
37+
static DAI_KOVAN: Lazy<ethereum::Address> =
38+
Lazy::new(|| parse_unchecked("0xc4375b7de8af5a38a93548eb8453a498222c4ff2"));
39+
2840
static COMIT_SOCKET: Lazy<Multiaddr> = Lazy::new(|| parse_unchecked("/ip4/0.0.0.0/tcp/9939"));
2941

3042
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
@@ -118,14 +130,16 @@ impl From<Bitcoin> for file::Bitcoin {
118130
pub struct Ethereum {
119131
pub chain_id: ChainId,
120132
pub geth: Geth,
133+
pub tokens: Tokens,
121134
}
122135

123136
impl Ethereum {
124-
fn new(chain_id: ChainId) -> Self {
125-
Self {
137+
fn new(chain_id: ChainId) -> Result<Ethereum> {
138+
Ok(Self {
126139
chain_id,
127140
geth: Geth::new(),
128-
}
141+
tokens: Tokens::new(chain_id)?,
142+
})
129143
}
130144

131145
fn from_file(ethereum: file::Ethereum, comit_network: Option<comit::Network>) -> Result<Self> {
@@ -143,8 +157,16 @@ impl Ethereum {
143157

144158
let chain_id = ethereum.chain_id;
145159
let geth = ethereum.geth.unwrap_or_else(Geth::new);
160+
let tokens = ethereum.tokens.map_or_else(
161+
|| Tokens::new(chain_id),
162+
|file| Tokens::from_file(file, chain_id),
163+
)?;
146164

147-
Ok(Ethereum { chain_id, geth })
165+
Ok(Ethereum {
166+
chain_id,
167+
geth,
168+
tokens,
169+
})
148170
}
149171
}
150172

@@ -153,6 +175,15 @@ impl From<Ethereum> for file::Ethereum {
153175
file::Ethereum {
154176
chain_id: ethereum.chain_id,
155177
geth: Some(ethereum.geth),
178+
tokens: Some(ethereum.tokens.into()),
179+
}
180+
}
181+
}
182+
183+
impl From<Tokens> for file::Tokens {
184+
fn from(tokens: Tokens) -> Self {
185+
file::Tokens {
186+
dai: Some(tokens.dai),
156187
}
157188
}
158189
}
@@ -170,6 +201,36 @@ impl Geth {
170201
}
171202
}
172203

204+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
205+
pub struct Tokens {
206+
pub dai: ethereum::Address,
207+
}
208+
209+
impl Tokens {
210+
fn new(chain_id: ChainId) -> Result<Self> {
211+
let dai = dai_address_from_chain_id(chain_id)?;
212+
213+
Ok(Self { dai })
214+
}
215+
216+
fn from_file(file: file::Tokens, id: ChainId) -> Result<Self> {
217+
let dai = file.dai.map_or_else(|| dai_address_from_chain_id(id), Ok)?;
218+
219+
Ok(Self { dai })
220+
}
221+
}
222+
223+
fn dai_address_from_chain_id(id: ChainId) -> Result<ethereum::Address> {
224+
Ok(match id {
225+
ChainId::MAINNET => *DAI_MAINNET,
226+
ChainId::KOVAN => *DAI_KOVAN,
227+
id => anyhow::bail!(
228+
"unable to infer DAI token contract from chain-ID {}",
229+
u32::from(id)
230+
),
231+
})
232+
}
233+
173234
#[derive(Clone, Debug, PartialEq, Serialize)]
174235
pub struct Lightning {
175236
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

0 commit comments

Comments
 (0)