four-flap-meme-sdk
Version:
SDK for Flap bonding curve and four.meme TokenManager
105 lines (104 loc) • 4.17 kB
JavaScript
export class FourClient {
constructor(cfg) {
// 默认使用 Cloudflare Workers 代理,已配置 CORS,浏览器可直接使用
this.baseUrl = cfg?.baseUrl || 'https://throbbing-shape-a160.paulalsop072.workers.dev';
}
async generateNonce(req) {
const r = await fetch(`${this.baseUrl}/v1/private/user/nonce/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(req),
});
const j = await r.json();
// code 可能是字符串 '0' 或数字 0
if (j.code !== '0' && j.code !== 0) {
throw new Error(`generateNonce failed: ${JSON.stringify(j)}`);
}
return j.data;
}
async loginDex(body) {
const r = await fetch(`${this.baseUrl}/v1/private/user/login/dex`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
const j = await r.json();
if (j.code !== '0' && j.code !== 0) {
throw new Error(`login failed: ${JSON.stringify(j)}`);
}
return j.data; // access_token
}
async uploadImage(accessToken, file, filename) {
const form = new FormData();
// 如果提供了文件名,使用它;否则根据 MIME 类型生成
const finalFilename = filename || this.getFilenameFromBlob(file);
form.append('file', file, finalFilename);
const r = await fetch(`${this.baseUrl}/v1/private/token/upload`, {
method: 'POST',
headers: { 'meme-web-access': accessToken },
body: form,
});
const j = await r.json();
if (j.code !== '0' && j.code !== 0) {
throw new Error(`upload failed: ${JSON.stringify(j)}`);
}
return j.data; // imgUrl
}
getFilenameFromBlob(blob) {
const mimeToExt = {
'image/jpeg': 'jpg',
'image/jpg': 'jpg',
'image/png': 'png',
'image/gif': 'gif',
'image/bmp': 'bmp',
'image/webp': 'webp',
};
const ext = mimeToExt[blob.type] || 'png';
return `image-${Date.now()}.${ext}`;
}
async createToken(accessToken, req) {
const r = await fetch(`${this.baseUrl}/v1/private/token/create`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'meme-web-access': accessToken },
body: JSON.stringify(req),
});
const j = await r.json();
if (j.code !== '0' && j.code !== 0) {
throw new Error(`createToken failed: ${JSON.stringify(j)}`);
}
return j.data;
}
async getPublicConfig() {
const r = await fetch(`${this.baseUrl}/v1/public/config`);
return await r.json();
}
async getTokenByAddress(address, accessToken) {
const r = await fetch(`${this.baseUrl}/v1/private/token/get?address=${address}`, {
headers: accessToken ? { 'meme-web-access': accessToken } : undefined,
});
const j = await r.json();
if (j.code && j.code !== '0' && j.code !== 0) {
throw new Error(`getTokenByAddress failed: ${JSON.stringify(j)}`);
}
return j.data ?? j;
}
async getTokensByAddresses(addresses, accessToken) {
if (!Array.isArray(addresses) || addresses.length === 0)
return [];
const tasks = addresses.map((addr) => this.getTokenByAddress(addr, accessToken).catch((e) => ({ address: addr, error: String(e) })));
return await Promise.all(tasks);
}
async getTokenById(id, accessToken) {
const r = await fetch(`${this.baseUrl}/v1/private/token/getById?id=${id}`, {
headers: accessToken ? { 'meme-web-access': accessToken } : undefined,
});
const j = await r.json();
if (j.code && j.code !== '0' && j.code !== 0) {
throw new Error(`getTokenById failed: ${JSON.stringify(j)}`);
}
return j.data ?? j;
}
}
export function buildLoginMessage(nonce) {
return `You are sign in Meme ${nonce}`;
}