Skip to content

Commit

Permalink
Merge pull request #106 from KeystoneHQ/feat/avax
Browse files Browse the repository at this point in the history
merge Feat/avax
  • Loading branch information
soralit authored Jan 10, 2025
2 parents cb8bea1 + 6affcbb commit 1177ced
Show file tree
Hide file tree
Showing 9 changed files with 669 additions and 420 deletions.
715 changes: 296 additions & 419 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion libs/ur-registry-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ serde_json = "1.0"
uuid = "1.3.0"
serde = { version = "1.0", features = ["derive"] }
secp256k1 = "0.24.0"
protobuf = "3.2.0"
protobuf = "=3.2.0"
ethabi = "18.0.0"
bs58 = "0.5.1"
bip32 = "0.5.0"
Expand Down
191 changes: 191 additions & 0 deletions libs/ur-registry/src/avalanche/avax_sign_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use crate::cbor::{cbor_array, cbor_map};
use crate::error::{URError, URResult};
use crate::registry_types::{RegistryType, AVAX_SIGN_REQUEST, UUID};
use crate::traits::{From as FromCbor, RegistryItem, To};
use crate::types::{Bytes, Fingerprint};
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use minicbor::data::{Int, Tag};
use minicbor::encode::Write;
use minicbor::{Decoder, Encoder};

const REQUEST_ID: u8 = 1;
const SIGN_DATA: u8 = 2;
const MASTER_FINGERPRINT: u8 = 3;
const XPUB: u8 = 6;
const WALLET_INDEX: u8 = 7;

#[derive(Debug, Clone, Default)]
pub struct AvaxSignRequest {
request_id: Bytes,
sign_data: Bytes,
master_fingerprint: Fingerprint,
xpub: String,
wallet_index: u64,
}

impl AvaxSignRequest {
pub fn new(
request_id: Bytes,
sign_data: Bytes,
master_fingerprint: Fingerprint,
xpub: String,
wallet_index: u64,
) -> Self {
AvaxSignRequest {
request_id,
sign_data,
master_fingerprint,
xpub,
wallet_index,
}
}

pub fn get_request_id(&self) -> Bytes {
self.request_id.clone()
}

pub fn set_request_id(&mut self, id: Bytes) {
self.request_id = id;
}

pub fn get_tx_data(&self) -> Bytes {
self.sign_data.clone()
}

pub fn set_tx_data(&mut self, data: Bytes) {
self.sign_data = data;
}

pub fn get_master_fingerprint(&self) -> Fingerprint {
self.master_fingerprint
}

pub fn get_xpub(&self) -> String {
self.xpub.clone()
}

pub fn set_xpub(&mut self, xpub: String) {
self.xpub = xpub;
}

pub fn get_wallet_index(&self) -> u64 {
self.wallet_index
}

pub fn set_wallet_index(&mut self, index: u64) {
self.wallet_index = index;
}
}

impl RegistryItem for AvaxSignRequest {
fn get_registry_type() -> RegistryType<'static> {
AVAX_SIGN_REQUEST
}
}

impl<C> minicbor::Encode<C> for AvaxSignRequest {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
_ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.map(5)?;
e.int(Int::from(REQUEST_ID))?
.tag(Tag::Unassigned(UUID.get_tag()))?
.bytes(&self.request_id)?;
e.int(Int::from(SIGN_DATA))?.bytes(&self.sign_data)?;
e.int(Int::from(MASTER_FINGERPRINT))?.int(
Int::try_from(u32::from_be_bytes(self.master_fingerprint))
.map_err(|e| minicbor::encode::Error::message(e.to_string()))?,
)?;
e.int(Int::from(XPUB))?.str(&self.xpub)?;
e.int(Int::from(WALLET_INDEX))?.u64(self.wallet_index)?;
Ok(())
}
}

