Skip to content

Instantly share code, notes, and snippets.

@judeebene
Created April 18, 2022 13:56
Show Gist options
  • Save judeebene/5a158bad9e712b11f511c593f7df73ae to your computer and use it in GitHub Desktop.
Save judeebene/5a158bad9e712b11f511c593f7df73ae to your computer and use it in GitHub Desktop.
This is a sample cardano wallet connect for Nami
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;
@judeebene
Copy link
Author

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

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