@unslept/sdk 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +41 -2
- package/dist/abi.d.ts +1425 -0
- package/dist/abi.js +5 -0
- package/dist/chain.d.ts +67 -0
- package/dist/chain.js +9 -0
- package/dist/client.d.ts +90 -0
- package/dist/client.js +237 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +19 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/viem.d.ts +20 -0
- package/dist/viem.js +134 -0
- package/dist/x402.d.ts +112 -0
- package/dist/x402.js +82 -0
- package/package.json +51 -4
package/dist/viem.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { BaseError, ContractFunctionRevertedError, createPublicClient, createWalletClient, defineChain, http, parseEventLogs, } from "viem";
|
|
2
|
+
import { agentAccountAbi, settlementRouterAbi, unsleptBufferAbi } from "./abi.js";
|
|
3
|
+
import { UnsleptError } from "./errors.js";
|
|
4
|
+
function need(name) {
|
|
5
|
+
const v = process.env[name]?.trim();
|
|
6
|
+
if (!v)
|
|
7
|
+
throw new UnsleptError("CONFIG", `${name} is not set. See .env.example for the Robinhood Chain settings.`);
|
|
8
|
+
return v;
|
|
9
|
+
}
|
|
10
|
+
/** Builds the chain client for the "robinhood-chain" preset from environment variables. Nothing is assumed. */
|
|
11
|
+
export function createViemChainFromEnv(args) {
|
|
12
|
+
if (args.network !== "robinhood-chain") {
|
|
13
|
+
throw new UnsleptError("CONFIG", `Unknown network preset "${args.network}". Pass a chain implementation instead.`);
|
|
14
|
+
}
|
|
15
|
+
const id = Number(need("UNSLEPT_CHAIN_ID"));
|
|
16
|
+
const rpc = need("UNSLEPT_RPC_URL");
|
|
17
|
+
const chain = defineChain({
|
|
18
|
+
id,
|
|
19
|
+
name: process.env.UNSLEPT_CHAIN_NAME ?? "Robinhood Chain",
|
|
20
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
21
|
+
rpcUrls: { default: { http: [rpc] } },
|
|
22
|
+
});
|
|
23
|
+
const account = args.signer;
|
|
24
|
+
if (!account || typeof account !== "object" || !("address" in account)) {
|
|
25
|
+
throw new UnsleptError("CONFIG", "signer must be a viem Account, for example privateKeyToAccount(key).");
|
|
26
|
+
}
|
|
27
|
+
const buffers = need("UNSLEPT_BUFFERS")
|
|
28
|
+
.split(",")
|
|
29
|
+
.map((s) => s.trim());
|
|
30
|
+
return createViemChain({
|
|
31
|
+
agentAccount: args.agentAccount,
|
|
32
|
+
usdg: need("UNSLEPT_USDG"),
|
|
33
|
+
router: need("UNSLEPT_ROUTER"),
|
|
34
|
+
buffers,
|
|
35
|
+
publicClient: createPublicClient({ chain, transport: http(rpc) }),
|
|
36
|
+
walletClient: createWalletClient({ chain, transport: http(rpc), account }),
|
|
37
|
+
account,
|
|
38
|
+
fromBlock: process.env.UNSLEPT_FROM_BLOCK ? BigInt(process.env.UNSLEPT_FROM_BLOCK) : undefined,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const revertMap = {
|
|
42
|
+
PerCallCapExceeded: (a) => new UnsleptError("PER_CALL_CAP", "Payment is above the per-call cap. Raise the cap in the spending policy.", { amount: a[0], cap: a[1] }),
|
|
43
|
+
DailyCapExceeded: (a) => new UnsleptError("DAILY_CAP", "Payment would pass today's cap. Wait for 00:00 UTC or raise the daily cap.", { spent: a[0], amount: a[1], cap: a[2] }),
|
|
44
|
+
PayeeNotAllowed: (a) => new UnsleptError("PAYEE_NOT_ALLOWED", `Payee ${String(a[0])} is not on the allowlist. Add it in the spending policy.`),
|
|
45
|
+
AgentPaused: () => new UnsleptError("AGENT_PAUSED", "This agent is paused. Its owner can resume it in the Unslept app."),
|
|
46
|
+
AgentRevoked: () => new UnsleptError("AGENT_REVOKED", "This agent was revoked. Create a new agent account."),
|
|
47
|
+
InsufficientCollateral: (a) => new UnsleptError("INSUFFICIENT_COLLATERAL", "The agent does not hold enough to cover this payment. Fund the agent account.", { debtAfter: a[0], value: a[1] }),
|
|
48
|
+
InsufficientBufferLiquidity: (a) => new UnsleptError("INSUFFICIENT_BUFFER_LIQUIDITY", "The buffer is short of liquidity right now. Retry shortly; it refills from parked positions.", { amount: a[0], available: a[1] }),
|
|
49
|
+
};
|
|
50
|
+
function mapRevert(err) {
|
|
51
|
+
if (err instanceof BaseError) {
|
|
52
|
+
const r = err.walk((e) => e instanceof ContractFunctionRevertedError);
|
|
53
|
+
if (r instanceof ContractFunctionRevertedError && r.data?.errorName) {
|
|
54
|
+
const f = revertMap[r.data.errorName];
|
|
55
|
+
if (f)
|
|
56
|
+
throw f(r.data.args ?? []);
|
|
57
|
+
throw new UnsleptError("SETTLEMENT_FAILED", `Settlement reverted with ${r.data.errorName}.`, { args: r.data.args });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
throw new UnsleptError("SETTLEMENT_FAILED", `Settlement transaction failed: ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
export function createViemChain(cfg) {
|
|
63
|
+
const pc = cfg.publicClient;
|
|
64
|
+
const acct = { address: cfg.agentAccount, abi: agentAccountAbi };
|
|
65
|
+
const chainId = pc.chain?.id;
|
|
66
|
+
if (!chainId)
|
|
67
|
+
throw new UnsleptError("CONFIG", "The public client has no chain configured.");
|
|
68
|
+
return {
|
|
69
|
+
chainId,
|
|
70
|
+
usdg: cfg.usdg,
|
|
71
|
+
async isPayeeAllowed(payee, domain) {
|
|
72
|
+
return pc.readContract({ ...acct, functionName: "isPayeeAllowed", args: [payee, domain] });
|
|
73
|
+
},
|
|
74
|
+
async getAccountState() {
|
|
75
|
+
const [policy, remainingToday, paused, revoked, liquid, parked, debt] = await Promise.all([
|
|
76
|
+
pc.readContract({ ...acct, functionName: "policy" }),
|
|
77
|
+
pc.readContract({ ...acct, functionName: "remainingToday" }),
|
|
78
|
+
pc.readContract({ ...acct, functionName: "paused" }),
|
|
79
|
+
pc.readContract({ ...acct, functionName: "revoked" }),
|
|
80
|
+
pc.readContract({ ...acct, functionName: "liquid" }),
|
|
81
|
+
pc.readContract({ ...acct, functionName: "parkedValue" }),
|
|
82
|
+
Promise.all(cfg.buffers.map((b) => pc.readContract({ address: b, abi: unsleptBufferAbi, functionName: "debtOf", args: [cfg.agentAccount] }))).then((d) => d.reduce((x, y) => x + y, 0n)),
|
|
83
|
+
]);
|
|
84
|
+
return { policy: { perCallCap: policy[0], dailyCap: policy[1] }, remainingToday, paused, revoked, liquid, parked, debt };
|
|
85
|
+
},
|
|
86
|
+
async requestPayment(input) {
|
|
87
|
+
try {
|
|
88
|
+
const { request } = await pc.simulateContract({
|
|
89
|
+
...acct,
|
|
90
|
+
functionName: "requestPayment",
|
|
91
|
+
args: [input],
|
|
92
|
+
account: cfg.account,
|
|
93
|
+
});
|
|
94
|
+
const hash = await cfg.walletClient.writeContract(request);
|
|
95
|
+
const receipt = await pc.waitForTransactionReceipt({ hash });
|
|
96
|
+
if (receipt.status !== "success")
|
|
97
|
+
throw new UnsleptError("SETTLEMENT_FAILED", `Transaction ${hash} reverted.`);
|
|
98
|
+
const [ev] = parseEventLogs({ abi: settlementRouterAbi, logs: receipt.logs, eventName: "Settled" });
|
|
99
|
+
if (!ev)
|
|
100
|
+
throw new UnsleptError("SETTLEMENT_FAILED", `Transaction ${hash} has no Settled event.`);
|
|
101
|
+
return { transactionHash: hash, fee: ev.args.fee, buffer: ev.args.buffer };
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
if (e instanceof UnsleptError)
|
|
105
|
+
throw e;
|
|
106
|
+
mapRevert(e);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
async setPolicy(policy) {
|
|
110
|
+
const { request } = await pc.simulateContract({ ...acct, functionName: "setPolicy", args: [policy], account: cfg.account });
|
|
111
|
+
return cfg.walletClient.writeContract(request);
|
|
112
|
+
},
|
|
113
|
+
async getPayments(opts) {
|
|
114
|
+
const logs = await pc.getContractEvents({
|
|
115
|
+
address: cfg.router,
|
|
116
|
+
abi: settlementRouterAbi,
|
|
117
|
+
eventName: "Settled",
|
|
118
|
+
args: { agent: cfg.agentAccount },
|
|
119
|
+
fromBlock: cfg.fromBlock ?? 0n,
|
|
120
|
+
});
|
|
121
|
+
const rows = logs.map((l) => ({
|
|
122
|
+
paymentId: l.args.paymentId,
|
|
123
|
+
payee: l.args.payee,
|
|
124
|
+
amount: l.args.amount,
|
|
125
|
+
fee: l.args.fee,
|
|
126
|
+
endpoint: l.args.endpoint,
|
|
127
|
+
transactionHash: l.transactionHash,
|
|
128
|
+
blockNumber: l.blockNumber,
|
|
129
|
+
}));
|
|
130
|
+
rows.reverse();
|
|
131
|
+
return opts?.limit ? rows.slice(0, opts.limit) : rows;
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
package/dist/x402.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x402 v2 HTTP transport types and helpers.
|
|
3
|
+
* Field names follow the x402 v2 specification (x402-foundation/x402, specs/x402-specification-v2.md)
|
|
4
|
+
* and the HTTP transport (specs/transports-v2/http.md): PAYMENT-REQUIRED, PAYMENT-SIGNATURE and
|
|
5
|
+
* PAYMENT-RESPONSE headers, each carrying base64-encoded JSON.
|
|
6
|
+
*/
|
|
7
|
+
export declare const X402_VERSION: 2;
|
|
8
|
+
export declare const HEADER_REQUIRED = "PAYMENT-REQUIRED";
|
|
9
|
+
export declare const HEADER_SIGNATURE = "PAYMENT-SIGNATURE";
|
|
10
|
+
export declare const HEADER_RESPONSE = "PAYMENT-RESPONSE";
|
|
11
|
+
/** Standard x402 scheme. Settled by a facilitator from an EIP-3009 authorization. */
|
|
12
|
+
export declare const SCHEME_EXACT = "exact";
|
|
13
|
+
/**
|
|
14
|
+
* Unslept scheme for merchants that opt in: the buffer pays `payTo` on-chain first and the client sends
|
|
15
|
+
* the transaction hash. The merchant (or its facilitator) verifies the SettlementRouter `Settled` event.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SCHEME_UNSLEPT = "unslept-direct";
|
|
18
|
+
export interface ResourceInfo {
|
|
19
|
+
url: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
mimeType?: string;
|
|
22
|
+
serviceName?: string;
|
|
23
|
+
tags?: string[];
|
|
24
|
+
iconUrl?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface PaymentRequirements {
|
|
27
|
+
scheme: string;
|
|
28
|
+
network: string;
|
|
29
|
+
amount: string;
|
|
30
|
+
asset: string;
|
|
31
|
+
payTo: string;
|
|
32
|
+
maxTimeoutSeconds: number;
|
|
33
|
+
extra?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
export interface PaymentRequired {
|
|
36
|
+
x402Version: number;
|
|
37
|
+
error?: string;
|
|
38
|
+
resource: ResourceInfo;
|
|
39
|
+
accepts: PaymentRequirements[];
|
|
40
|
+
extensions?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
export interface PaymentPayload<P = unknown> {
|
|
43
|
+
x402Version: number;
|
|
44
|
+
resource?: ResourceInfo;
|
|
45
|
+
accepted: PaymentRequirements;
|
|
46
|
+
payload: P;
|
|
47
|
+
extensions?: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
export interface Eip3009Authorization {
|
|
50
|
+
from: string;
|
|
51
|
+
to: string;
|
|
52
|
+
value: string;
|
|
53
|
+
validAfter: string;
|
|
54
|
+
validBefore: string;
|
|
55
|
+
nonce: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ExactEip3009Payload {
|
|
58
|
+
signature: string;
|
|
59
|
+
authorization: Eip3009Authorization;
|
|
60
|
+
}
|
|
61
|
+
export interface UnsleptDirectPayload {
|
|
62
|
+
transaction: string;
|
|
63
|
+
paymentId: string;
|
|
64
|
+
agentAccount: string;
|
|
65
|
+
}
|
|
66
|
+
export interface SettlementResponse {
|
|
67
|
+
success: boolean;
|
|
68
|
+
errorReason?: string;
|
|
69
|
+
payer?: string;
|
|
70
|
+
transaction: string;
|
|
71
|
+
network: string;
|
|
72
|
+
}
|
|
73
|
+
export declare const eip3009Types: {
|
|
74
|
+
readonly TransferWithAuthorization: readonly [{
|
|
75
|
+
readonly name: "from";
|
|
76
|
+
readonly type: "address";
|
|
77
|
+
}, {
|
|
78
|
+
readonly name: "to";
|
|
79
|
+
readonly type: "address";
|
|
80
|
+
}, {
|
|
81
|
+
readonly name: "value";
|
|
82
|
+
readonly type: "uint256";
|
|
83
|
+
}, {
|
|
84
|
+
readonly name: "validAfter";
|
|
85
|
+
readonly type: "uint256";
|
|
86
|
+
}, {
|
|
87
|
+
readonly name: "validBefore";
|
|
88
|
+
readonly type: "uint256";
|
|
89
|
+
}, {
|
|
90
|
+
readonly name: "nonce";
|
|
91
|
+
readonly type: "bytes32";
|
|
92
|
+
}];
|
|
93
|
+
};
|
|
94
|
+
export declare function encodeHeader(value: unknown): string;
|
|
95
|
+
export declare function decodeHeader<T>(value: string): T;
|
|
96
|
+
export declare class X402FormatError extends Error {
|
|
97
|
+
readonly code = "X402_FORMAT";
|
|
98
|
+
}
|
|
99
|
+
/** Reads the PaymentRequired object from a 402 response. */
|
|
100
|
+
export declare function readPaymentRequired(res: {
|
|
101
|
+
headers: {
|
|
102
|
+
get(name: string): string | null;
|
|
103
|
+
};
|
|
104
|
+
}): PaymentRequired;
|
|
105
|
+
export interface SelectOptions {
|
|
106
|
+
network: string;
|
|
107
|
+
asset: string;
|
|
108
|
+
schemes: string[];
|
|
109
|
+
}
|
|
110
|
+
/** Picks the first requirement matching our network and asset, by scheme preference. */
|
|
111
|
+
export declare function selectRequirement(pr: PaymentRequired, opts: SelectOptions): PaymentRequirements | undefined;
|
|
112
|
+
export declare function chainIdFromCaip2(network: string): number;
|
package/dist/x402.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x402 v2 HTTP transport types and helpers.
|
|
3
|
+
* Field names follow the x402 v2 specification (x402-foundation/x402, specs/x402-specification-v2.md)
|
|
4
|
+
* and the HTTP transport (specs/transports-v2/http.md): PAYMENT-REQUIRED, PAYMENT-SIGNATURE and
|
|
5
|
+
* PAYMENT-RESPONSE headers, each carrying base64-encoded JSON.
|
|
6
|
+
*/
|
|
7
|
+
export const X402_VERSION = 2;
|
|
8
|
+
export const HEADER_REQUIRED = "PAYMENT-REQUIRED";
|
|
9
|
+
export const HEADER_SIGNATURE = "PAYMENT-SIGNATURE";
|
|
10
|
+
export const HEADER_RESPONSE = "PAYMENT-RESPONSE";
|
|
11
|
+
/** Standard x402 scheme. Settled by a facilitator from an EIP-3009 authorization. */
|
|
12
|
+
export const SCHEME_EXACT = "exact";
|
|
13
|
+
/**
|
|
14
|
+
* Unslept scheme for merchants that opt in: the buffer pays `payTo` on-chain first and the client sends
|
|
15
|
+
* the transaction hash. The merchant (or its facilitator) verifies the SettlementRouter `Settled` event.
|
|
16
|
+
*/
|
|
17
|
+
export const SCHEME_UNSLEPT = "unslept-direct";
|
|
18
|
+
export const eip3009Types = {
|
|
19
|
+
TransferWithAuthorization: [
|
|
20
|
+
{ name: "from", type: "address" },
|
|
21
|
+
{ name: "to", type: "address" },
|
|
22
|
+
{ name: "value", type: "uint256" },
|
|
23
|
+
{ name: "validAfter", type: "uint256" },
|
|
24
|
+
{ name: "validBefore", type: "uint256" },
|
|
25
|
+
{ name: "nonce", type: "bytes32" },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
function toBase64(s) {
|
|
29
|
+
let bin = "";
|
|
30
|
+
for (const byte of new TextEncoder().encode(s))
|
|
31
|
+
bin += String.fromCharCode(byte);
|
|
32
|
+
return btoa(bin);
|
|
33
|
+
}
|
|
34
|
+
function fromBase64(b64) {
|
|
35
|
+
return new TextDecoder().decode(Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)));
|
|
36
|
+
}
|
|
37
|
+
export function encodeHeader(value) {
|
|
38
|
+
return toBase64(JSON.stringify(value));
|
|
39
|
+
}
|
|
40
|
+
export function decodeHeader(value) {
|
|
41
|
+
return JSON.parse(fromBase64(value.trim()));
|
|
42
|
+
}
|
|
43
|
+
export class X402FormatError extends Error {
|
|
44
|
+
code = "X402_FORMAT";
|
|
45
|
+
}
|
|
46
|
+
/** Reads the PaymentRequired object from a 402 response. */
|
|
47
|
+
export function readPaymentRequired(res) {
|
|
48
|
+
const header = res.headers.get(HEADER_REQUIRED);
|
|
49
|
+
if (!header) {
|
|
50
|
+
throw new X402FormatError("The server answered 402 without a PAYMENT-REQUIRED header. Only x402 v2 servers are supported.");
|
|
51
|
+
}
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = decodeHeader(header);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
throw new X402FormatError("The PAYMENT-REQUIRED header is not valid base64-encoded JSON.");
|
|
58
|
+
}
|
|
59
|
+
if (parsed.x402Version !== X402_VERSION) {
|
|
60
|
+
throw new X402FormatError(`Unsupported x402Version ${parsed.x402Version}. Expected ${X402_VERSION}.`);
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(parsed.accepts) || parsed.accepts.length === 0) {
|
|
63
|
+
throw new X402FormatError("The PAYMENT-REQUIRED header lists no accepted payment methods.");
|
|
64
|
+
}
|
|
65
|
+
return parsed;
|
|
66
|
+
}
|
|
67
|
+
/** Picks the first requirement matching our network and asset, by scheme preference. */
|
|
68
|
+
export function selectRequirement(pr, opts) {
|
|
69
|
+
const asset = opts.asset.toLowerCase();
|
|
70
|
+
for (const scheme of opts.schemes) {
|
|
71
|
+
const match = pr.accepts.find((r) => r.scheme === scheme && r.network === opts.network && r.asset.toLowerCase() === asset);
|
|
72
|
+
if (match)
|
|
73
|
+
return match;
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
export function chainIdFromCaip2(network) {
|
|
78
|
+
const m = /^eip155:(\d+)$/.exec(network);
|
|
79
|
+
if (!m)
|
|
80
|
+
throw new X402FormatError(`Network ${network} is not an EVM CAIP-2 identifier.`);
|
|
81
|
+
return Number(m[1]);
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unslept/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pay x402 paywalls from an Unslept agent account on Robinhood Chain.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./viem": {
|
|
15
|
+
"types": "./dist/viem.d.ts",
|
|
16
|
+
"import": "./dist/viem.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"README.md",
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -p tsconfig.json",
|
|
27
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
28
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"viem": "^2.21.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"viem": {
|
|
35
|
+
"optional": true
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
40
|
+
"tsx": "^4.19.0",
|
|
41
|
+
"typescript": "^5.6.0",
|
|
42
|
+
"viem": "^2.21.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/Unsleptxyz/Unslept.git",
|
|
50
|
+
"directory": "packages/sdk-ts"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://unslept-o29g.vercel.app/docs"
|
|
53
|
+
}
|