@paper-clip/pc 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -9
- package/baked-config.json +2 -2
- package/dist/chain-adapter.js +44 -0
- package/dist/evm-adapter.js +308 -0
- package/dist/index.js +269 -282
- package/dist/privy-evm.js +113 -0
- package/dist/privy.js +2 -2
- package/dist/settings.js +8 -0
- package/dist/solana-adapter.js +299 -0
- package/package.json +2 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privy EVM Integration
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side Ethereum wallet management and gas-sponsored
|
|
5
|
+
* transaction sending via Privy's REST API.
|
|
6
|
+
*
|
|
7
|
+
* Agents never touch raw private keys — signing happens on Privy's servers.
|
|
8
|
+
* Gas fees are sponsored by Privy, so agents can transact without holding
|
|
9
|
+
* native tokens (MON, ETH, etc.).
|
|
10
|
+
*
|
|
11
|
+
* This module is only used when WALLET_TYPE === "privy" and chain === "evm".
|
|
12
|
+
*/
|
|
13
|
+
import { privyFetch } from "./privy.js";
|
|
14
|
+
import { loadSettings, saveSettings } from "./settings.js";
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Wallet provisioning
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/**
|
|
19
|
+
* Create a new Ethereum server wallet via Privy.
|
|
20
|
+
* Called once during `pc init` for EVM chains.
|
|
21
|
+
*/
|
|
22
|
+
export async function createPrivyEvmWallet() {
|
|
23
|
+
const data = await privyFetch("/wallets", {
|
|
24
|
+
method: "POST",
|
|
25
|
+
body: JSON.stringify({ chain_type: "ethereum" }),
|
|
26
|
+
});
|
|
27
|
+
return { id: data.id, address: data.address };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get an existing EVM wallet's info from Privy.
|
|
31
|
+
*/
|
|
32
|
+
export async function getPrivyEvmWalletInfo(walletId) {
|
|
33
|
+
const data = await privyFetch(`/wallets/${walletId}`);
|
|
34
|
+
return { id: data.id, address: data.address };
|
|
35
|
+
}
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Sponsored transaction sending
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
/**
|
|
40
|
+
* Send a gas-sponsored EVM transaction via Privy.
|
|
41
|
+
*
|
|
42
|
+
* Uses `eth_sendTransaction` with `sponsor: true` — Privy pays gas.
|
|
43
|
+
* Returns the transaction hash.
|
|
44
|
+
*
|
|
45
|
+
* @param walletId Privy wallet ID
|
|
46
|
+
* @param chainId EVM chain ID (e.g. 10143 for Monad testnet)
|
|
47
|
+
* @param tx Transaction parameters (to, data, value?)
|
|
48
|
+
*/
|
|
49
|
+
export async function sendSponsoredEvmTransaction(walletId, chainId, tx) {
|
|
50
|
+
const caip2 = `eip155:${chainId}`;
|
|
51
|
+
const transaction = {
|
|
52
|
+
to: tx.to,
|
|
53
|
+
data: tx.data,
|
|
54
|
+
};
|
|
55
|
+
if (tx.value) {
|
|
56
|
+
transaction.value = tx.value;
|
|
57
|
+
}
|
|
58
|
+
const data = await privyFetch(`/wallets/${walletId}/rpc`, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
body: JSON.stringify({
|
|
61
|
+
method: "eth_sendTransaction",
|
|
62
|
+
caip2,
|
|
63
|
+
sponsor: true,
|
|
64
|
+
params: { transaction },
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
const hash = data?.data?.hash ||
|
|
68
|
+
data?.hash ||
|
|
69
|
+
data?.data?.transaction_hash;
|
|
70
|
+
if (typeof hash !== "string" || hash.length === 0) {
|
|
71
|
+
throw new Error(`Invalid Privy eth_sendTransaction response: ${JSON.stringify(data)}`);
|
|
72
|
+
}
|
|
73
|
+
return hash;
|
|
74
|
+
}
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Provision + persist
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
/**
|
|
79
|
+
* Provision a new EVM wallet and save to settings.
|
|
80
|
+
* Called during `pc init` when using Privy mode on an EVM chain.
|
|
81
|
+
* Returns the wallet address.
|
|
82
|
+
*/
|
|
83
|
+
export async function provisionPrivyEvmWallet() {
|
|
84
|
+
const settings = loadSettings();
|
|
85
|
+
// Already provisioned?
|
|
86
|
+
if (settings.privyEvmWalletId && settings.privyEvmWalletAddress) {
|
|
87
|
+
return {
|
|
88
|
+
id: settings.privyEvmWalletId,
|
|
89
|
+
address: settings.privyEvmWalletAddress,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// Create new wallet
|
|
93
|
+
const info = await createPrivyEvmWallet();
|
|
94
|
+
// Persist
|
|
95
|
+
settings.privyEvmWalletId = info.id;
|
|
96
|
+
settings.privyEvmWalletAddress = info.address;
|
|
97
|
+
saveSettings(settings);
|
|
98
|
+
return info;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get existing EVM Privy wallet info from settings.
|
|
102
|
+
* Throws if no wallet has been provisioned yet.
|
|
103
|
+
*/
|
|
104
|
+
export function getPersistedEvmWallet() {
|
|
105
|
+
const settings = loadSettings();
|
|
106
|
+
if (!settings.privyEvmWalletId || !settings.privyEvmWalletAddress) {
|
|
107
|
+
throw new Error("No Privy EVM wallet found. Run `pc init` to create one.");
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
id: settings.privyEvmWalletId,
|
|
111
|
+
address: settings.privyEvmWalletAddress,
|
|
112
|
+
};
|
|
113
|
+
}
|
package/dist/privy.js
CHANGED
|
@@ -17,7 +17,7 @@ const DUMMY_RECENT_BLOCKHASH = "11111111111111111111111111111111";
|
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
// HTTP helpers
|
|
19
19
|
// ---------------------------------------------------------------------------
|
|
20
|
-
function authHeaders() {
|
|
20
|
+
export function authHeaders() {
|
|
21
21
|
const encoded = Buffer.from(`${PRIVY_APP_ID}:${PRIVY_APP_SECRET}`).toString("base64");
|
|
22
22
|
return {
|
|
23
23
|
Authorization: `Basic ${encoded}`,
|
|
@@ -25,7 +25,7 @@ function authHeaders() {
|
|
|
25
25
|
"Content-Type": "application/json",
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
async function privyFetch(path, init = {}) {
|
|
28
|
+
export async function privyFetch(path, init = {}) {
|
|
29
29
|
const url = `${PRIVY_API_BASE}${path}`;
|
|
30
30
|
const headers = {
|
|
31
31
|
...authHeaders(),
|
package/dist/settings.js
CHANGED
|
@@ -70,6 +70,14 @@ export function setNetwork(network) {
|
|
|
70
70
|
settings.network = network;
|
|
71
71
|
saveSettings(settings);
|
|
72
72
|
}
|
|
73
|
+
export function getServer() {
|
|
74
|
+
return loadSettings().server;
|
|
75
|
+
}
|
|
76
|
+
export function setServer(server) {
|
|
77
|
+
const settings = loadSettings();
|
|
78
|
+
settings.server = server;
|
|
79
|
+
saveSettings(settings);
|
|
80
|
+
}
|
|
73
81
|
export function configPath() {
|
|
74
82
|
return CONFIG_FILE;
|
|
75
83
|
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana Adapter
|
|
3
|
+
*
|
|
4
|
+
* Wraps the existing Anchor client code into the ChainAdapter interface.
|
|
5
|
+
* No logic changes — just reorganization of existing code from index.ts and client.ts.
|
|
6
|
+
*/
|
|
7
|
+
import * as anchor from "@coral-xyz/anchor";
|
|
8
|
+
import bs58 from "bs58";
|
|
9
|
+
import { fromFixedBytes, getAgentPda, getClaimPda, getInvitePda, getProgram, getProtocolPda, getTaskPda, toFixedBytes, } from "./client.js";
|
|
10
|
+
import { provisionPrivyWallet } from "./privy.js";
|
|
11
|
+
import { WALLET_TYPE } from "./config.js";
|
|
12
|
+
const NO_PREREQ_TASK_ID = 0xffffffff;
|
|
13
|
+
const TASK_IS_ACTIVE_OFFSET = 154;
|
|
14
|
+
export class SolanaAdapter {
|
|
15
|
+
chain = "solana";
|
|
16
|
+
serverName;
|
|
17
|
+
serverConfig;
|
|
18
|
+
_program = null;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.serverConfig = config;
|
|
21
|
+
this.serverName = config.name;
|
|
22
|
+
}
|
|
23
|
+
async program() {
|
|
24
|
+
if (!this._program) {
|
|
25
|
+
this._program = await getProgram();
|
|
26
|
+
}
|
|
27
|
+
return this._program;
|
|
28
|
+
}
|
|
29
|
+
async provider() {
|
|
30
|
+
const p = await this.program();
|
|
31
|
+
return p.provider;
|
|
32
|
+
}
|
|
33
|
+
async pubkey() {
|
|
34
|
+
const prov = await this.provider();
|
|
35
|
+
return prov.wallet.publicKey;
|
|
36
|
+
}
|
|
37
|
+
async getWalletAddress() {
|
|
38
|
+
return (await this.pubkey()).toBase58();
|
|
39
|
+
}
|
|
40
|
+
async getAgent(wallet) {
|
|
41
|
+
const p = await this.program();
|
|
42
|
+
const walletPubkey = new anchor.web3.PublicKey(wallet);
|
|
43
|
+
const agentPda = getAgentPda(p.programId, walletPubkey);
|
|
44
|
+
try {
|
|
45
|
+
const acct = await p.account.agentAccount.fetch(agentPda);
|
|
46
|
+
return {
|
|
47
|
+
exists: true,
|
|
48
|
+
wallet,
|
|
49
|
+
clipsBalance: acct.clipsBalance.toNumber(),
|
|
50
|
+
efficiencyTier: acct.efficiencyTier,
|
|
51
|
+
tasksCompleted: acct.tasksCompleted,
|
|
52
|
+
registeredAt: acct.registeredAt?.toNumber?.() ?? acct.registeredAt,
|
|
53
|
+
lastActiveAt: acct.lastActiveAt?.toNumber?.() ?? acct.lastActiveAt,
|
|
54
|
+
invitesSent: acct.invitesSent,
|
|
55
|
+
invitesRedeemed: acct.invitesRedeemed,
|
|
56
|
+
invitedBy: this.isZeroPubkey(acct.invitedBy) ? null : this.asPubkeyStr(acct.invitedBy),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async getTask(taskId) {
|
|
64
|
+
const p = await this.program();
|
|
65
|
+
const taskPda = getTaskPda(p.programId, taskId);
|
|
66
|
+
try {
|
|
67
|
+
const acct = await p.account.taskRecord.fetch(taskPda);
|
|
68
|
+
return this.mapTask(acct);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async getClaim(taskId, agent) {
|
|
75
|
+
const p = await this.program();
|
|
76
|
+
const agentPubkey = new anchor.web3.PublicKey(agent);
|
|
77
|
+
const claimPda = getClaimPda(p.programId, taskId, agentPubkey);
|
|
78
|
+
try {
|
|
79
|
+
const acct = await p.account.claimRecord.fetch(claimPda);
|
|
80
|
+
return {
|
|
81
|
+
exists: true,
|
|
82
|
+
taskId: acct.taskId,
|
|
83
|
+
agent,
|
|
84
|
+
proofCid: fromFixedBytes(acct.proofCid),
|
|
85
|
+
clipsAwarded: acct.clipsAwarded.toNumber(),
|
|
86
|
+
completedAt: acct.completedAt?.toNumber?.() ?? acct.completedAt,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async getInvite(inviter) {
|
|
94
|
+
const p = await this.program();
|
|
95
|
+
const inviterPubkey = new anchor.web3.PublicKey(inviter);
|
|
96
|
+
const invitePda = getInvitePda(p.programId, inviterPubkey);
|
|
97
|
+
try {
|
|
98
|
+
const acct = await p.account.inviteRecord.fetch(invitePda);
|
|
99
|
+
return {
|
|
100
|
+
exists: true,
|
|
101
|
+
inviterWallet: this.asPubkeyStr(acct.inviterWallet),
|
|
102
|
+
invitesRedeemed: acct.invitesRedeemed,
|
|
103
|
+
createdAt: acct.createdAt?.toNumber?.() ?? acct.createdAt,
|
|
104
|
+
isActive: acct.isActive,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async listActiveTasks() {
|
|
112
|
+
const p = await this.program();
|
|
113
|
+
const activeFilter = {
|
|
114
|
+
memcmp: {
|
|
115
|
+
offset: TASK_IS_ACTIVE_OFFSET,
|
|
116
|
+
bytes: bs58.encode(Buffer.from([1])),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
const tasks = await p.account.taskRecord.all([activeFilter]);
|
|
120
|
+
return tasks
|
|
121
|
+
.filter((t) => t.account.currentClaims < t.account.maxClaims)
|
|
122
|
+
.map((t) => this.mapTask(t.account));
|
|
123
|
+
}
|
|
124
|
+
async listDoableTasks(agentWallet, agentTier) {
|
|
125
|
+
const p = await this.program();
|
|
126
|
+
const agentPubkey = new anchor.web3.PublicKey(agentWallet);
|
|
127
|
+
const allActive = await this.listActiveTasks();
|
|
128
|
+
if (allActive.length === 0)
|
|
129
|
+
return [];
|
|
130
|
+
const tierEligible = allActive.filter((t) => agentTier >= t.minTier);
|
|
131
|
+
if (tierEligible.length === 0)
|
|
132
|
+
return [];
|
|
133
|
+
// Batch check claims
|
|
134
|
+
const claimPdas = tierEligible.map((t) => getClaimPda(p.programId, t.taskId, agentPubkey));
|
|
135
|
+
const connection = p.provider.connection;
|
|
136
|
+
const claimInfos = await connection.getMultipleAccountsInfo(claimPdas);
|
|
137
|
+
const unclaimed = tierEligible.filter((_t, idx) => !claimInfos[idx]);
|
|
138
|
+
// Check prerequisites
|
|
139
|
+
const gated = unclaimed.filter((t) => t.requiredTaskId !== null);
|
|
140
|
+
if (gated.length === 0)
|
|
141
|
+
return unclaimed;
|
|
142
|
+
const prereqPdas = gated.map((t) => getClaimPda(p.programId, t.requiredTaskId, agentPubkey));
|
|
143
|
+
const prereqInfos = await connection.getMultipleAccountsInfo(prereqPdas);
|
|
144
|
+
const doableGated = new Set();
|
|
145
|
+
gated.forEach((t, idx) => {
|
|
146
|
+
if (prereqInfos[idx])
|
|
147
|
+
doableGated.add(t.taskId);
|
|
148
|
+
});
|
|
149
|
+
return unclaimed.filter((t) => t.requiredTaskId === null || doableGated.has(t.taskId));
|
|
150
|
+
}
|
|
151
|
+
async registerAgent() {
|
|
152
|
+
const p = await this.program();
|
|
153
|
+
const pk = await this.pubkey();
|
|
154
|
+
const protocolPda = getProtocolPda(p.programId);
|
|
155
|
+
const agentPda = getAgentPda(p.programId, pk);
|
|
156
|
+
await p.methods
|
|
157
|
+
.registerAgent()
|
|
158
|
+
.accounts({
|
|
159
|
+
protocol: protocolPda,
|
|
160
|
+
agentAccount: agentPda,
|
|
161
|
+
agent: pk,
|
|
162
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
163
|
+
})
|
|
164
|
+
.rpc();
|
|
165
|
+
const agent = await p.account.agentAccount.fetch(agentPda);
|
|
166
|
+
return {
|
|
167
|
+
wallet: pk.toBase58(),
|
|
168
|
+
clipsBalance: agent.clipsBalance.toNumber(),
|
|
169
|
+
invitedBy: this.isZeroPubkey(agent.invitedBy) ? null : this.asPubkeyStr(agent.invitedBy),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async registerAgentWithInvite(inviteCode) {
|
|
173
|
+
const p = await this.program();
|
|
174
|
+
const pk = await this.pubkey();
|
|
175
|
+
const inviterPubkey = new anchor.web3.PublicKey(inviteCode);
|
|
176
|
+
const protocolPda = getProtocolPda(p.programId);
|
|
177
|
+
const agentPda = getAgentPda(p.programId, pk);
|
|
178
|
+
const inviterAgentPda = getAgentPda(p.programId, inviterPubkey);
|
|
179
|
+
const invitePda = getInvitePda(p.programId, inviterPubkey);
|
|
180
|
+
await p.methods
|
|
181
|
+
.registerAgentWithInvite(Array.from(inviterPubkey.toBuffer()))
|
|
182
|
+
.accounts({
|
|
183
|
+
protocol: protocolPda,
|
|
184
|
+
agentAccount: agentPda,
|
|
185
|
+
inviterAgent: inviterAgentPda,
|
|
186
|
+
inviteRecord: invitePda,
|
|
187
|
+
agent: pk,
|
|
188
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
189
|
+
})
|
|
190
|
+
.rpc();
|
|
191
|
+
const agent = await p.account.agentAccount.fetch(agentPda);
|
|
192
|
+
return {
|
|
193
|
+
wallet: pk.toBase58(),
|
|
194
|
+
clipsBalance: agent.clipsBalance.toNumber(),
|
|
195
|
+
invitedBy: this.isZeroPubkey(agent.invitedBy) ? null : this.asPubkeyStr(agent.invitedBy),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
async createInvite() {
|
|
199
|
+
const p = await this.program();
|
|
200
|
+
const pk = await this.pubkey();
|
|
201
|
+
const agentPda = getAgentPda(p.programId, pk);
|
|
202
|
+
const invitePda = getInvitePda(p.programId, pk);
|
|
203
|
+
// Check if invite already exists
|
|
204
|
+
try {
|
|
205
|
+
const existing = await p.account.inviteRecord.fetch(invitePda);
|
|
206
|
+
const code = new anchor.web3.PublicKey(existing.inviteCode).toBase58();
|
|
207
|
+
return { inviteCode: code, invitesRedeemed: existing.invitesRedeemed };
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Invite doesn't exist yet — create it
|
|
211
|
+
}
|
|
212
|
+
await p.methods
|
|
213
|
+
.createInvite()
|
|
214
|
+
.accounts({
|
|
215
|
+
protocol: getProtocolPda(p.programId),
|
|
216
|
+
agentAccount: agentPda,
|
|
217
|
+
inviteRecord: invitePda,
|
|
218
|
+
agent: pk,
|
|
219
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
220
|
+
})
|
|
221
|
+
.rpc();
|
|
222
|
+
const invite = await p.account.inviteRecord.fetch(invitePda);
|
|
223
|
+
const code = new anchor.web3.PublicKey(invite.inviteCode).toBase58();
|
|
224
|
+
return { inviteCode: code, invitesRedeemed: invite.invitesRedeemed };
|
|
225
|
+
}
|
|
226
|
+
async submitProof(taskId, proofCid) {
|
|
227
|
+
const p = await this.program();
|
|
228
|
+
const pk = await this.pubkey();
|
|
229
|
+
const prov = await this.provider();
|
|
230
|
+
const taskPda = getTaskPda(p.programId, taskId);
|
|
231
|
+
const agentPda = getAgentPda(p.programId, pk);
|
|
232
|
+
const claimPda = getClaimPda(p.programId, taskId, pk);
|
|
233
|
+
const protocolPda = getProtocolPda(p.programId);
|
|
234
|
+
const task = await p.account.taskRecord.fetch(taskPda);
|
|
235
|
+
const submitBuilder = p.methods
|
|
236
|
+
.submitProof(taskId, toFixedBytes(proofCid, 64))
|
|
237
|
+
.accounts({
|
|
238
|
+
protocol: protocolPda,
|
|
239
|
+
task: taskPda,
|
|
240
|
+
agentAccount: agentPda,
|
|
241
|
+
claim: claimPda,
|
|
242
|
+
agent: pk,
|
|
243
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
244
|
+
});
|
|
245
|
+
if (task.requiredTaskId !== NO_PREREQ_TASK_ID) {
|
|
246
|
+
const prereqClaimPda = getClaimPda(p.programId, task.requiredTaskId, pk);
|
|
247
|
+
const prereqInfo = await prov.connection.getAccountInfo(prereqClaimPda);
|
|
248
|
+
if (!prereqInfo) {
|
|
249
|
+
throw new Error(`Task requires completing task ${task.requiredTaskId} first`);
|
|
250
|
+
}
|
|
251
|
+
submitBuilder.remainingAccounts([
|
|
252
|
+
{ pubkey: prereqClaimPda, isWritable: false, isSigner: false },
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
255
|
+
await submitBuilder.rpc();
|
|
256
|
+
return { clipsAwarded: task.rewardClips.toNumber() };
|
|
257
|
+
}
|
|
258
|
+
async provisionWallet() {
|
|
259
|
+
if (WALLET_TYPE === "privy") {
|
|
260
|
+
await provisionPrivyWallet();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// =========================================================================
|
|
264
|
+
// Private helpers
|
|
265
|
+
// =========================================================================
|
|
266
|
+
mapTask(acct) {
|
|
267
|
+
return {
|
|
268
|
+
taskId: acct.taskId,
|
|
269
|
+
creator: this.asPubkeyStr(acct.creator),
|
|
270
|
+
title: fromFixedBytes(acct.title),
|
|
271
|
+
contentCid: fromFixedBytes(acct.contentCid),
|
|
272
|
+
rewardClips: acct.rewardClips.toNumber(),
|
|
273
|
+
maxClaims: acct.maxClaims,
|
|
274
|
+
currentClaims: acct.currentClaims,
|
|
275
|
+
isActive: acct.isActive,
|
|
276
|
+
createdAt: acct.createdAt?.toNumber?.() ?? acct.createdAt,
|
|
277
|
+
minTier: acct.minTier,
|
|
278
|
+
requiredTaskId: acct.requiredTaskId === NO_PREREQ_TASK_ID ? null : acct.requiredTaskId,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
asPubkeyStr(value) {
|
|
282
|
+
if (typeof value === "string")
|
|
283
|
+
return value;
|
|
284
|
+
if (value instanceof anchor.web3.PublicKey)
|
|
285
|
+
return value.toBase58();
|
|
286
|
+
return new anchor.web3.PublicKey(value).toBase58();
|
|
287
|
+
}
|
|
288
|
+
isZeroPubkey(value) {
|
|
289
|
+
try {
|
|
290
|
+
const pk = value instanceof anchor.web3.PublicKey
|
|
291
|
+
? value
|
|
292
|
+
: new anchor.web3.PublicKey(value);
|
|
293
|
+
return pk.toBuffer().equals(Buffer.alloc(32));
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paper-clip/pc",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"bs58": "^5.0.0",
|
|
29
29
|
"chalk": "^5.6.2",
|
|
30
30
|
"commander": "^12.1.0",
|
|
31
|
+
"ethers": "^6.16.0",
|
|
31
32
|
"ora": "^8.2.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|