@inco/shield-js 0.0.0-bootstrap.0 → 0.2.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 +52 -308
- package/dist/contracts/abi.d.ts +2000 -4513
- package/dist/contracts/abi.js +2400 -5651
- package/dist/contracts/index.d.ts +2 -2
- package/dist/contracts/index.js +2 -2
- package/dist/contracts/utils.d.ts +5 -24
- package/dist/contracts/utils.js +13 -24
- package/dist/errors.d.ts +16 -47
- package/dist/errors.js +6 -53
- package/dist/generated/inco/shield/v2/conductor_pb.d.ts +77 -0
- package/dist/generated/inco/shield/v2/conductor_pb.js +6 -0
- package/dist/generated/inco/shield/v2/deposit_pb.d.ts +12 -103
- package/dist/generated/inco/shield/v2/deposit_pb.js +4 -141
- package/dist/generated/inco/shield/v2/drafting_pb.d.ts +45 -269
- package/dist/generated/inco/shield/v2/drafting_pb.js +8 -361
- package/dist/generated/inco/shield/v2/types_pb.d.ts +47 -166
- package/dist/generated/inco/shield/v2/types_pb.js +12 -261
- package/dist/index.d.ts +1 -7
- package/dist/index.js +1 -14
- package/dist/shield/abi.d.ts +5 -0
- package/dist/shield/abi.js +28 -0
- package/dist/shield/client.d.ts +27 -51
- package/dist/shield/client.js +73 -65
- package/dist/shield/convert.d.ts +2 -14
- package/dist/shield/convert.js +39 -115
- package/dist/shield/encryption.d.ts +0 -67
- package/dist/shield/encryption.js +0 -67
- package/dist/shield/envelope.d.ts +10 -11
- package/dist/shield/envelope.js +15 -14
- package/dist/shield/fee.d.ts +24 -0
- package/dist/shield/fee.js +151 -0
- package/dist/shield/index.d.ts +9 -3
- package/dist/shield/index.js +7 -2
- package/dist/shield/intent.d.ts +20 -933
- package/dist/shield/intent.js +92 -112
- package/dist/shield/permission-status.d.ts +9 -0
- package/dist/shield/permission-status.js +8 -0
- package/dist/shield/rpc.d.ts +50 -49
- package/dist/shield/rpc.js +207 -354
- package/dist/shield/shield.eip712.gen.d.ts +1096 -43
- package/dist/shield/shield.eip712.gen.js +572 -62
- package/dist/shield/types.d.ts +7 -102
- package/dist/shield/types.js +1 -4
- package/dist/shield/userop/gas-estimation.d.ts +8 -23
- package/dist/shield/userop/gas-estimation.js +100 -137
- package/dist/shield/userop/paymaster.d.ts +15 -0
- package/dist/shield/userop/paymaster.js +17 -0
- package/dist/shield/userop/token-exchange.d.ts +5 -52
- package/dist/shield/userop/token-exchange.js +41 -149
- package/dist/shield/userop/types.d.ts +3 -28
- package/dist/shield/userop/types.js +0 -3
- package/dist/shield/userop/userOp.d.ts +0 -19
- package/dist/shield/userop/userOp.js +4 -22
- package/dist/shield/utils/chain.d.ts +1 -0
- package/dist/shield/utils/chain.js +3 -0
- package/dist/shield/validate-withdrawal-split.d.ts +13 -0
- package/dist/shield/validate-withdrawal-split.js +93 -0
- package/package.json +18 -27
- package/dist/generated/inco/shield/v2/conductor_connect.d.ts +0 -137
- package/dist/generated/inco/shield/v2/conductor_connect.js +0 -141
- package/dist/generated/inco/shield/v2/permission_pb.d.ts +0 -443
- package/dist/generated/inco/shield/v2/permission_pb.js +0 -639
- package/dist/generated/inco/shield/v2/query_pb.d.ts +0 -103
- package/dist/generated/inco/shield/v2/query_pb.js +0 -141
- package/dist/shield/userop/index.d.ts +0 -1
- package/dist/shield/userop/index.js +0 -1
- package/dist/uniswap-adapter/config.d.ts +0 -34
- package/dist/uniswap-adapter/config.js +0 -45
- package/dist/uniswap-adapter/find-pools.d.ts +0 -91
- package/dist/uniswap-adapter/find-pools.js +0 -108
- package/dist/uniswap-adapter/index.d.ts +0 -10
- package/dist/uniswap-adapter/index.js +0 -13
- package/dist/uniswap-adapter/swap.d.ts +0 -344
- package/dist/uniswap-adapter/swap.js +0 -309
package/dist/shield/rpc.js
CHANGED
|
@@ -1,39 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { create, fromBinary, toBinary } from '@bufbuild/protobuf';
|
|
2
|
+
import { encodeAbiParameters, keccak256, zeroHash, } from 'viem';
|
|
2
3
|
import { bytesToHex, hexToBytes } from '../binary.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { encodeIntent } from './intent.js';
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
* `permissionId = keccak256(abi.encode(...))` over the 9 identity fields. Must
|
|
17
|
-
* match the umbo helper bit-for-bit; cross-checked in `sdk/test/permission-id.test.ts`.
|
|
18
|
-
*
|
|
19
|
-
* Off-chain only — no on-chain code recomputes this.
|
|
20
|
-
*/
|
|
4
|
+
import { InvalidWithdrawalIntentOp, ShieldError } from '../errors.js';
|
|
5
|
+
import { GetLatestHandleRequestSchema, } from '../generated/inco/shield/v2/conductor_pb.js';
|
|
6
|
+
import { DepositAttestationRequestSchema } from '../generated/inco/shield/v2/deposit_pb.js';
|
|
7
|
+
import { DraftRequestSchema, DraftResponseSchema, IntentKind, } from '../generated/inco/shield/v2/drafting_pb.js';
|
|
8
|
+
import { AddressSchema, } from '../generated/inco/shield/v2/types_pb.js';
|
|
9
|
+
import { decodeStruct, encodeStruct } from './abi.js';
|
|
10
|
+
import { parseUserOp07 } from './convert.js';
|
|
11
|
+
import { openAead } from './encryption.js';
|
|
12
|
+
import { generateRandomSalt, generateResponseKey, makeEnvelope, openSignedResponse, } from './envelope.js';
|
|
13
|
+
import { withProtocolFeeLeg } from './fee.js';
|
|
14
|
+
import { createDelegatedTransferIntent, createDelegatedWithdrawIntent, createIndividualRevocationIntent, createTransferIntent, createUserWideRevocationIntent, createWithdrawIntent, encodeIntent, } from './intent.js';
|
|
15
|
+
import { EIP712_TYPES, } from './shield.eip712.gen.js';
|
|
16
|
+
import { assertWithdrawalSplitMatchesIntent } from './validate-withdrawal-split.js';
|
|
21
17
|
export function computePermissionId(input) {
|
|
22
18
|
return keccak256(encodeAbiParameters([
|
|
23
|
-
{ type: 'address' },
|
|
24
|
-
{ type: 'address' },
|
|
25
|
-
{ type: 'bytes32' },
|
|
26
|
-
{ type: 'uint256' },
|
|
27
|
-
{ type: '
|
|
28
|
-
{ type: 'uint256' },
|
|
29
|
-
{ type: 'uint256' },
|
|
30
|
-
{ type: '
|
|
31
|
-
{ type: 'bytes32' },
|
|
19
|
+
{ type: 'address' },
|
|
20
|
+
{ type: 'address' },
|
|
21
|
+
{ type: 'bytes32' },
|
|
22
|
+
{ type: 'uint256' },
|
|
23
|
+
{ type: 'address[]' },
|
|
24
|
+
{ type: 'uint256' },
|
|
25
|
+
{ type: 'uint256' },
|
|
26
|
+
{ type: 'uint256' },
|
|
27
|
+
{ type: 'bytes32' },
|
|
28
|
+
{ type: 'bytes32' },
|
|
32
29
|
], [
|
|
33
30
|
input.owner,
|
|
34
31
|
input.spender,
|
|
35
32
|
input.assetId,
|
|
36
33
|
input.allowance,
|
|
34
|
+
input.to,
|
|
37
35
|
input.start,
|
|
38
36
|
input.end,
|
|
39
37
|
input.period,
|
|
@@ -41,383 +39,238 @@ export function computePermissionId(input) {
|
|
|
41
39
|
input.salt,
|
|
42
40
|
]));
|
|
43
41
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
assetId: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
51
|
-
maxAmount: 0n,
|
|
52
|
-
callGasLimit: 2000000n,
|
|
53
|
-
verificationGasLimit: 1000000n,
|
|
54
|
-
preVerificationGas: 100000n,
|
|
55
|
-
paymasterVerificationGasLimit: 0n,
|
|
56
|
-
paymasterPostOpGasLimit: 0n,
|
|
57
|
-
maxFeePerGas: 30000000000n, // 30 gwei
|
|
58
|
-
maxPriorityFeePerGas: 1000000000n, // 1 gwei
|
|
59
|
-
};
|
|
60
|
-
export async function issueTransfer(ctx, intentOp) {
|
|
61
|
-
const intent = {
|
|
62
|
-
kind: IntentKind.TRANSFER,
|
|
63
|
-
op: intentOp,
|
|
64
|
-
signer: ctx.signer.address,
|
|
65
|
-
gas: TODO_GAS,
|
|
66
|
-
salt: generateRandomSalt(),
|
|
67
|
-
};
|
|
68
|
-
const digest = transferIntentDigest(intent, ctx.domainSeparator);
|
|
69
|
-
const signature = await ctx.signer.sign({ hash: digest });
|
|
70
|
-
const responseKey = generateResponseKey();
|
|
71
|
-
const draftRequest = new DraftRequest({
|
|
72
|
-
intent: new Uint8Array(hexToBytes(encodeIntent(IntentKind.TRANSFER, intent))),
|
|
73
|
-
intentKind: IntentKind.TRANSFER,
|
|
74
|
-
eip712Signature: new Uint8Array(hexToBytes(signature)),
|
|
75
|
-
responseKey,
|
|
42
|
+
function signTypedIntent(ctx, primaryType, message) {
|
|
43
|
+
return ctx.signer.signTypedData({
|
|
44
|
+
domain: ctx.domain,
|
|
45
|
+
types: EIP712_TYPES,
|
|
46
|
+
primaryType,
|
|
47
|
+
message,
|
|
76
48
|
});
|
|
77
|
-
const envelope = await makeEnvelope(ctx.teePublicKey, draftRequest);
|
|
78
|
-
const encResponse = await ctx.rpc.draft(envelope);
|
|
79
|
-
const response = await decryptResponse(encResponse, responseKey, DraftResponse);
|
|
80
|
-
return { userOp: parseUserOp07(response.userOp) };
|
|
81
49
|
}
|
|
82
|
-
|
|
83
|
-
// IssueWithdraw
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
export async function issueWithdraw(ctx, intentOp) {
|
|
86
|
-
const intent = {
|
|
87
|
-
kind: IntentKind.WITHDRAW,
|
|
88
|
-
op: intentOp,
|
|
89
|
-
signer: ctx.signer.address,
|
|
90
|
-
gas: TODO_GAS,
|
|
91
|
-
salt: generateRandomSalt(),
|
|
92
|
-
};
|
|
93
|
-
const digest = withdrawalIntentDigest(intent, ctx.domainSeparator);
|
|
94
|
-
const signature = await ctx.signer.sign({ hash: digest });
|
|
50
|
+
async function draft(ctx, intentKind, intent, signature) {
|
|
95
51
|
const responseKey = generateResponseKey();
|
|
96
|
-
const
|
|
97
|
-
intent:
|
|
98
|
-
intentKind
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
52
|
+
const body = toBinary(DraftRequestSchema, create(DraftRequestSchema, {
|
|
53
|
+
intent: hexToBytes(encodeIntent(intentKind, intent)),
|
|
54
|
+
intentKind,
|
|
55
|
+
}));
|
|
56
|
+
const envelope = await makeEnvelope(ctx.teePublicKey, { eip712Signature: hexToBytes(signature), responseKey }, body);
|
|
57
|
+
const resp = await ctx.rpc.draft(envelope);
|
|
58
|
+
const plaintext = await openAead(responseKey, resp.nonce, resp.ciphertext);
|
|
59
|
+
return parseUserOp07(fromBinary(DraftResponseSchema, plaintext).userOp);
|
|
60
|
+
}
|
|
61
|
+
const DEFAULT_USER_GAS = {
|
|
62
|
+
maxFeePerGas: 30000000000n,
|
|
63
|
+
maxPriorityFeePerGas: 1000000000n,
|
|
64
|
+
};
|
|
65
|
+
function feeContextOf(config) {
|
|
66
|
+
return {
|
|
67
|
+
adapter: config.feeAccrualAdapter,
|
|
68
|
+
treasury: config.protocolFeeTreasury,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export async function issueTransfer(ctx, intentOp, gas = DEFAULT_USER_GAS) {
|
|
72
|
+
const intent = await createTransferIntent(intentOp, generateRandomSalt(), ctx.signer, ctx.shieldConfig, ctx.publicClient, gas, ctx.paymasterContext);
|
|
73
|
+
const signature = await signTypedIntent(ctx, 'TransferIntent', intent);
|
|
74
|
+
return { userOp: await draft(ctx, IntentKind.TRANSFER, intent, signature) };
|
|
75
|
+
}
|
|
76
|
+
export async function issueWithdraw(ctx, intentOp, gas = DEFAULT_USER_GAS) {
|
|
77
|
+
const intent = await createWithdrawIntent(withProtocolFeeLeg(intentOp, feeContextOf(ctx.shieldConfig)), generateRandomSalt(), ctx.signer, ctx.shieldConfig, ctx.publicClient, gas, ctx.paymasterContext);
|
|
78
|
+
const signature = await signTypedIntent(ctx, 'WithdrawalIntent', intent);
|
|
79
|
+
const userOp = await draft(ctx, IntentKind.WITHDRAW, intent, signature);
|
|
80
|
+
assertWithdrawalSplitMatchesIntent(userOp, intent, ctx.shieldConfig.shieldAddress);
|
|
81
|
+
return { userOp };
|
|
106
82
|
}
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
// RequestPermissionAttestation
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
|
-
/**
|
|
111
|
-
* Owner-side issuance flow: sign the intent, hand it to the TEE, receive a
|
|
112
|
-
* long-lived `PermissionAttestation` for the spender. The spender presents
|
|
113
|
-
* the attestation on every subsequent draft.
|
|
114
|
-
*
|
|
115
|
-
* Doc-mandated client cross-check: recompute `permissionId` locally from
|
|
116
|
-
* intent terms and verify the TEE's returned attestation matches. A mismatch
|
|
117
|
-
* means the TEE substituted a different identity — fail closed.
|
|
118
|
-
*/
|
|
119
83
|
export async function requestPermissionAttestation(ctx, req) {
|
|
120
84
|
const { permission } = req;
|
|
121
85
|
if (ctx.signer.address.toLowerCase() !== permission.owner.toLowerCase()) {
|
|
122
86
|
throw new ShieldError({
|
|
123
|
-
message: 'requestPermissionAttestation: signer.address must match
|
|
87
|
+
message: 'requestPermissionAttestation: signer.address must match permission.owner',
|
|
124
88
|
});
|
|
125
89
|
}
|
|
126
90
|
const expectedPid = computePermissionId(permission);
|
|
127
|
-
const
|
|
128
|
-
const ownerSignature = await ctx.signer.sign({ hash: digest });
|
|
91
|
+
const ownerSignature = await signTypedIntent(ctx, 'PermissionRequest', req);
|
|
129
92
|
const responseKey = generateResponseKey();
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
responseKey,
|
|
135
|
-
});
|
|
136
|
-
const envelope = await makeEnvelope(ctx.teePublicKey, request);
|
|
137
|
-
const encResponse = await ctx.rpc.requestPermissionAttestation(envelope);
|
|
138
|
-
const response = await decryptResponse(encResponse, responseKey, RequestPermissionAttestationResponse);
|
|
139
|
-
const [attestation] = decodeAbiParameters([getAbiFromUnitFunction(incoShieldAbi, 'permissionAttestation')], toHex(response.permissionAttestationBytes));
|
|
140
|
-
if (attestation.payload.permissionId !== expectedPid) {
|
|
93
|
+
const envelope = await makeEnvelope(ctx.teePublicKey, { eip712Signature: hexToBytes(ownerSignature), responseKey }, hexToBytes(encodeStruct('PermissionRequest', req)));
|
|
94
|
+
const { body, teeSignature } = await openSignedResponse(await ctx.rpc.permit(envelope), responseKey);
|
|
95
|
+
const payload = decodeStruct('PermissionAttestationPayload', bytesToHex(body));
|
|
96
|
+
if (payload.permissionId !== expectedPid) {
|
|
141
97
|
throw new ShieldError({
|
|
142
|
-
message: `requestPermissionAttestation: TEE returned mismatched permissionId (expected ${expectedPid}, got ${
|
|
98
|
+
message: `requestPermissionAttestation: TEE returned mismatched permissionId (expected ${expectedPid}, got ${payload.permissionId})`,
|
|
143
99
|
});
|
|
144
100
|
}
|
|
145
|
-
return
|
|
101
|
+
return { payload, signature: bytesToHex(teeSignature) };
|
|
146
102
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
* owner's `PermissionAttestation` to the TEE, receive a spending note. The TEE
|
|
153
|
-
* verifies the attestation signature and runs all 7 validation steps before
|
|
154
|
-
* drafting the spend.
|
|
155
|
-
*/
|
|
156
|
-
export async function issueDelegatedTransfer(ctx, intentOp, attestation) {
|
|
157
|
-
const intent = {
|
|
158
|
-
kind: IntentKind.DELEGATED_TRANSFER,
|
|
159
|
-
intent: {
|
|
160
|
-
op: intentOp,
|
|
161
|
-
signer: ctx.signer.address,
|
|
162
|
-
gas: TODO_GAS,
|
|
163
|
-
salt: generateRandomSalt(),
|
|
164
|
-
},
|
|
165
|
-
spender: ctx.signer.address,
|
|
166
|
-
attestation,
|
|
103
|
+
export async function issueDelegatedTransfer(ctx, intentOp, permissionId, gas = DEFAULT_USER_GAS) {
|
|
104
|
+
const intent = await createDelegatedTransferIntent(intentOp, generateRandomSalt(), ctx.signer.address, permissionId, ctx.signer, ctx.shieldConfig, ctx.publicClient, gas, ctx.paymasterContext);
|
|
105
|
+
const spenderSignature = await signTypedIntent(ctx, 'DelegatedTransferIntent', intent);
|
|
106
|
+
return {
|
|
107
|
+
userOp: await draft(ctx, IntentKind.DELEGATED_TRANSFER, intent, spenderSignature),
|
|
167
108
|
};
|
|
168
|
-
const digest = delegatedTransferIntentDigest(intent, ctx.domainSeparator);
|
|
169
|
-
const spenderSignature = await ctx.signer.sign({ hash: digest });
|
|
170
|
-
const responseKey = generateResponseKey();
|
|
171
|
-
const draftRequest = new DraftRequest({
|
|
172
|
-
intent: new Uint8Array(hexToBytes(encodeIntent(IntentKind.DELEGATED_TRANSFER, intent))),
|
|
173
|
-
intentKind: IntentKind.DELEGATED_TRANSFER,
|
|
174
|
-
eip712Signature: new Uint8Array(hexToBytes(spenderSignature)),
|
|
175
|
-
responseKey,
|
|
176
|
-
});
|
|
177
|
-
const envelope = await makeEnvelope(ctx.teePublicKey, draftRequest);
|
|
178
|
-
const encResponse = await ctx.rpc.draft(envelope);
|
|
179
|
-
const response = await decryptResponse(encResponse, responseKey, DraftResponse);
|
|
180
|
-
return { userOp: parseUserOp07(response.userOp) };
|
|
181
109
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
attestation,
|
|
110
|
+
export async function issueDelegatedWithdraw(ctx, intentOp, permissionId, gas = DEFAULT_USER_GAS) {
|
|
111
|
+
if (intentOp.core.assetMovements.length !== 1) {
|
|
112
|
+
throw new InvalidWithdrawalIntentOp({
|
|
113
|
+
message: `delegated withdrawal must carry exactly one recipient movement, got ${intentOp.core.assetMovements.length}`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const intent = await createDelegatedWithdrawIntent(withProtocolFeeLeg(intentOp, feeContextOf(ctx.shieldConfig)), generateRandomSalt(), ctx.signer.address, permissionId, ctx.signer, ctx.shieldConfig, ctx.publicClient, gas, ctx.paymasterContext);
|
|
117
|
+
const spenderSignature = await signTypedIntent(ctx, 'DelegatedWithdrawalIntent', intent);
|
|
118
|
+
const userOp = await draft(ctx, IntentKind.DELEGATED_WITHDRAW, intent, spenderSignature);
|
|
119
|
+
assertWithdrawalSplitMatchesIntent(userOp, intent.intent, ctx.shieldConfig.shieldAddress);
|
|
120
|
+
return { userOp };
|
|
121
|
+
}
|
|
122
|
+
export async function revokeOne(ctx, params, gas = DEFAULT_USER_GAS) {
|
|
123
|
+
const intent = await createIndividualRevocationIntent({ permissionId: params.permissionId }, generateRandomSalt(), ctx.signer, ctx.shieldConfig, ctx.publicClient, gas, ctx.paymasterContext);
|
|
124
|
+
const signature = await signTypedIntent(ctx, 'IndividualRevocationIntent', intent);
|
|
125
|
+
return {
|
|
126
|
+
userOp: await draft(ctx, IntentKind.INDIVIDUAL_REVOCATION, intent, signature),
|
|
200
127
|
};
|
|
201
|
-
const digest = delegatedWithdrawalIntentDigest(intent, ctx.domainSeparator);
|
|
202
|
-
const spenderSignature = await ctx.signer.sign({ hash: digest });
|
|
203
|
-
const responseKey = generateResponseKey();
|
|
204
|
-
const draftRequest = new DraftRequest({
|
|
205
|
-
intent: new Uint8Array(hexToBytes(encodeIntent(IntentKind.DELEGATED_WITHDRAW, intent))),
|
|
206
|
-
intentKind: IntentKind.DELEGATED_WITHDRAW,
|
|
207
|
-
eip712Signature: new Uint8Array(hexToBytes(spenderSignature)),
|
|
208
|
-
responseKey,
|
|
209
|
-
});
|
|
210
|
-
const envelope = await makeEnvelope(ctx.teePublicKey, draftRequest);
|
|
211
|
-
const encResponse = await ctx.rpc.draft(envelope);
|
|
212
|
-
const response = await decryptResponse(encResponse, responseKey, DraftResponse);
|
|
213
|
-
return { userOp: parseUserOp07(response.userOp) };
|
|
214
128
|
}
|
|
215
|
-
export async function
|
|
216
|
-
const intent = {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
gas: TODO_GAS,
|
|
221
|
-
salt: generateRandomSalt(),
|
|
129
|
+
export async function revokeAll(ctx, gas = DEFAULT_USER_GAS) {
|
|
130
|
+
const intent = await createUserWideRevocationIntent({ empty: '0x' }, generateRandomSalt(), ctx.signer, ctx.shieldConfig, ctx.publicClient, gas, ctx.paymasterContext);
|
|
131
|
+
const signature = await signTypedIntent(ctx, 'UserWideRevocationIntent', intent);
|
|
132
|
+
return {
|
|
133
|
+
userOp: await draft(ctx, IntentKind.USER_WIDE_REVOCATION, intent, signature),
|
|
222
134
|
};
|
|
223
|
-
|
|
224
|
-
|
|
135
|
+
}
|
|
136
|
+
const ZERO_BYTES32 = zeroHash;
|
|
137
|
+
const FIRST_PAGE_CURSOR = {
|
|
138
|
+
anchorHandle: ZERO_BYTES32,
|
|
139
|
+
afterTimestamp: 0n,
|
|
140
|
+
afterOpIndex: 0n,
|
|
141
|
+
};
|
|
142
|
+
export async function createViewingSession(ctx, params) {
|
|
225
143
|
const responseKey = generateResponseKey();
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
144
|
+
const session = {
|
|
145
|
+
owner: params.owner ?? ctx.signer.address,
|
|
146
|
+
issuedAt: BigInt(Math.floor(Date.now() / 1000)),
|
|
147
|
+
expiresAt: params.expiresAt,
|
|
148
|
+
periodStart: params.periodStart ?? 0n,
|
|
149
|
+
periodEnd: params.periodEnd ?? 0n,
|
|
150
|
+
responseKey: bytesToHex(responseKey),
|
|
151
|
+
salt: generateRandomSalt(),
|
|
152
|
+
};
|
|
153
|
+
const signature = await signTypedIntent(ctx, 'ViewingSession', session);
|
|
154
|
+
return { session, signature };
|
|
155
|
+
}
|
|
156
|
+
async function sessionQuery(ctx, rpcMethod, viewing, name, query) {
|
|
157
|
+
const responseKey = hexToBytes(viewing.session.responseKey);
|
|
158
|
+
const envelope = await makeEnvelope(ctx.teePublicKey, { eip712Signature: hexToBytes(viewing.signature), responseKey }, hexToBytes(encodeStruct(name, query)));
|
|
159
|
+
const { body } = await openSignedResponse(await rpcMethod(envelope), responseKey);
|
|
160
|
+
return body;
|
|
161
|
+
}
|
|
162
|
+
export async function getBalance(ctx, viewing, params) {
|
|
163
|
+
const body = await sessionQuery(ctx, (e) => ctx.rpc.getBalance(e), viewing, 'BalanceQuery', {
|
|
164
|
+
session: viewing.session,
|
|
165
|
+
assetId: params.assetId,
|
|
231
166
|
});
|
|
232
|
-
|
|
233
|
-
const encResponse = await ctx.rpc.draft(envelope);
|
|
234
|
-
const response = await decryptResponse(encResponse, responseKey, DraftResponse);
|
|
235
|
-
return { userOp: parseUserOp07(response.userOp) };
|
|
167
|
+
return decodeStruct('BalanceResponse', bytesToHex(body));
|
|
236
168
|
}
|
|
237
|
-
export async function
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
169
|
+
export async function getPortfolio(ctx, viewing) {
|
|
170
|
+
const body = await sessionQuery(ctx, (e) => ctx.rpc.getPortfolio(e), viewing, 'PortfolioQuery', { session: viewing.session });
|
|
171
|
+
return decodeStruct('PortfolioResponse', bytesToHex(body));
|
|
172
|
+
}
|
|
173
|
+
export function nextHistoryCursor(response) {
|
|
174
|
+
if (!response.hasMore)
|
|
175
|
+
return undefined;
|
|
176
|
+
return {
|
|
177
|
+
anchorHandle: response.anchorHandle,
|
|
178
|
+
afterTimestamp: response.nextTimestamp,
|
|
179
|
+
afterOpIndex: response.nextOpIndex,
|
|
244
180
|
};
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
181
|
+
}
|
|
182
|
+
function historyPagination(options) {
|
|
183
|
+
return {
|
|
184
|
+
cursor: options?.cursor ?? FIRST_PAGE_CURSOR,
|
|
185
|
+
limit: options?.limit ?? 0,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
export async function getBalanceHistory(ctx, viewing, assetId, options) {
|
|
189
|
+
if (assetId === ZERO_BYTES32) {
|
|
190
|
+
throw new ShieldError({
|
|
191
|
+
message: 'getBalanceHistory requires a nonzero assetId; use getPortfolioHistory',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const body = await sessionQuery(ctx, (e) => ctx.rpc.getBalanceHistory(e), viewing, 'BalanceHistoryQuery', {
|
|
195
|
+
session: viewing.session,
|
|
196
|
+
assetId,
|
|
197
|
+
pagination: historyPagination(options),
|
|
253
198
|
});
|
|
254
|
-
|
|
255
|
-
const encResponse = await ctx.rpc.draft(envelope);
|
|
256
|
-
const response = await decryptResponse(encResponse, responseKey, DraftResponse);
|
|
257
|
-
return { userOp: parseUserOp07(response.userOp) };
|
|
199
|
+
return decodeStruct('BalanceHistoryResponse', bytesToHex(body));
|
|
258
200
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
owner: params.address,
|
|
266
|
-
assetId: params.assetId,
|
|
267
|
-
requester: ctx.signer.address,
|
|
268
|
-
queryTimestamp,
|
|
269
|
-
}, ctx.domainSeparator);
|
|
270
|
-
const signature = await ctx.signer.sign({ hash: digest });
|
|
271
|
-
const responseKey = generateResponseKey();
|
|
272
|
-
const envelope = await makeEnvelope(ctx.teePublicKey, new GetBalanceRequest({
|
|
273
|
-
address: new ProtoAddress({ value: hexToBytes(params.address) }),
|
|
274
|
-
assetId: new ProtoAssetId({ value: hexToBytes(params.assetId) }),
|
|
275
|
-
atBlock: params.atBlock ?? 0n,
|
|
276
|
-
eip712Signature: hexToBytes(signature),
|
|
277
|
-
responseKey,
|
|
278
|
-
queryTimestamp,
|
|
279
|
-
}));
|
|
280
|
-
const response = await ctx.rpc.getBalance(envelope);
|
|
281
|
-
return parseBalance(await decryptResponse(response, responseKey, GetBalanceResponse));
|
|
201
|
+
export async function getPortfolioHistory(ctx, viewing, options) {
|
|
202
|
+
const body = await sessionQuery(ctx, (e) => ctx.rpc.getPortfolioHistory(e), viewing, 'PortfolioHistoryQuery', {
|
|
203
|
+
session: viewing.session,
|
|
204
|
+
pagination: historyPagination(options),
|
|
205
|
+
});
|
|
206
|
+
return decodeStruct('PortfolioHistoryResponse', bytesToHex(body));
|
|
282
207
|
}
|
|
283
|
-
// ---------------------------------------------------------------------------
|
|
284
|
-
// RequestDepositAttestation
|
|
285
|
-
// ---------------------------------------------------------------------------
|
|
286
208
|
export async function requestDepositAttestation(ctx, params) {
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
const assetId = computeErc20AssetId(tokenAddress);
|
|
290
|
-
const digest = depositAttestationRequestDigest({
|
|
291
|
-
recipient: params.recipient,
|
|
292
|
-
assetId,
|
|
293
|
-
}, ctx.domainSeparator);
|
|
294
|
-
const signature = await ctx.signer.sign({ hash: digest });
|
|
295
|
-
const responseKey = generateResponseKey();
|
|
296
|
-
const envelope = await makeEnvelope(ctx.teePublicKey, new RequestDepositAttestationRequest({
|
|
297
|
-
recipient: new ProtoAddress({
|
|
209
|
+
const body = toBinary(DepositAttestationRequestSchema, create(DepositAttestationRequestSchema, {
|
|
210
|
+
recipient: create(AddressSchema, {
|
|
298
211
|
value: hexToBytes(params.recipient),
|
|
299
212
|
}),
|
|
300
|
-
assetAdapter:
|
|
213
|
+
assetAdapter: create(AddressSchema, {
|
|
301
214
|
value: hexToBytes(params.assetAdapter),
|
|
302
215
|
}),
|
|
303
216
|
assetIdentificationArgs: hexToBytes(params.assetIdentificationArgs),
|
|
304
217
|
memo: hexToBytes(params.memo),
|
|
305
218
|
escapeHatch: hexToBytes(params.escapeHatch),
|
|
306
|
-
eip712Signature: hexToBytes(signature),
|
|
307
|
-
responseKey,
|
|
308
|
-
depositor: new ProtoAddress({ value: hexToBytes(depositor) }),
|
|
309
219
|
}));
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
return {
|
|
316
|
-
toCiphertext: bytesToHex(decrypted.toCiphertext),
|
|
317
|
-
teeSignature: bytesToHex(decrypted.teeSignature),
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
// ---------------------------------------------------------------------------
|
|
321
|
-
// Permission query helpers
|
|
322
|
-
// ---------------------------------------------------------------------------
|
|
323
|
-
// Each query signs its own Solidity struct so every field is authenticated.
|
|
324
|
-
// Self-query: `requester` is always the signer.
|
|
325
|
-
async function signGetPermissionsByOwner(ctx, includeExpired) {
|
|
326
|
-
const query = {
|
|
327
|
-
requester: ctx.signer.address,
|
|
328
|
-
includeExpired,
|
|
329
|
-
timestamp: BigInt(Math.floor(Date.now() / 1000)),
|
|
330
|
-
};
|
|
331
|
-
const digest = getPermissionsByOwnerDigest(query, ctx.domainSeparator);
|
|
332
|
-
const sig = await ctx.signer.sign({ hash: digest });
|
|
333
|
-
const encodedQuery = encodeAbiParameters([getAbiFromUnitFunction(incoShieldAbi, 'getPermissionsByOwner')], [query]);
|
|
334
|
-
return { encodedQuery: hexToBytes(encodedQuery), signature: hexToBytes(sig) };
|
|
220
|
+
const responseKey = generateResponseKey();
|
|
221
|
+
const envelope = await makeEnvelope(ctx.teePublicKey, { responseKey }, body);
|
|
222
|
+
const { body: responseBody, teeSignature } = await openSignedResponse(await ctx.rpc.requestDepositAttestation(envelope), responseKey);
|
|
223
|
+
const payload = decodeStruct('DepositAttestationPayload', bytesToHex(responseBody));
|
|
224
|
+
return { payload, signature: bytesToHex(teeSignature) };
|
|
335
225
|
}
|
|
336
|
-
async function
|
|
226
|
+
export async function getPermissionsByOwner(ctx, params = {}) {
|
|
337
227
|
const query = {
|
|
338
228
|
requester: ctx.signer.address,
|
|
339
|
-
includeExpired,
|
|
340
|
-
timestamp: BigInt(Math.floor(Date.now() / 1000)),
|
|
229
|
+
includeExpired: params.includeExpired ?? false,
|
|
341
230
|
};
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
|
|
231
|
+
const signature = await signTypedIntent(ctx, 'GetPermissionsByOwner', query);
|
|
232
|
+
const responseKey = generateResponseKey();
|
|
233
|
+
const envelope = await makeEnvelope(ctx.teePublicKey, { eip712Signature: hexToBytes(signature), responseKey }, hexToBytes(encodeStruct('GetPermissionsByOwner', query)));
|
|
234
|
+
const { body } = await openSignedResponse(await ctx.rpc.getPermissionsByOwner(envelope), responseKey);
|
|
235
|
+
return decodeStruct('PermissionListResponse', bytesToHex(body));
|
|
346
236
|
}
|
|
347
|
-
async function
|
|
237
|
+
export async function getPermissionsBySpender(ctx, params = {}) {
|
|
348
238
|
const query = {
|
|
349
239
|
requester: ctx.signer.address,
|
|
350
|
-
|
|
351
|
-
timestamp: BigInt(Math.floor(Date.now() / 1000)),
|
|
240
|
+
includeExpired: params.includeExpired ?? false,
|
|
352
241
|
};
|
|
353
|
-
const
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
|
|
242
|
+
const signature = await signTypedIntent(ctx, 'GetPermissionsBySpender', query);
|
|
243
|
+
const responseKey = generateResponseKey();
|
|
244
|
+
const envelope = await makeEnvelope(ctx.teePublicKey, { eip712Signature: hexToBytes(signature), responseKey }, hexToBytes(encodeStruct('GetPermissionsBySpender', query)));
|
|
245
|
+
const { body } = await openSignedResponse(await ctx.rpc.getPermissionsBySpender(envelope), responseKey);
|
|
246
|
+
return decodeStruct('PermissionListResponse', bytesToHex(body));
|
|
357
247
|
}
|
|
358
|
-
async function
|
|
248
|
+
export async function getPermissionDetails(ctx, params) {
|
|
359
249
|
const query = {
|
|
360
250
|
requester: ctx.signer.address,
|
|
361
|
-
|
|
251
|
+
permissionId: params.permissionId,
|
|
362
252
|
};
|
|
363
|
-
const
|
|
364
|
-
const sig = await ctx.signer.sign({ hash: digest });
|
|
365
|
-
const encodedQuery = encodeAbiParameters([getAbiFromUnitFunction(incoShieldAbi, 'getOwnerGeneration')], [query]);
|
|
366
|
-
return { encodedQuery: hexToBytes(encodedQuery), signature: hexToBytes(sig) };
|
|
367
|
-
}
|
|
368
|
-
// ---------------------------------------------------------------------------
|
|
369
|
-
// GetPermissionsByOwner
|
|
370
|
-
// ---------------------------------------------------------------------------
|
|
371
|
-
export async function getPermissionsByOwner(ctx, params) {
|
|
372
|
-
const { encodedQuery, signature } = await signGetPermissionsByOwner(ctx, params.includeExpired ?? false);
|
|
253
|
+
const signature = await signTypedIntent(ctx, 'GetPermissionDetails', query);
|
|
373
254
|
const responseKey = generateResponseKey();
|
|
374
|
-
const envelope = await makeEnvelope(ctx.teePublicKey,
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
responseKey,
|
|
378
|
-
}));
|
|
379
|
-
const response = await ctx.rpc.getPermissionsByOwner(envelope);
|
|
380
|
-
return parsePermissions(await decryptResponse(response, responseKey, GetPermissionsResponse));
|
|
381
|
-
}
|
|
382
|
-
// ---------------------------------------------------------------------------
|
|
383
|
-
// GetPermissionsBySpender
|
|
384
|
-
// ---------------------------------------------------------------------------
|
|
385
|
-
export async function getPermissionsBySpender(ctx, params) {
|
|
386
|
-
const { encodedQuery, signature } = await signGetPermissionsBySpender(ctx, params.includeExpired ?? false);
|
|
387
|
-
const responseKey = generateResponseKey();
|
|
388
|
-
const envelope = await makeEnvelope(ctx.teePublicKey, new GetPermissionsBySpenderRequest({
|
|
389
|
-
getPermissionsBySpenderBytes: encodedQuery,
|
|
390
|
-
eip712Signature: signature,
|
|
391
|
-
responseKey,
|
|
392
|
-
}));
|
|
393
|
-
const response = await ctx.rpc.getPermissionsBySpender(envelope);
|
|
394
|
-
return parsePermissions(await decryptResponse(response, responseKey, GetPermissionsResponse));
|
|
255
|
+
const envelope = await makeEnvelope(ctx.teePublicKey, { eip712Signature: hexToBytes(signature), responseKey }, hexToBytes(encodeStruct('GetPermissionDetails', query)));
|
|
256
|
+
const { body } = await openSignedResponse(await ctx.rpc.getPermissionDetails(envelope), responseKey);
|
|
257
|
+
return decodeStruct('PermissionDetailsResponse', bytesToHex(body));
|
|
395
258
|
}
|
|
396
|
-
// ---------------------------------------------------------------------------
|
|
397
|
-
// GetPermissionDetails
|
|
398
|
-
// ---------------------------------------------------------------------------
|
|
399
|
-
export async function getPermissionDetails(ctx, params) {
|
|
400
|
-
const { encodedQuery, signature } = await signGetPermissionDetails(ctx, params.permissionId);
|
|
401
|
-
const responseKey = generateResponseKey();
|
|
402
|
-
const envelope = await makeEnvelope(ctx.teePublicKey, new GetPermissionDetailsRequest({
|
|
403
|
-
getPermissionDetailsBytes: encodedQuery,
|
|
404
|
-
eip712Signature: signature,
|
|
405
|
-
responseKey,
|
|
406
|
-
}));
|
|
407
|
-
const response = await ctx.rpc.getPermissionDetails(envelope);
|
|
408
|
-
return parsePermissionDetails(await decryptResponse(response, responseKey, GetPermissionDetailsResponse));
|
|
409
|
-
}
|
|
410
|
-
// ---------------------------------------------------------------------------
|
|
411
|
-
// GetOwnerGeneration
|
|
412
|
-
// ---------------------------------------------------------------------------
|
|
413
259
|
export async function getOwnerGeneration(ctx) {
|
|
414
|
-
const {
|
|
260
|
+
const query = { requester: ctx.signer.address };
|
|
261
|
+
const signature = await signTypedIntent(ctx, 'GetOwnerGeneration', query);
|
|
415
262
|
const responseKey = generateResponseKey();
|
|
416
|
-
const envelope = await makeEnvelope(ctx.teePublicKey,
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const response = await ctx.rpc.
|
|
422
|
-
|
|
263
|
+
const envelope = await makeEnvelope(ctx.teePublicKey, { eip712Signature: hexToBytes(signature), responseKey }, hexToBytes(encodeStruct('GetOwnerGeneration', query)));
|
|
264
|
+
const { body } = await openSignedResponse(await ctx.rpc.getOwnerGeneration(envelope), responseKey);
|
|
265
|
+
return decodeStruct('OwnerGenerationResponse', bytesToHex(body));
|
|
266
|
+
}
|
|
267
|
+
export async function getLatestHandle(ctx) {
|
|
268
|
+
const response = await ctx.rpc.getLatestHandle(create(GetLatestHandleRequestSchema));
|
|
269
|
+
const value = response.handle?.value;
|
|
270
|
+
if (value === undefined || value.length === 0) {
|
|
271
|
+
throw new ShieldError({
|
|
272
|
+
message: 'GetLatestHandle response is missing the handle',
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return bytesToHex(value);
|
|
423
276
|
}
|