Ufree Mini-App Quick Reference
One-page cheat sheet. For full context see MINI_APP_DEVELOPERS.md (中文) or MINI_APP_DEVELOPERS_EN.md (English).
Install SDK
<script src="https://YOUR-UFREE-HOST/sdk/ufree.js"></script>
Exposes window.ufree, plus aliases window.ethereum (EIP-1193) and window.nostr (NIP-07) if not already claimed.
EIP-1193 methods (window.ethereum.request)
| Method |
Params |
Returns |
Status |
eth_requestAccounts |
— |
[address] |
✅ |
eth_accounts |
— |
[address] |
✅ |
eth_chainId |
— |
"0x1" |
✅ |
net_version |
— |
"1" |
✅ |
personal_sign |
[message, address] |
"0x...130-char sig" |
✅ |
eth_sign |
[address, message] |
sig |
✅ |
eth_sendTransaction |
[txObject] |
tx hash |
⏳ 4900 |
wallet_switchEthereumChain |
[{chainId: "0x89"}] |
null |
⏳ 4900 |
wallet_addEthereumChain |
— |
— |
❌ 4200 |
NIP-07 methods (window.nostr)
| Method |
Params |
Returns |
getPublicKey() |
— |
"hex pubkey" |
signEvent(event) |
EventTemplate |
SignedEvent |
Antelope (raw bridge — no global standard yet)
| Method |
Params |
Returns |
Status |
wallet.antelope.getAccount |
— |
"accountname" |
✅ |
wallet.antelope.getChain |
— |
"eos" or "eos-jungle4" |
✅ |
wallet.antelope.signTransaction |
Action[] |
signed tx |
⏳ 4900 |
wallet.antelope.switchChain |
{chainId} |
null |
⏳ 4900 |
wallet.antelope.getTableRows |
{code, table, scope, ...} |
rows |
⏳ 4900 |
Call via window.ufree.request('wallet.antelope.X', params).
App / Wallet generic
| Method |
Returns |
app.getInfo |
{npub, name, about, picture, version} |
app.close |
closes mini-app (null) |
wallet.getType |
"evm" or "antelope" |
Error codes
| Code |
Meaning |
4001 |
User rejected |
4200 |
Method not supported |
4900 |
Not yet implemented |
4901 |
Wrong key family (EVM op on Antelope wallet, or vice versa) |
4902 |
Wallet locked |
-32601 |
Method not found |
-32602 |
Invalid params |
-32603 |
Internal error |
Manifest schema
{
name: string; // required
version: string; // required, semver-ish
bundleUrl: string; // required — https:// or ipfs://
about?: string;
picture?: string; // https / ipfs / data:image/
bundleHash?: string; // 64-char lowercase sha256 hex
}
Bundle modes
| Mode |
bundleHash |
srcdoc? |
Backend API? |
Use when |
| Single-file + hash |
yes |
yes |
❌ |
Pure-frontend (chain RPC / Nostr only) |
| Multi-file no hash |
no |
no |
✅ |
Standard SPA with backend |
| Multi-file + hash |
yes |
yes (+<base> injected) |
❌ |
Static entry HTML, no API |
Rule of thumb: have a backend? Don’t ship bundleHash.
Sign-in with wallet (replaces cookies)
const { challenge } = await fetch('/api/auth/challenge').then(r => r.json());
const [addr] = await window.ethereum.request({ method: 'eth_requestAccounts' });
const sig = await window.ethereum.request({
method: 'personal_sign',
params: [challenge, addr],
});
const { token } = await fetch('/api/auth/verify', {
method: 'POST',
body: JSON.stringify({ address: addr, signature: sig, challenge }),
}).then(r => r.json());
Backend verifies with ethers.verifyMessage(challenge, sig) === address.
Container constraints (TL;DR)
- ✅ Most Web APIs work (DOM, fetch, WebSocket, Canvas, WebGL, IndexedDB, Web Workers, Web Audio)
- ✅ Browser-prompted permissions: camera, mic, clipboard, geolocation, fullscreen, sensors, payment
- ⚠️ Third-party cookies invisible — user must re-login inside the container
- ❌ WebUSB / WebBluetooth / WebNFC / WebSerial
- ❌ Service Workers (use with extreme care)
- 🚫 srcdoc mode (when bundleHash present): no backend API CORS, no cookies
Raw postMessage protocol
// Request (mini-app → host)
window.parent.postMessage({
ufree: 1, kind: 'request', id: '1',
method: 'wallet.evm.signMessage', params: { message: 'hi' }
}, '*');
// Response (host → mini-app, on window 'message' event)
{ ufree: 1, kind: 'response', id: '1', result: '0x...' }
// or
{ ufree: 1, kind: 'response', id: '1', error: { code: 4001, message: 'rejected' } }
Looking for comments…
Searching Nostr relays. This may take a moment the first time this article is opened.
Looking for comments…
Searching Nostr relays. This may take a moment the first time this article is opened.