@multiagency/sdk 0.3.1 → 0.4.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/CHANGES.md +25 -0
- package/dist/contract/app-access.d.ts +21 -1
- package/dist/contract/app-access.js +18 -2
- package/dist/contract/app-client.d.ts +11 -6
- package/dist/contract/app-client.js +5 -1
- package/dist/contract/app-consent.d.ts +7 -1
- package/dist/contract/app-consent.js +6 -3
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.js +1 -0
- package/dist/contract/owner-capabilities.d.ts +5 -0
- package/dist/contract/owner-capabilities.js +2 -0
- package/dist/contract/owner-contract.d.ts +4 -2
- package/dist/contract/owner-inputs.d.ts +8 -0
- package/dist/contract/owner-outputs.d.ts +364 -0
- package/dist/contract/owner-outputs.js +10 -1
- package/dist/contract/package-version.d.ts +1 -1
- package/dist/contract/package-version.js +1 -1
- package/dist/contract/permissions.js +6 -3
- package/dist/contract/treasury-proposals.d.ts +193 -0
- package/dist/contract/treasury-proposals.js +102 -0
- package/package.json +1 -1
- package/provenance.json +20 -18
- package/skills/app-server/SKILL.md +3 -3
- package/skills/getting-started/SKILL.md +4 -3
- package/skills/owner-workflows/SKILL.md +3 -1
- package/skills/payee-workflows/SKILL.md +1 -1
- package/skills/treasury-app/SKILL.md +117 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Treasury proposals (docs/enforcement.md, "Treasury proposals"): an app proposes swaps now; the owner approves their
|
|
4
|
+
* exact terms later by signing `proposalText` (NEP-413, recipient the platform host); the platform executes them once,
|
|
5
|
+
* through the same plan execution as any saved plan, under the owner's policy. Pure schemas and the text builder,
|
|
6
|
+
* shared by the platform and apps, so an app can show and check the text it relays.
|
|
7
|
+
*/
|
|
8
|
+
/** At most this long between proposing and expiry: Sputnik's default `proposal_period`. */
|
|
9
|
+
export declare const MAX_PROPOSAL_MS: number;
|
|
10
|
+
/** Proposals per agent neither claimed nor closed; another is refused. */
|
|
11
|
+
export declare const MAX_OPEN_PROPOSALS = 32;
|
|
12
|
+
export declare const MAX_PROPOSAL_LEGS = 8;
|
|
13
|
+
/** The platform's assets as the owner's text names them. */
|
|
14
|
+
export declare const PROPOSAL_TOKENS: Record<string, {
|
|
15
|
+
symbol: string;
|
|
16
|
+
decimals: number;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const proposalId: z.ZodString;
|
|
19
|
+
/** A swap an app chooses: what it spends, buys, and optionally the least it accepts (raised to the quote's floor). */
|
|
20
|
+
export declare const proposalSwap: z.ZodObject<{
|
|
21
|
+
from: z.ZodEnum<{
|
|
22
|
+
[x: string]: string;
|
|
23
|
+
}>;
|
|
24
|
+
to: z.ZodEnum<{
|
|
25
|
+
[x: string]: string;
|
|
26
|
+
}>;
|
|
27
|
+
amount: z.ZodString;
|
|
28
|
+
minAmountOut: z.ZodOptional<z.ZodString>;
|
|
29
|
+
}, z.core.$strict>;
|
|
30
|
+
/**
|
|
31
|
+
* `legs`: the app's chosen swaps, in order, or `"strategy"` for the legs a rebalance toward the owner's strategy picks.
|
|
32
|
+
* `expiresAt`: at most `MAX_PROPOSAL_MS` ahead; the proposal may be approved and executed until then.
|
|
33
|
+
*/
|
|
34
|
+
export declare const proposalCreateInput: z.ZodObject<{
|
|
35
|
+
requestId: z.ZodString;
|
|
36
|
+
legs: z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
|
|
37
|
+
from: z.ZodEnum<{
|
|
38
|
+
[x: string]: string;
|
|
39
|
+
}>;
|
|
40
|
+
to: z.ZodEnum<{
|
|
41
|
+
[x: string]: string;
|
|
42
|
+
}>;
|
|
43
|
+
amount: z.ZodString;
|
|
44
|
+
minAmountOut: z.ZodOptional<z.ZodString>;
|
|
45
|
+
}, z.core.$strict>>, z.ZodLiteral<"strategy">]>;
|
|
46
|
+
expiresAt: z.ZodISODateTime;
|
|
47
|
+
note: z.ZodOptional<z.ZodString>;
|
|
48
|
+
}, z.core.$strict>;
|
|
49
|
+
/** The owner's NEP-413 signature over the proposal's text, as the app relays it. */
|
|
50
|
+
export declare const proposalSignature: z.ZodObject<{
|
|
51
|
+
message: z.ZodString;
|
|
52
|
+
nonce: z.ZodString;
|
|
53
|
+
publicKey: z.ZodString;
|
|
54
|
+
signature: z.ZodString;
|
|
55
|
+
}, z.core.$strict>;
|
|
56
|
+
export declare const proposalSigned: z.ZodObject<{
|
|
57
|
+
id: z.ZodString;
|
|
58
|
+
signed: z.ZodObject<{
|
|
59
|
+
message: z.ZodString;
|
|
60
|
+
nonce: z.ZodString;
|
|
61
|
+
publicKey: z.ZodString;
|
|
62
|
+
signature: z.ZodString;
|
|
63
|
+
}, z.core.$strict>;
|
|
64
|
+
}, z.core.$strict>;
|
|
65
|
+
export declare const proposalRef: z.ZodObject<{
|
|
66
|
+
id: z.ZodString;
|
|
67
|
+
}, z.core.$strict>;
|
|
68
|
+
export declare const ProposalState: z.ZodEnum<{
|
|
69
|
+
approved: "approved";
|
|
70
|
+
executed: "executed";
|
|
71
|
+
executing: "executing";
|
|
72
|
+
expired: "expired";
|
|
73
|
+
invalidated: "invalidated";
|
|
74
|
+
proposed: "proposed";
|
|
75
|
+
rejected: "rejected";
|
|
76
|
+
stopped: "stopped";
|
|
77
|
+
unknown: "unknown";
|
|
78
|
+
withdrawn: "withdrawn";
|
|
79
|
+
}>;
|
|
80
|
+
export type ProposalState = z.infer<typeof ProposalState>;
|
|
81
|
+
/** A proposal as the platform shows it: its signed terms, its state, and what the journal holds for its execution. */
|
|
82
|
+
export declare const proposalView: z.ZodObject<{
|
|
83
|
+
id: z.ZodString;
|
|
84
|
+
requestId: z.ZodString;
|
|
85
|
+
agentId: z.ZodString;
|
|
86
|
+
deployment: z.ZodString;
|
|
87
|
+
organization: z.ZodString;
|
|
88
|
+
source: z.ZodEnum<{
|
|
89
|
+
external: "external";
|
|
90
|
+
rebalance: "rebalance";
|
|
91
|
+
}>;
|
|
92
|
+
legs: z.ZodArray<z.ZodObject<{
|
|
93
|
+
from: z.ZodString;
|
|
94
|
+
to: z.ZodString;
|
|
95
|
+
amount: z.ZodString;
|
|
96
|
+
minAmountOut: z.ZodString;
|
|
97
|
+
}, z.core.$loose>>;
|
|
98
|
+
quoted: z.ZodArray<z.ZodObject<{
|
|
99
|
+
amountOut: z.ZodString;
|
|
100
|
+
costBps: z.ZodNumber;
|
|
101
|
+
usdIn: z.ZodNumber;
|
|
102
|
+
}, z.core.$loose>>;
|
|
103
|
+
policyRevision: z.ZodNullable<z.ZodNumber>;
|
|
104
|
+
policyDigest: z.ZodNullable<z.ZodString>;
|
|
105
|
+
strategyDigest: z.ZodString;
|
|
106
|
+
createdAt: z.ZodString;
|
|
107
|
+
expiresAt: z.ZodString;
|
|
108
|
+
note: z.ZodNullable<z.ZodString>;
|
|
109
|
+
sha256: z.ZodString;
|
|
110
|
+
state: z.ZodEnum<{
|
|
111
|
+
approved: "approved";
|
|
112
|
+
executed: "executed";
|
|
113
|
+
executing: "executing";
|
|
114
|
+
expired: "expired";
|
|
115
|
+
invalidated: "invalidated";
|
|
116
|
+
proposed: "proposed";
|
|
117
|
+
rejected: "rejected";
|
|
118
|
+
stopped: "stopped";
|
|
119
|
+
unknown: "unknown";
|
|
120
|
+
withdrawn: "withdrawn";
|
|
121
|
+
}>;
|
|
122
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
123
|
+
approvedAt: z.ZodNullable<z.ZodString>;
|
|
124
|
+
planId: z.ZodNullable<z.ZodString>;
|
|
125
|
+
operations: z.ZodArray<z.ZodObject<{
|
|
126
|
+
method: z.ZodOptional<z.ZodString>;
|
|
127
|
+
status: z.ZodNullable<z.ZodString>;
|
|
128
|
+
correlationId: z.ZodNullable<z.ZodString>;
|
|
129
|
+
refused: z.ZodNullable<z.ZodString>;
|
|
130
|
+
}, z.core.$loose>>;
|
|
131
|
+
}, z.core.$loose>;
|
|
132
|
+
export type ProposalView = z.infer<typeof proposalView>;
|
|
133
|
+
export declare const proposalList: z.ZodObject<{
|
|
134
|
+
proposals: z.ZodArray<z.ZodObject<{
|
|
135
|
+
id: z.ZodString;
|
|
136
|
+
requestId: z.ZodString;
|
|
137
|
+
agentId: z.ZodString;
|
|
138
|
+
deployment: z.ZodString;
|
|
139
|
+
organization: z.ZodString;
|
|
140
|
+
source: z.ZodEnum<{
|
|
141
|
+
external: "external";
|
|
142
|
+
rebalance: "rebalance";
|
|
143
|
+
}>;
|
|
144
|
+
legs: z.ZodArray<z.ZodObject<{
|
|
145
|
+
from: z.ZodString;
|
|
146
|
+
to: z.ZodString;
|
|
147
|
+
amount: z.ZodString;
|
|
148
|
+
minAmountOut: z.ZodString;
|
|
149
|
+
}, z.core.$loose>>;
|
|
150
|
+
quoted: z.ZodArray<z.ZodObject<{
|
|
151
|
+
amountOut: z.ZodString;
|
|
152
|
+
costBps: z.ZodNumber;
|
|
153
|
+
usdIn: z.ZodNumber;
|
|
154
|
+
}, z.core.$loose>>;
|
|
155
|
+
policyRevision: z.ZodNullable<z.ZodNumber>;
|
|
156
|
+
policyDigest: z.ZodNullable<z.ZodString>;
|
|
157
|
+
strategyDigest: z.ZodString;
|
|
158
|
+
createdAt: z.ZodString;
|
|
159
|
+
expiresAt: z.ZodString;
|
|
160
|
+
note: z.ZodNullable<z.ZodString>;
|
|
161
|
+
sha256: z.ZodString;
|
|
162
|
+
state: z.ZodEnum<{
|
|
163
|
+
approved: "approved";
|
|
164
|
+
executed: "executed";
|
|
165
|
+
executing: "executing";
|
|
166
|
+
expired: "expired";
|
|
167
|
+
invalidated: "invalidated";
|
|
168
|
+
proposed: "proposed";
|
|
169
|
+
rejected: "rejected";
|
|
170
|
+
stopped: "stopped";
|
|
171
|
+
unknown: "unknown";
|
|
172
|
+
withdrawn: "withdrawn";
|
|
173
|
+
}>;
|
|
174
|
+
reason: z.ZodNullable<z.ZodString>;
|
|
175
|
+
approvedAt: z.ZodNullable<z.ZodString>;
|
|
176
|
+
planId: z.ZodNullable<z.ZodString>;
|
|
177
|
+
operations: z.ZodArray<z.ZodObject<{
|
|
178
|
+
method: z.ZodOptional<z.ZodString>;
|
|
179
|
+
status: z.ZodNullable<z.ZodString>;
|
|
180
|
+
correlationId: z.ZodNullable<z.ZodString>;
|
|
181
|
+
refused: z.ZodNullable<z.ZodString>;
|
|
182
|
+
}, z.core.$loose>>;
|
|
183
|
+
}, z.core.$loose>>;
|
|
184
|
+
}, z.core.$loose>;
|
|
185
|
+
/** A raw amount in whole units, exact, without trailing zeros. */
|
|
186
|
+
export declare function tokenAmount(raw: string, decimals: number): string;
|
|
187
|
+
type Terms = Pick<ProposalView, "id" | "agentId" | "deployment" | "organization" | "legs" | "policyRevision" | "policyDigest" | "strategyDigest" | "expiresAt" | "sha256">;
|
|
188
|
+
/**
|
|
189
|
+
* The exact text the owner signs to approve (or reject) a proposal: the platform accepts only these bytes. The host is
|
|
190
|
+
* the NEP-413 recipient, so it isn't repeated here.
|
|
191
|
+
*/
|
|
192
|
+
export declare function proposalText(proposal: Terms, verdict: "approve" | "reject"): string;
|
|
193
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ASSETS } from "./assets.js";
|
|
3
|
+
import { payoutRequestId } from "./inputs.js";
|
|
4
|
+
/**
|
|
5
|
+
* Treasury proposals (docs/enforcement.md, "Treasury proposals"): an app proposes swaps now; the owner approves their
|
|
6
|
+
* exact terms later by signing `proposalText` (NEP-413, recipient the platform host); the platform executes them once,
|
|
7
|
+
* through the same plan execution as any saved plan, under the owner's policy. Pure schemas and the text builder,
|
|
8
|
+
* shared by the platform and apps, so an app can show and check the text it relays.
|
|
9
|
+
*/
|
|
10
|
+
/** At most this long between proposing and expiry: Sputnik's default `proposal_period`. */
|
|
11
|
+
export const MAX_PROPOSAL_MS = 7 * 86_400_000;
|
|
12
|
+
/** Proposals per agent neither claimed nor closed; another is refused. */
|
|
13
|
+
export const MAX_OPEN_PROPOSALS = 32;
|
|
14
|
+
export const MAX_PROPOSAL_LEGS = 8;
|
|
15
|
+
/** The platform's assets as the owner's text names them. */
|
|
16
|
+
export const PROPOSAL_TOKENS = {
|
|
17
|
+
[ASSETS.USDC]: { symbol: "USDC", decimals: 6 },
|
|
18
|
+
[ASSETS.wNEAR]: { symbol: "wNEAR", decimals: 24 },
|
|
19
|
+
[ASSETS.ETH]: { symbol: "ETH", decimals: 18 },
|
|
20
|
+
};
|
|
21
|
+
const asset = z.enum(Object.values(ASSETS));
|
|
22
|
+
const amount = z.string().regex(/^[0-9]{1,40}$/).refine(value => BigInt(value) > 0n, "Amount must be positive");
|
|
23
|
+
export const proposalId = z.string().regex(/^[a-f0-9]{32}$/);
|
|
24
|
+
/** A swap an app chooses: what it spends, buys, and optionally the least it accepts (raised to the quote's floor). */
|
|
25
|
+
export const proposalSwap = z.strictObject({ from: asset, to: asset, amount, minAmountOut: amount.optional() })
|
|
26
|
+
.refine(swap => swap.from !== swap.to, "Swap assets must differ");
|
|
27
|
+
/**
|
|
28
|
+
* `legs`: the app's chosen swaps, in order, or `"strategy"` for the legs a rebalance toward the owner's strategy picks.
|
|
29
|
+
* `expiresAt`: at most `MAX_PROPOSAL_MS` ahead; the proposal may be approved and executed until then.
|
|
30
|
+
*/
|
|
31
|
+
export const proposalCreateInput = z.strictObject({
|
|
32
|
+
requestId: payoutRequestId,
|
|
33
|
+
legs: z.union([z.array(proposalSwap).min(1).max(MAX_PROPOSAL_LEGS), z.literal("strategy")]),
|
|
34
|
+
expiresAt: z.iso.datetime(),
|
|
35
|
+
note: z.string().max(280).optional(),
|
|
36
|
+
});
|
|
37
|
+
/** The owner's NEP-413 signature over the proposal's text, as the app relays it. */
|
|
38
|
+
export const proposalSignature = z.strictObject({
|
|
39
|
+
message: z.string().min(1).max(4_096),
|
|
40
|
+
nonce: z.string().min(1).max(64),
|
|
41
|
+
publicKey: z.string().regex(/^ed25519:[1-9A-HJ-NP-Za-km-z]{32,64}$/),
|
|
42
|
+
signature: z.string().min(1).max(200),
|
|
43
|
+
});
|
|
44
|
+
export const proposalSigned = z.strictObject({ id: proposalId, signed: proposalSignature });
|
|
45
|
+
export const proposalRef = z.strictObject({ id: proposalId });
|
|
46
|
+
export const ProposalState = z.enum(["proposed", "approved", "executing", "executed", "stopped", "unknown", "rejected", "withdrawn", "expired", "invalidated"]);
|
|
47
|
+
const leg = z.looseObject({ from: z.string(), to: z.string(), amount: z.string(), minAmountOut: z.string() });
|
|
48
|
+
/** A proposal as the platform shows it: its signed terms, its state, and what the journal holds for its execution. */
|
|
49
|
+
export const proposalView = z.looseObject({
|
|
50
|
+
id: z.string(),
|
|
51
|
+
requestId: z.string(),
|
|
52
|
+
agentId: z.string(),
|
|
53
|
+
deployment: z.string(),
|
|
54
|
+
organization: z.string(),
|
|
55
|
+
source: z.enum(["external", "rebalance"]),
|
|
56
|
+
/** The terms the owner signs: each leg's floor is the least it may return. */
|
|
57
|
+
legs: z.array(leg),
|
|
58
|
+
/** Quotes when proposed: shown, never relied on. */
|
|
59
|
+
quoted: z.array(z.looseObject({ amountOut: z.string(), costBps: z.number(), usdIn: z.number() })),
|
|
60
|
+
policyRevision: z.number().nullable(),
|
|
61
|
+
policyDigest: z.string().nullable(),
|
|
62
|
+
strategyDigest: z.string(),
|
|
63
|
+
createdAt: z.string(),
|
|
64
|
+
expiresAt: z.string(),
|
|
65
|
+
note: z.string().nullable(),
|
|
66
|
+
/** sha256 of the canonical record: named in the owner's text. */
|
|
67
|
+
sha256: z.string(),
|
|
68
|
+
state: ProposalState,
|
|
69
|
+
/** Why it was closed (rejected, withdrawn, expired, invalidated), or null. */
|
|
70
|
+
reason: z.string().nullable(),
|
|
71
|
+
approvedAt: z.string().nullable(),
|
|
72
|
+
/** The plan its execution ran, once claimed. */
|
|
73
|
+
planId: z.string().nullable(),
|
|
74
|
+
/** Its plan's operations in the journal, oldest first. */
|
|
75
|
+
operations: z.array(z.looseObject({ method: z.string().optional(), status: z.string().nullable(), correlationId: z.string().nullable(), refused: z.string().nullable() })),
|
|
76
|
+
});
|
|
77
|
+
export const proposalList = z.looseObject({ proposals: z.array(proposalView) });
|
|
78
|
+
/** A raw amount in whole units, exact, without trailing zeros. */
|
|
79
|
+
export function tokenAmount(raw, decimals) {
|
|
80
|
+
const digits = raw.padStart(decimals + 1, "0");
|
|
81
|
+
const whole = digits.slice(0, digits.length - decimals);
|
|
82
|
+
const fraction = digits.slice(digits.length - decimals).replace(/0+$/, "");
|
|
83
|
+
return fraction ? `${whole}.${fraction}` : whole;
|
|
84
|
+
}
|
|
85
|
+
const shown = (asset, raw) => {
|
|
86
|
+
const token = PROPOSAL_TOKENS[asset];
|
|
87
|
+
return token ? `${tokenAmount(raw, token.decimals)} ${token.symbol} (${raw})` : `${raw} of ${asset}`;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* The exact text the owner signs to approve (or reject) a proposal: the platform accepts only these bytes. The host is
|
|
91
|
+
* the NEP-413 recipient, so it isn't repeated here.
|
|
92
|
+
*/
|
|
93
|
+
export function proposalText(proposal, verdict) {
|
|
94
|
+
return [
|
|
95
|
+
`${verdict === "approve" ? "Approve" : "Reject"} Treasury proposal ${proposal.id}`,
|
|
96
|
+
`Agent ${proposal.agentId}`,
|
|
97
|
+
`Proposed by ${proposal.deployment} for ${proposal.organization}`,
|
|
98
|
+
...proposal.legs.map((leg, i) => `${i + 1}. Swap ${shown(leg.from, leg.amount)} for at least ${shown(leg.to, leg.minAmountOut)}`),
|
|
99
|
+
`Under policy revision ${proposal.policyRevision ?? "none"} (${proposal.policyDigest ?? "none"}), strategy ${proposal.strategyDigest}`,
|
|
100
|
+
`Valid until ${proposal.expiresAt}. Proposal sha256 ${proposal.sha256}.`,
|
|
101
|
+
].join("\n");
|
|
102
|
+
}
|
package/package.json
CHANGED
package/provenance.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiagency/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"wire": {
|
|
5
5
|
"payeeManifestVersion": 1,
|
|
6
6
|
"capabilities": [
|
|
@@ -12,50 +12,52 @@
|
|
|
12
12
|
"supplier-agent-connections-v1"
|
|
13
13
|
]
|
|
14
14
|
},
|
|
15
|
-
"sourceHead": "
|
|
15
|
+
"sourceHead": "1a7fe9818e2a83c548e1ba8d587f10acac37716a",
|
|
16
16
|
"dirtyPaths": [],
|
|
17
17
|
"snapshot": {
|
|
18
|
-
"runDigest": "
|
|
18
|
+
"runDigest": "c8dafe626d05552a1d101d8119f41441f67bb4c25dec60331dc98e2e62589de8",
|
|
19
19
|
"overlayDiffSha256": null
|
|
20
20
|
},
|
|
21
21
|
"sources": {
|
|
22
22
|
"src/compensation-contract.ts": "3443b0690db7921d6dec04375d9281a2e1df3f4ffbe48c2b821e89af177f14c1",
|
|
23
23
|
"src/contract/agent-connection.ts": "cfe3ec3b3cdc4da2773b20de008e1a004f2862499d0df5831923637f8b39a746",
|
|
24
|
-
"src/contract/app-access.ts": "
|
|
25
|
-
"src/contract/app-client.ts": "
|
|
26
|
-
"src/contract/app-consent.ts": "
|
|
24
|
+
"src/contract/app-access.ts": "9385fc9ef5fbb26734b70ed9ddba916113815ebbaf6f2fb8e6fbc3809bb78f59",
|
|
25
|
+
"src/contract/app-client.ts": "a505db256012feb3659c02f001444b4bddc90fc7a091200c36b32c88d63832d5",
|
|
26
|
+
"src/contract/app-consent.ts": "6649a7d27f19ab84f23433deeefb877b4e386934d64f3ac2040d04b9d3309d3d",
|
|
27
27
|
"src/contract/assets.ts": "b6cc3a160e488bfecf273b50e747e4640c8f4ba4d000bd6898db4f805ae9f310",
|
|
28
28
|
"src/contract/client.ts": "1eabd53e3119078d3c01a18850c180fab45385eec6ad210b92aa7fa158a03996",
|
|
29
29
|
"src/contract/gateway.ts": "e66bb199ab504b007e5a04617e0cf53b5b3d9641328a799c7df33f8901fa473b",
|
|
30
|
-
"src/contract/index.ts": "
|
|
30
|
+
"src/contract/index.ts": "e1b0f7dc2618543de5e98c6a94c9ed979c43a17e625445daa395d4be42a0396c",
|
|
31
31
|
"src/contract/inputs.ts": "a7b16ff1ff05a65bc2bd6b8bdaa978296d912e45098d105e0a578a8174a029d3",
|
|
32
32
|
"src/contract/intents-account.ts": "8e88f6f41de28fdb48fa49243ccb4e0035dc7823586931658922927cf92f8fea",
|
|
33
33
|
"src/contract/orpc-client.ts": "94cbd34bc64bd06152f2dbf17d2ff6b270d1d3b703e3bb7019483bcce93eeb04",
|
|
34
|
-
"src/contract/owner-capabilities.ts": "
|
|
34
|
+
"src/contract/owner-capabilities.ts": "31aadb5a44b776ea34dc5f7b5a4306a765c6deec0b4a802337fe6fefd4373f69",
|
|
35
35
|
"src/contract/owner-client.ts": "468a2e06784e95242087ecf45af805092de28af4e59ad0d445b29999b145b9f6",
|
|
36
36
|
"src/contract/owner-contract.ts": "339f63d7b585d652f1fd2175ebc94a5d8ef5e31a1f4730e554c2b0b89b24ab11",
|
|
37
|
-
"src/contract/owner-inputs.ts": "
|
|
38
|
-
"src/contract/owner-outputs.ts": "
|
|
39
|
-
"src/contract/package-version.ts": "
|
|
37
|
+
"src/contract/owner-inputs.ts": "e8dab47bbbffcf1dbe268fae4e0ffa703b8c46bb79b2cc5659bb1cc6ecd9c2e2",
|
|
38
|
+
"src/contract/owner-outputs.ts": "1e88bf152ddfdf2f7ee74ae53d3a05dafc3f994b01a6e22b720856dfa616f4aa",
|
|
39
|
+
"src/contract/package-version.ts": "e97007b462544c8e245b29c002930918e2f9a357be0b3346fc02a68ecdb8a1a6",
|
|
40
40
|
"src/contract/payee-entry.ts": "9557ec0f53dd4ab3411d3a81ccd82667f83766386332fb84e93a50caa8287bef",
|
|
41
41
|
"src/contract/payee.ts": "ec6e0c628be8592b997a5ceccfcd1ca18f891bb969731214fd4205b7e3f07a66",
|
|
42
|
-
"src/contract/permissions.ts": "
|
|
42
|
+
"src/contract/permissions.ts": "03171e6e382cf70f264760c6d9734e06d3bfd371c3bc12238335f763cd0b4242",
|
|
43
43
|
"src/contract/proposals.ts": "8a9abb34f1ee09fcfb1de2cc2ced2773d3fa189f20faa0bc1a31ed9fae82beef",
|
|
44
44
|
"src/contract/run-log-schema.ts": "2ef522a589f6a70cff58e4ded63ddf41a6d3998b2fde96a1bf9176b363da3847",
|
|
45
45
|
"src/contract/run-progress-schema.ts": "5eead5bde373e8f058f7f42cf588c4a71e1960d5d7097e0381384c03f6a4bbfb",
|
|
46
|
+
"src/contract/treasury-proposals.ts": "8b0e9cfca940e706689a3da92fd781f0e8279753bbd8f866452a8ad249d6cae3",
|
|
46
47
|
"src/payee-contract.ts": "800260c35c1edbc6e8361f9213fe5f4ccdc22d8d7b1510db08a590f83a152351",
|
|
47
48
|
"src/contract/release-server.mjs": "a2177a0fc28bc19edefae2e5d5e8e11dffa3c4f099f604cee82457cb8eca51c7",
|
|
48
49
|
"src/contract/release-server.d.mts": "05fb535f8d1f3ff4aac62395b717144aecf1f4b0c5b388ca83d8b4cb677d2764",
|
|
49
|
-
"docs/sdk-changes.md": "
|
|
50
|
+
"docs/sdk-changes.md": "5cd4a1fab6ab8ae15c27bd99a587bbcae9ee393d00214e38dea7321d4189a154",
|
|
50
51
|
"src/contract/README.md": "e80e5cbbab40fef5d4a8deadb90b02133b2998df7fc17ff031910e818e6909a9",
|
|
51
52
|
"LICENSE": "0f90c37abe95e72f99ddb9b9dfc5e54b663d8bfed929d4c261669059443ba822",
|
|
52
|
-
"src/contract/skills/app-server/SKILL.md": "
|
|
53
|
-
"src/contract/skills/getting-started/SKILL.md": "
|
|
54
|
-
"src/contract/skills/owner-workflows/SKILL.md": "
|
|
55
|
-
"src/contract/skills/payee-workflows/SKILL.md": "
|
|
53
|
+
"src/contract/skills/app-server/SKILL.md": "fe120aaec480521947ea4cef71ea50e810db7619aa8a8b2ca7cf04e598de63b1",
|
|
54
|
+
"src/contract/skills/getting-started/SKILL.md": "f46e923f1c5b69746f99f3fc7809248c2c7b6a036d45e53fbaa27040d18d05c1",
|
|
55
|
+
"src/contract/skills/owner-workflows/SKILL.md": "f4d8585663430808e89c9f527b010c76519ad21592e7281f443d4c7d675cd34d",
|
|
56
|
+
"src/contract/skills/payee-workflows/SKILL.md": "6839aef4532fcfd6c5a4fdf1bb34faff1477a879c06f0ede2bd514961aa24680",
|
|
57
|
+
"src/contract/skills/treasury-app/SKILL.md": "d53226515032de733f75a34c91f5287241df99be67d2ebd248d35ed69258d7ab"
|
|
56
58
|
},
|
|
57
59
|
"sourcesDirty": [],
|
|
58
60
|
"node": "v24.22.1",
|
|
59
61
|
"typescript": "Version 7.0.2",
|
|
60
|
-
"builtAt": "2026-
|
|
62
|
+
"builtAt": "2026-11-03T01:24:16.514Z"
|
|
61
63
|
}
|
|
@@ -10,7 +10,7 @@ metadata:
|
|
|
10
10
|
purpose: 'An app server''s access to agents: credential, consents, relayed owner actions, and payout requests by requestId with recovery by state.'
|
|
11
11
|
type: core
|
|
12
12
|
library: '@multiagency/sdk'
|
|
13
|
-
library_version: '0.
|
|
13
|
+
library_version: '0.4.0'
|
|
14
14
|
requires:
|
|
15
15
|
- getting-started
|
|
16
16
|
- owner-workflows
|
|
@@ -25,8 +25,8 @@ sources:
|
|
|
25
25
|
|
|
26
26
|
Read getting-started and owner-workflows first. `appClient` runs on your server with the deployment's service credential
|
|
27
27
|
(`tap_…`), issued by the platform operator: a server secret, never sent to a browser. A call reaches an agent only
|
|
28
|
-
under a live owner consent for that Organization and agent, and only within the consent's scopes: `read`, `owner`,
|
|
29
|
-
`payout
|
|
28
|
+
under a live owner consent for that Organization and agent, and only within the consent's scopes: `read`, `owner`,
|
|
29
|
+
`payout`, and for Treasury `treasury` and `treasury-execute` (see treasury-app). No production MultiAgency host serves the `/apps/v1` routes yet; the operator runs them on a staging host for
|
|
30
30
|
its own tests. Until a production host does, build against the SDK repository's local stack
|
|
31
31
|
(`pnpm test:e2e:stack --app <deployment>`). Use only `/apps/v1` and the confirm page on the platform's host: it serves
|
|
32
32
|
nothing else (no sign-in or owner page), and setup there is for Payables agents only.
|
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
purpose: 'Orients an app or agent author: the two clients, the four identities and their server-side limits, recovery kinds, and the supported surface of this package version.'
|
|
10
10
|
type: lifecycle
|
|
11
11
|
library: '@multiagency/sdk'
|
|
12
|
-
library_version: '0.
|
|
12
|
+
library_version: '0.4.0'
|
|
13
13
|
sources:
|
|
14
14
|
- 'MultiAgency/platform:src/contract/index.ts'
|
|
15
15
|
- 'MultiAgency/platform:src/contract/payee-entry.ts'
|
|
@@ -62,11 +62,12 @@ it may retry.
|
|
|
62
62
|
|
|
63
63
|
## What this package version supports
|
|
64
64
|
|
|
65
|
-
`@multiagency/sdk` 0.
|
|
65
|
+
`@multiagency/sdk` 0.4.0, entries:
|
|
66
66
|
- `@multiagency/sdk`: `ownerClient` with `OwnerResult`/`OwnerFailure`, `appClient` with `AppRouteResult` and the
|
|
67
67
|
consent and setup texts (`consentMessage`, `setupMessage`), `ownerOutputs` and `OwnerInputs`/`OwnerOutput`
|
|
68
68
|
per procedure, `ownerCapabilities` and `mayCall` (display only), gateway discovery (`gatewayDiscovery`,
|
|
69
|
-
`gatewayDenialScope`), schemas for runs (`RecordingSchema`)
|
|
69
|
+
`gatewayDenialScope`), schemas for runs (`RecordingSchema`), DAO proposals and Treasury proposals (`proposalView`,
|
|
70
|
+
`proposalText`; see treasury-app), `ASSETS` and account helpers. It also
|
|
70
71
|
re-exports the payee entry. Peer: zod ^4.6.5. It bundles `@orpc/client` 2.0.0-beta.47 and loads no other oRPC
|
|
71
72
|
package, so it runs beside a host that shares its own oRPC. It depends on `@ag-ui/core` 1.0.3.
|
|
72
73
|
- `@multiagency/sdk/contract`: `ownerContract`, `ownerErrors` and `OwnerContract`, for an app implementing a server
|
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
purpose: 'Owner reads, owner-signed actions and money streams with ownerClient, with recovery by result kind and retained identities.'
|
|
10
10
|
type: core
|
|
11
11
|
library: '@multiagency/sdk'
|
|
12
|
-
library_version: '0.
|
|
12
|
+
library_version: '0.4.0'
|
|
13
13
|
requires:
|
|
14
14
|
- getting-started
|
|
15
15
|
sources:
|
|
@@ -120,6 +120,8 @@ export function machineClient(origin: string, agent: string, token: string) {
|
|
|
120
120
|
Result: `await machineClient(origin, "treasury", token).call("overview")` answers like the browser client. A procedure
|
|
121
121
|
outside the credential's scopes, or an expired or revoked credential, answers `forbidden` or `session`. Results, streams
|
|
122
122
|
and recovery are the same as above: a lost `executePlan` or `sweep` is `unknown`, and nothing is sent again.
|
|
123
|
+
`executePlan` runs only while the owner's live grant binds the saved strategy (`treasury:<suffix>`, signed once per
|
|
124
|
+
saved strategy); otherwise it answers one `rejected` event naming the grant, and nothing is claimed.
|
|
123
125
|
|
|
124
126
|
## Recover an action that returned no identity
|
|
125
127
|
|
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
purpose: 'Payee reports and their payment status with payeeClient, from a browser session or an agent connection, with recovery by kept identities.'
|
|
10
10
|
type: core
|
|
11
11
|
library: '@multiagency/sdk'
|
|
12
|
-
library_version: '0.
|
|
12
|
+
library_version: '0.4.0'
|
|
13
13
|
requires:
|
|
14
14
|
- getting-started
|
|
15
15
|
sources:
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: treasury-app
|
|
3
|
+
description: >
|
|
4
|
+
Use when an app's server proposes Treasury swaps for an Organization's agent through @multiagency/sdk and has them
|
|
5
|
+
executed once the owner approves later: proposals by requestId, the exact text the owner signs, one execution per
|
|
6
|
+
proposal with recovery by state, and the two Treasury consent scopes. Server side only. Read app-server first.
|
|
7
|
+
metadata:
|
|
8
|
+
purpose: 'Treasury proposals through an app: propose by requestId, the owner signs the exact terms, execute once, recover by state.'
|
|
9
|
+
type: core
|
|
10
|
+
library: '@multiagency/sdk'
|
|
11
|
+
library_version: '0.4.0'
|
|
12
|
+
requires:
|
|
13
|
+
- getting-started
|
|
14
|
+
- app-server
|
|
15
|
+
sources:
|
|
16
|
+
- 'MultiAgency/platform:src/contract/treasury-proposals.ts'
|
|
17
|
+
- 'MultiAgency/platform:src/contract/app-access.ts'
|
|
18
|
+
- 'MultiAgency/platform:src/proposals/service.ts'
|
|
19
|
+
- 'MultiAgency/platform:docs/enforcement.md'
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Treasury proposals from an app's server
|
|
23
|
+
|
|
24
|
+
Read app-server first: the same credential, consent and result kinds apply. An agent runtime (your CLI, an assistant)
|
|
25
|
+
calls your server, never the platform. A proposal is a list of swaps your app proposes now; the agent's owner approves
|
|
26
|
+
its exact terms later (hours or days), by signing a text in their own wallet; then your server asks for its execution,
|
|
27
|
+
which runs once, under the owner's signed policy. No production host serves this yet; build against the SDK
|
|
28
|
+
repository's local stack.
|
|
29
|
+
|
|
30
|
+
Two consent scopes, each named in the text the owner signs:
|
|
31
|
+
- `treasury`: Treasury reads (`strategy/get`, `autopilot/get`, `autopilot/evidence`, `autopilot/log`, `treasury/get`),
|
|
32
|
+
strategy settings (`strategy/save`, which act only once the owner signs a grant binding them), `autopilot/enable` and
|
|
33
|
+
`autopilot/stop`, `sweep`, and every `proposals/*` procedure.
|
|
34
|
+
- `treasury-execute`, only beside `treasury`: `rebalance`, `externalPlan` and `executePlan`, five-minute plans executed
|
|
35
|
+
within the policy without a signature per swap, and only while the owner's live grant binds the strategy they were
|
|
36
|
+
planned under. `sweep`, `rebalance` and `executePlan` are streams: read them with `app.agent(...).stream(path, input)`.
|
|
37
|
+
|
|
38
|
+
## Propose, once per requestId
|
|
39
|
+
|
|
40
|
+
Store the `requestId` (1–64 of `[A-Za-z0-9_-]`) and the request before calling. `legs` is your chosen swaps in raw units
|
|
41
|
+
(at most 8; a `minAmountOut` you require is kept if it is above the quote's floor), or `"strategy"` for the legs a
|
|
42
|
+
rebalance toward the owner's strategy picks now. `expiresAt` is at most 7 days ahead.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { appClient, proposalText, type ProposalView } from "@multiagency/sdk";
|
|
46
|
+
|
|
47
|
+
type Request = { requestId: string; legs: { from: string; to: string; amount: string; minAmountOut?: string }[] | "strategy"; expiresAt: string };
|
|
48
|
+
type SignNep413 = (message: string, recipient: string, nonce: Uint8Array) => Promise<{ publicKey: string; signature: string }>;
|
|
49
|
+
|
|
50
|
+
export async function propose(app: ReturnType<typeof appClient>, organization: string, agent: string, request: Request) {
|
|
51
|
+
const calls = app.agent(organization, agent);
|
|
52
|
+
const made = await calls.call("proposals/create", request);
|
|
53
|
+
if (made.ok || (made.kind !== "unknown" && made.kind !== "unavailable")) return made;
|
|
54
|
+
// The answer was lost: the same requestId and request answer the stored proposal and store nothing new.
|
|
55
|
+
return calls.call("proposals/create", request);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function approve(app: ReturnType<typeof appClient>, organization: string, agent: string, host: string,
|
|
59
|
+
proposal: ProposalView, sign: SignNep413) {
|
|
60
|
+
// The platform accepts only this exact text; show the owner this text, which their wallet shows as it signs.
|
|
61
|
+
const message = proposalText(proposal, "approve");
|
|
62
|
+
const nonce = crypto.getRandomValues(new Uint8Array(32));
|
|
63
|
+
const { publicKey, signature } = await sign(message, host, nonce);
|
|
64
|
+
return app.agent(organization, agent).call("proposals/approve", { id: proposal.id,
|
|
65
|
+
signed: { message, nonce: btoa(String.fromCharCode(...nonce)), publicKey, signature } });
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Result: `{ ok: true, value: proposal }` with `state` `proposed`, then `approved`. The text names each swap with its
|
|
70
|
+
floor (the least it may return), the policy revision and strategy it was proposed under, the expiry and the
|
|
71
|
+
proposal's sha256; the host is the NEP-413 recipient. Approving records the signature and sends nothing. Refusals carry
|
|
72
|
+
`code`: `request_conflict` (another request under that `requestId`), `expiry_invalid`, `too_many_proposals`,
|
|
73
|
+
`not_proposable` (a provider warning, an output the policy forbids, a floor above the quote, cost over the strategy's
|
|
74
|
+
limit, or a balance short: the message says which), `signature_invalid`, `invalidated` (the policy, strategy or agent
|
|
75
|
+
changed since: propose again), `not_open`, `expired`, `withdrawn` (made under a consent that has since ended). The owner
|
|
76
|
+
rejects with `proposalText(proposal, "reject")` and `proposals/reject`; your app withdraws with `proposals/withdraw`.
|
|
77
|
+
|
|
78
|
+
## Execute once, and follow it
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { appClient } from "@multiagency/sdk";
|
|
82
|
+
|
|
83
|
+
export async function execute(app: ReturnType<typeof appClient>, organization: string, agent: string, id: string) {
|
|
84
|
+
const calls = app.agent(organization, agent);
|
|
85
|
+
const ran = await calls.call("proposals/execute", { id });
|
|
86
|
+
if (ran.ok || (ran.kind !== "unknown" && ran.kind !== "unavailable")) return ran;
|
|
87
|
+
// The answer was lost: read the proposal. Never propose the same swaps again because of a lost answer.
|
|
88
|
+
return calls.call("proposals/get", { id });
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Result: the proposal, `state`:
|
|
93
|
+
- `executed`: every swap succeeded; `operations` lists them.
|
|
94
|
+
- `stopped`: claimed, then a swap didn't succeed or was refused, or nothing was sent. It never runs again: propose the
|
|
95
|
+
rest anew.
|
|
96
|
+
- `unknown`: a swap's outcome isn't proven. It blocks the agent's sends until resolved and is never resent: read later.
|
|
97
|
+
- `executing`: running now; read later.
|
|
98
|
+
- `invalidated`, `expired`, `rejected`, `withdrawn`: it ended without running.
|
|
99
|
+
|
|
100
|
+
A refusal `not_executable` means the attempt was refused before anything was claimed (price below a signed floor, a
|
|
101
|
+
provider warning, a short balance, no live treasury grant, the policy not in force): nothing was sent, the proposal
|
|
102
|
+
stays `approved`, and asking again later is safe. `not_approved`: the owner hasn't approved it. Execution re-quotes
|
|
103
|
+
every swap and sends each with a floor at least the signed one.
|
|
104
|
+
|
|
105
|
+
## Common mistakes
|
|
106
|
+
|
|
107
|
+
- **Building your own approval text** (CRITICAL): sign only `proposalText(proposal, "approve")` from the proposal the
|
|
108
|
+
platform returned; any other text is refused, and a summary of yours is not what the owner signs.
|
|
109
|
+
- **A new requestId after `unknown`** (CRITICAL): the swaps may have run. Read `proposals/get`.
|
|
110
|
+
- **Treating `stopped` as nothing sent** (HIGH): read `operations`; a swap may have succeeded before it stopped.
|
|
111
|
+
- **Asking for `treasury-execute` when proposals suffice** (HIGH): it lets your app swap within the policy without a
|
|
112
|
+
signature per swap. Ask for it only when the owner wants unattended execution.
|
|
113
|
+
|
|
114
|
+
## Completion
|
|
115
|
+
|
|
116
|
+
Every proposal you created has a read state; none reads `unknown` or `executing` without a later `proposals/get`.
|
|
117
|
+
Report anything you could not read; do not act again.
|