Skip to content

Commit

Permalink
feat: Support tg
Browse files Browse the repository at this point in the history
  • Loading branch information
wenty22 committed Jan 7, 2025
1 parent ea8368e commit fcb224c
Show file tree
Hide file tree
Showing 11 changed files with 232 additions and 36 deletions.
10 changes: 8 additions & 2 deletions examples/vite/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import {
WalletKitConfig,
WalletKitProvider,
} from '@node-real/walletkit';
import { defaultEvmConfig, trustWallet, metaMask, walletConnect } from '@node-real/walletkit/evm';
import {
defaultEvmConfig,
trustWallet,
metaMask,
walletConnect,
binanceWallet,
} from '@node-real/walletkit/evm';
import { mainnet } from 'viem/chains';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useAccount, useDisconnect } from 'wagmi';
Expand All @@ -20,7 +26,7 @@ const config: WalletKitConfig = {
autoConnect: true,
initialChainId: 1,
walletConnectProjectId: '518ee55b46bc23b5b496b03b1322aa13',
wallets: [metaMask(), trustWallet(), walletConnect()],
wallets: [binanceWallet(), metaMask(), trustWallet(), walletConnect()],
chains: [mainnet],
}),
};
Expand Down
46 changes: 37 additions & 9 deletions packages/walletkit/__dev__/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import './style.css';
import {
ConnectModal,
isMobile,
useConnectModal,
useSwitchNetworkModal,
WalletKitConfig,
WalletKitProvider,
} from '@/core/index';
Expand All @@ -27,7 +27,7 @@ import {
} from '@/solana/index';
import { bsc, mainnet, dfk } from 'viem/chains';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useAccount, useConnectors, useDisconnect } from 'wagmi';
import { useAccount, useDisconnect } from 'wagmi';
import { defaultTronConfig, tronLink, useTronWallet } from '@/tron/index';
import { uxuyWallet } from '@/evm/wallets/uxuyWallet';
import { useEvmSwitchChain } from '@/evm/hooks/useEvmSwitchChain';
Expand Down Expand Up @@ -93,22 +93,50 @@ export default function App() {
);
}

export const getIsAndroid = () => {
const ua = navigator.userAgent;
const android = Boolean(ua.match(/Android/i));
return android;
};

export const getHref = (isAndroid: boolean, wc?: string) => {
const appID = 'xoqXxUSMRccLCrZNRebmzj';
const startPagePath = 'L3BhZ2VzL2Rhc2hib2FyZC1uZXcvaW5kZXg=';

let qs = `appId=${appID}&startPagePath=${startPagePath}`;
if (wc) {
const startPageQuery = encodeURI(
`wc=${encodeURIComponent(wc)}&isDeepLink=true&id=${+new Date()}`,
);
qs = `${qs}&startPageQuery=${startPageQuery}`;
}
const host = '//app.binance.com';
if (isAndroid) {
return `bnc:${host}/mp/app?${qs}`;
}
return `https:${host}/?_dp=${encodeURI(`/mp/app?${qs}`)}`;
};

export const openBinanceDeepLink = (wc?: string) => {
const href = getHref(true, wc);
if (!isMobile()) return;

const a = document.createElement('a');
a.href = href;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};

