@inco/shield-js 0.1.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 +7 -22
- 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 +2 -27
- 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/types.d.ts
CHANGED
|
@@ -1,119 +1,24 @@
|
|
|
1
1
|
import type { Transport as ConnectTransport } from '@connectrpc/connect';
|
|
2
|
-
import type { Hex, Transport as ViemTransport, WalletClient } from 'viem';
|
|
3
|
-
/** Minimal signer interface, structurally compatible with viem LocalAccount. */
|
|
2
|
+
import type { Hex, TypedDataDefinition, Transport as ViemTransport, WalletClient } from 'viem';
|
|
4
3
|
export interface ShieldSigner {
|
|
5
4
|
address: Hex;
|
|
6
5
|
sign(params: {
|
|
7
6
|
hash: Hex;
|
|
8
7
|
}): Promise<Hex>;
|
|
9
8
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*/
|
|
9
|
+
export interface ResolvedSigner {
|
|
10
|
+
address: Hex;
|
|
11
|
+
signTypedData(typedData: TypedDataDefinition): Promise<Hex>;
|
|
12
|
+
}
|
|
15
13
|
export type ShieldSignerInput = WalletClient | ShieldSigner;
|
|
16
|
-
/** Configuration for creating a ShieldClient. */
|
|
17
14
|
export interface ShieldConfig {
|
|
18
15
|
grpcTransport: ConnectTransport;
|
|
19
|
-
/**
|
|
20
|
-
* RPC transport for Ethereum-compatible chains.
|
|
21
|
-
*/
|
|
22
16
|
viemTransport?: ViemTransport;
|
|
23
17
|
chainId: bigint;
|
|
24
18
|
shieldAddress: Hex;
|
|
25
19
|
signer: ShieldSignerInput;
|
|
26
|
-
/**
|
|
27
|
-
* Serialized TEE public key for encrypting envelopes (known out of band).
|
|
28
|
-
* TODO: SDK should fetch this from the IncoShield contract rather than
|
|
29
|
-
* requiring it as config. Tracked separately.
|
|
30
|
-
*/
|
|
31
20
|
teePublicKey: Uint8Array;
|
|
32
|
-
/** Bundler URL. Defaults to the Pimlico public bundler if not provided. */
|
|
33
21
|
bundlerUrl?: string;
|
|
22
|
+
feeAccrualAdapter: Hex;
|
|
23
|
+
protocolFeeTreasury: Hex;
|
|
34
24
|
}
|
|
35
|
-
/** Parameters for the `getBalance` RPC call. */
|
|
36
|
-
export interface GetBalanceParams {
|
|
37
|
-
address: Hex;
|
|
38
|
-
assetId: Hex;
|
|
39
|
-
/** Query balance at a specific block. Defaults to latest. */
|
|
40
|
-
atBlock?: bigint;
|
|
41
|
-
}
|
|
42
|
-
/** Balance breakdown for an account and asset. */
|
|
43
|
-
export interface Balance {
|
|
44
|
-
confirmed: bigint;
|
|
45
|
-
pending: bigint;
|
|
46
|
-
available: bigint;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Per-issuance salt — 32-byte hex. Distinguishes intents that are otherwise
|
|
50
|
-
* field-identical and gives `permissionId` cryptographic unguessability against
|
|
51
|
-
* outside observers of revocation events.
|
|
52
|
-
*/
|
|
53
|
-
export type Salt = Hex;
|
|
54
|
-
/**
|
|
55
|
-
* Owner's terms for a delegated-spend grant. Owner signs this struct (EIP-712)
|
|
56
|
-
* once and submits it to the TEE during issuance; the TEE returns a
|
|
57
|
-
* `PermissionAttestation` and the spender carries that attestation thereafter.
|
|
58
|
-
*
|
|
59
|
-
* `permissionId` is recomputed from these fields by both sides; it is not
|
|
60
|
-
* carried in the intent itself. The TEE's `draftingHandle` anchor is chosen
|
|
61
|
-
* at issuance from enclave state, not supplied by the user.
|
|
62
|
-
*
|
|
63
|
-
* @deprecated
|
|
64
|
-
* TODO: Remove as part of https://github.com/Inco-fhevm/shield/issues/499
|
|
65
|
-
*/
|
|
66
|
-
export interface PermissionIntent {
|
|
67
|
-
owner: Hex;
|
|
68
|
-
spender: Hex;
|
|
69
|
-
assetId: Hex;
|
|
70
|
-
allowance: bigint;
|
|
71
|
-
start: bigint;
|
|
72
|
-
end: bigint;
|
|
73
|
-
period: bigint;
|
|
74
|
-
agreementHash: Hex;
|
|
75
|
-
salt: Salt;
|
|
76
|
-
intentExpiry: bigint;
|
|
77
|
-
}
|
|
78
|
-
/** Parameters for the `requestDepositAttestation` RPC call. */
|
|
79
|
-
export interface RequestDepositAttestationParams {
|
|
80
|
-
recipient: Hex;
|
|
81
|
-
assetAdapter: Hex;
|
|
82
|
-
assetIdentificationArgs: Hex;
|
|
83
|
-
memo: Hex;
|
|
84
|
-
escapeHatch: Hex;
|
|
85
|
-
}
|
|
86
|
-
/** TEE-issued deposit attestation: ciphertext for the recipient and signature over it. */
|
|
87
|
-
export interface DepositAttestation {
|
|
88
|
-
toCiphertext: Hex;
|
|
89
|
-
teeSignature: Hex;
|
|
90
|
-
}
|
|
91
|
-
export interface GetPermissionsByOwnerParams {
|
|
92
|
-
includeExpired?: boolean;
|
|
93
|
-
}
|
|
94
|
-
export interface GetPermissionsBySpenderParams {
|
|
95
|
-
includeExpired?: boolean;
|
|
96
|
-
}
|
|
97
|
-
export type PermissionStatusType = 'unspecified' | 'active' | 'not_yet_active' | 'expired' | 'revoked' | 'generation_revoked';
|
|
98
|
-
export interface PermissionInfo {
|
|
99
|
-
permissionId: Hex;
|
|
100
|
-
owner: Hex;
|
|
101
|
-
spender: Hex;
|
|
102
|
-
assetId: Hex;
|
|
103
|
-
allowance: bigint;
|
|
104
|
-
start: bigint;
|
|
105
|
-
end: bigint;
|
|
106
|
-
period: bigint;
|
|
107
|
-
agreementHash: Hex;
|
|
108
|
-
spentInCurrentPeriod: bigint;
|
|
109
|
-
availableAllowance: bigint;
|
|
110
|
-
status: PermissionStatusType;
|
|
111
|
-
}
|
|
112
|
-
export interface GetPermissionDetailsParams {
|
|
113
|
-
permissionId: Hex;
|
|
114
|
-
}
|
|
115
|
-
export interface PermissionDetails {
|
|
116
|
-
permission: PermissionInfo;
|
|
117
|
-
found: boolean;
|
|
118
|
-
}
|
|
119
|
-
export { ShieldError } from '../errors.js';
|
package/dist/shield/types.js
CHANGED
|
@@ -1,4 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// Errors (re-export — concrete classes live in `../errors.ts`)
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
export { ShieldError } from '../errors.js';
|
|
1
|
+
export {};
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Constitution: Creates an intent with gas estimation.
|
|
3
|
-
*
|
|
4
|
-
* The flow is:
|
|
5
|
-
* 1. Create a placeholder UserOp from the user's intent op
|
|
6
|
-
* 2. Get the gas estimate from the bundler using the placeholder UserOp
|
|
7
|
-
* 3. Assemble the final intent with the gas estimate
|
|
8
|
-
*/
|
|
9
1
|
import { type Address, type Hex, type PublicClient } from 'viem';
|
|
10
2
|
import { type UserOperation } from 'viem/account-abstraction';
|
|
11
3
|
import type { IntentOp, UserGasParams } from '../intent.js';
|
|
12
|
-
import type { ShieldConfig
|
|
4
|
+
import type { ShieldConfig } from '../types.js';
|
|
13
5
|
declare const ENTRYPOINT_VERSION = "0.7";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
export interface EstimationPlaceholder {
|
|
7
|
+
readonly userOp: UserOperation<typeof ENTRYPOINT_VERSION>;
|
|
8
|
+
readonly signerCount: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function reserveUnmeasuredSignatureGas(verificationGasLimit: bigint, signerCount: number): bigint;
|
|
17
11
|
export declare function placeholderMultisig(signers: readonly Address[]): Hex;
|
|
18
|
-
/** Exported for tests that pin the expected placeholder byte counts. */
|
|
19
12
|
export declare const PLACEHOLDER_SIZES: {
|
|
20
13
|
readonly transferCiphertext: number;
|
|
21
14
|
readonly withdrawalCiphertext: number;
|
|
@@ -24,13 +17,5 @@ export declare const PLACEHOLDER_SIZES: {
|
|
|
24
17
|
readonly erc20AssetIdentificationArgs: 32;
|
|
25
18
|
readonly erc20MovementArgs: 32;
|
|
26
19
|
};
|
|
27
|
-
|
|
28
|
-
* Build an unsigned UserOp for bundler gas estimation.
|
|
29
|
-
*
|
|
30
|
-
* Invariant: every wire-shape field matches the size of what the conductor will
|
|
31
|
-
* submit. Cheque fields are sized against worst-case plaintexts (over-estimate,
|
|
32
|
-
* never under); adapter args are ERC-20-sized — new adapters need entries in
|
|
33
|
-
* `PLACEHOLDER_SIZES`.
|
|
34
|
-
*/
|
|
35
|
-
export declare function userOpFromIntentForGasEstimation(intentOp: IntentOp, signer: ShieldSigner, shieldConfig: ShieldConfig, publicClient: PublicClient, gas: UserGasParams): Promise<UserOperation<typeof ENTRYPOINT_VERSION>>;
|
|
20
|
+
export declare function userOpFromIntentForGasEstimation(intentOp: IntentOp, shieldConfig: ShieldConfig, publicClient: PublicClient, gas: UserGasParams, paymaster?: Address): Promise<EstimationPlaceholder>;
|
|
36
21
|
export {};
|
|
@@ -1,110 +1,83 @@
|
|
|
1
|
-
|
|
2
|
-
* Constitution: Creates an intent with gas estimation.
|
|
3
|
-
*
|
|
4
|
-
* The flow is:
|
|
5
|
-
* 1. Create a placeholder UserOp from the user's intent op
|
|
6
|
-
* 2. Get the gas estimate from the bundler using the placeholder UserOp
|
|
7
|
-
* 3. Assemble the final intent with the gas estimate
|
|
8
|
-
*/
|
|
9
|
-
import { bytesToHex, concat, encodeAbiParameters, isAddress, isHex, maxUint64, maxUint256, zeroAddress, zeroHash, } from 'viem';
|
|
1
|
+
import { bytesToHex, concat, encodeAbiParameters, isAddress, maxUint64, maxUint256, zeroAddress, zeroHash, } from 'viem';
|
|
10
2
|
import { entryPoint07Abi, entryPoint07Address, } from 'viem/account-abstraction';
|
|
11
|
-
import { incoShieldAbi,
|
|
12
|
-
import {
|
|
3
|
+
import { incoShieldAbi, teeSmartAccountAbi } from '../../contracts/abi.js';
|
|
4
|
+
import { erc20Asset, NATIVE_ASSET } from '../../contracts/utils.js';
|
|
13
5
|
import { InvalidContractReturnType } from '../../errors.js';
|
|
14
6
|
import { IntentKind } from '../../generated/inco/shield/v2/drafting_pb.js';
|
|
7
|
+
import { encodeStruct } from '../abi.js';
|
|
15
8
|
import { chequeBlobSize } from '../encryption.js';
|
|
16
9
|
import { assembleUserOp, buildUnsignedUserOp } from './userOp.js';
|
|
10
|
+
const ESTIMATION_STUB_PAYMASTER_DATA = encodeAbiParameters([{ type: 'bytes' }, { type: 'bytes' }], ['0x', '0x']);
|
|
17
11
|
const ENTRYPOINT_VERSION = '0.7';
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
const GENESIS_HANDLE = zeroHash;
|
|
13
|
+
const PER_SIGNATURE_VERIFICATION_GAS = 4000n;
|
|
14
|
+
export function reserveUnmeasuredSignatureGas(verificationGasLimit, signerCount) {
|
|
15
|
+
const unmeasuredSignatures = BigInt(Math.max(0, signerCount - 1));
|
|
16
|
+
return (verificationGasLimit + unmeasuredSignatures * PER_SIGNATURE_VERIFICATION_GAS);
|
|
17
|
+
}
|
|
20
18
|
const PLACEHOLDER_ECDSA_SIG = concat([
|
|
21
|
-
bytesToHex(new Uint8Array(32).fill(0xff)),
|
|
22
|
-
`0x7${'a'.repeat(63)}`,
|
|
23
|
-
'0x1c',
|
|
19
|
+
bytesToHex(new Uint8Array(32).fill(0xff)),
|
|
20
|
+
`0x7${'a'.repeat(63)}`,
|
|
21
|
+
'0x1c',
|
|
24
22
|
]);
|
|
25
|
-
/** Placeholder `abi.encode(bytes[] signers, bytes[] sigs)`. Real signer addresses
|
|
26
|
-
* required: OZ's `isSigner` check runs before any `ecrecover`, so zero-address
|
|
27
|
-
* signers short-circuit the validation loop and under-quote gas. */
|
|
28
23
|
export function placeholderMultisig(signers) {
|
|
29
24
|
const sigs = signers.map(() => PLACEHOLDER_ECDSA_SIG);
|
|
30
25
|
return encodeAbiParameters([{ type: 'bytes[]' }, { type: 'bytes[]' }], [signers, sigs]);
|
|
31
26
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Given a plaintext encoded as a 0x-prefixed hex string, return the ciphertext size.
|
|
34
|
-
*/
|
|
35
27
|
function ptToCtSize(pt) {
|
|
36
|
-
|
|
37
|
-
// We should use ciphertext size.
|
|
38
|
-
return chequeBlobSize(pt.length / 2 - 1); // Hex to bytes length
|
|
28
|
+
return chequeBlobSize(pt.length / 2 - 1);
|
|
39
29
|
}
|
|
40
|
-
const TRANSFER_PLAINTEXT =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const SENDER_ESCAPE_PLAINTEXT =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const INDIVIDUAL_REVOCATION_PLAINTEXT = encodeAbiParameters([getAbiFromUnitFunction(shieldAbiStubAbi, 'individualRevocationPlaintext')], [{ owner: zeroAddress, permissionId: zeroHash }]);
|
|
73
|
-
const USER_WIDE_REVOCATION_PLAINTEXT = encodeAbiParameters([getAbiFromUnitFunction(shieldAbiStubAbi, 'userWideRevocationPlaintext')], [{ owner: zeroAddress }]);
|
|
74
|
-
/** `abi.encode(address)` is one ABI word (32 bytes) regardless of adapter. */
|
|
30
|
+
const TRANSFER_PLAINTEXT = encodeStruct('TransferPlaintext', {
|
|
31
|
+
sender: zeroAddress,
|
|
32
|
+
receiver: zeroAddress,
|
|
33
|
+
amount: maxUint256,
|
|
34
|
+
assetId: zeroHash,
|
|
35
|
+
});
|
|
36
|
+
const WITHDRAWAL_PLAINTEXT = encodeStruct('WithdrawalPlaintext', {
|
|
37
|
+
sender: zeroAddress,
|
|
38
|
+
});
|
|
39
|
+
const SENDER_ESCAPE_PLAINTEXT = encodeStruct('SenderEscapePlaintext', {
|
|
40
|
+
sender: zeroAddress,
|
|
41
|
+
assetId: zeroHash,
|
|
42
|
+
draftingHandle: zeroHash,
|
|
43
|
+
spendAmount: maxUint256,
|
|
44
|
+
balanceSnapshot: maxUint256,
|
|
45
|
+
chequeNumber: maxUint64,
|
|
46
|
+
oldestPossiblyOutstanding: maxUint64,
|
|
47
|
+
});
|
|
48
|
+
const RECEIVER_ESCAPE_PLAINTEXT = encodeStruct('ReceiverEscapePlaintext', {
|
|
49
|
+
receiver: zeroAddress,
|
|
50
|
+
assetId: zeroHash,
|
|
51
|
+
draftingHandle: zeroHash,
|
|
52
|
+
receiveAmount: maxUint256,
|
|
53
|
+
balanceSnapshot: maxUint256,
|
|
54
|
+
chequeNullifier: zeroHash,
|
|
55
|
+
senderChequeNumber: maxUint64,
|
|
56
|
+
oldestPossiblyOutstandingSend: maxUint64,
|
|
57
|
+
});
|
|
58
|
+
const INDIVIDUAL_REVOCATION_PLAINTEXT = encodeStruct('IndividualRevocationPlaintext', { owner: zeroAddress, permissionId: zeroHash });
|
|
59
|
+
const USER_WIDE_REVOCATION_PLAINTEXT = encodeStruct('UserWideRevocationPlaintext', {
|
|
60
|
+
owner: zeroAddress,
|
|
61
|
+
});
|
|
75
62
|
const ERC20_ASSET_IDENTIFICATION_ARGS_SIZE = 32;
|
|
76
|
-
/** `abi.encode(uint256)` is one ABI word (32 bytes). */
|
|
77
63
|
const ERC20_MOVEMENT_ARGS_SIZE = 32;
|
|
78
|
-
/** Non-zero hex of `byteLength` bytes. Zero bytes cost 4 gas vs 16 for non-zero,
|
|
79
|
-
* so `zeroHash`-style placeholders under-quote calldata gas by ~4×. */
|
|
80
64
|
function nonZeroPlaceholder(byteLength) {
|
|
81
65
|
return bytesToHex(new Uint8Array(byteLength).fill(0xaa));
|
|
82
66
|
}
|
|
83
|
-
|
|
84
|
-
* (memo, recipient, asset movement, …) are real; enclave-derived fields use
|
|
85
|
-
* worst-case-sized placeholders so the estimate never under-quotes. Per-kind
|
|
86
|
-
* cases keep TS narrowing intact across the discriminated union.
|
|
87
|
-
*
|
|
88
|
-
* `draftingHandle` must be a handle known on-chain — `IncoShield.transfer` and
|
|
89
|
-
* `processWithdrawalCore` revert with `PastShieldHandleNotFound` against an
|
|
90
|
-
* unknown handle. Caller reads the live handle via `IncoShield.getHandle()` and
|
|
91
|
-
* threads it in, though it need not be the handle the enclave ultimately drafts
|
|
92
|
-
* against. `bytes32` slots use a nonzero placeholder so calldata gas (16/byte)
|
|
93
|
-
* matches the eventual real values rather than zero bytes (4/byte). */
|
|
94
|
-
function toPlaceholderSettlementOp(intentOp, currentHandle) {
|
|
67
|
+
function toPlaceholderSettlementOp(intentOp) {
|
|
95
68
|
switch (intentOp.kind) {
|
|
96
69
|
case IntentKind.TRANSFER:
|
|
97
70
|
case IntentKind.DELEGATED_TRANSFER:
|
|
98
71
|
return {
|
|
99
72
|
kind: intentOp.kind,
|
|
100
|
-
op: transferPlaceholder(intentOp.op.memo
|
|
73
|
+
op: transferPlaceholder(intentOp.op.memo),
|
|
101
74
|
};
|
|
102
75
|
case IntentKind.WITHDRAW:
|
|
103
76
|
case IntentKind.DELEGATED_WITHDRAW:
|
|
104
77
|
return {
|
|
105
78
|
kind: intentOp.kind,
|
|
106
79
|
op: {
|
|
107
|
-
core: withdrawalCorePlaceholder(intentOp.op.core
|
|
80
|
+
core: withdrawalCorePlaceholder(intentOp.op.core),
|
|
108
81
|
memo: intentOp.op.memo,
|
|
109
82
|
},
|
|
110
83
|
};
|
|
@@ -113,7 +86,7 @@ function toPlaceholderSettlementOp(intentOp, currentHandle) {
|
|
|
113
86
|
return {
|
|
114
87
|
kind: intentOp.kind,
|
|
115
88
|
op: {
|
|
116
|
-
withdrawal: withdrawalCorePlaceholder(intentOp.op.withdrawal
|
|
89
|
+
withdrawal: withdrawalCorePlaceholder(intentOp.op.withdrawal),
|
|
117
90
|
defiAdapter: intentOp.op.defiAdapter,
|
|
118
91
|
adapterArgs: intentOp.op.adapterArgs,
|
|
119
92
|
memo: intentOp.op.memo,
|
|
@@ -139,9 +112,9 @@ function toPlaceholderSettlementOp(intentOp, currentHandle) {
|
|
|
139
112
|
};
|
|
140
113
|
}
|
|
141
114
|
}
|
|
142
|
-
function transferPlaceholder(memo
|
|
115
|
+
function transferPlaceholder(memo) {
|
|
143
116
|
return {
|
|
144
|
-
draftingHandle:
|
|
117
|
+
draftingHandle: GENESIS_HANDLE,
|
|
145
118
|
draftingTimestamp: maxUint64,
|
|
146
119
|
nullifier: nonZeroPlaceholder(32),
|
|
147
120
|
senderEscapeCommitment: nonZeroPlaceholder(32),
|
|
@@ -152,20 +125,18 @@ function transferPlaceholder(memo, currentHandle) {
|
|
|
152
125
|
memo,
|
|
153
126
|
};
|
|
154
127
|
}
|
|
155
|
-
function withdrawalCorePlaceholder(core
|
|
128
|
+
function withdrawalCorePlaceholder(core) {
|
|
156
129
|
return {
|
|
157
|
-
|
|
130
|
+
asset: core.asset,
|
|
131
|
+
assetMovements: core.assetMovements,
|
|
158
132
|
nullifier: nonZeroPlaceholder(32),
|
|
159
|
-
|
|
160
|
-
draftingHandle: currentHandle,
|
|
133
|
+
draftingHandle: GENESIS_HANDLE,
|
|
161
134
|
draftingTimestamp: maxUint64,
|
|
162
|
-
assetMovement: core.assetMovement,
|
|
163
135
|
ciphertext: nonZeroPlaceholder(ptToCtSize(WITHDRAWAL_PLAINTEXT)),
|
|
164
136
|
senderEscapeCommitment: nonZeroPlaceholder(32),
|
|
165
137
|
senderEscapeData: nonZeroPlaceholder(ptToCtSize(SENDER_ESCAPE_PLAINTEXT)),
|
|
166
138
|
};
|
|
167
139
|
}
|
|
168
|
-
/** Exported for tests that pin the expected placeholder byte counts. */
|
|
169
140
|
export const PLACEHOLDER_SIZES = {
|
|
170
141
|
transferCiphertext: ptToCtSize(TRANSFER_PLAINTEXT),
|
|
171
142
|
withdrawalCiphertext: ptToCtSize(WITHDRAWAL_PLAINTEXT),
|
|
@@ -174,23 +145,22 @@ export const PLACEHOLDER_SIZES = {
|
|
|
174
145
|
erc20AssetIdentificationArgs: ERC20_ASSET_IDENTIFICATION_ARGS_SIZE,
|
|
175
146
|
erc20MovementArgs: ERC20_MOVEMENT_ARGS_SIZE,
|
|
176
147
|
};
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* never under); adapter args are ERC-20-sized — new adapters need entries in
|
|
183
|
-
* `PLACEHOLDER_SIZES`.
|
|
184
|
-
*/
|
|
185
|
-
export async function userOpFromIntentForGasEstimation(intentOp, signer, shieldConfig, publicClient, gas) {
|
|
186
|
-
const [nonce, signersForPlaceholder, currentHandle, paymasterGasLimits] = await Promise.all([
|
|
187
|
-
getEntryPointNonce(publicClient, signer.address),
|
|
188
|
-
getQuorumSignersForPlaceholder(publicClient, signer.address),
|
|
189
|
-
getCurrentHandle(publicClient, shieldConfig.shieldAddress),
|
|
148
|
+
export async function userOpFromIntentForGasEstimation(intentOp, shieldConfig, publicClient, gas, paymaster) {
|
|
149
|
+
const teeSmartAccount = await getTeeSmartAccount(publicClient, shieldConfig.shieldAddress);
|
|
150
|
+
const [nonce, signersForPlaceholder, paymasterGasLimits] = await Promise.all([
|
|
151
|
+
getEntryPointNonce(publicClient, teeSmartAccount),
|
|
152
|
+
getQuorumSignersForPlaceholder(publicClient, teeSmartAccount),
|
|
190
153
|
getPaymasterGasLimits(),
|
|
191
154
|
]);
|
|
192
155
|
const placeholderGas = {
|
|
193
|
-
|
|
156
|
+
asset: gas.feeToken === undefined ? NATIVE_ASSET : erc20Asset(gas.feeToken),
|
|
157
|
+
assetMovement: {
|
|
158
|
+
account: zeroAddress,
|
|
159
|
+
assetAdapter: zeroAddress,
|
|
160
|
+
movementArgs: '0x',
|
|
161
|
+
},
|
|
162
|
+
maxFeePerGas: gas.maxFeePerGas,
|
|
163
|
+
maxPriorityFeePerGas: gas.maxPriorityFeePerGas,
|
|
194
164
|
callGasLimit: 0n,
|
|
195
165
|
verificationGasLimit: 0n,
|
|
196
166
|
preVerificationGas: 0n,
|
|
@@ -198,16 +168,26 @@ export async function userOpFromIntentForGasEstimation(intentOp, signer, shieldC
|
|
|
198
168
|
paymasterPostOpGasLimit: paymasterGasLimits.paymasterPostOpGasLimit,
|
|
199
169
|
maxAmount: 0n,
|
|
200
170
|
};
|
|
171
|
+
const paymasterFields = paymaster === undefined
|
|
172
|
+
? undefined
|
|
173
|
+
: {
|
|
174
|
+
paymaster,
|
|
175
|
+
paymasterVerificationGasLimit: paymasterGasLimits.paymasterVerificationGasLimit,
|
|
176
|
+
paymasterPostOpGasLimit: paymasterGasLimits.paymasterPostOpGasLimit,
|
|
177
|
+
paymasterData: ESTIMATION_STUB_PAYMASTER_DATA,
|
|
178
|
+
};
|
|
201
179
|
const unsigned = buildUnsignedUserOp({
|
|
202
|
-
settlementOp: toPlaceholderSettlementOp(intentOp
|
|
203
|
-
sender:
|
|
180
|
+
settlementOp: toPlaceholderSettlementOp(intentOp),
|
|
181
|
+
sender: teeSmartAccount,
|
|
204
182
|
nonce,
|
|
205
183
|
shieldAddress: shieldConfig.shieldAddress,
|
|
206
184
|
gas: placeholderGas,
|
|
185
|
+
paymaster: paymasterFields,
|
|
207
186
|
});
|
|
208
|
-
return
|
|
209
|
-
|
|
210
|
-
|
|
187
|
+
return {
|
|
188
|
+
userOp: assembleUserOp(unsigned, placeholderMultisig(signersForPlaceholder)),
|
|
189
|
+
signerCount: signersForPlaceholder.length,
|
|
190
|
+
};
|
|
211
191
|
async function getEntryPointNonce(publicClient, sender) {
|
|
212
192
|
const nonce = await publicClient.readContract({
|
|
213
193
|
address: entryPoint07Address,
|
|
@@ -224,23 +204,13 @@ export async function userOpFromIntentForGasEstimation(intentOp, signer, shieldC
|
|
|
224
204
|
}
|
|
225
205
|
return nonce;
|
|
226
206
|
}
|
|
227
|
-
/** Read the TEE quorum signers from `TeeSmartAccount` and slice to threshold T.
|
|
228
|
-
* Real addresses let the bundler's `isSigner` loop traverse fully before
|
|
229
|
-
* short-circuiting on `DUMMY_ECDSA_SIG`. */
|
|
230
207
|
async function getQuorumSignersForPlaceholder(publicClient, account) {
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}),
|
|
238
|
-
publicClient.readContract({
|
|
239
|
-
address: account,
|
|
240
|
-
abi: teeSmartAccountAbi,
|
|
241
|
-
functionName: 'threshold',
|
|
242
|
-
}),
|
|
243
|
-
]);
|
|
208
|
+
const signersRaw = await publicClient.readContract({
|
|
209
|
+
address: account,
|
|
210
|
+
abi: teeSmartAccountAbi,
|
|
211
|
+
functionName: 'getSigners',
|
|
212
|
+
args: [0n, maxUint64],
|
|
213
|
+
});
|
|
244
214
|
if (!Array.isArray(signersRaw) ||
|
|
245
215
|
!signersRaw.every((s) => isAddress(s))) {
|
|
246
216
|
throw new InvalidContractReturnType({
|
|
@@ -249,29 +219,22 @@ export async function userOpFromIntentForGasEstimation(intentOp, signer, shieldC
|
|
|
249
219
|
expectedType: 'readonly Address[]',
|
|
250
220
|
});
|
|
251
221
|
}
|
|
252
|
-
|
|
253
|
-
throw new InvalidContractReturnType({
|
|
254
|
-
message: `TeeSmartAccount.threshold returned non-bigint: ${typeof thresholdRaw}`,
|
|
255
|
-
functionName: 'TeeSmartAccount.threshold',
|
|
256
|
-
expectedType: 'bigint',
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
return signersRaw.slice(0, Number(thresholdRaw));
|
|
222
|
+
return signersRaw;
|
|
260
223
|
}
|
|
261
|
-
async function
|
|
262
|
-
const
|
|
224
|
+
async function getTeeSmartAccount(publicClient, shieldAddress) {
|
|
225
|
+
const account = await publicClient.readContract({
|
|
263
226
|
address: shieldAddress,
|
|
264
227
|
abi: incoShieldAbi,
|
|
265
|
-
functionName: '
|
|
228
|
+
functionName: 'teeSmartAccount',
|
|
266
229
|
});
|
|
267
|
-
if (!
|
|
230
|
+
if (!isAddress(account)) {
|
|
268
231
|
throw new InvalidContractReturnType({
|
|
269
|
-
message: `IncoShield.
|
|
270
|
-
functionName: 'IncoShield.
|
|
271
|
-
expectedType: '
|
|
232
|
+
message: `IncoShield.teeSmartAccount returned a non-address: ${String(account)}`,
|
|
233
|
+
functionName: 'IncoShield.teeSmartAccount',
|
|
234
|
+
expectedType: 'Address',
|
|
272
235
|
});
|
|
273
236
|
}
|
|
274
|
-
return
|
|
237
|
+
return account;
|
|
275
238
|
}
|
|
276
239
|
async function getPaymasterGasLimits() {
|
|
277
240
|
const defaultPMVerificationGasLimit = 100000n;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
import type { Gas, Withdrawal } from '../shield.eip712.gen.js';
|
|
3
|
+
export type BuildPaymasterDataParams = {
|
|
4
|
+
paymaster: Address;
|
|
5
|
+
gas: Gas;
|
|
6
|
+
withdrawal: Withdrawal;
|
|
7
|
+
teeSignature?: Hex;
|
|
8
|
+
};
|
|
9
|
+
export type PaymasterFields = {
|
|
10
|
+
paymaster: Address;
|
|
11
|
+
paymasterVerificationGasLimit: bigint;
|
|
12
|
+
paymasterPostOpGasLimit: bigint;
|
|
13
|
+
paymasterData: Hex;
|
|
14
|
+
};
|
|
15
|
+
export declare function buildPaymasterData(params: BuildPaymasterDataParams): PaymasterFields;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { encodeAbiParameters } from 'viem';
|
|
2
|
+
import { encodeStruct } from '../abi.js';
|
|
3
|
+
const paymasterDataAbiParams = [{ type: 'bytes' }, { type: 'bytes' }];
|
|
4
|
+
export function buildPaymasterData(params) {
|
|
5
|
+
return {
|
|
6
|
+
paymaster: params.paymaster,
|
|
7
|
+
paymasterVerificationGasLimit: params.gas.paymasterVerificationGasLimit,
|
|
8
|
+
paymasterPostOpGasLimit: params.gas.paymasterPostOpGasLimit,
|
|
9
|
+
paymasterData: encodeAbiParameters(paymasterDataAbiParams, [
|
|
10
|
+
encodeWithdrawal(params.withdrawal),
|
|
11
|
+
params.teeSignature ?? '0x',
|
|
12
|
+
]),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function encodeWithdrawal(withdrawal) {
|
|
16
|
+
return encodeStruct('Withdrawal', withdrawal);
|
|
17
|
+
}
|
|
@@ -1,54 +1,7 @@
|
|
|
1
|
-
import { type Address, type
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
readonly decimals: number;
|
|
5
|
-
readonly updatedAt: bigint;
|
|
6
|
-
readonly description: string;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Fetch the latest price from a single Chainlink aggregator contract.
|
|
10
|
-
*
|
|
11
|
-
* The aggregator's `description` (e.g. "ETH / USD") encodes the pair —
|
|
12
|
-
* there is no on-chain way to enforce that callers passed the right address.
|
|
13
|
-
*/
|
|
14
|
-
export declare function getAggregatorRate(publicClient: PublicClient, aggregator: Address): Promise<AggregatorRate>;
|
|
15
|
-
declare const UnknownAssetFeed_base: new <Fields extends Record<string, unknown>>(fields: Fields) => Error & {
|
|
16
|
-
readonly _tag: "UnknownAssetFeed";
|
|
17
|
-
} & Readonly<Fields>;
|
|
18
|
-
export declare class UnknownAssetFeed extends UnknownAssetFeed_base<{
|
|
19
|
-
readonly assetId: Hex;
|
|
20
|
-
}> {
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Resolve the Chainlink aggregator for a Shield `assetId` (= ERC-20 token
|
|
24
|
-
* address padded to bytes32) and return its latest rate.
|
|
25
|
-
*/
|
|
26
|
-
export declare function getAssetRate(publicClient: PublicClient, assetId: Hex): Promise<AggregatorRate>;
|
|
27
|
-
/** RAY = 1e27, the fixed-point scale used by the Shield paymaster. */
|
|
1
|
+
import { type Address, type PublicClient } from 'viem';
|
|
2
|
+
export declare function getPaymasterAssetAdapter(publicClient: PublicClient, paymaster: Address): Promise<Address>;
|
|
3
|
+
export declare function assertFeeTokenSupported(publicClient: PublicClient, paymaster: Address, token: Address): Promise<void>;
|
|
28
4
|
export declare const RAY: bigint;
|
|
29
|
-
|
|
30
|
-
* Compute the RAY-precision exchange rate (ETH priced in token units)
|
|
31
|
-
* that the Shield paymaster consumes. Mirrors the on-chain convention
|
|
32
|
-
* from SHIELD_PAYMASTER.md:
|
|
33
|
-
*
|
|
34
|
-
* tokenAmount = ceil(gasCostWei * exchangeRate / 1e27)
|
|
35
|
-
*
|
|
36
|
-
* Worked example: if 1 ETH = 2000 USDC and USDC has 6 decimals,
|
|
37
|
-
* exchangeRate = 2000 * 1e6 * 1e27 / 1e18 = 2e18
|
|
38
|
-
*
|
|
39
|
-
* Derivation here uses two Chainlink USD feeds:
|
|
40
|
-
* ethInToken (real) = ethUsd / tokenUsd
|
|
41
|
-
* rate (RAY) = ethInToken * 10^tokenDec * 1e27 / 1e18
|
|
42
|
-
* = ethUsd * 10^tokenDec * 10^tokenFeedDec * RAY
|
|
43
|
-
* / (tokenUsd * 10^ethFeedDec * 1e18)
|
|
44
|
-
*/
|
|
45
|
-
export declare function getEthExchangeRateRay(publicClient: PublicClient, assetId: Hex): Promise<bigint>;
|
|
46
|
-
/** `ceil(ethWei * exchangeRate / RAY)` — matches the paymaster's rounding. */
|
|
5
|
+
export declare function getEthExchangeRateRay(publicClient: PublicClient, paymaster: Address, token: Address): Promise<bigint>;
|
|
47
6
|
export declare function applyExchangeRateRayCeil(ethWei: bigint, exchangeRateRay: bigint): bigint;
|
|
48
|
-
|
|
49
|
-
* Convenience: fetch the RAY rate for `assetId` and apply it to `ethWei`.
|
|
50
|
-
* Use `getEthExchangeRateRay` directly when the caller also needs to
|
|
51
|
-
* forward the rate to the paymaster signer.
|
|
52
|
-
*/
|
|
53
|
-
export declare function convertEthWeiToTokenUnits(publicClient: PublicClient, assetId: Hex, ethWei: bigint): Promise<bigint>;
|
|
54
|
-
export {};
|
|
7
|
+
export declare function convertEthWeiToTokenUnits(publicClient: PublicClient, paymaster: Address, token: Address, ethWei: bigint): Promise<bigint>;
|