Skip to content

Commit

Permalink
enhancement: create lib own rpc class and some refactory (#6)
Browse files Browse the repository at this point in the history
* create dapp rpc class

* fix build

* fix export

* fix balance

* remove unwanted comment

* more refactory

* fix build

* update dapp pkgs and improve
  • Loading branch information
baymac authored May 8, 2024
1 parent e529bea commit 2085de3
Show file tree
Hide file tree
Showing 13 changed files with 349 additions and 235 deletions.
8 changes: 4 additions & 4 deletions apps/dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
},
"dependencies": {
"@minswap/wc-dapp": "workspace:*",
"@vercel/examples-ui": "^1.0.5",
"next": "13.4.4",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"@vercel/examples-ui": "^2.0.3",
"next": "^14.2.3",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@types/node": "^20.2.5",
Expand Down
54 changes: 48 additions & 6 deletions apps/dapp/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {
CardanoWcProvider,
CardanoProvider,
CHAIN,
EnabledAPI,
EnabledWalletEmulator,
REGIONALIZED_RELAYER_ENDPOINTS
} from '@minswap/wc-dapp';
import { Button, Input, Layout, Page } from '@vercel/examples-ui';
Expand All @@ -25,27 +24,29 @@ const sleep = (ms: number) => {
};

export default function Index() {
const [wc, setWc] = useState<CardanoWcProvider | null>(null);
const [wc, setWc] = useState<CardanoProvider | null>(null);
const [chain, setChain] = useState<string | undefined>(undefined);
const [enabledApi, setEnabledApi] = useState<EnabledAPI | null>(null);
const [baseAddr, setBaseAddr] = useState<string | null | undefined>(null);
const [utxos, setUtxos] = useState<string[] | null | undefined>(null);
const [addressLoading, setAddressLoading] = useState(false);
const [utxosLoading, setUtxosLoading] = useState(false);
const [disconnectLoading, setDisconnectLoading] = useState(false);
const [sam, setSam] = useState(false);

const [legacyMode, setLegacyMode] = useState(true);

const updateSam = useCallback(() => {
if (!enabledApi) return;
(enabledApi as unknown as EnabledWalletEmulator).setSam = !sam;
enabledApi.setSam = !sam;
setSam(!sam);
}, [sam, setSam, enabledApi]);

const [tx, setTx] = useState<string | undefined>(undefined);

const initWc = async () => {
try {
const walletConnectConnector = await CardanoWcProvider.init({
const walletConnectConnector = await CardanoProvider.init({
chains: [CHAIN.MAINNET],
projectId: process.env['NEXT_PUBLIC_WC_PROJECT_ID'] ?? '97b4dbc5d1f1492a20c9e5d4d7047d63',
relayerRegion: REGIONALIZED_RELAYER_ENDPOINTS.DEFAULT,
Expand Down Expand Up @@ -86,7 +87,7 @@ export default function Index() {
console.info('[APP] fetching address');
await timeoutPromise(
enabledApi
.getUtxos()
.getUnusedAddresses()
.then(addr => {
console.info('[APP] addr', addr);
return addr;
Expand All @@ -111,6 +112,37 @@ export default function Index() {
setAddressLoading(false);
};

const getUtxos = async () => {
if (!enabledApi) return;
setUtxosLoading(true);
console.info('[APP] fetching utxos');
await timeoutPromise(
enabledApi
.getUtxos()
.then(utxos => {
console.info('[APP] utxos', utxos);
return utxos;
})
.catch((err: unknown) => {
// when request times out, client throws an error with empty message. we should ignore it as we are timing out the request ourselves.
console.info('[APP] get utxos error', err);
if ((err as Error).message) {
throw err;
}
})
)
.then(utxos => {
setUtxos(utxos as string[]);
})
.catch(err => {
if (err === TIMEOUT_ERR_MESSAGE) {
console.info(TIMEOUT_ERR_MESSAGE);
}
});
await sleep(1000); // not required just for demo
setUtxosLoading(false);
};

const signTx = async () => {
if (!enabledApi || !tx) return;
console.info('signing tx');
Expand Down Expand Up @@ -165,6 +197,7 @@ export default function Index() {
<div className={styles.baseAddr}>{baseAddr}</div>
</>
)}

<div className={styles.buttonContainer}>
{!wc && (
<>
Expand All @@ -191,6 +224,9 @@ export default function Index() {
<button className={styles.button} onClick={getAddress} disabled={addressLoading}>
Unused Addresses
</button>
<button className={styles.button} onClick={getUtxos} disabled={utxosLoading}>
Utxos
</button>
<button className={styles.button} onClick={disconnectWc} disabled={disconnectLoading}>
Disconnect
</button>
Expand Down Expand Up @@ -224,6 +260,12 @@ export default function Index() {
</Button>
</div>
)}
{utxos && (
<>
<div className={styles.addrLabel}>Utxos:</div>
<div className={styles.baseAddr}>{utxos}</div>
</>
)}
</div>
</Page>
);
Expand Down
9 changes: 4 additions & 5 deletions apps/dapp/utils/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* eslint-disable unused-imports/no-unused-vars */
import { SubmitTxRequest, TRpc, UtxoRequest, UtxoResponse } from '@minswap/wc-dapp';
import { TRpc, UtxoResponse } from '@minswap/wc-dapp';

export class WalletConnectRpc implements TRpc {
getUtxos(params: UtxoRequest): Promise<UtxoResponse> {
getUtxos(): Promise<UtxoResponse> {
return Promise.resolve([
'828258201664a8e468b30b6ea2c00c11f9a9dda64a6814d66ccbd9bfc73c940aaf6af8bf0082583901e9a5e9be2940740d33efc004e9a3835766c9ef4c422ed00cf7c4bfa1e45c4c8895ff4da15c38d4cf0d90bae5f0a7df717dc5e912878590c21a07459280',
'828258201b3d8c6990e5e218f12dc98297bb909bb8b026bc48e2bdcb8f32a3a8abef955e008258390101986a78d618f446f3da79835e795078a7250becfdfd2bd266675012e45c4c8895ff4da15c38d4cf0d90bae5f0a7df717dc5e912878590c21a2929d915',
Expand All @@ -26,12 +25,12 @@ export class WalletConnectRpc implements TRpc {
'82825820fd701ad912a1e180c0258c11d1431c1779ef798692240af3879fe40dbd0b97c40082583901f081f6eaae55a2b0077fc125f16f91451aa59473562d86c470ed5150e45c4c8895ff4da15c38d4cf0d90bae5f0a7df717dc5e912878590c21a59851764'
]);
}
getBalance(params: UtxoRequest): Promise<string> {
getBalance(): Promise<string> {
return Promise.resolve(
'821b000000011a11d035a7581c0ece814aa1cc2c98981c7690083dbcb51c5bb1279ae408873d8c8762a158205976683677342b4f7a62492f64613050624361637841514b316a626f4566363901581c29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6a1434d494e1b00000069751f3890581c54ef11805333453c8c3d6fbaa0d4496ddeb94857e1d7f3411bb79489a143414452192710581c5653fadee9993813f02f37a9af55ca78e78bc89e6a7d9b38df1ffa5aa14642554e4348411b00000574fbde6000581cefd7bd84c73fa01400d3c892365ce90b203981aab678bcffb6f3406aa14931373730333038393101581cf0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9aa14c6275796d65637279702e746f01581cf66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b69880a144695553441a002c0eab'
);
}
submitTx(params: SubmitTxRequest): Promise<string> {
submitTx(): Promise<string> {
return Promise.resolve('xyz');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,42 @@ import { SessionTypes, SignClientTypes } from '@walletconnect/types';
import UniversalProvider, { ConnectParams } from '@walletconnect/universal-provider';

import { ACCOUNT, DEFAULT_LOGGER, STORAGE, SUPPORTED_EXPLORER_WALLETS } from '../constants';
import { TRpc } from '../types';
import { EnabledAPI } from '../types/cip30';
import { CardanoProviderOpts, TRpc } from '../types';
import { EnabledAPI } from './enabledApi';
import {
CARDANO_NAMESPACE_NAME,
CHAIN,
CHAIN_EVENTS,
getOptionalCardanoNamespace,
getRequiredCardanoNamespace
} from './chain';
import { EnabledWalletEmulator } from './enabledWalletEmulator';
import { CardanoWcProviderOpts } from './types';
} from './utils';

export class CardanoWcProvider {
export class CardanoProvider {
private modal: WalletConnectModal | undefined;
private enabled = false;
private chains: CHAIN[] | undefined;
private rpc: TRpc;
private provider: UniversalProvider | undefined;
private enabledApi: EnabledAPI | undefined;
private qrcode: boolean;
private legacyMode: boolean | undefined;

private constructor({
provider,
qrcode,
modal,
chains,
rpc,
legacyMode
}: {
provider: UniversalProvider;
} & Omit<CardanoWcProviderOpts, 'projectId' | 'metadata' | 'relayerRegion'>) {
} & Omit<CardanoProviderOpts, 'projectId' | 'metadata' | 'relayerRegion' | 'qrCode'>) {
this.chains = chains;
this.provider = provider;
this.modal = modal;
this.qrcode = Boolean(qrcode);
this.rpc = rpc;
this.registerEventListeners();
this.legacyMode = legacyMode;
}

static async init(opts: CardanoWcProviderOpts) {
static async init(opts: CardanoProviderOpts) {
invariant(opts.projectId.length > 0, 'Wallet Connect project ID not set');
const provider = await UniversalProvider.init({
logger: DEFAULT_LOGGER,
Expand All @@ -54,22 +48,21 @@ export class CardanoWcProvider {
metadata: opts.metadata
});
let modal: WalletConnectModal | undefined;
if (opts.qrcode) {
const { qrcode, chains, legacyMode, rpc } = opts;
if (qrcode) {
modal = getWeb3Modal(opts.projectId, opts.chains);
}
return new CardanoWcProvider({
qrcode: opts.qrcode,
return new CardanoProvider({
provider,
modal,
chains: opts.chains,
rpc: opts.rpc,
legacyMode: opts.legacyMode
chains,
rpc,
legacyMode
});
}

async enable(sam?: boolean) {
const session = this.provider?.session;
// Edge Case: sometimes pairing is lost, so we disconnect session and reconnect
if (!session) {
await this.connect({ sam });
} else {
Expand Down Expand Up @@ -121,7 +114,7 @@ export class CardanoWcProvider {
}

private async isEnabled(): Promise<boolean> {
return Promise.resolve(this.enabled);
return Promise.resolve(Boolean(this.enabledApi));
}

private async loadPersistedSession(sam?: boolean) {
Expand All @@ -132,16 +125,16 @@ export class CardanoWcProvider {
const addresses = defaultAccount.split(':')[2].split('-');
const stakeAddress = addresses[0];
const baseAddress = addresses[1];
const overrideSam = this.legacyMode ? false : sam; // emulator api will never act in SAM if legacy mode is enabled
this.enabledApi = new EnabledWalletEmulator({
// enabled api will never act in SAM if legacy mode is enabled
const overrideSam = this.legacyMode ? false : sam;
this.enabledApi = new EnabledAPI({
provider: provider,
chain: `${CARDANO_NAMESPACE_NAME}:${defaultChainId}` as CHAIN,
rpc: this.rpc,
stakeAddress,
baseAddress,
sam: overrideSam
});
this.enabled = true;
}

private async connect(
Expand All @@ -153,17 +146,15 @@ export class CardanoWcProvider {
const cardanoOptionalNamespace = getOptionalCardanoNamespace(this.chains, this.legacyMode);
try {
const session = await new Promise<SessionTypes.Struct | undefined>((resolve, reject) => {
if (this.qrcode) {
this.modal?.subscribeModal(async state => {
if (!state.open && !provider.session) {
// the modal was closed so reject the promise
provider.abortPairingAttempt();
await provider.cleanupPendingPairings({ deletePairings: true });
this.reset();
reject(new Error('Connection aborted by user.'));
}
});
}
this.modal?.subscribeModal(async state => {
if (!state.open && !provider.session) {
// the modal was closed so reject the promise
provider.abortPairingAttempt();
await provider.cleanupPendingPairings({ deletePairings: true });
this.reset();
reject(new Error('Connection aborted by user.'));
}
});
provider
?.connect({
namespaces: { ...cardanoNamespace },
Expand All @@ -187,16 +178,13 @@ export class CardanoWcProvider {

private reset() {
this.provider = undefined;
this.enabled = false;
this.enabledApi = undefined;
}

private onDisplayUri = (uri: string) => {
console.info('pairing uri', uri);
if (this.qrcode) {
this.modal?.closeModal();
void this.modal?.openModal({ uri });
}
this.modal?.closeModal();
void this.modal?.openModal({ uri });
};

private onSessionDelete = () => {
Expand All @@ -219,26 +207,27 @@ export class CardanoWcProvider {
private onAccountChange = async (account: string) => {
const isEnabled = await this.isEnabled();
if (isEnabled) {
invariant(this.enabledApi, 'Enabled API not initialized');
const stakeAddress = account.split(':')[2].split('-')[0];
const baseAddress = account.split(':')[2].split('-')[1];
(this.enabledApi as EnabledWalletEmulator).baseAddress = baseAddress;
(this.enabledApi as EnabledWalletEmulator).stakeAddress = stakeAddress;
(this.enabledApi as EnabledWalletEmulator).events.emit(CHAIN_EVENTS.ACCOUNT_CHANGE, account);
this.enabledApi.baseAddress = baseAddress;
this.enabledApi.stakeAddress = stakeAddress;
this.enabledApi.events.emit(CHAIN_EVENTS.ACCOUNT_CHANGE, account);
await this.persist(ACCOUNT, account);
}
};

private onChainChange = async (account: string) => {
const isEnabled = await this.isEnabled();
if (isEnabled) {
invariant(this.enabledApi, 'Enabled API not initialized');
const chainId = account.split(':')[1];
const stakeAddress = account.split(':')[2].split('-')[0];
const baseAddress = account.split(':')[2].split('-')[1];
(this.enabledApi as EnabledWalletEmulator).chain =
`${CARDANO_NAMESPACE_NAME}:${chainId}` as CHAIN;
(this.enabledApi as EnabledWalletEmulator).baseAddress = baseAddress;
(this.enabledApi as EnabledWalletEmulator).stakeAddress = stakeAddress;
(this.enabledApi as EnabledWalletEmulator).events.emit(CHAIN_EVENTS.NETWORK_CHANGE, account);
this.enabledApi.chain = `${CARDANO_NAMESPACE_NAME}:${chainId}` as CHAIN;
this.enabledApi.baseAddress = baseAddress;
this.enabledApi.stakeAddress = stakeAddress;
this.enabledApi.events.emit(CHAIN_EVENTS.NETWORK_CHANGE, account);
await this.persist(ACCOUNT, account);
}
};
Expand Down
Loading

0 comments on commit 2085de3

Please sign in to comment.