function ConnectButton() {
const { onOpen } = useConnectModal();
const { onOpen: openSwitchNetwork } = useSwitchNetworkModal();

const { address, chainId } = useAccount();
const { disconnect } = useDisconnect();
const { publicKey, disconnect: solanaDisconnect } = useSolanaWallet();
const { address: tronAddress, disconnect: tronDisconnect } = useTronWallet();
const { switchChain } = useEvmSwitchChain();

const connectors = useConnectors();

connectors?.forEach((e) => {
console.log(e.id);
});

return (
<>
<div>
Expand Down
2 changes: 2 additions & 0 deletions packages/walletkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
"wagmi": "^2"
},
"dependencies": {
"@binance/w3w-ethereum-provider": "1.1.8-alpha.0",
"@binance/w3w-utils": "^1.1.6",
"@binance/w3w-wagmi-connector-v2": "^1.2.3",
"@metamask/jazzicon": "^2",
"@solana/wallet-adapter-react": "^0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
import { useState, useRef, useMemo, useCallback } from 'react';
import { RouteContext } from './context';
import { EvmConnectingView } from '@/evm/components/EvmConnectingView';
import { EvmQRCodeView } from '@/evm/components/EvmQRCodeView';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isMobile, isPC, isTMA } from '@/core/base/utils/mobile';
import { isMobile, isTMA } from '@/core/base/utils/mobile';
import { UseWalletRenderProps } from '@/core/hooks/useWalletRender';
import { useConnectModal } from '@/core/modals/ConnectModal/context';
import { ViewRoutes } from '@/core/providers/RouteProvider';
Expand All @@ -8,7 +8,7 @@ import { openLink } from '@/core/utils/common';
import { useEvmConnect } from '@/evm/hooks/useEvmConnect';
import { useWalletConnectModal } from '@/evm/hooks/useWalletConnectModal';
import {
binanceWeb3Wallet,
binanceWallet,
codexFieldWallet,
EvmWallet,
isWalletConnect,
Expand Down Expand Up @@ -84,7 +84,7 @@ export function SetEvmWalletClickRef(props: SetEvmWalletClickRefProps) {
disconnect();
clearTimeout(timerRef.current);

const useSDK = [binanceWeb3Wallet().id].includes(walletId) && isPC();
const useSDK = [binanceWallet().id].includes(walletId);
const delay = useSDK ? 0 : 300;

const handleJumping = () => {
Expand All @@ -99,13 +99,13 @@ export function SetEvmWalletClickRef(props: SetEvmWalletClickRefProps) {
return;
}

// 1. TMA
if (isTMA()) {
if ([uxuyWallet().id, codexFieldWallet().id].includes(walletId)) {
jumpToConnectingView();
return;
}

// 1. TMA
if (isMobile()) {
// 1.1 mobile
if (isWalletConnect(walletId)) {
Expand Down
6 changes: 3 additions & 3 deletions packages/walletkit/src/evm/utils/defaultEvmConfig.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { http, createConfig, CreateConnectorFn, type CreateConfigParameters } from 'wagmi';
import { Chain, mainnet } from 'wagmi/chains';
import {
binanceWeb3Wallet,
binanceWallet,
coinbaseWallet,
EvmWallet,
isWalletConnect,
Expand Down Expand Up @@ -72,8 +72,8 @@ export function defaultEvmConfig(params: CustomizedEvmConfig) {
if (connector.id === 'codex-field-wallet') {
(connector as any).id = codexFieldWallet().id;
}
if (connector.id === 'BinanceW3WSDK') {
(connector as any).id = binanceWeb3Wallet().id;
if (connector.id === 'wallet.binance.com') {
(connector as any).id = binanceWallet().id;
}
});

Expand Down
61 changes: 46 additions & 15 deletions packages/walletkit/src/evm/wallets/binanceWallet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { BinanceW3WParameters, getWagmiConnectorV2 } from '@binance/w3w-wagmi-connector-v2';
import { isAndroid, isTMA } from '@/core/base/utils/mobile';
import { isInBinance } from '@binance/w3w-utils';
import { isMobile, isTMA } from '@/core/base/utils/mobile';
import { binanceWalletConfig } from '@/core/configs/binanceWallet';
import { EvmWallet } from '../types';
import { getEvmInjectedProvider } from '../utils';
import { sleep } from 'tronweb/utils';
import { injected } from '../injected';

export interface BinanceWalletOptions extends Partial<EvmWallet> {
connectorOptions?: BinanceW3WParameters;
}

export function binanceWallet(props: BinanceWalletOptions = {}): EvmWallet {
const { connectorOptions, ...restProps } = props;
const { connectorOptions = {}, ...restProps } = props;

return {
...binanceWalletConfig,
id: 'binanceWallet',
id: 'binanceWeb3Wallet',
walletType: 'evm',
showQRCode: false,
platforms: ['tg-android', 'tg-ios', 'tg-pc', 'browser-android', 'browser-ios', 'browser-pc'],
Expand All @@ -34,13 +37,49 @@ export function binanceWallet(props: BinanceWalletOptions = {}): EvmWallet {
return http;
},
getUri(uri) {
let encodedUri = encodeURIComponent(uri);
if (isTMA() && isAndroid()) {
encodedUri = encodeURIComponent(encodedUri);
}
const encodedUri = encodeURIComponent(uri);
return `https://app.binance.com/cedefi/wc?uri=${encodedUri}`;
},
getCreateConnectorFn() {
if (isInBinance()) {
let isReady = false;

return injected({
shimDisconnect: true,
target: {
id: this.id,
name: binanceWallet().name,
async provider() {
if (isMobile() && binanceWallet().isInstalled() && !isReady) {
await sleep(3000);
}
isReady = true;
return getProvider();
},
},
...connectorOptions,
});
}

if (typeof window !== 'undefined') {
const originalAppendChild = document.body.appendChild;

document.body.appendChild = function (node, ...params) {
if (node instanceof HTMLAnchorElement && node.href?.startsWith('bnc://')) {
node.href = `https://app.binance.com/en/download?_dp=${window.btoa(node.href)}`;
node.target = '_blank';
// node.href = node.href.replace('bnc://', 'https://');

// const qs = node.href.replace('bnc://app.binance.com/mp/app?', '');
// node.href = `https://app.binance.com/?_dp=${encodeURI(`/mp/app?${qs}`)}`;
const div = document.createElement('div');
div.textContent = node.href;
document.body.appendChild(div);
}
return originalAppendChild.call(document.body, node, ...params) as any;
};
}

const connector = getWagmiConnectorV2();
return connector({
...connectorOptions,
Expand All @@ -50,14 +89,6 @@ export function binanceWallet(props: BinanceWalletOptions = {}): EvmWallet {
};
}

// binance web3 wallet changes its name to `binance wallet`, retaining the previous wallet id
export function binanceWeb3Wallet(props: BinanceWalletOptions = {}): EvmWallet {
return {
...binanceWallet(props),
id: 'binanceWeb3Wallet',
};
}

function getProvider() {
if (typeof window === 'undefined') return;
return getEvmInjectedProvider('isBinance');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from 'codexfield-wallet-connector';
import { getEvmGlobalData } from '@/evm/globalData';
import { codexFieldWalletConfig } from '@/core/configs/codexFieldWallet';
import { isTMA } from '@/core/base/utils/mobile';

interface CodexFieldWalletOptions extends Partial<EvmWallet> {
connectorOptions?: Partial<WalletConnectParameters>;
Expand All @@ -20,7 +21,7 @@ export function codexFieldWallet(props: CodexFieldWalletOptions = {}): EvmWallet
showQRCode: false,
platforms: ['tg-android', 'tg-ios', 'tg-pc'],
isInstalled() {
return true;
return isTMA();
},
getDeepLink() {
return undefined;
Expand Down
3 changes: 2 additions & 1 deletion packages/walletkit/src/evm/wallets/uxuyWallet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { uxuyWalletConfig } from '@/core/configs/uyuxWallet';
import { injected } from '../injected';
import { EvmWallet, InjectedEvmWalletOptions } from '../types';
import { isTMA } from '@/core/base/utils/mobile';

export function uxuyWallet(props: InjectedEvmWalletOptions = {}): EvmWallet {
const { connectorOptions, ...restProps } = props;
Expand All @@ -12,7 +13,7 @@ export function uxuyWallet(props: InjectedEvmWalletOptions = {}): EvmWallet {
showQRCode: false,
platforms: ['tg-android', 'tg-ios', 'tg-pc'],
isInstalled() {
return true;
return isTMA();
},
getDeepLink() {
return undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// vite.config.ts
import { defineConfig } from "file:///Users/liwen/Documents/node-real/walletkit/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected]/node_modules/vite/dist/node/index.js";
import react from "file:///Users/liwen/Documents/node-real/walletkit/node_modules/.pnpm/@[email protected][email protected]_@[email protected][email protected][email protected]_/node_modules/@vitejs/plugin-react/dist/index.mjs";
import dts from "file:///Users/liwen/Documents/node-real/walletkit/node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected]_@types+nod_hqvoadjptfr5fbnz34ggrdvpia/node_modules/vite-plugin-dts/dist/index.mjs";
import peerDepsExternal from "file:///Users/liwen/Documents/node-real/walletkit/node_modules/.pnpm/[email protected][email protected]/node_modules/rollup-plugin-peer-deps-external/dist/rollup-plugin-peer-deps-external.js";
import { vanillaExtractPlugin } from "file:///Users/liwen/Documents/node-real/walletkit/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_lightningcss@_cxnzwayg2birdbvurx6hdeqcfa/node_modules/@vanilla-extract/vite-plugin/dist/vanilla-extract-vite-plugin.cjs.js";
import path from "path";
import mkcert from "file:///Users/liwen/Documents/node-real/walletkit/node_modules/.pnpm/[email protected][email protected]_@[email protected][email protected][email protected]_/node_modules/vite-plugin-mkcert/dist/mkcert.mjs";
var __vite_injected_original_dirname = "/Users/liwen/Documents/node-real/walletkit/packages/walletkit";
var vite_config_default = defineConfig({
server: {
https: false
},
plugins: [
react(),
vanillaExtractPlugin({
identifiers: ({ hash }) => `wk_${hash}`
}),
// cssInjectedByJsPlugin({
// injectCode: (cssCode: string) => {
// return `try{if(typeof document != 'undefined'){var elementStyle = document.createElement('style');elementStyle.appendChild(document.createTextNode(${cssCode}));document.head.insertBefore(elementStyle,document.head.firstChild);}}catch(e){console.error('vite-plugin-css-injected-by-js', e);}`;
// },
// }),
dts({
include: "src"
}),
mkcert()
],
resolve: {
alias: {
"@": path.resolve(__vite_injected_original_dirname, "src")
}
},
build: {
target: "esnext",
minify: false,
lib: {
formats: ["es"],
entry: {
"evm/index": "src/evm/index.ts",
"solana/index": "src/solana/index.ts",
"tron/index": "src/tron/index.ts",
"core/index": "src/core/index.ts"
}
},
rollupOptions: {
plugins: [
peerDepsExternal({
includeDependencies: true
})
],
output: {
chunkFileNames: "chunks/chunk.js"
}
}
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvbGl3ZW4vRG9jdW1lbnRzL25vZGUtcmVhbC93YWxsZXRraXQvcGFja2FnZXMvd2FsbGV0a2l0XCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvbGl3ZW4vRG9jdW1lbnRzL25vZGUtcmVhbC93YWxsZXRraXQvcGFja2FnZXMvd2FsbGV0a2l0L3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9saXdlbi9Eb2N1bWVudHMvbm9kZS1yZWFsL3dhbGxldGtpdC9wYWNrYWdlcy93YWxsZXRraXQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJztcbmltcG9ydCByZWFjdCBmcm9tICdAdml0ZWpzL3BsdWdpbi1yZWFjdCc7XG5pbXBvcnQgZHRzIGZyb20gJ3ZpdGUtcGx1Z2luLWR0cyc7XG5pbXBvcnQgcGVlckRlcHNFeHRlcm5hbCBmcm9tICdyb2xsdXAtcGx1Z2luLXBlZXItZGVwcy1leHRlcm5hbCc7XG5pbXBvcnQgeyB2YW5pbGxhRXh0cmFjdFBsdWdpbiB9IGZyb20gJ0B2YW5pbGxhLWV4dHJhY3Qvdml0ZS1wbHVnaW4nO1xuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgbWtjZXJ0IGZyb20gJ3ZpdGUtcGx1Z2luLW1rY2VydCc7XG4vLyBpbXBvcnQgY3NzSW5qZWN0ZWRCeUpzUGx1Z2luIGZyb20gJ3ZpdGUtcGx1Z2luLWNzcy1pbmplY3RlZC1ieS1qcyc7XG5cbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuICBzZXJ2ZXI6IHtcbiAgICBodHRwczogZmFsc2UsXG4gIH0sXG4gIHBsdWdpbnM6IFtcbiAgICByZWFjdCgpLFxuICAgIHZhbmlsbGFFeHRyYWN0UGx1Z2luKHtcbiAgICAgIGlkZW50aWZpZXJzOiAoeyBoYXNoIH0pID0+IGB3a18ke2hhc2h9YCxcbiAgICB9KSxcbiAgICAvLyBjc3NJbmplY3RlZEJ5SnNQbHVnaW4oe1xuICAgIC8vICAgaW5qZWN0Q29kZTogKGNzc0NvZGU6IHN0cmluZykgPT4ge1xuICAgIC8vICAgICByZXR1cm4gYHRyeXtpZih0eXBlb2YgZG9jdW1lbnQgIT0gJ3VuZGVmaW5lZCcpe3ZhciBlbGVtZW50U3R5bGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzdHlsZScpO2VsZW1lbnRTdHlsZS5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSgke2Nzc0NvZGV9KSk7ZG9jdW1lbnQuaGVhZC5pbnNlcnRCZWZvcmUoZWxlbWVudFN0eWxlLGRvY3VtZW50LmhlYWQuZmlyc3RDaGlsZCk7fX1jYXRjaChlKXtjb25zb2xlLmVycm9yKCd2aXRlLXBsdWdpbi1jc3MtaW5qZWN0ZWQtYnktanMnLCBlKTt9YDtcbiAgICAvLyAgIH0sXG4gICAgLy8gfSksXG4gICAgZHRzKHtcbiAgICAgIGluY2x1ZGU6ICdzcmMnLFxuICAgIH0pLFxuICAgIG1rY2VydCgpLFxuICBdLFxuICByZXNvbHZlOiB7XG4gICAgYWxpYXM6IHtcbiAgICAgICdAJzogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJ3NyYycpLFxuICAgIH0sXG4gIH0sXG4gIGJ1aWxkOiB7XG4gICAgdGFyZ2V0OiAnZXNuZXh0JyxcbiAgICBtaW5pZnk6IGZhbHNlLFxuICAgIGxpYjoge1xuICAgICAgZm9ybWF0czogWydlcyddLFxuICAgICAgZW50cnk6IHtcbiAgICAgICAgJ2V2bS9pbmRleCc6ICdzcmMvZXZtL2luZGV4LnRzJyxcbiAgICAgICAgJ3NvbGFuYS9pbmRleCc6ICdzcmMvc29sYW5hL2luZGV4LnRzJyxcbiAgICAgICAgJ3Ryb24vaW5kZXgnOiAnc3JjL3Ryb24vaW5kZXgudHMnLFxuICAgICAgICAnY29yZS9pbmRleCc6ICdzcmMvY29yZS9pbmRleC50cycsXG4gICAgICB9LFxuICAgIH0sXG4gICAgcm9sbHVwT3B0aW9uczoge1xuICAgICAgcGx1Z2luczogW1xuICAgICAgICBwZWVyRGVwc0V4dGVybmFsKHtcbiAgICAgICAgICBpbmNsdWRlRGVwZW5kZW5jaWVzOiB0cnVlLFxuICAgICAgICB9KSxcbiAgICAgIF0sXG4gICAgICBvdXRwdXQ6IHtcbiAgICAgICAgY2h1bmtGaWxlTmFtZXM6ICdjaHVua3MvY2h1bmsuanMnLFxuICAgICAgfSxcbiAgICB9LFxuICB9LFxufSk7XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQXlXLFNBQVMsb0JBQW9CO0FBQ3RZLE9BQU8sV0FBVztBQUNsQixPQUFPLFNBQVM7QUFDaEIsT0FBTyxzQkFBc0I7QUFDN0IsU0FBUyw0QkFBNEI7QUFDckMsT0FBTyxVQUFVO0FBQ2pCLE9BQU8sWUFBWTtBQU5uQixJQUFNLG1DQUFtQztBQVV6QyxJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixRQUFRO0FBQUEsSUFDTixPQUFPO0FBQUEsRUFDVDtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1AsTUFBTTtBQUFBLElBQ04scUJBQXFCO0FBQUEsTUFDbkIsYUFBYSxDQUFDLEVBQUUsS0FBSyxNQUFNLE1BQU0sSUFBSTtBQUFBLElBQ3ZDLENBQUM7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsSUFNRCxJQUFJO0FBQUEsTUFDRixTQUFTO0FBQUEsSUFDWCxDQUFDO0FBQUEsSUFDRCxPQUFPO0FBQUEsRUFDVDtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1AsT0FBTztBQUFBLE1BQ0wsS0FBSyxLQUFLLFFBQVEsa0NBQVcsS0FBSztBQUFBLElBQ3BDO0FBQUEsRUFDRjtBQUFBLEVBQ0EsT0FBTztBQUFBLElBQ0wsUUFBUTtBQUFBLElBQ1IsUUFBUTtBQUFBLElBQ1IsS0FBSztBQUFBLE1BQ0gsU0FBUyxDQUFDLElBQUk7QUFBQSxNQUNkLE9BQU87QUFBQSxRQUNMLGFBQWE7QUFBQSxRQUNiLGdCQUFnQjtBQUFBLFFBQ2hCLGNBQWM7QUFBQSxRQUNkLGNBQWM7QUFBQSxNQUNoQjtBQUFBLElBQ0Y7QUFBQSxJQUNBLGVBQWU7QUFBQSxNQUNiLFNBQVM7QUFBQSxRQUNQLGlCQUFpQjtBQUFBLFVBQ2YscUJBQXFCO0FBQUEsUUFDdkIsQ0FBQztBQUFBLE1BQ0g7QUFBQSxNQUNBLFFBQVE7QUFBQSxRQUNOLGdCQUFnQjtBQUFBLE1BQ2xCO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
Loading

0 comments on commit fcb224c

Please sign in to comment.