
브라우저에 EVM 지갑 확장이 설치되어 있으면, 해당 지갑은 보통 window.ethereum 객체(Provider)를 브라우저 전역에 주입한다.
window.ethereum의 목적은 DApp이 지갑에 계정 접근 요청, 서명 요청, 트랜잭션 전송 요청 등을 보내기 위한 인터페이스를 제공한다.
그렇다면 MetaMask, Coinbase Wallet, Rabby Wallet처럼 여러 지갑이 동시에 설치되어 있다면 어떻게 될까?
EIP-1193 방식 (기존 injected provider)
window.ethereum에 직접 접근해 지갑 연결을 시도해 보자
MetaMask와 Coinbase Wallet만 설치된 상태에서는 Coinbase Wallet이 먼저 열리면서, 그 안에서 MetaMask를 사용할지 Coinbase를 사용할지 선택하는 UI가 나타난다.
여기에 Rabby Wallet을 추가로 설치하면, 다른 지갑들은 무시된 것처럼 보이고 Rabby Wallet만 계속 열린다.
추가로 콘솔에는 window.ethereum 관련 에러가 발생하기도 한다.
왜 Rabby Wallet만 뜰까?
여러 지갑 확장은 서로를 생각하지 않고, 각자 자신이 제공하는 provider를 window.ethereum에 주입하려고 한다.
이 과정에서 마지막에 주입한 지갑의 provider가 window.ethereum을 덮어쓰는 형태가 되면, 결과적으로 그 지갑(Rabby)만 동작하는 것처럼 보이게 된다.
즉, EIP-1193 방식에서는 멀티 지갑 환경에서 window.ethereum이 단일 진입점이기 때문에, 어떤 지갑이 최종적으로 주입했는지에 따라 동작이 달라진다.
그러면 원하는 지갑을 선택해서 쓰려면?
EIP-6963 방식 (멀티 injected provider discovery)
이런 쟁탈 문제를 완화하기 위해 EIP-6963 방식이 등장했다.
EIP-6963은 window.ethereum 하나만 신뢰하는 방식이 아니라, 브라우저에 설치된 지갑들에게
"provider가 있으면 알려줘"라고 요청하고, 지갑들이 각자 응답하도록 한다.
🦊 : 여기 MetaMask 있어요!
🐰 : RabbyWallet도 있어요!
같이 응답을 보내준다.
export async function discoverWalletProviders(
timeoutMs = 300,
): Promise<DiscoveredWalletProvider[]> {
const found: DiscoveredWalletProvider[] = [];
const handler = (event: Event) => {
const e = event as EIP6963AnnounceEvent;
found.push(e.detail);
};
window.addEventListener('eip6963:announceProvider', handler);
window.dispatchEvent(new Event('eip6963:requestProvider'));
await new Promise((r) => setTimeout(r, timeoutMs));
window.removeEventListener('eip6963:announceProvider', handler);
return found;
}
이렇게 수집한 provider 목록에는 지갑의 rdns, name 같은 메타데이터가 포함된다.
따라서 이를 기반으로 사용자가 원하는 지갑을 선택해 연결할 수 있고, Dialog로 설치된 지갑 리스트를 표시한 뒤 선택 연결하는 UI도 구현 가능하다.
느낀 점
wagmi를 사용하면 React Hook 기반으로 멀티 지갑 환경을 더 편하게 다룰 수 있다.
wagmi를 사용하자!!!
참고 - https://eips.ethereum.org/EIPS/eip-6963
EIP-6963: Multi Injected Provider Discovery
Using window events to announce injected Wallet Providers
eips.ethereum.org