@gol/sdk 0.3.0 → 0.5.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/README.md +24 -17
- package/dist/account-dialects.d.ts +14 -6
- package/dist/account-dialects.d.ts.map +1 -1
- package/dist/account-dialects.js +31 -4
- package/dist/account-dialects.js.map +1 -1
- package/dist/agent.d.ts +232 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +299 -0
- package/dist/agent.js.map +1 -0
- package/dist/eip7702.d.ts +73 -0
- package/dist/eip7702.d.ts.map +1 -0
- package/dist/eip7702.js +173 -0
- package/dist/eip7702.js.map +1 -0
- package/dist/gas-approval.d.ts +8 -3
- package/dist/gas-approval.d.ts.map +1 -1
- package/dist/gas-approval.js +17 -9
- package/dist/gas-approval.js.map +1 -1
- package/dist/generated/platform.d.ts +2924 -544
- package/dist/generated/platform.d.ts.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/owner.d.ts +131 -27
- package/dist/owner.d.ts.map +1 -1
- package/dist/owner.js +257 -92
- package/dist/owner.js.map +1 -1
- package/dist/server.d.ts +38 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +108 -2
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/v3.d.ts +39 -0
- package/dist/v3.d.ts.map +1 -0
- package/dist/v3.js +41 -0
- package/dist/v3.js.map +1 -0
- package/package.json +18 -19
package/dist/owner.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { concatHex, encodeAbiParameters, encodeFunctionData, getAddress, hashTypedData, keccak256, numberToHex, padHex, parseAbi, size, stringToHex, } from "viem";
|
|
2
2
|
import { BASE_SEPOLIA_CHAIN_ID, ownerUserOpNonceKey, packOwnerSignature, packOwnerUserOpSignature, } from "./account-dialects.js";
|
|
3
3
|
import { hashGasMandate, hashOwnerGasPolicy, } from "./gas-approval.js";
|
|
4
|
+
import { contractProjectId } from "./project-id.js";
|
|
5
|
+
import { CHARGEABLE_OUTCOMES, CORE_DOMAIN_NAME, CORE_DOMAIN_VERSION, coreDomain, GAS_FORMULA_INLINE, HOSTED_TREE_LIMITS, NATIVE_FEE_ASSET, } from "./v3.js";
|
|
4
6
|
/** Circle USDC on Base Sepolia, the only transfer asset in this candidate. */
|
|
5
7
|
export const BASE_SEPOLIA_USDC = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
6
8
|
export const ENTRY_POINT_V07 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
@@ -53,7 +55,37 @@ export function encodeTransferPolicy(input) {
|
|
|
53
55
|
...rules,
|
|
54
56
|
]).toLowerCase();
|
|
55
57
|
}
|
|
56
|
-
/**
|
|
58
|
+
/** Owner safety actions of the core (encoding section 6.1). */
|
|
59
|
+
export const SAFETY_ACTION_CODES = { pause: 1, resume: 2, revoke: 3 };
|
|
60
|
+
export const GOL_OWNER_PAYLOAD_TYPES = {
|
|
61
|
+
CombinedGasApproval: [
|
|
62
|
+
{ name: "mandateId", type: "bytes32" },
|
|
63
|
+
{ name: "gasPolicyId", type: "bytes32" },
|
|
64
|
+
],
|
|
65
|
+
GasRevocation: [
|
|
66
|
+
{ name: "gasPolicyId", type: "bytes32" },
|
|
67
|
+
{ name: "nonce", type: "uint256" },
|
|
68
|
+
{ name: "deadline", type: "uint48" },
|
|
69
|
+
],
|
|
70
|
+
SafetyAction: [
|
|
71
|
+
{ name: "mandateId", type: "bytes32" },
|
|
72
|
+
{ name: "action", type: "uint8" },
|
|
73
|
+
{ name: "nonce", type: "uint256" },
|
|
74
|
+
{ name: "deadline", type: "uint48" },
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
/** EIP-712 struct hash of a GOL owner payload. */
|
|
78
|
+
export function golOwnerPayloadStructHash(payload) {
|
|
79
|
+
if (payload.primaryType === "CombinedGasApproval")
|
|
80
|
+
return combinedApprovalStructHash(payload.message.mandateId, payload.message.gasPolicyId);
|
|
81
|
+
if (payload.primaryType === "GasRevocation")
|
|
82
|
+
return revocationStructHash(payload.message.gasPolicyId, BigInt(payload.message.nonce), Number(payload.message.deadline));
|
|
83
|
+
const action = Number(payload.message.action);
|
|
84
|
+
if (action !== 1 && action !== 2 && action !== 3)
|
|
85
|
+
throw new Error("invalid_safety_action");
|
|
86
|
+
return safetyActionStructHash(payload.message.mandateId, action, BigInt(payload.message.nonce), Number(payload.message.deadline));
|
|
87
|
+
}
|
|
88
|
+
/** EIP-712 domain separator of the version 3 core. */
|
|
57
89
|
export function coreDomainSeparator(chainId, core) {
|
|
58
90
|
return keccak256(encodeAbiParameters([
|
|
59
91
|
{ type: "bytes32" },
|
|
@@ -63,8 +95,8 @@ export function coreDomainSeparator(chainId, core) {
|
|
|
63
95
|
{ type: "address" },
|
|
64
96
|
], [
|
|
65
97
|
DOMAIN_TYPEHASH,
|
|
66
|
-
keccak256(stringToHex(
|
|
67
|
-
keccak256(stringToHex(
|
|
98
|
+
keccak256(stringToHex(CORE_DOMAIN_NAME)),
|
|
99
|
+
keccak256(stringToHex(CORE_DOMAIN_VERSION)),
|
|
68
100
|
BigInt(chainId),
|
|
69
101
|
core,
|
|
70
102
|
]));
|
|
@@ -94,18 +126,33 @@ const FORMAT = {
|
|
|
94
126
|
nexus: "nexus_k1",
|
|
95
127
|
kernel: "kernel_root",
|
|
96
128
|
alchemy: "alchemy_entity_zero",
|
|
129
|
+
nexus7702: "eoa",
|
|
97
130
|
};
|
|
98
131
|
/**
|
|
99
132
|
* Build the exact `eth_signTypedData_v4` payload whose hash the selected
|
|
100
|
-
* account's reviewed owner path checks for a core digest.
|
|
133
|
+
* account's reviewed owner path checks for a core digest. A delegated EOA
|
|
134
|
+
* (`nexus7702`) signs the GOL payload itself, so its hash is the digest.
|
|
101
135
|
*/
|
|
102
136
|
export function buildWalletSigningPayload(input) {
|
|
103
137
|
const chainId = input.chainId ?? BASE_SEPOLIA_CHAIN_ID;
|
|
104
138
|
const account = getAddress(input.account);
|
|
139
|
+
const structHash = golOwnerPayloadStructHash(input.payload);
|
|
105
140
|
const domainSeparator = coreDomainSeparator(chainId, input.core);
|
|
106
|
-
const digest = keccak256(concatHex(["0x1901", domainSeparator,
|
|
141
|
+
const digest = keccak256(concatHex(["0x1901", domainSeparator, structHash]));
|
|
107
142
|
let typedData;
|
|
108
|
-
if (input.family === "
|
|
143
|
+
if (input.family === "nexus7702") {
|
|
144
|
+
typedData = {
|
|
145
|
+
domain: coreDomain(chainId, getAddress(input.core)),
|
|
146
|
+
types: {
|
|
147
|
+
[input.payload.primaryType]: [
|
|
148
|
+
...GOL_OWNER_PAYLOAD_TYPES[input.payload.primaryType],
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
primaryType: input.payload.primaryType,
|
|
152
|
+
message: { ...input.payload.message },
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
else if (input.family === "safe") {
|
|
109
156
|
typedData = {
|
|
110
157
|
domain: { chainId, verifyingContract: account },
|
|
111
158
|
types: { SafeMessage: [{ name: "message", type: "bytes" }] },
|
|
@@ -126,7 +173,7 @@ export function buildWalletSigningPayload(input) {
|
|
|
126
173
|
types: { PersonalSign: [{ name: "prefixed", type: "bytes" }] },
|
|
127
174
|
primaryType: "PersonalSign",
|
|
128
175
|
message: {
|
|
129
|
-
prefixed: concatHex(["0x1901", domainSeparator,
|
|
176
|
+
prefixed: concatHex(["0x1901", domainSeparator, structHash]),
|
|
130
177
|
},
|
|
131
178
|
};
|
|
132
179
|
}
|
|
@@ -155,11 +202,14 @@ export function buildWalletSigningPayload(input) {
|
|
|
155
202
|
message: { hash: digest },
|
|
156
203
|
};
|
|
157
204
|
}
|
|
205
|
+
const expectedHash = hashWalletTypedData(typedData);
|
|
206
|
+
if (input.family === "nexus7702" && expectedHash !== digest)
|
|
207
|
+
throw new Error("wallet_payload_mismatch");
|
|
158
208
|
return {
|
|
159
209
|
family: input.family,
|
|
160
210
|
method: "eth_signTypedData_v4",
|
|
161
211
|
typedData,
|
|
162
|
-
expectedHash
|
|
212
|
+
expectedHash,
|
|
163
213
|
digest,
|
|
164
214
|
signatureFormat: FORMAT[input.family],
|
|
165
215
|
};
|
|
@@ -193,33 +243,41 @@ export async function signWalletSigningPayload(signer, payload) {
|
|
|
193
243
|
// Core calls
|
|
194
244
|
// ---------------------------------------------------------------------------
|
|
195
245
|
const coreAbi = parseAbi([
|
|
196
|
-
"function createMandateWithGas((bytes32 parentId,address account,address agent,bytes32 accountProfileId,uint64 installEpoch,bytes32 projectId,uint8 mode,uint8 policyVersion,bytes32 policyHash,uint48 validFrom,uint48 expiresAt,uint32 maxDepth,uint32 maxActiveDescendants,uint48 approvalExpiresAt,bytes32 salt) terms,bytes policy,(bytes32 mandateId,address account,bytes32 projectId,bytes32 accountProfileId,uint64 installEpoch,address feeAsset,address recipient,address attester,uint128 maxPerAction,uint128 maxTotal,uint48 expiresAt,uint8 chargeableOutcomes,uint8 formulaVersion,uint256 nonce) gasPolicy,bytes ownerSignature) returns (bytes32 mandateId,bytes32 gasPolicyId)",
|
|
246
|
+
"function createMandateWithGas((bytes32 parentId,address account,address agent,bytes32 accountProfileId,uint64 installEpoch,bytes32 projectId,uint8 mode,uint8 policyVersion,bytes32 policyHash,uint48 validFrom,uint48 expiresAt,uint32 maxDepth,uint32 maxActiveDescendants,uint48 approvalExpiresAt,bytes32 salt) terms,bytes policy,(bytes32 mandateId,address account,bytes32 projectId,bytes32 accountProfileId,uint64 installEpoch,address feeAsset,address submitter,address recipient,address attester,uint128 maxPerAction,uint128 maxTotal,uint48 expiresAt,uint8 chargeableOutcomes,uint8 formulaVersion,uint256 nonce) gasPolicy,bytes ownerSignature) returns (bytes32 mandateId,bytes32 gasPolicyId)",
|
|
247
|
+
"function createChildMandate((bytes32 parentId,address account,address agent,bytes32 accountProfileId,uint64 installEpoch,bytes32 projectId,uint8 mode,uint8 policyVersion,bytes32 policyHash,uint48 validFrom,uint48 expiresAt,uint32 maxDepth,uint32 maxActiveDescendants,uint48 approvalExpiresAt,bytes32 salt) terms,bytes policy,bytes authorization) returns (bytes32 mandateId)",
|
|
197
248
|
"function revokeGasPolicy(bytes32 policyId,uint48 deadline,bytes signature)",
|
|
198
249
|
"function safetyAction(bytes32 mandateId,uint8 action,uint48 deadline,bytes signature)",
|
|
199
250
|
]);
|
|
200
251
|
/** Calldata for the core. Anyone may send it; authority comes only from the owner signature. */
|
|
252
|
+
function termsArgument(terms) {
|
|
253
|
+
return {
|
|
254
|
+
parentId: terms.parentId,
|
|
255
|
+
account: terms.account,
|
|
256
|
+
agent: terms.agent,
|
|
257
|
+
accountProfileId: terms.accountProfileId,
|
|
258
|
+
installEpoch: BigInt(terms.installEpoch),
|
|
259
|
+
projectId: terms.projectId,
|
|
260
|
+
mode: Number(terms.mode),
|
|
261
|
+
policyVersion: Number(terms.policyVersion),
|
|
262
|
+
policyHash: terms.policyHash,
|
|
263
|
+
validFrom: Number(terms.validFrom),
|
|
264
|
+
expiresAt: Number(terms.expiresAt),
|
|
265
|
+
maxDepth: Number(terms.maxDepth),
|
|
266
|
+
maxActiveDescendants: Number(terms.maxActiveDescendants),
|
|
267
|
+
approvalExpiresAt: Number(terms.approvalExpiresAt),
|
|
268
|
+
salt: terms.salt,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/** Normalize API mandate terms into the typed form the encoders hash. */
|
|
272
|
+
export function toGasMandateTerms(terms) {
|
|
273
|
+
return termsArgument(terms);
|
|
274
|
+
}
|
|
201
275
|
export function encodeCreateMandateWithGas(terms, mandatePolicy, gasPolicy, packedOwnerSignature) {
|
|
202
276
|
return encodeFunctionData({
|
|
203
277
|
abi: coreAbi,
|
|
204
278
|
functionName: "createMandateWithGas",
|
|
205
279
|
args: [
|
|
206
|
-
|
|
207
|
-
parentId: terms.parentId,
|
|
208
|
-
account: terms.account,
|
|
209
|
-
agent: terms.agent,
|
|
210
|
-
accountProfileId: terms.accountProfileId,
|
|
211
|
-
installEpoch: BigInt(terms.installEpoch),
|
|
212
|
-
projectId: terms.projectId,
|
|
213
|
-
mode: Number(terms.mode),
|
|
214
|
-
policyVersion: Number(terms.policyVersion),
|
|
215
|
-
policyHash: terms.policyHash,
|
|
216
|
-
validFrom: Number(terms.validFrom),
|
|
217
|
-
expiresAt: Number(terms.expiresAt),
|
|
218
|
-
maxDepth: Number(terms.maxDepth),
|
|
219
|
-
maxActiveDescendants: Number(terms.maxActiveDescendants),
|
|
220
|
-
approvalExpiresAt: Number(terms.approvalExpiresAt),
|
|
221
|
-
salt: terms.salt,
|
|
222
|
-
},
|
|
280
|
+
termsArgument(terms),
|
|
223
281
|
mandatePolicy,
|
|
224
282
|
{
|
|
225
283
|
mandateId: gasPolicy.mandateId,
|
|
@@ -228,6 +286,7 @@ export function encodeCreateMandateWithGas(terms, mandatePolicy, gasPolicy, pack
|
|
|
228
286
|
accountProfileId: gasPolicy.accountProfileId,
|
|
229
287
|
installEpoch: BigInt(gasPolicy.installEpoch),
|
|
230
288
|
feeAsset: gasPolicy.feeAsset,
|
|
289
|
+
submitter: gasPolicy.submitter,
|
|
231
290
|
recipient: gasPolicy.recipient,
|
|
232
291
|
attester: gasPolicy.attester,
|
|
233
292
|
maxPerAction: BigInt(gasPolicy.maxPerAction),
|
|
@@ -249,7 +308,18 @@ export function encodeRevokeGasPolicy(gasPolicyId, deadline, packedOwnerSignatur
|
|
|
249
308
|
args: [gasPolicyId, deadline, packedOwnerSignature],
|
|
250
309
|
});
|
|
251
310
|
}
|
|
252
|
-
/**
|
|
311
|
+
/**
|
|
312
|
+
* Child mandate creation. `authorization` is the parent agent's 65-byte
|
|
313
|
+
* signature over the child's `MandateTerms`; anyone may send the call.
|
|
314
|
+
*/
|
|
315
|
+
export function encodeCreateChildMandate(terms, mandatePolicy, authorization) {
|
|
316
|
+
return encodeFunctionData({
|
|
317
|
+
abi: coreAbi,
|
|
318
|
+
functionName: "createChildMandate",
|
|
319
|
+
args: [termsArgument(terms), mandatePolicy, authorization],
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/** Relayed owner pause, resume, or revoke; a direct account call uses deadline 0 and no signature. */
|
|
253
323
|
export function encodeSafetyAction(mandateId, actionCode, deadline, packedOwnerSignature) {
|
|
254
324
|
return encodeFunctionData({
|
|
255
325
|
abi: coreAbi,
|
|
@@ -262,7 +332,9 @@ export function encodeSafetyAction(mandateId, actionCode, deadline, packedOwnerS
|
|
|
262
332
|
// ---------------------------------------------------------------------------
|
|
263
333
|
/** Make the account call `target` through its own execution path. */
|
|
264
334
|
export function accountExecuteCall(family, account, target, data) {
|
|
265
|
-
|
|
335
|
+
// A Safe module transaction and a delegated EOA's own transaction both
|
|
336
|
+
// call the target directly.
|
|
337
|
+
if (family === "safe" || family === "nexus7702")
|
|
266
338
|
return { to: target, data, operation: 0 };
|
|
267
339
|
if (family === "alchemy")
|
|
268
340
|
return {
|
|
@@ -331,6 +403,21 @@ export async function prepareOwnerOperation(client, family, account, call, optio
|
|
|
331
403
|
if (chainId !== BASE_SEPOLIA_CHAIN_ID)
|
|
332
404
|
throw new Error("wrong_chain");
|
|
333
405
|
account = getAddress(account);
|
|
406
|
+
if (family === "nexus7702") {
|
|
407
|
+
if (call.operation !== 0)
|
|
408
|
+
throw new Error("eoa_call_must_be_direct");
|
|
409
|
+
return {
|
|
410
|
+
kind: "eoa_transaction",
|
|
411
|
+
family,
|
|
412
|
+
account,
|
|
413
|
+
transaction: {
|
|
414
|
+
from: account,
|
|
415
|
+
to: getAddress(call.to),
|
|
416
|
+
data: call.data,
|
|
417
|
+
value: 0n,
|
|
418
|
+
},
|
|
419
|
+
};
|
|
420
|
+
}
|
|
334
421
|
if (family === "safe") {
|
|
335
422
|
const nonce = await client.readContract({
|
|
336
423
|
address: account,
|
|
@@ -456,6 +543,8 @@ export async function prepareOwnerOperation(client, family, account, call, optio
|
|
|
456
543
|
}
|
|
457
544
|
/** Sign a prepared owner operation with a caller-supplied owner wallet. */
|
|
458
545
|
export async function signOwnerOperation(signer, operation) {
|
|
546
|
+
if (operation.kind === "eoa_transaction")
|
|
547
|
+
throw new Error("eoa_transaction_is_sent_by_owner_wallet");
|
|
459
548
|
if (operation.kind === "safe_transaction")
|
|
460
549
|
return signer.signTypedData(operation.typedData);
|
|
461
550
|
if (!signer.signMessage)
|
|
@@ -476,29 +565,13 @@ export function verifyPreparedGasPolicy(prepared, expected = {}) {
|
|
|
476
565
|
const { authority, review, signing } = prepared;
|
|
477
566
|
const t = authority.mandateTerms;
|
|
478
567
|
const g = authority.gasPolicy;
|
|
479
|
-
const r = review;
|
|
480
568
|
const chainId = prepared.chainId;
|
|
481
569
|
if (chainId !== BASE_SEPOLIA_CHAIN_ID)
|
|
482
570
|
fail("chain");
|
|
483
|
-
|
|
571
|
+
const core = getAddress(prepared.core);
|
|
572
|
+
if (expected.core && !eq(expected.core, core))
|
|
484
573
|
fail("core");
|
|
485
|
-
const terms =
|
|
486
|
-
parentId: t.parentId,
|
|
487
|
-
account: t.account,
|
|
488
|
-
agent: t.agent,
|
|
489
|
-
accountProfileId: t.accountProfileId,
|
|
490
|
-
installEpoch: BigInt(t.installEpoch),
|
|
491
|
-
projectId: t.projectId,
|
|
492
|
-
mode: Number(t.mode),
|
|
493
|
-
policyVersion: Number(t.policyVersion),
|
|
494
|
-
policyHash: t.policyHash,
|
|
495
|
-
validFrom: Number(t.validFrom),
|
|
496
|
-
expiresAt: Number(t.expiresAt),
|
|
497
|
-
maxDepth: Number(t.maxDepth),
|
|
498
|
-
maxActiveDescendants: Number(t.maxActiveDescendants),
|
|
499
|
-
approvalExpiresAt: Number(t.approvalExpiresAt),
|
|
500
|
-
salt: t.salt,
|
|
501
|
-
};
|
|
574
|
+
const terms = toGasMandateTerms(t);
|
|
502
575
|
const policy = {
|
|
503
576
|
mandateId: g.mandateId,
|
|
504
577
|
account: g.account,
|
|
@@ -506,6 +579,7 @@ export function verifyPreparedGasPolicy(prepared, expected = {}) {
|
|
|
506
579
|
accountProfileId: g.accountProfileId,
|
|
507
580
|
installEpoch: BigInt(g.installEpoch),
|
|
508
581
|
feeAsset: g.feeAsset,
|
|
582
|
+
submitter: g.submitter,
|
|
509
583
|
recipient: g.recipient,
|
|
510
584
|
attester: g.attester,
|
|
511
585
|
maxPerAction: BigInt(g.maxPerAction),
|
|
@@ -517,42 +591,77 @@ export function verifyPreparedGasPolicy(prepared, expected = {}) {
|
|
|
517
591
|
};
|
|
518
592
|
if (terms.parentId !== `0x${"00".repeat(32)}` ||
|
|
519
593
|
terms.mode !== 1 ||
|
|
520
|
-
terms.
|
|
521
|
-
terms.
|
|
594
|
+
terms.policyVersion !== 1 ||
|
|
595
|
+
terms.maxDepth !== review.maxDepth ||
|
|
596
|
+
terms.maxActiveDescendants !== review.maxActiveDescendants ||
|
|
597
|
+
terms.maxDepth > HOSTED_TREE_LIMITS.maxDepth ||
|
|
598
|
+
terms.maxActiveDescendants > HOSTED_TREE_LIMITS.maxActiveDescendants ||
|
|
599
|
+
terms.validFrom !== review.mandateValidFrom ||
|
|
600
|
+
terms.expiresAt !== review.mandateExpiresAt)
|
|
522
601
|
fail("mandate shape");
|
|
523
602
|
if (!eq(terms.account, prepared.account) ||
|
|
524
603
|
!eq(policy.account, prepared.account))
|
|
525
604
|
fail("account");
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
605
|
+
const projectBinding = contractProjectId(review.projectId);
|
|
606
|
+
if (!eq(review.contractProjectId, projectBinding) ||
|
|
607
|
+
!eq(terms.projectId, projectBinding) ||
|
|
608
|
+
!eq(policy.projectId, projectBinding) ||
|
|
609
|
+
(expected.projectId !== undefined &&
|
|
610
|
+
!eq(contractProjectId(expected.projectId), projectBinding)))
|
|
611
|
+
fail("project binding");
|
|
612
|
+
if (!eq(policy.accountProfileId, terms.accountProfileId) ||
|
|
613
|
+
policy.installEpoch !== terms.installEpoch)
|
|
614
|
+
fail("account profile");
|
|
615
|
+
if (!eq(policy.feeAsset, NATIVE_FEE_ASSET) ||
|
|
616
|
+
policy.formulaVersion !== GAS_FORMULA_INLINE)
|
|
617
|
+
fail("gas formula");
|
|
618
|
+
if (!eq(review.asset, BASE_SEPOLIA_USDC) || review.assetDecimals !== 6)
|
|
619
|
+
fail("asset");
|
|
620
|
+
const recipients = review.recipients;
|
|
530
621
|
const compiled = encodeTransferPolicy({
|
|
531
|
-
asset:
|
|
622
|
+
asset: review.asset,
|
|
532
623
|
recipients,
|
|
533
|
-
maxPerActionBaseUnits: BigInt(
|
|
534
|
-
maxTotalBaseUnits: BigInt(
|
|
624
|
+
maxPerActionBaseUnits: BigInt(review.maxPerActionBaseUnits),
|
|
625
|
+
maxTotalBaseUnits: BigInt(review.maxTotalBaseUnits),
|
|
535
626
|
});
|
|
536
627
|
if (!eq(compiled, authority.mandatePolicy) ||
|
|
537
628
|
!eq(keccak256(authority.mandatePolicy), terms.policyHash))
|
|
538
629
|
fail("policy bytes");
|
|
539
|
-
|
|
540
|
-
fail("asset");
|
|
541
|
-
const mandateId = hashGasMandate(terms, chainId, prepared.core);
|
|
630
|
+
const mandateId = hashGasMandate(terms, chainId, core);
|
|
542
631
|
if (!eq(mandateId, authority.mandateId) || !eq(policy.mandateId, mandateId))
|
|
543
632
|
fail("mandate id");
|
|
544
|
-
const gasPolicyId = hashOwnerGasPolicy(policy, chainId,
|
|
633
|
+
const gasPolicyId = hashOwnerGasPolicy(policy, chainId, core);
|
|
545
634
|
if (!eq(gasPolicyId, authority.gasPolicyId))
|
|
546
635
|
fail("gas policy id");
|
|
547
|
-
if (!eq(
|
|
548
|
-
!eq(
|
|
549
|
-
!eq(
|
|
550
|
-
|
|
551
|
-
BigInt(
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
636
|
+
if (!eq(review.agent, terms.agent) ||
|
|
637
|
+
!eq(review.submitter, policy.submitter) ||
|
|
638
|
+
!eq(review.gasRecipient, policy.recipient) ||
|
|
639
|
+
!eq(review.attester, policy.attester) ||
|
|
640
|
+
BigInt(review.maxGasPerActionWei) !== policy.maxPerAction ||
|
|
641
|
+
BigInt(review.maxGasTotalWei) !== policy.maxTotal ||
|
|
642
|
+
review.chargeableOutcomesMask !== policy.chargeableOutcomes ||
|
|
643
|
+
review.gasExpiresAt !== policy.expiresAt)
|
|
555
644
|
fail("review summary");
|
|
645
|
+
if (policy.maxPerAction <= 0n ||
|
|
646
|
+
policy.maxPerAction > policy.maxTotal ||
|
|
647
|
+
policy.chargeableOutcomes < 1 ||
|
|
648
|
+
policy.chargeableOutcomes > 7 ||
|
|
649
|
+
policy.expiresAt > terms.expiresAt ||
|
|
650
|
+
eq(policy.submitter, NATIVE_FEE_ASSET) ||
|
|
651
|
+
eq(policy.recipient, NATIVE_FEE_ASSET))
|
|
652
|
+
fail("gas policy bounds");
|
|
653
|
+
const revert = (policy.chargeableOutcomes & CHARGEABLE_OUTCOMES.revert) !== 0;
|
|
654
|
+
if (review.relayerMode === "developer") {
|
|
655
|
+
// A developer relayer has no GOL attester, so no revert claim.
|
|
656
|
+
if (revert || !eq(policy.attester, NATIVE_FEE_ASSET))
|
|
657
|
+
fail("developer relayer policy");
|
|
658
|
+
}
|
|
659
|
+
else if (review.relayerMode === "gol") {
|
|
660
|
+
if (revert && eq(policy.attester, NATIVE_FEE_ASSET))
|
|
661
|
+
fail("revert claim attester");
|
|
662
|
+
}
|
|
663
|
+
else
|
|
664
|
+
fail("relayer mode");
|
|
556
665
|
if (expected.account && !eq(expected.account, terms.account))
|
|
557
666
|
fail("expected account");
|
|
558
667
|
if (expected.agent && !eq(expected.agent, terms.agent))
|
|
@@ -562,10 +671,10 @@ export function verifyPreparedGasPolicy(prepared, expected = {}) {
|
|
|
562
671
|
!expected.recipients.every((a) => recipients.some((b) => eq(a, b)))))
|
|
563
672
|
fail("expected recipients");
|
|
564
673
|
if (expected.maxPerActionBaseUnits !== undefined &&
|
|
565
|
-
BigInt(
|
|
674
|
+
BigInt(review.maxPerActionBaseUnits) !== expected.maxPerActionBaseUnits)
|
|
566
675
|
fail("expected per-action cap");
|
|
567
676
|
if (expected.maxTotalBaseUnits !== undefined &&
|
|
568
|
-
BigInt(
|
|
677
|
+
BigInt(review.maxTotalBaseUnits) !== expected.maxTotalBaseUnits)
|
|
569
678
|
fail("expected total cap");
|
|
570
679
|
if (expected.maxGasPerActionWei !== undefined &&
|
|
571
680
|
policy.maxPerAction !== expected.maxGasPerActionWei)
|
|
@@ -573,11 +682,36 @@ export function verifyPreparedGasPolicy(prepared, expected = {}) {
|
|
|
573
682
|
if (expected.maxGasTotalWei !== undefined &&
|
|
574
683
|
policy.maxTotal !== expected.maxGasTotalWei)
|
|
575
684
|
fail("expected gas total cap");
|
|
685
|
+
if (expected.chargeableOutcomesMask !== undefined &&
|
|
686
|
+
policy.chargeableOutcomes !== expected.chargeableOutcomesMask)
|
|
687
|
+
fail("expected chargeable outcomes");
|
|
688
|
+
if (expected.relayer) {
|
|
689
|
+
if (expected.relayer.mode !== review.relayerMode)
|
|
690
|
+
fail("expected relayer");
|
|
691
|
+
if (expected.relayer.submitter &&
|
|
692
|
+
!eq(expected.relayer.submitter, policy.submitter))
|
|
693
|
+
fail("expected submitter");
|
|
694
|
+
if (expected.relayer.recipient &&
|
|
695
|
+
!eq(expected.relayer.recipient, policy.recipient))
|
|
696
|
+
fail("expected gas recipient");
|
|
697
|
+
}
|
|
698
|
+
if (expected.attester && !eq(expected.attester, policy.attester))
|
|
699
|
+
fail("expected attester");
|
|
700
|
+
if (expected.tree &&
|
|
701
|
+
(expected.tree.maxDepth !== terms.maxDepth ||
|
|
702
|
+
expected.tree.maxActiveDescendants !== terms.maxActiveDescendants))
|
|
703
|
+
fail("expected tree limits");
|
|
704
|
+
if (!eq(prepared.submission.to, core) ||
|
|
705
|
+
prepared.submission.functionName !== "createMandateWithGas")
|
|
706
|
+
fail("submission");
|
|
576
707
|
verifyWalletSigningPayload(signing, {
|
|
577
708
|
family: prepared.family,
|
|
578
709
|
account: prepared.account,
|
|
579
|
-
core
|
|
580
|
-
|
|
710
|
+
core,
|
|
711
|
+
payload: {
|
|
712
|
+
primaryType: "CombinedGasApproval",
|
|
713
|
+
message: { mandateId, gasPolicyId },
|
|
714
|
+
},
|
|
581
715
|
chainId,
|
|
582
716
|
});
|
|
583
717
|
if (!eq(signing.digest, authority.combinedDigest))
|
|
@@ -588,48 +722,79 @@ export async function signPreparedGasPolicy(signer, prepared, expected = {}) {
|
|
|
588
722
|
verifyPreparedGasPolicy(prepared, expected);
|
|
589
723
|
const signature = await signWalletSigningPayload(signer, prepared.signing);
|
|
590
724
|
return {
|
|
591
|
-
to: prepared.core,
|
|
725
|
+
to: getAddress(prepared.core),
|
|
592
726
|
data: encodeCreateMandateWithGas(prepared.authority.mandateTerms, prepared.authority.mandatePolicy, prepared.authority.gasPolicy, signature),
|
|
593
727
|
};
|
|
594
728
|
}
|
|
595
729
|
/** Verify a prepared relayed revocation, sign it with the owner wallet, and return the core call. */
|
|
596
730
|
export async function signPreparedRevocation(signer, prepared) {
|
|
597
|
-
|
|
731
|
+
const core = getAddress(prepared.core);
|
|
732
|
+
if (prepared.chainId !== BASE_SEPOLIA_CHAIN_ID ||
|
|
733
|
+
prepared.relayed.functionName !== "revokeGasPolicy" ||
|
|
734
|
+
!eq(prepared.directOwnerCall.to, core) ||
|
|
735
|
+
!eq(prepared.directOwnerCall.data, encodeRevokeGasPolicy(prepared.gasPolicyId, 0, "0x")))
|
|
736
|
+
throw new Error("revocation_mismatch");
|
|
737
|
+
const signing = prepared.relayed.signing;
|
|
738
|
+
verifyWalletSigningPayload(signing, {
|
|
598
739
|
family: prepared.family,
|
|
599
740
|
account: prepared.account,
|
|
600
|
-
core
|
|
601
|
-
|
|
741
|
+
core,
|
|
742
|
+
payload: {
|
|
743
|
+
primaryType: "GasRevocation",
|
|
744
|
+
message: {
|
|
745
|
+
gasPolicyId: prepared.gasPolicyId,
|
|
746
|
+
nonce: prepared.nonce,
|
|
747
|
+
deadline: prepared.deadline,
|
|
748
|
+
},
|
|
749
|
+
},
|
|
602
750
|
chainId: prepared.chainId,
|
|
603
751
|
});
|
|
604
|
-
|
|
752
|
+
if (!eq(prepared.digest, signing.digest))
|
|
753
|
+
throw new Error("revocation_mismatch");
|
|
754
|
+
const signature = await signWalletSigningPayload(signer, signing);
|
|
605
755
|
return {
|
|
606
|
-
to:
|
|
756
|
+
to: core,
|
|
607
757
|
data: encodeRevokeGasPolicy(prepared.gasPolicyId, prepared.deadline, signature),
|
|
608
758
|
};
|
|
609
759
|
}
|
|
610
|
-
/**
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
760
|
+
/**
|
|
761
|
+
* Recompute the core digest and wallet payload before signing an owner pause,
|
|
762
|
+
* resume, or revoke of any mandate in the policy's tree.
|
|
763
|
+
*/
|
|
764
|
+
export async function signPreparedSafetyAction(signer, prepared, expected = {}) {
|
|
765
|
+
const actionCode = SAFETY_ACTION_CODES[prepared.action];
|
|
766
|
+
const core = getAddress(prepared.core);
|
|
767
|
+
if (actionCode === undefined ||
|
|
768
|
+
prepared.chainId !== BASE_SEPOLIA_CHAIN_ID ||
|
|
616
769
|
prepared.actionCode !== actionCode ||
|
|
617
|
-
prepared.relayed.functionName !== "safetyAction"
|
|
770
|
+
prepared.relayed.functionName !== "safetyAction" ||
|
|
771
|
+
(expected.action !== undefined && expected.action !== prepared.action) ||
|
|
772
|
+
(expected.mandateId !== undefined &&
|
|
773
|
+
!eq(expected.mandateId, prepared.mandateId)) ||
|
|
774
|
+
!eq(prepared.directOwnerCall.to, core) ||
|
|
775
|
+
!eq(prepared.directOwnerCall.data, encodeSafetyAction(prepared.mandateId, actionCode, 0, "0x")))
|
|
618
776
|
throw new Error("safety_action_mismatch");
|
|
619
777
|
const signing = prepared.relayed.signing;
|
|
620
|
-
|
|
778
|
+
verifyWalletSigningPayload(signing, {
|
|
621
779
|
family: prepared.family,
|
|
622
780
|
account: prepared.account,
|
|
623
|
-
core
|
|
624
|
-
|
|
781
|
+
core,
|
|
782
|
+
payload: {
|
|
783
|
+
primaryType: "SafetyAction",
|
|
784
|
+
message: {
|
|
785
|
+
mandateId: prepared.mandateId,
|
|
786
|
+
action: actionCode,
|
|
787
|
+
nonce: prepared.nonce,
|
|
788
|
+
deadline: prepared.deadline,
|
|
789
|
+
},
|
|
790
|
+
},
|
|
625
791
|
chainId: prepared.chainId,
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
if (prepared.digest.toLowerCase() !== signing.digest.toLowerCase())
|
|
792
|
+
});
|
|
793
|
+
if (!eq(prepared.digest, signing.digest))
|
|
629
794
|
throw new Error("safety_action_mismatch");
|
|
630
795
|
const signature = await signWalletSigningPayload(signer, signing);
|
|
631
796
|
return {
|
|
632
|
-
to:
|
|
797
|
+
to: core,
|
|
633
798
|
data: encodeSafetyAction(prepared.mandateId, actionCode, prepared.deadline, signature),
|
|
634
799
|
};
|
|
635
800
|
}
|