Created
April 18, 2022 13:56
-
-
Save judeebene/5a158bad9e712b11f511c593f7df73ae to your computer and use it in GitHub Desktop.
This is a sample cardano wallet connect for Nami
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState } from 'react'; | |
import { useModel } from 'umi'; | |
import { Avatar, Button, Col, Modal, Row, Statistic } from 'antd'; | |
import Loader from '../../utils/loader'; | |
import type Paginate from '@/interfaces/Paginate'; | |
import type { WalletError } from '@/components/WalletConnect/WalletErrors'; | |
import { useCallback } from 'react'; | |
import { fromHex } from '@/utils/utils'; | |
import { fiatToDiemHumanFriendly } from '@/utils/amount-precision'; | |
import { UserOutlined } from '@ant-design/icons'; | |
// backwards compatibility until all Nami users have updated | |
const isBrowser = () => typeof window !== 'undefined'; | |
if (isBrowser() && window.cardano && window.cardano.enable && !window.cardano.nami) { | |
window.cardano.nami = { | |
enable: async () => { | |
if (await window.cardano.enable()) { | |
return { | |
getBalance: () => window.cardano.getBalance(), | |
signData: (address: string, payload: string) => window.cardano.signData(address, payload), | |
signTx: (tx: string, partialSign: boolean) => window.cardano.signTx(tx, partialSign), | |
submitTx: (tx: string) => window.cardano.submitTx(tx), | |
getUtxos: (amount: any, paginate: Paginate) => window.cardano.getUtxos(amount, paginate), | |
getUsedAddresses: () => window.cardano.getUsedAddresses(), | |
getUnusedAddresses: async () => [], | |
getChangeAddress: () => window.cardano.getChangeAddress(), | |
getRewardAddresses: () => window.cardano.getRewardAddresses(), | |
getNetworkId: () => window.cardano.getNetworkId(), | |
experimental: { | |
on: (eventName: string, callback: any) => { | |
if (eventName == 'accountChange') window.cardano.onAccountChange(callback); | |
else if (eventName == 'networkChange') window.cardano.onNetworkChange(callback); | |
}, | |
off: (eventName: string, callback: any) => window.cardano.off(eventName, callback), | |
getCollateral: () => window.cardano.getCollateral(), | |
}, | |
}; | |
} else { | |
return false; | |
} | |
}, | |
isEnabled: () => window.cardano.isEnabled(), | |
apiVersion: '0.1.0', | |
name: 'Nami', | |
icon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 486.17 499.86'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%23349ea3;%7D%3C/style%3E%3C/defs%3E%3Cg id='Layer_2' data-name='Layer 2'%3E%3Cg id='Layer_1-2' data-name='Layer 1'%3E%3Cpath id='path16' class='cls-1' d='M73.87,52.15,62.11,40.07A23.93,23.93,0,0,1,41.9,61.87L54,73.09,486.17,476ZM102.4,168.93V409.47a23.76,23.76,0,0,1,32.13-2.14V245.94L395,499.86h44.87Zm303.36-55.58a23.84,23.84,0,0,1-16.64-6.68v162.8L133.46,15.57H84L421.28,345.79V107.6A23.72,23.72,0,0,1,405.76,113.35Z'/%3E%3Cpath id='path18' class='cls-1' d='M38.27,0A38.25,38.25,0,1,0,76.49,38.27v0A38.28,38.28,0,0,0,38.27,0ZM41.9,61.8a22,22,0,0,1-3.63.28A23.94,23.94,0,1,1,62.18,38.13V40A23.94,23.94,0,0,1,41.9,61.8Z'/%3E%3Cpath id='path20' class='cls-1' d='M405.76,51.2a38.24,38.24,0,0,0,0,76.46,37.57,37.57,0,0,0,15.52-3.3A38.22,38.22,0,0,0,405.76,51.2Zm15.52,56.4a23.91,23.91,0,1,1,8.39-18.18A23.91,23.91,0,0,1,421.28,107.6Z'/%3E%3Cpath id='path22' class='cls-1' d='M134.58,390.81A38.25,38.25,0,1,0,157.92,426a38.24,38.24,0,0,0-23.34-35.22Zm-15,59.13A23.91,23.91,0,1,1,143.54,426a23.9,23.9,0,0,1-23.94,23.91Z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E", | |
_events: {}, | |
}; | |
} | |
const addressToBech32 = async () => { | |
await Loader.load(); | |
const address = (await window.cardano.selectedWallet.getUsedAddresses())[0]; | |
return Loader.Cardano.Address.from_bytes(Buffer.from(address, 'hex')).to_bech32(); | |
}; | |
// const getUtxos = async() => { | |
// return (await window.cardano.selectedWallet.getUtxos()).map((utxo: any) => | |
// Loader.Cardano.TransactionUnspentOutput.from_bytes(fromHex(utxo)), | |
// ); | |
//} | |
const getBalance = async () => { | |
await Loader.load(); | |
const balance = await window.cardano.selectedWallet.getBalance(); | |
const value = Loader.Cardano.Value.from_bytes(fromHex(balance)); | |
const lovelace = parseInt(value.coin().to_str()); | |
return lovelace; | |
// use valueToAsset to add all asset. | |
}; | |
const NoNami = () => { | |
if (window.cardano) return true; | |
Modal.info({ | |
title: 'No compatible wallet detected!', | |
content: ( | |
<div> | |
<p>We currently support Nami Wallet Only!</p> | |
<p> Download and Installed Nami Broswer wallet to get Started</p> | |
</div> | |
), | |
okText: 'Download', | |
onOk: () => window.open('https://namiwallet.io'), | |
}); | |
return false; | |
}; | |
const WrongNetworkToast = async (api: any): Promise<boolean> => { | |
if (0 === (await api.getNetworkId())) return Promise.resolve(true); | |
Modal.info({ | |
title: 'Wrong Cardano Network Detected!!', | |
content: ( | |
<div> | |
<p>Change your Cardano Network!</p> | |
</div> | |
), | |
}); | |
return false; | |
}; | |
const checkStatus = async (api: any) => { | |
return await WrongNetworkToast(api); | |
}; | |
const WalletConnect = () => { | |
const [hasCardano, setHasCardano] = useState(null); | |
const [flag, setFlag] = React.useState(false); | |
const { wallet, connectWallet } = useModel('wallet'); | |
// const [shortAddress, setShortAddress] = useState(''); | |
const [balance, setBalance] = useState(0); | |
React.useEffect(() => { | |
if (wallet.connected && !flag) { | |
return ( | |
window.cardano.selectedWallet.name === 'Nami' && | |
window.cardano.selectedWallet.experimental.on( | |
'accountChange', | |
async () => { | |
const address = await addressToBech32(); | |
connectWallet({ | |
...wallet, | |
connected: true, | |
address: address, | |
}); | |
setFlag(true); | |
}, | |
) | |
); | |
} | |
}, [wallet, connectWallet]); | |
React.useEffect(() => { | |
setHasCardano(window.cardano); | |
}, []); | |
const checkConnection = useCallback(async () => { | |
if (window.cardano) { | |
const session = JSON.parse(localStorage.getItem('session') || '{}'); | |
// check if session is empty | |
if (Object.keys(session).length === 0) return; | |
if (!(await window.cardano[session.walletName].isEnabled())) return; | |
const api = await window.cardano[session.walletName].enable().catch((e: WalletError) => { | |
console.log(e); | |
}); | |
if (api) { | |
if (!(await checkStatus(api))) { | |
return; | |
} | |
if (session.walletName === 'flint') { | |
window.cardano.selectedWallet = { | |
...window.cardano[session.walletName], | |
...api, | |
experimental: { | |
getCollateral: api.getCollateral, | |
}, | |
}; | |
} else { | |
window.cardano.selectedWallet = { | |
...window.cardano[session.walletName], | |
...api, | |
}; | |
} | |
} | |
if (Date.now() - parseInt(session.time) < 6000000) { | |
const connectAddress = await addressToBech32(); | |
const walletBalance = await getBalance(); | |
setBalance(walletBalance); | |
// if (connectAddress.length < 11) { | |
// setShortAddress(connectAddress); | |
// } else { | |
// setShortAddress(`${connectAddress.slice(0, 6)}...${connectAddress.slice(-4)}`); | |
// } | |
connectWallet({ | |
...wallet, | |
connected: api ? true : false, | |
address: connectAddress, | |
}); | |
} | |
} | |
}, []); | |
React.useEffect(() => { | |
checkConnection(); | |
}, [checkConnection]); | |
return wallet.connected ? ( | |
<Row gutter={6}> | |
{/* <Meta | |
avatar={<Avatar src={window.cardano.selectedWallet.icon} style={{ height: '18px' }} />} | |
/> */} | |
<Col span={2}> | |
{/* <Meta | |
avatar={<Avatar src={window.cardano.selectedWallet.icon} />} | |
/> */} | |
<Avatar shape="square" size="small" src={window.cardano.selectedWallet.icon} /> | |
</Col> | |
<Col span={4}> | |
<Avatar size="small" icon={<UserOutlined />} /> | |
</Col> | |
<Col span={18}> | |
<Statistic value={fiatToDiemHumanFriendly(balance)} precision={2} suffix="₳" /> | |
</Col> | |
</Row> | |
) : ( | |
<Button | |
onClick={async () => { | |
return hasCardano | |
? // 1. enable wallet to connect | |
window.cardano && | |
Object.keys(window.cardano) | |
.filter((walletName) => walletName == 'nami' || walletName == 'ccvault') | |
.map(async (walletName) => { | |
const api = await window.cardano[walletName].enable().catch((e: WalletError) => { | |
console.log(e); | |
}); | |
if (api) { | |
if (!(await checkStatus(api))) { | |
return; | |
} | |
window.cardano.selectedWallet = { | |
...window.cardano[walletName], | |
...api, | |
}; | |
localStorage.setItem( | |
'session', | |
JSON.stringify({ | |
time: Date.now().toString(), | |
walletName, | |
}), | |
); | |
// set global wallet state | |
const address = await addressToBech32(); | |
// setShortAddress(`${address.slice(0, 6)}...${address.slice(-4)}`); | |
connectWallet({ | |
...wallet, | |
connected: true, | |
address: address, | |
}); | |
} | |
}) | |
: // end of wallet enabled | |
NoNami(); | |
}} | |
type="primary" | |
size="large" | |
> | |
Connect your wallet | |
</Button> | |
); | |
}; | |
export default WalletConnect; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Browser Wallet inject content inside the web context. The exposed API follows CIP-0030. To implement a wallet , reading CIP-0030 is the starting point.
Each of the wallet providers implement the CIP-0030 functions and exposed it for developers.
The returned types are in cbor/bytes format. see http://cbor.io/ - which is a format Cardano is using to serialising the data.
The serializing and de-serializing these low-level data structures is using serialization-lib. To verify a signature returned from cardano.dataSign(address, payload) the message-signing library helps.
Basic Usage
Detect the Cardano provider (window.cardano) and detect Nami (window.cardano.nami)
Request the api from window.cardano.nami.enable()
Detect which Cardano network the user is connected to (ID 1 = Mainnet, ID 0 = Testnet)
Get the user's Cardano account