@uptickproject/allowance-plugin
v0.1.0
Published
Uptick allowance(grant) plugin SDK: activity queries, EIP712 signing, receiveAllowance transactions, Turnstile captcha and API authentication.
Maintainers
Readme
@uptickproject/allowance-plugin
津贴(Grant)插件 SDK:活动查询、EIP712 签发、构造 receiveAllowance calldata、可选上链。
App 必须走前端钱包登录 + token。apiKey 是公开标识,可以放在客户端;apiSecret 只能放服务端。合约 proxy 地址由 /v2/allowance/info 下发,不必写死在 App。
与签到 SDK 共用同一套 uptick-plugin 服务(
/api/1.0/v2/*)、同一套 token 认证与同一个apiKey,也复用同一套 Turnstile 人机验证。
依赖
peerDependency(接入方已有则不必再装):
web3^1.10axios>= 0.27react-native-device-info>= 10(仅 RN 默认DeviceIdProvider;Node 须自注入)react-native-encrypted-storage>= 4(可选,用于持久化 token;缺失时回退到不持久化)
内置:js-sha256(仅 v1 HMAC)。
App 接入(v2,推荐)
import {
AllowanceClient,
AllowanceError,
createWeb3TransactionSigner,
getAllowanceClient,
buildTurnstileHtml,
parseTurnstileMessage,
isTurnstileNavigationAllowed,
TURNSTILE_MAX_ERROR,
TURNSTILE_WEBVIEW_PROPS,
} from '@uptickproject/allowance-plugin';
// 1) 把 Upward 现有 web3 工具适配成 SDK TransactionSigner
const transactionSigner = createWeb3TransactionSigner({
signTransaction: (tx, pk, url) => signTransaction(tx, pk || privateKey, url || rpcUrl),
sendSignedTransaction: (signed, url) => sendSignedTransaction(signed, url || rpcUrl),
privateKey,
rpcUrl,
});
// 2) 取 Client(按 address:chainId:authUrl:apiKey 缓存,内部完成登录与 token 续期)
const client = await getAllowanceClient({
authUrl, // uptick-plugin 服务地址
apiKey, // 与插件库 project.api_key 一致的公开标识
address: wallet.address,
rpcUrl,
chainId,
appVersion: DeviceInfo.getVersion(),
signMessage: (message) => signMessage(message, privateKey),
transactionSigner,
});
// 3) 拉取我的期次列表(含归一化状态)
const cycles = await client.getCycles(true);
// 4) 领取(Turnstile token 由 WebView 取到后传入)
try {
const receipt = await client.claim(undefined, {
turnstileToken, // 可选:服务端配置了 Turnstile 则必填
allowanceId: cycles[0].allowanceId,
});
if (client.isSuccessful(receipt)) {
// 领取成功
}
} catch (error) {
if (error instanceof AllowanceError && error.code === 'INSUFFICIENT_BALANCE') {
// 余额不足付 gas,引导用户领取水龙头
}
if (error instanceof AllowanceError && error.code === 'TURNSTILE_FAILED') {
// 人机验证失败
}
}claim(signer?, options?):不传 signer 时使用初始化传入的 transactionSigner。流程为:取 nonce → 调 /v2/allowance/sign 拿 EIP712 签名 → 构造 calldata → 余额校验 → 交易签名广播 → 上报 txHash。整个过程 SDK 不上收用户私钥,交易签名仍由接入方 TransactionSigner 完成。
人机验证(Turnstile)
服务端的 node.config.turnstile-secret 未配置时默认放行(fail-open,便于本地开发),上线前必须配置(配置后为 fail-closed)。App 侧用 WebView 跑 Cloudflare Turnstile widget,拿到 token 后透传给 SDK:
import { WebView, type WebViewMessageEvent } from 'react-native-webview';
const onMessage = (event: WebViewMessageEvent) => {
const msg = parseTurnstileMessage(event.nativeEvent.data);
if (msg.type === 'success' && msg.token) onVerified(msg.token);
if (msg.type === 'error') { /* 累计失败达 TURNSTILE_MAX_ERROR 提示 */ }
};
<WebView
{...TURNSTILE_WEBVIEW_PROPS}
source={{ html: buildTurnstileHtml(siteKey), baseUrl /* 与 Cloudflare widget hostname 一致 */ }}
onShouldStartLoadWithRequest={(req) => isTurnstileNavigationAllowed(req?.url)}
onMessage={onMessage}
/>详见 UPWARD_INTEGRATION.md。
防伪:deviceId 与 IP
- deviceId:默认 RN
DeviceInfo.getUniqueId(),再normalizeDeviceId(去横线;长度 ≥32 时去掉前 5 位)。 - 非 RN(Node 脚本等)必须注入
deviceIdProvider,否则会因缺少react-native-device-info抛错。 - IP:
getPublicIp()只是辅助指纹;限流以服务端连接层 IP 为准。 - v2 签发后,服务端把 地址与 deviceId 绑定,换设备再签会被拒。
分步 API
await client.getActivity(); // allowance_info 活动信息
await client.getCycles(); // 我的期次列表(带 cycleStatus)
await client.getCurrentCycle(); // 当前应展示的期次
await client.getState(); // 聚合渲染状态(含余额 / 可领判断)
await client.claim(signer, options);
client.isSuccessful(receipt);Node 冒烟(不打包 App)
cd web-plugins/allowance
npm install
npm run smoke # 默认连本机、project 5、origin 1170,并广播上链
SMOKE_SKIP_BROADCAST=1 npm run smoke
WALLET_JSON=/abs/path/0x....json npm run smoke脚本注入自定义 DeviceIdProvider,用钱包 json 做 personal_sign 与交易签名。
v1 HMAC(仅服务端)
有 apiKey/apiSecret 的机器调用 /api/1.0/allowance/receiveAllowance。App 不要 走这条。
const sdk = new AllowanceSDK(
{ signServiceUrl, apiKey, apiSecret, rpcUrl, chainId, contractAddress },
);
await sdk.getSign(user, deviceId, allowanceId);目录
web-plugins/allowance/
├── src/ AllowanceSDK、client、auth、query、chain、device、sign、turnstile、state、types
├── scripts/ smoke-v2.ts
├── README.md
└── UPWARD_INTEGRATION.md