impl<'b, C> minicbor::Decode<'b, C> for AvaxSignRequest {
fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
let mut result = AvaxSignRequest::default();

cbor_map(d, &mut result, |key, obj, d| {
let key =
u8::try_from(key).map_err(|e| minicbor::decode::Error::message(e.to_string()))?;
match key {
REQUEST_ID => {
d.tag()?;
obj.request_id = d.bytes()?.to_vec();
}
SIGN_DATA => {
obj.sign_data = d.bytes()?.to_vec();
}
MASTER_FINGERPRINT => {
let mfp = u32::try_from(d.int()?)
.map_err(|e| minicbor::decode::Error::message(e.to_string()));
obj.master_fingerprint = u32::to_be_bytes(mfp?);
}
XPUB => {
obj.xpub = d.str()?.to_string();
}
WALLET_INDEX => {
obj.wallet_index = d.u64()?;
}
_ => {}
}
Ok(())
})?;
Ok(result)
}
}

impl To for AvaxSignRequest {
fn to_bytes(&self) -> URResult<Vec<u8>> {
minicbor::to_vec(self.clone()).map_err(|e| URError::CborEncodeError(e.to_string()))
}
}

impl FromCbor<AvaxSignRequest> for AvaxSignRequest {
fn from_cbor(bytes: Vec<u8>) -> URResult<AvaxSignRequest> {
minicbor::decode(&bytes).map_err(|e| URError::CborDecodeError(e.to_string()))
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::traits::RegistryItem;
use alloc::vec::Vec;
use hex::FromHex;
extern crate std;
use std::println;

#[test]
fn test_avax_encode() {
let unsigned_data = AvaxSignRequest {
request_id: [12, 34, 56, 78].to_vec(),
sign_data: Vec::from_hex("000000000022000000050000000000000000000000000000000000000000000000000000000000000000000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000000f42400000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000004d58ade90000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae00000001410b47f7c7aa13f88122be58735c5e985edc65d86fb0baf0b016359c22253d75000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000004d680464000000010000000000000000")
.unwrap(),
master_fingerprint: [0, 0, 0, 0],
xpub: "xpub6DXryz8Kd7XchtXvDnkjara83shGJH8ubu7KZhHhPfp4L1shvDEYiFZm32EKHnyo4bva4gxXjabFGqY7fNs8Ggd4khYz2oNs2KYLf56a9GX".to_string(),
wallet_index: 0,
};
let result: Vec<u8> = unsigned_data.try_into().unwrap();
println!("result = {:?}", hex::encode(&result));
let ur = ur::encode(&result, AvaxSignRequest::get_registry_type().get_type());
assert_eq!(ur, "ur:avax-sign-request/onadtpdafybncpetglaohkaddmaeaeaeaeaecpaeaeaeahaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaofsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeataeaeaeaeaebsfwfzaeaeaeaeaeaeaeaeaeaeaeadaeaeaeadeyeojlltbzutehftfwhsgosfsfbzrddisraxfsplfsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeataeaeaeaegthdpmwlaeaeaeaeaeaeaeaeaeaeaeadaeaeaeadeyeojlltbzutehftfwhsgosfsfbzrddisraxfsplaeaeaeadfpbdflylstpkbwyalycprnhdjkhhhymkhyuoihtpjlpfrdwtpfcmecnscpdafskpaeaeaeadfsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeahaeaeaeaegtisaaieaeaeaeadaeaeaeaeaeaeaeaeaxaeamksjlksjokpidenfyhdjpkkknetgrieemhdiaisjyhdkofyjtjeimhsjphseteojkisflgefdetkpidkpemgrhtisfdisgdiyjoeegsehjkiskofyfehkinfghtjneoeyfegrfdjtkkjleeidkohseeiokshdimhsidfgfljshkemiygljketflioieeejeishkkneyjlgljkeygrhkgsiyecenhsesflhdataedapawecf");
}

#[test]
fn test_avax_decode() {
let bytes =
Vec::from_hex("a501d825440c22384e0258de00000000000000000001ed5f38341e436e5d46e2bb00b45d62ae97d1b050c64bc634ae10626739e35c4b0000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000007000000000089544000000000000000000000000100000001512e7191685398f00663e12197a3d8f6012d9ea300000001db720ad6707915cc4751fb7e5491a3af74e127a1d81817abe9438590c0833fe10000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000050000000000989680000000010000000000000000031a0102030406786f7870756236445872797a384b6437586368745876446e6b6a61726138337368474a4838756275374b5a684868506670344c3173687644455969465a6d3332454b486e796f34627661346778586a61624647715937664e7338476764346b68597a326f4e73324b594c663536613947580706")
.unwrap();
let data = AvaxSignRequest::try_from(bytes).unwrap();
assert_eq!(
data.get_tx_data(),
Vec::from_hex("00000000000000000001ed5f38341e436e5d46e2bb00b45d62ae97d1b050c64bc634ae10626739e35c4b0000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000007000000000089544000000000000000000000000100000001512e7191685398f00663e12197a3d8f6012d9ea300000001db720ad6707915cc4751fb7e5491a3af74e127a1d81817abe9438590c0833fe10000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000050000000000989680000000010000000000000000")
.unwrap()
);
}
}
133 changes: 133 additions & 0 deletions libs/ur-registry/src/avalanche/avax_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use crate::cbor::cbor_map;
use crate::error::{URError, URResult};
use crate::registry_types::{RegistryType, AVAX_SIGNATURE, UUID};
use crate::traits::{From as FromCbor, RegistryItem, To};
use crate::types::Bytes;
use alloc::string::ToString;
use alloc::vec::Vec;
use minicbor::data::{Int, Tag};
use minicbor::encode::Write;
use minicbor::{Decoder, Encoder};

const REQUEST_ID: u8 = 1;
const SIGNATURE: u8 = 2;

#[derive(Clone, Debug, Default)]
pub struct AvaxSignature {
request_id: Bytes,
signature: Bytes,
}

impl AvaxSignature {
pub fn default() -> Self {
Default::default()
}

pub fn set_request_id(&mut self, id: Bytes) {
self.request_id = id;
}

pub fn set_signature(&mut self, signature: Bytes) {
self.signature = signature;
}

pub fn new(request_id: Bytes, signature: Bytes) -> Self {
AvaxSignature {
request_id,
signature,
}
}

pub fn get_request_id(&self) -> Bytes {
self.request_id.clone()
}
pub fn get_signature(&self) -> Bytes {
self.signature.clone()
}
}

impl RegistryItem for AvaxSignature {
fn get_registry_type() -> RegistryType<'static> {
AVAX_SIGNATURE
}
}

