Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(walletconnect): walletconnect integration #2223

Open
wants to merge 159 commits into
base: dev
Choose a base branch
from
Open

Conversation

borngraced
Copy link
Member

@borngraced borngraced commented Sep 16, 2024

#1543
This PR introduces the integration of WalletConnect into the Komodo DeFi Framework (KDF), enabling secure wallet connections for Cosmos and EVM-based chains. KDF acts as the DApp(in this PR), allowing users to initiate and manage transactions securely with external wallets e.g via Metamask.
Key changes include:

  • Implement multi-session handling for concurrent WalletConnect connections across accounts, integrates SQLite/IndexedDB for persistent session storage across devices, enhances relayer disconnection handling for smoother session management
  • Implemented WalletConnect coin activation for Tendermint and EVM.
  • Implemented Withdraw and Swap functionalities for Tendermint and EVM

https://specs.walletconnect.com/2.0/specs/clients/sign/
https://specs.walletconnect.com/2.0/specs/clients/core/pairing/
https://specs.walletconnect.com/2.0/specs/clients/core/crypto/
https://specs.walletconnect.com/2.0/specs/servers/relay/
https://docs.reown.com/advanced/multichain/rpc-reference/ethereum-rpc

Additional improvements include cleanup of unused dependencies, minor code refinements, and WASM compatibility

Updated deps:
Added deps:
Removed deps:

How to test using EVM coin e.g ETH (Native && WASM)

  1. Start kdf (cargo run)
  2. Create WalletConnect session connection
    • Use the RPC below
    • Copy the connection URL and either:
      • Paste directly in your wallet or
      • Generate QR code using QR Code Generator and scan with your wallet (e.g., MetaMask)
{
     "method": "wc_new_connection",
	"userpass": "{{ _.userpass }}",
	"mmrpc": "2.0",
	"params": {
		"required_namespaces": {
			"eip155": {
				"chains": ["eip155:1"],
				"methods": [
					"eth_sendTransaction",
					"eth_signTransaction",
					"personal_sign"
				],
				"events": [
					"accountsChanged",
					"chainChanged"
				]
			}
		}
	}
}
  1. Activate ETH coin (set "priv_key_policy": "WalletConnect", in activation params).
  2. Approve authentication request in your Wallet
  3. Withdraw or trade.

Note: To add more eip155 chains, modify the chains array like this: ["eip155:1", "eip155:250"]

@borngraced borngraced self-assigned this Sep 16, 2024
mm2src/kdf_walletconnect/src/session/rpc/event.rs Outdated Show resolved Hide resolved
mm2src/crypto/Cargo.toml Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/Cargo.toml Outdated Show resolved Hide resolved
Some(value) => Some(serde_json::from_value(value)?),
None => None,
};
let (topic, url) = self.pairing.create(self.metadata.clone(), None)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think then we should clean up these old pairings.

/// Information about the controlling party (typically a wallet).
pub controller: Controller,
/// Information about the proposing party (typically a dApp).
pub proposer: Proposer,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the other one is always Default::default() so an enum makes sense here then we can store only one of them.

mm2src/kdf_walletconnect/src/session/rpc/update.rs Outdated Show resolved Hide resolved
session.relay = settle.relay;
session.expiry = settle.expiry;

if let Some(value) = settle.session_properties {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean we want to be able to discard this field if we were not able to deserialize it?
i think then the serde_json::from_str::<SessionProperties>(&value.to_string())? should be optional, i.e. we shouldn't fail (?) the whole function if we fail deserializing the arbitrary value.

p.s. we can also use serde_json::from_value instead of from_str and ditch .to_string()

mm2src/kdf_walletconnect/src/storage/sqlite.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/connection_handler.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/lib.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/connection_handler.rs Outdated Show resolved Hide resolved
let topic = session.topic.clone();
let pairing_topic = session.pairing_topic.clone();
debug!("[{topic}] Session found! activating");
self.session_manager.add_session(session);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we add their pairings?

Copy link
Member Author

@borngraced borngraced Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no use for storing pairings as they're only needed to activate a session and then we'll move to using session for all communication. albeit I still consider extending pairing storage just like session storage but there's not any use for it right now.

Comment on lines 30 to 32
{
ctx.pairing.activate(topic)?;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the extra scope not needed.
also this always returns error (check KomodoPlatform/WalletConnectRust#3 (comment))

mm2src/kdf_walletconnect/src/pairing.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/session/rpc/propose.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/lib.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/lib.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/lib.rs Outdated Show resolved Hide resolved
let irn_metadata = param.irn_metadata();
let ttl = irn_metadata.ttl;
Copy link
Collaborator

@mariocynicys mariocynicys Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use a timed/expirable map for this case. we do that for electrum and eth also i think. this is to avoid network-induced memory leaks.

mm2src/kdf_walletconnect/src/inbound_message.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/session/rpc/delete.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@mariocynicys mariocynicys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i mainly went over some of old comments and resolved the ones which i can see have been updated (could use a helping hand in the remaining ones by pointing where the updates are).

my estimation right now is i would need one more all-over iteration for approval (+ old comments), but i don't wanna promise since break my promises :D

Thanks for that huge work!

mm2src/kdf_walletconnect/src/lib.rs Outdated Show resolved Hide resolved
mm2src/kdf_walletconnect/src/lib.rs Outdated Show resolved Hide resolved
session.relay = settle.relay;
session.expiry = settle.expiry;

if let Some(value) = settle.session_properties {
Copy link
Collaborator

@mariocynicys mariocynicys Dec 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so,

  • if session_properties isn't sent, it's fine since it's optional,
  • if session_properties is sent along in the request, we MUST succeed in parsing it (that means we must have an enumration of all the possible shapes it could be)
    • if we fail to de-serialize session_properties we will fail the request (the MUST part).

why do we need it as Option<Value> (instead of Option<SessionProperties>) if we will fail the request if we fail to deserialize it?


self.validate_chain_id(&session, chain_id)?;

// TODO: uncomment when WalletConnect wallets start listening to chainChanged event
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So nothing happens(I mean wallets don't listen to this event) if we send such request

so what about just sending it for now and when wallets listen to it later we get this feat for free

@borngraced
Copy link
Member Author

so what about just sending it for now and when wallets listen to it later we get this feat for free

Two reasons I didn't want to, this will add extra latency and corresponding wallet will return error as they don't recognize such session request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants