Skip to content

Commit

Permalink
Merge pull request #1205 from lukso-network/docs/up-provider
Browse files Browse the repository at this point in the history
Add up-provider library
  • Loading branch information
frozeman authored Jan 27, 2025
2 parents ea74b19 + 16d7176 commit 6548599
Showing 11 changed files with 329 additions and 3 deletions.
123 changes: 123 additions & 0 deletions docs/learn/mini-apps/connect-upprovider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
sidebar_label: '🔌 Connect to a mini-app'
description: 'How to connect your mini-app using the UP Provider'
sidebar_position: 1
---

# Connect to a mini-app

<img width="300" alt="Screenshot 2025-01-27 at 14 15 22" src="https://github.com/user-attachments/assets/7f0b7875-c402-440d-b77f-935cf90f241d" align="right" />

Mini-apps are dApps that run in an iframe of a parent page that hosts them. [universaleverything.io](https://universaleverything.io) is such a website.
An example of mini-apps can be found at [the app-store profile](https://universaleverything.io/0x7b258dD350227CFc9Da1EDD7f4D978f7Df20fD40) (See on the right).

For users connecting to a mini-app would mean to connect to each mini-app via a connect button, web3 modal, or wallet connect process. This makes connecting to mini-apps cumbersome and not fun.

The [up-provider](https://github.com/lukso-network/tools-up-provider) solves this by giving mini-apps a way for the user visiting the parent page, to connect to the mini-app directly with one-click.

**Additionally the mini-app has access to `context addresses`**, which in the case of [universaleverything.io](https://universaleverything.io) is the universal profile under which the mini-app is hosted.

The up-provider is a [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible provider, meaning it will work with all major web3 libraries. For examples using viem, web3.js or ethers, [see the readme of the up-provider](https://github.com/lukso-network/tools-up-provider/blob/main/README.md#provider-for-mini-apps).

## Installation

```bash
npm install @lukso/up-provider
```

## Example implementation using react

Here's a step-by-step guide to implement UP Provider connection in your react application:

1. First, import the necessary dependencies:

```typescript
import { createClientUPProvider } from '@lukso/up-provider';
```

2. Create a provider instance outside your component:

```typescript
const provider = createClientUPProvider();
```

3. To implement the UP Provider connection in your component, you can use the following code:

```typescript
// Track connected accounts
const [accounts, setAccounts] = useState<Array<`0x${string}`>>([]);
const [contextAccounts, setContextAccounts] = useState<Array<`0x${string}`>>(
[],
);
const [profileConnected, setProfileConnected] = useState(false);

// Helper to check connection status
const updateConnected = useCallback(
(_accounts: Array<`0x${string}`>, _contextAccounts: Array<`0x${string}`>) => {
setProfileConnected(_accounts.length > 0 && _contextAccounts.length > 0);
},
[],
);

useEffect(() => {
async function init() {
try {
const _accounts = provider.accounts as Array<`0x${string}`>;
setAccounts(_accounts);

const _contextAccounts = provider.contextAccounts;
updateConnected(_accounts, _contextAccounts);
} catch (error) {
console.error('Failed to initialize provider:', error);
}
}

// Handle account changes
const accountsChanged = (_accounts: Array<`0x${string}`>) => {
setAccounts(_accounts);
updateConnected(_accounts, contextAccounts);
};

const contextAccountsChanged = (_accounts: Array<`0x${string}`>) => {
setContextAccounts(_accounts);
updateConnected(accounts, _accounts);
};

init();

// Set up event listeners
provider.on('accountsChanged', accountsChanged);
provider.on('contextAccountsChanged', contextAccountsChanged);

// Cleanup listeners
return () => {
provider.removeListener('accountsChanged', accountsChanged);
provider.removeListener('contextAccountsChanged', contextAccountsChanged);
};
}, [accounts[0], contextAccounts[0], updateConnected]);
```

## Understanding the Implementation

### Provider Creation

The `createClientUPProvider()` function creates a new instance of the UP Provider. This should be done outside your component to maintain a single instance.

### State variables

This implementation gives you access to:

- `accounts`: Array of connected accounts, in our case the universal profile of the visitor
- `contextAccounts`: Array of context accounts, which in our case is the universal profile on [universaleverything.io](https://universaleverything.io) where the mini-app is hosted under.
- `profileConnected`: Boolean indicating if a user is connected to the mini-app

### Event Handling

The provider emits two important events:

- `accountsChanged`: Triggered when the connected accounts change, this is universal profile of the visitor
- `contextAccountsChanged`: Triggered when the context accounts change, this is the universal profile on [universaleverything.io](https://universaleverything.io) where the mini-app is hosted under.

### Cleanup

Clean up the event listeners when unmounting to prevent memory leaks.
41 changes: 41 additions & 0 deletions docs/learn/mini-apps/testing-miniapps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
sidebar_label: '🧪 Testing Mini-Apps Locally'
description: 'How to run and test your mini-app locally'
sidebar_position: 2
---

# 🧪 Testing Mini-Apps Locally

To test mini-apps in action on Universal Everything, we need to expose our mini-app to an URL that can be accessed by the UP Provider.

We will be using [localtunnel](https://github.com/localtunnel/localtunnel) for this purpose.

## Install `localtunnel`

Install `localtunnel` globally to access it from any directory.

```bash
npm install -g localtunnel
```

## Run `localtunnel`

In a new terminal window, run the following command:

```bash
lt --port <PORT> // localhost port that your mini-app is running on (e.g. 3000)
```

Running the command will generate a random URL. Initial visit to this URL will ask for a password, you can use **your IP address** to access your mini-app.

![localtunnel-visit](/img/learn/localtunnel-visit.png)

:::info

If you don't have your IP address in hand, you can visit the URL on the page to retrieve it.

:::

## Test your Mini-App on Universal Everything

Go to your Universal Profile on Universal Everything, and add your mini-app with using the provided URL to your Grid.
2 changes: 1 addition & 1 deletion docs/standards/access-control/lsp6-key-manager.md
Original file line number Diff line number Diff line change
@@ -538,7 +538,7 @@ To grant permission(s) to a controller, set the following data key-value pair in

A caller can craft a payload via `ERC725X.execute(...)` to be sent back to the KeyManager, leading to potential re-entrancy attacks.

Such transaction flow can lead an initial caller to use more permissions than allowed initially by re-using the permissions granted to the linked ERC725Account's address.
Such transaction flow can lead an initial caller to use more permissions than allowed initially by reusing the permissions granted to the linked ERC725Account's address.

:::

2 changes: 1 addition & 1 deletion docs/standards/accounts/lsp17-contract-extension.md
Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ The 52 bytes of additional calldata appended to the call to the extension contra
### Extension Re-usability

While contracts can deploy and customize their own extensions, many smart contracts **share almost the same logic** for certain functions. In this case, the same extensions can be re-used by different contracts supporting LSP17.
While contracts can deploy and customize their own extensions, many smart contracts **share almost the same logic** for certain functions. In this case, the same extensions can be reused by different contracts supporting LSP17.

For example, **_smart contract A & B_** are two independent contracts that implement different functions but share the same logic for verifying signatures. Therefore, they can use the same extension for signature validation for the `isValidSignature(..)` function.

2 changes: 1 addition & 1 deletion docs/tools/dapps/lsp-factoryjs/_category_.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
label: '🏭 lsp-factory.js (deprecated)'
collapsed: true
position: 5
position: 6
4 changes: 4 additions & 0 deletions docs/tools/dapps/up-provider/_category_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
label: '🗞️ up-provider'
collapsed: true
position: 5

84 changes: 84 additions & 0 deletions docs/tools/dapps/up-provider/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
sidebar_position: 1
title: 'Getting Started'
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Getting Started

The `@up-provider` library lets dApps run as mini-apps on Grid and allows parent applications to one-click-connect to your mini-app.

## Installation

```bash
npm install @lukso/up-provider
```

## Usage in mini-apps

Below are the examples of how to use the `up-provider` with different libraries:

<Tabs groupId="provider-lib">
<TabItem value="viem" label="viem">

```js
import { createClientUPProvider } from '@lukso/up-provider';
import { createWalletClient, createPublicClient, custom } from 'viem';
import { lukso } from 'viem/chains';

// Construct the up-provider
const provider = createClientUPProvider();

// Create public client if you need direct connection to RPC
const publicClient = createPublicClient({
chain: lukso,
transport: http(),
});

// Create wallet client to connect to provider
const walletClient = createWalletClient({
chain: lukso,
transport: custom(provider),
});
```

</TabItem>

<TabItem value="ethers" label="ethers" >

```js
import { createClientUPProvider } from '@lukso/up-provider'
import { type Eip1193Provider, ethers } from 'ethers'

// Create the up-provider
const provider = createClientUPProvider()

// Wrap provider into ethers for usage.
const browserProvider = new ethers.BrowserProvider(upProvider as unknown as Eip1193Provider)
```

</TabItem>
<TabItem value="web3" label="web3" >

```js
import { createClientUPProvider } from '@lukso/up-provider';
import Web3, { type EthExecutionAPI, type SupportedProviders } from 'web3';

// Create the up-provider
const provider = createClientUPProvider();

// Wrap provider into web3 for usage.
const web3 = new Web3(provider as SupportedProviders<EthExecutionAPI>);
```

</TabItem>

</Tabs>

## Resources

- [GitHub repo](https://github.com/lukso-network/tools-up-provider/)
- [NPM package](https://www.npmjs.com/package/@lukso/up-provider)
- [Try out the example mini-app on your grid!](https://nextjs.miniapp-example.lukso.tech/)
36 changes: 36 additions & 0 deletions docs/tools/dapps/up-provider/grid-providers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
sidebar_position: 2
title: 'Grid Providers'
---

:::info About Grid Providers

Developers can use Grid Providers to host and connect mini-apps on their pages.

:::

# Grid Providers

Mini-apps can be hosted on a parent page by passing UP connections to a parent provider like `window.ethereum`.

```js
import { UPClientChannel, createUPProviderConnector } from '@lukso/up-provider';

// Pass in the provider you want the page to use.
const providerConnector = createUPProviderConnector(originalProvider, [
'https://rpc.mainnet.lukso.network',
]);
// or later on call
// globalProvider.setupProvider(originalProvider, ['https://rpc.mainnet.lukso.network'])

providerConnector.on('channelCreated', ({ channel, id }) => {
// Called when an iframe connects.
// then channel can control the connection.
// Usually you would store this in a ref and use it within a dialog to control the connection.

// for example
channel.enabled = true;
// The addresses and chainId it will cause addressChanged and chainChanged events on the client provider.
channel.setAllowedAccounts([profileAddress, ...extraAddresses]);
});
```
28 changes: 28 additions & 0 deletions docs/tools/index.mdx
Original file line number Diff line number Diff line change
@@ -137,6 +137,34 @@ Sign EIP-191 messages with a private key, to be used for preparing gas-less tran

</ReferenceCard>

<ReferenceCard name="@up-provider" showAsCode={true} links={[
{label: 'Overview', to: '/tools/dapps/up-provider/getting-started' },
{label: 'GitHub', to: 'https://github.com/lukso-network/tools-up-provider' },
]}>

<a
class="imageLink"
href="https://www.npmjs.com/package/@lukso/up-provider"
target="_blank"
rel="noopener noreferrer"
>
<img
style={{ verticalAlign: 'middle' }}
alt="Github badgen badge"
class="shield-badge"
src="https://img.shields.io/badge/Github-white?logo=github&logoColor=black&link=https://github.com/lukso-network/tools-up-provider
"
/>
</a>

```bash
npm install @lukso/up-provider
```

The @up-provider package helps building an Universal Everything integrated mini-apps for Grid.

</ReferenceCard>

<ReferenceCard name="web3-onboard-config" links={[
{label: 'GitHub', to: 'https://github.com/lukso-network/web3-onboard-config' },
]}>
10 changes: 10 additions & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
@@ -77,6 +77,16 @@ module.exports = {
},
],
},
{
type: 'category',
label: '🧃 Mini-Apps',
items: [
{
type: 'autogenerated',
dirName: 'learn/mini-apps',
},
],
},
{ type: 'html', value: '<hr/>', defaultStyle: false }, // -----

// Hide Vault guides for now, as LSP9 standard is still draft and will be re-worked
Binary file added static/img/learn/localtunnel-visit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6548599

Please sign in to comment.