impl<C> minicbor::Encode<C> for AvaxSignature {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
_ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
let mut size = 2;
e.map(size)?;
e.int(Int::from(REQUEST_ID))?.bytes(&self.request_id)?;
e.int(Int::from(SIGNATURE))?.bytes(&self.signature)?;
Ok(())
}
}

impl<'b, C> minicbor::Decode<'b, C> for AvaxSignature {
fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
let mut result = AvaxSignature::default();
cbor_map(d, &mut result, |key, obj, d| {
let key =
u8::try_from(key).map_err(|e| minicbor::decode::Error::message(e.to_string()))?;
match key {
REQUEST_ID => {
obj.request_id = d.bytes()?.to_vec();
}
SIGNATURE => {
obj.signature = d.bytes()?.to_vec();
}
_ => {}
}
Ok(())
})?;
Ok(result)
}
}

impl To for AvaxSignature {
fn to_bytes(&self) -> URResult<Vec<u8>> {
minicbor::to_vec(self.clone()).map_err(|e| URError::CborEncodeError(e.to_string()))
}
}

impl FromCbor<AvaxSignature> for AvaxSignature {
fn from_cbor(bytes: Vec<u8>) -> URResult<AvaxSignature> {
minicbor::decode(&bytes).map_err(|e| URError::CborDecodeError(e.to_string()))
}
}

#[cfg(test)]
mod tests {
use crate::{avalanche::avax_signature::AvaxSignature, traits::{From as FromCbor, To}};
use alloc::vec::Vec;
use hex::FromHex;

#[test]
fn test_avax_signature_encode() {
let request_id =
[01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15].to_vec();
let signature = hex::decode("80337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575").unwrap();
let avax_signature = AvaxSignature::new(request_id, signature);
assert_eq!(
"a2014f0102030405060708090a0b0c0d0e0f0259020080337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575",
hex::encode(avax_signature.to_bytes().unwrap()).to_lowercase()
);
}

#[test]
fn test_avax_signature_decode() {
let bytes = Vec::from_hex(
"a2014f0102030405060708090a0b0c0d0e0f0259020080337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575",
)
.unwrap();
let avax_signature = AvaxSignature::from_cbor(bytes).unwrap();
assert_eq!(
[01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15].to_vec(),
avax_signature.get_request_id()
);
assert_eq!(hex::decode("80337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575").unwrap(), avax_signature.get_signature());
}
}
2 changes: 2 additions & 0 deletions libs/ur-registry/src/avalanche/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod avax_sign_request;
pub mod avax_signature;
36 changes: 36 additions & 0 deletions libs/ur-registry/src/ethereum/eth_sign_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,10 @@ impl FromCbor<EthSignRequest> for EthSignRequest {

#[cfg(test)]
mod tests {
use super::*;
use crate::crypto_key_path::{CryptoKeyPath, PathComponent};
use crate::ethereum::eth_sign_request::{DataType, EthSignRequest};
use crate::traits::RegistryItem;
use crate::traits::{From as FromCbor, To};
use alloc::string::ToString;
use alloc::vec;
Expand Down Expand Up @@ -311,4 +313,38 @@ mod tests {
);
assert_eq!(DataType::Transaction, eth_sign_request.get_data_type());
}

#[test]
fn test_avax_c_chain_encode() {
let path1 = PathComponent::new(Some(44), true).unwrap();
let path2 = PathComponent::new(Some(60), true).unwrap();
let path3 = PathComponent::new(Some(0), true).unwrap();
let path4 = PathComponent::new(Some(0), false).unwrap();
let path5 = PathComponent::new(Some(6), false).unwrap();

let source_fingerprint: [u8; 4] = [0xbd, 0xee, 0xe7, 0x82];
let components = vec![path1, path2, path3, path4, path5];
let crypto_key_path = CryptoKeyPath::new(components, Some(source_fingerprint), None);

let request_id = Some(
[
155, 29, 235, 77, 59, 125, 75, 173, 155, 221, 43, 13, 123, 61, 203, 109,
]
.to_vec(),
);

let sign_data = hex::decode("02f87482a86901841dcd6500849502f9008252089446a836a6d5800dd3ab9a6b914c904ef8017b48c8880dcac353ec227a0080c001a03cebc64b4bd58567b7205897f1f68922c3f142366b3236fba169bea5ab875284a05291dae91b105ac2c0dc5479ecf1ed7890d93c2ab1e12695f1e8ecbc92a42e5a").unwrap();
let eth_sign_request = EthSignRequest::new(
request_id,
sign_data,
DataType::TypedTransaction,
Some(43113),
crypto_key_path,
None,
Some("core wallet".to_string()),
);
let data = hex::decode(hex::encode(eth_sign_request.to_bytes().unwrap())).unwrap();
let ur = ur::encode(&data, EthSignRequest::get_registry_type().get_type());
assert_eq!(ur, "ur:eth-sign-request/oladtpdagdndcawmgtfrkigrpmndutdnbtkgfssbjnaohdktaoyajylfpdinadlrcasnihaelrmdaoytaelfgmaymwfgpdenoltllabttepynyjemegsmhglyaadkgfdsplobtsgsrguwpcpknaelartadnbfnwmswgrgrtllpiorlcxhdmswnynldcpsrwnfwenjeeyenzooyinrnonpyltgmlrnbgmmetnwlcwbehtsartuoghkkwpwnweksmhtafndrpavydsmdwnvswprfmooxdmhtaxaaaacfpdinahtaaddyoeadlecsdwykcsfnykaeykaewkamwkaocyrywyvdlfatjeiajljpihcxkthsjzjzihjyfwkouyfp");
}
}
1 change: 1 addition & 0 deletions libs/ur-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ extern crate core;

pub mod aptos;
pub mod arweave;
pub mod avalanche;
pub mod bitcoin;
pub mod bytes;
pub mod cardano;
Expand Down
Loading

0 comments on commit 1177ced

Please sign in to comment.