@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.
Files changed (74) hide show
  1. package/README.md +52 -308
  2. package/dist/contracts/abi.d.ts +2000 -4513
  3. package/dist/contracts/abi.js +2400 -5651
  4. package/dist/contracts/index.d.ts +2 -2
  5. package/dist/contracts/index.js +2 -2
  6. package/dist/contracts/utils.d.ts +5 -24
  7. package/dist/contracts/utils.js +13 -24
  8. package/dist/errors.d.ts +16 -47
  9. package/dist/errors.js +6 -53
  10. package/dist/generated/inco/shield/v2/conductor_pb.d.ts +77 -0
  11. package/dist/generated/inco/shield/v2/conductor_pb.js +6 -0
  12. package/dist/generated/inco/shield/v2/deposit_pb.d.ts +12 -103
  13. package/dist/generated/inco/shield/v2/deposit_pb.js +4 -141
  14. package/dist/generated/inco/shield/v2/drafting_pb.d.ts +45 -269
  15. package/dist/generated/inco/shield/v2/drafting_pb.js +8 -361
  16. package/dist/generated/inco/shield/v2/types_pb.d.ts +47 -166
  17. package/dist/generated/inco/shield/v2/types_pb.js +12 -261
  18. package/dist/index.d.ts +1 -7
  19. package/dist/index.js +1 -14
  20. package/dist/shield/abi.d.ts +5 -0
  21. package/dist/shield/abi.js +28 -0
  22. package/dist/shield/client.d.ts +27 -51
  23. package/dist/shield/client.js +73 -65
  24. package/dist/shield/convert.d.ts +2 -14
  25. package/dist/shield/convert.js +39 -115
  26. package/dist/shield/encryption.d.ts +0 -67
  27. package/dist/shield/encryption.js +0 -67
  28. package/dist/shield/envelope.d.ts +10 -11
  29. package/dist/shield/envelope.js +15 -14
  30. package/dist/shield/fee.d.ts +24 -0
  31. package/dist/shield/fee.js +151 -0
  32. package/dist/shield/index.d.ts +9 -3
  33. package/dist/shield/index.js +7 -2
  34. package/dist/shield/intent.d.ts +20 -933
  35. package/dist/shield/intent.js +92 -112
  36. package/dist/shield/permission-status.d.ts +9 -0
  37. package/dist/shield/permission-status.js +8 -0
  38. package/dist/shield/rpc.d.ts +50 -49
  39. package/dist/shield/rpc.js +207 -354
  40. package/dist/shield/shield.eip712.gen.d.ts +1096 -43
  41. package/dist/shield/shield.eip712.gen.js +572 -62
  42. package/dist/shield/types.d.ts +7 -102
  43. package/dist/shield/types.js +1 -4
  44. package/dist/shield/userop/gas-estimation.d.ts +8 -23
  45. package/dist/shield/userop/gas-estimation.js +100 -137
  46. package/dist/shield/userop/paymaster.d.ts +15 -0
  47. package/dist/shield/userop/paymaster.js +17 -0
  48. package/dist/shield/userop/token-exchange.d.ts +5 -52
  49. package/dist/shield/userop/token-exchange.js +41 -149
  50. package/dist/shield/userop/types.d.ts +3 -28
  51. package/dist/shield/userop/types.js +0 -3
  52. package/dist/shield/userop/userOp.d.ts +0 -19
  53. package/dist/shield/userop/userOp.js +4 -22
  54. package/dist/shield/utils/chain.d.ts +1 -0
  55. package/dist/shield/utils/chain.js +3 -0
  56. package/dist/shield/validate-withdrawal-split.d.ts +13 -0
  57. package/dist/shield/validate-withdrawal-split.js +93 -0
  58. package/package.json +18 -27
  59. package/dist/generated/inco/shield/v2/conductor_connect.d.ts +0 -137
  60. package/dist/generated/inco/shield/v2/conductor_connect.js +0 -141
  61. package/dist/generated/inco/shield/v2/permission_pb.d.ts +0 -443
  62. package/dist/generated/inco/shield/v2/permission_pb.js +0 -639
  63. package/dist/generated/inco/shield/v2/query_pb.d.ts +0 -103
  64. package/dist/generated/inco/shield/v2/query_pb.js +0 -141
  65. package/dist/shield/userop/index.d.ts +0 -1
  66. package/dist/shield/userop/index.js +0 -1
  67. package/dist/uniswap-adapter/config.d.ts +0 -34
  68. package/dist/uniswap-adapter/config.js +0 -45
  69. package/dist/uniswap-adapter/find-pools.d.ts +0 -91
  70. package/dist/uniswap-adapter/find-pools.js +0 -108
  71. package/dist/uniswap-adapter/index.d.ts +0 -10
  72. package/dist/uniswap-adapter/index.js +0 -13
  73. package/dist/uniswap-adapter/swap.d.ts +0 -344
  74. package/dist/uniswap-adapter/swap.js +0 -309
@@ -1,165 +1,57 @@
1
- import { parseAbi } from 'viem';
2
- import { InvalidContractReturnType, TaggedError } from '../../errors.js';
3
- const aggregatorAbi = parseAbi([
4
- 'function latestRoundData() view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)',
5
- 'function decimals() view returns (uint8)',
6
- 'function description() view returns (string)',
7
- ]);
8
- const erc20DecimalsAbi = parseAbi(['function decimals() view returns (uint8)']);
9
- /** ETH / USD aggregator on Base Sepolia. */
10
- const BASE_SEPOLIA_ETH_USD_FEED = '0x4aDC67696bA383F43DD60A9e78F2C97Fbbfc7cb1';
11
- /**
12
- * Fetch the latest price from a single Chainlink aggregator contract.
13
- *
14
- * The aggregator's `description` (e.g. "ETH / USD") encodes the pair —
15
- * there is no on-chain way to enforce that callers passed the right address.
16
- */
17
- export async function getAggregatorRate(publicClient, aggregator) {
18
- const [roundDataRaw, decimalsRaw, descriptionRaw] = await Promise.all([
19
- publicClient.readContract({
20
- address: aggregator,
21
- abi: aggregatorAbi,
22
- functionName: 'latestRoundData',
23
- }),
24
- publicClient.readContract({
25
- address: aggregator,
26
- abi: aggregatorAbi,
27
- functionName: 'decimals',
28
- }),
29
- publicClient.readContract({
30
- address: aggregator,
31
- abi: aggregatorAbi,
32
- functionName: 'description',
33
- }),
34
- ]);
35
- const { answer, updatedAt } = parseLatestRoundData(roundDataRaw);
36
- const decimals = parseDecimals(decimalsRaw);
37
- if (typeof descriptionRaw !== 'string') {
1
+ import { zeroAddress } from 'viem';
2
+ import { shieldPaymasterAbi } from '../../contracts/abi.js';
3
+ import { InvalidContractReturnType, UnsupportedFeeToken, } from '../../errors.js';
4
+ export async function getPaymasterAssetAdapter(publicClient, paymaster) {
5
+ const adapter = await publicClient.readContract({
6
+ address: paymaster,
7
+ abi: shieldPaymasterAbi,
8
+ functionName: 'ALLOWED_ASSET_ADAPTER',
9
+ });
10
+ if (typeof adapter !== 'string') {
38
11
  throw new InvalidContractReturnType({
39
- message: `Aggregator.description returned non-string: ${typeof descriptionRaw}`,
40
- functionName: 'Aggregator.description',
41
- expectedType: 'string',
12
+ message: `ShieldPaymaster.ALLOWED_ASSET_ADAPTER returned non-address: ${typeof adapter}`,
13
+ functionName: 'ShieldPaymaster.ALLOWED_ASSET_ADAPTER',
14
+ expectedType: 'address',
42
15
  });
43
16
  }
44
- if (answer <= 0n) {
45
- throw new InvalidContractReturnType({
46
- message: `Aggregator ${aggregator} returned non-positive answer (${answer})`,
47
- functionName: 'Aggregator.latestRoundData',
48
- expectedType: 'positive int256',
17
+ return adapter;
18
+ }
19
+ export async function assertFeeTokenSupported(publicClient, paymaster, token) {
20
+ const [wrapper] = await publicClient.readContract({
21
+ address: paymaster,
22
+ abi: shieldPaymasterAbi,
23
+ functionName: 'priceFeeds',
24
+ args: [token],
25
+ });
26
+ if (wrapper === zeroAddress) {
27
+ throw new UnsupportedFeeToken({
28
+ message: `Fee token ${token} has no price feed on ShieldPaymaster ${paymaster}; it cannot be used to pay gas`,
29
+ feeToken: token,
49
30
  });
50
31
  }
51
- return { answer, decimals, updatedAt, description: descriptionRaw };
52
32
  }
53
- function parseLatestRoundData(value) {
54
- if (!Array.isArray(value) ||
55
- value.length < 5 ||
56
- typeof value[1] !== 'bigint' ||
57
- typeof value[3] !== 'bigint') {
33
+ export const RAY = 10n ** 27n;
34
+ export async function getEthExchangeRateRay(publicClient, paymaster, token) {
35
+ const rate = await publicClient.readContract({
36
+ address: paymaster,
37
+ abi: shieldPaymasterAbi,
38
+ functionName: 'exchangeRateRay',
39
+ args: [token],
40
+ });
41
+ if (typeof rate !== 'bigint') {
58
42
  throw new InvalidContractReturnType({
59
- message: 'Aggregator.latestRoundData returned an unexpected shape',
60
- functionName: 'Aggregator.latestRoundData',
61
- expectedType: '[uint80, int256, uint256, uint256, uint80]',
43
+ message: `ShieldPaymaster.exchangeRateRay returned non-bigint: ${typeof rate}`,
44
+ functionName: 'ShieldPaymaster.exchangeRateRay',
45
+ expectedType: 'uint256',
62
46
  });
63
47
  }
64
- return { answer: value[1], updatedAt: value[3] };
65
- }
66
- /**
67
- * Chainlink aggregators for fee-payment assets on Base Sepolia, keyed by
68
- * the ERC-20 token address (Shield's `assetId` is just the token address
69
- * left-padded to bytes32). Each feed is **TOKEN / USD** — direction matters
70
- * when converting gas cost from ETH to token units downstream.
71
- */
72
- const BASE_SEPOLIA_TOKEN_FEEDS = {
73
- // Circle native USDC on Base Sepolia → USDC/USD aggregator
74
- '0x036CbD53842c5426634e7929541eC2318f3dCF7e': '0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165',
75
- };
76
- export class UnknownAssetFeed extends TaggedError('UnknownAssetFeed') {
77
- }
78
- /**
79
- * Resolve the Chainlink aggregator for a Shield `assetId` (= ERC-20 token
80
- * address padded to bytes32) and return its latest rate.
81
- */
82
- export async function getAssetRate(publicClient, assetId) {
83
- const tokenAddress = tokenAddressFromAssetId(assetId);
84
- const aggregator = lookupFeed(tokenAddress);
85
- if (aggregator === undefined) {
86
- throw new UnknownAssetFeed({ assetId });
87
- }
88
- return getAggregatorRate(publicClient, aggregator);
89
- }
90
- /** Extract the trailing 20-byte ERC-20 address from a bytes32 assetId. */
91
- function tokenAddressFromAssetId(assetId) {
92
- // bytes32 hex = "0x" + 64 chars; last 40 are the address.
93
- const hex = assetId.startsWith('0x') ? assetId.slice(2) : assetId;
94
- return `0x${hex.slice(-40)}`;
95
- }
96
- /** Case-insensitive lookup against the feed registry. */
97
- function lookupFeed(token) {
98
- const target = token.toLowerCase();
99
- for (const [key, aggregator] of Object.entries(BASE_SEPOLIA_TOKEN_FEEDS)) {
100
- if (key.toLowerCase() === target)
101
- return aggregator;
102
- }
103
- return undefined;
104
- }
105
- /** RAY = 1e27, the fixed-point scale used by the Shield paymaster. */
106
- export const RAY = 10n ** 27n;
107
- /**
108
- * Compute the RAY-precision exchange rate (ETH priced in token units)
109
- * that the Shield paymaster consumes. Mirrors the on-chain convention
110
- * from SHIELD_PAYMASTER.md:
111
- *
112
- * tokenAmount = ceil(gasCostWei * exchangeRate / 1e27)
113
- *
114
- * Worked example: if 1 ETH = 2000 USDC and USDC has 6 decimals,
115
- * exchangeRate = 2000 * 1e6 * 1e27 / 1e18 = 2e18
116
- *
117
- * Derivation here uses two Chainlink USD feeds:
118
- * ethInToken (real) = ethUsd / tokenUsd
119
- * rate (RAY) = ethInToken * 10^tokenDec * 1e27 / 1e18
120
- * = ethUsd * 10^tokenDec * 10^tokenFeedDec * RAY
121
- * / (tokenUsd * 10^ethFeedDec * 1e18)
122
- */
123
- export async function getEthExchangeRateRay(publicClient, assetId) {
124
- const tokenAddress = tokenAddressFromAssetId(assetId);
125
- const [tokenRate, ethRate, tokenDecimalsRaw] = await Promise.all([
126
- getAssetRate(publicClient, assetId),
127
- getAggregatorRate(publicClient, BASE_SEPOLIA_ETH_USD_FEED),
128
- publicClient.readContract({
129
- address: tokenAddress,
130
- abi: erc20DecimalsAbi,
131
- functionName: 'decimals',
132
- }),
133
- ]);
134
- const tokenDecimals = parseDecimals(tokenDecimalsRaw);
135
- const numerator = ethRate.answer *
136
- 10n ** BigInt(tokenDecimals) *
137
- 10n ** BigInt(tokenRate.decimals) *
138
- RAY;
139
- const denominator = tokenRate.answer * 10n ** BigInt(ethRate.decimals) * 10n ** 18n;
140
- return numerator / denominator;
48
+ return rate;
141
49
  }
142
- /** `ceil(ethWei * exchangeRate / RAY)` — matches the paymaster's rounding. */
143
50
  export function applyExchangeRateRayCeil(ethWei, exchangeRateRay) {
144
51
  const product = ethWei * exchangeRateRay;
145
52
  return (product + RAY - 1n) / RAY;
146
53
  }
147
- /**
148
- * Convenience: fetch the RAY rate for `assetId` and apply it to `ethWei`.
149
- * Use `getEthExchangeRateRay` directly when the caller also needs to
150
- * forward the rate to the paymaster signer.
151
- */
152
- export async function convertEthWeiToTokenUnits(publicClient, assetId, ethWei) {
153
- const rate = await getEthExchangeRateRay(publicClient, assetId);
54
+ export async function convertEthWeiToTokenUnits(publicClient, paymaster, token, ethWei) {
55
+ const rate = await getEthExchangeRateRay(publicClient, paymaster, token);
154
56
  return applyExchangeRateRayCeil(ethWei, rate);
155
57
  }
156
- function parseDecimals(value) {
157
- if (typeof value !== 'number') {
158
- throw new InvalidContractReturnType({
159
- message: `Aggregator.decimals returned non-number: ${typeof value}`,
160
- functionName: 'Aggregator.decimals',
161
- expectedType: 'uint8',
162
- });
163
- }
164
- return value;
165
- }
@@ -1,22 +1,10 @@
1
- /**
2
- * Type-only surface for the UserOp builder. Runtime code lives in `userOp.ts`.
3
- */
4
1
  import type { Address } from 'viem';
5
2
  import type { UserOperation } from 'viem/account-abstraction';
6
3
  import type { IntentKind } from '../../generated/inco/shield/v2/drafting_pb.js';
7
4
  import type * as abi from '../shield.eip712.gen.js';
8
5
  import type { DefiInteraction, IndividualRevocation, Transfer, UserWideRevocation, Withdrawal } from '../shield.eip712.gen.js';
9
- export declare const ENTRYPOINT_VERSION: "0.7";
10
- /**
11
- * A drafted op — the full on-chain settlement struct the enclave produces from
12
- * an intent via `draft()`, tagged by {@link IntentKind}. This is the "cheque":
13
- * an intent is what the user signs *before* `draft()`; the cheque is what comes
14
- * *after*, and is exactly what `IncoShield.{transfer,withdraw,...}` settles.
15
- *
16
- * Delegated kinds settle through the same on-chain function — and thus the same
17
- * struct — as their non-delegated counterparts; the delegation context rides
18
- * inside the intent, not in a separate selector.
19
- */
6
+ import type { PaymasterFields } from './paymaster.js';
7
+ export declare const ENTRYPOINT_VERSION = "0.7";
20
8
  export type SettlementOp = {
21
9
  kind: IntentKind.TRANSFER;
22
10
  op: Transfer;
@@ -42,13 +30,6 @@ export type SettlementOp = {
42
30
  kind: IntentKind.USER_WIDE_REVOCATION;
43
31
  op: UserWideRevocation;
44
32
  };
45
- /**
46
- * Domain Intent: a signed EIP-712 intent struct tagged by its {@link IntentKind}
47
- * discriminant. This is what the user signs *before* `draft()` — the enclave
48
- * processes an `Intent` and produces the {@link Cheque}. Each variant is the
49
- * generated `abi.{*}Intent` struct plus a `kind` tag; delegated kinds wrap
50
- * their non-delegated inner intent inside the generated `Delegated*Intent` shape.
51
- */
52
33
  export type Intent = TransferIntent | WithdrawalIntent | DefiInteractionIntent | IndividualRevocationIntent | UserWideRevocationIntent | DelegatedTransferIntent | DelegatedWithdrawalIntent | DelegatedDefiInteractionIntent;
53
34
  export type TransferIntent = {
54
35
  readonly kind: IntentKind.TRANSFER;
@@ -74,18 +55,12 @@ export type DelegatedWithdrawalIntent = {
74
55
  export type DelegatedDefiInteractionIntent = {
75
56
  readonly kind: IntentKind.DELEGATED_DEFI_INTERACTION;
76
57
  } & abi.DelegatedDefiInteractionIntent;
77
- /**
78
- * A v0.7 UserOperation before the quorum signature is attached. Quorum signing
79
- * happens enclave-side over {@link userOpHash}; the SDK only builds and assembles.
80
- */
81
58
  export type UnsignedUserOperation = Omit<UserOperation<typeof ENTRYPOINT_VERSION>, 'signature'>;
82
59
  export type BuildUnsignedUserOpParams = {
83
- /** The drafted on-chain struct to settle, tagged by intent kind. */
84
60
  settlementOp: SettlementOp;
85
- /** AA account submitting the op — the TeeSmartAccount (sole TEE_ROLE holder). */
86
61
  sender: Address;
87
62
  nonce: bigint;
88
63
  shieldAddress: Address;
89
- /** Bundler-estimated limits — everything except the signed fee fields. */
90
64
  gas: abi.Gas;
65
+ paymaster?: PaymasterFields;
91
66
  };
@@ -1,4 +1 @@
1
- /**
2
- * Type-only surface for the UserOp builder. Runtime code lives in `userOp.ts`.
3
- */
4
1
  export const ENTRYPOINT_VERSION = '0.7';
@@ -1,26 +1,7 @@
1
- /**
2
- * Constitution: builds a v0.7 UserOperation from a drafted cheque in three pure
3
- * stages — (1) assemble the unsigned op, (2) compute the hash the quorum signs,
4
- * (3) attach a signature. Every stage is deterministic: identical inputs produce
5
- * identical bytes across all quorum members. That is the property the on-chain
6
- * TEE smart account relies on when it verifies that every quorum signature is
7
- * over the same userOpHash. Quorum signing itself is enclave-side and
8
- * intentionally absent here — the SDK builds and assembles, it does not sign.
9
- */
10
1
  import { type Address, type Hex } from 'viem';
11
2
  import { type UserOperation } from 'viem/account-abstraction';
12
3
  import { type BuildUnsignedUserOpParams, ENTRYPOINT_VERSION, type SettlementOp, type UnsignedUserOperation } from './types.js';
13
4
  export declare function buildUnsignedUserOp(params: BuildUnsignedUserOpParams): UnsignedUserOperation;
14
5
  export declare function userOpHash(unsignedUserOp: UnsignedUserOperation, chainId: number, entryPointAddress?: Address): Hex;
15
6
  export declare function assembleUserOp(unsigned: UnsignedUserOperation, signature: Hex): UserOperation<typeof ENTRYPOINT_VERSION>;
16
- /**
17
- * Encode the shield settlement call for a drafted {@link OnchainCheque}: the
18
- * `kind` selects the on-chain function and `op` is its sole argument. This is
19
- * the inner call wrapped by {@link buildUnsignedUserOp} inside `execute`, and is
20
- * exported so the enclave can reuse the canonical encoding when it assembles
21
- * the cheque calldata itself.
22
- *
23
- * TODO(#639): the enclave OnChainOp path ABI-encodes only the struct while this
24
- * emits selector + struct. Harmonize so both produce identical calldata.
25
- */
26
7
  export declare function encodeSettlementOpCall(settlementOp: SettlementOp): Hex;
@@ -1,15 +1,6 @@
1
- /**
2
- * Constitution: builds a v0.7 UserOperation from a drafted cheque in three pure
3
- * stages — (1) assemble the unsigned op, (2) compute the hash the quorum signs,
4
- * (3) attach a signature. Every stage is deterministic: identical inputs produce
5
- * identical bytes across all quorum members. That is the property the on-chain
6
- * TEE smart account relies on when it verifies that every quorum signature is
7
- * over the same userOpHash. Quorum signing itself is enclave-side and
8
- * intentionally absent here — the SDK builds and assembles, it does not sign.
9
- */
10
1
  import { encodeFunctionData } from 'viem';
11
2
  import { entryPoint07Address, getUserOperationHash, } from 'viem/account-abstraction';
12
- import { shieldAbiStubAbi, teeSmartAccountAbi } from '../../contracts/abi.js';
3
+ import { incoShieldAbi, teeSmartAccountAbi } from '../../contracts/abi.js';
13
4
  import { IntentKind } from '../../generated/inco/shield/v2/drafting_pb.js';
14
5
  import { ENTRYPOINT_VERSION, } from './types.js';
15
6
  export function buildUnsignedUserOp(params) {
@@ -21,7 +12,6 @@ export function buildUnsignedUserOp(params) {
21
12
  functionName: 'execute',
22
13
  args: [
23
14
  params.shieldAddress,
24
- // value: shield ops carry no native ETH
25
15
  0n,
26
16
  encodeSettlementOpCall(params.settlementOp),
27
17
  ],
@@ -33,6 +23,8 @@ export function buildUnsignedUserOp(params) {
33
23
  maxPriorityFeePerGas: params.gas.maxPriorityFeePerGas,
34
24
  paymasterVerificationGasLimit: params.gas.paymasterVerificationGasLimit,
35
25
  paymasterPostOpGasLimit: params.gas.paymasterPostOpGasLimit,
26
+ paymaster: params.paymaster?.paymaster,
27
+ paymasterData: params.paymaster?.paymasterData,
36
28
  };
37
29
  }
38
30
  export function userOpHash(unsignedUserOp, chainId, entryPointAddress = entryPoint07Address) {
@@ -46,18 +38,8 @@ export function userOpHash(unsignedUserOp, chainId, entryPointAddress = entryPoi
46
38
  export function assembleUserOp(unsigned, signature) {
47
39
  return { ...unsigned, signature };
48
40
  }
49
- /**
50
- * Encode the shield settlement call for a drafted {@link OnchainCheque}: the
51
- * `kind` selects the on-chain function and `op` is its sole argument. This is
52
- * the inner call wrapped by {@link buildUnsignedUserOp} inside `execute`, and is
53
- * exported so the enclave can reuse the canonical encoding when it assembles
54
- * the cheque calldata itself.
55
- *
56
- * TODO(#639): the enclave OnChainOp path ABI-encodes only the struct while this
57
- * emits selector + struct. Harmonize so both produce identical calldata.
58
- */
59
41
  export function encodeSettlementOpCall(settlementOp) {
60
- const abi = shieldAbiStubAbi;
42
+ const abi = incoShieldAbi;
61
43
  switch (settlementOp.kind) {
62
44
  case IntentKind.TRANSFER:
63
45
  case IntentKind.DELEGATED_TRANSFER:
@@ -1,2 +1,3 @@
1
1
  import type { Chain } from 'viem';
2
2
  export declare function getChainFromId(chainId: number): Chain;
3
+ export declare function defaultBundlerUrl(chainId: number): string;
@@ -6,3 +6,6 @@ export function getChainFromId(chainId) {
6
6
  }
7
7
  return chain;
8
8
  }
9
+ export function defaultBundlerUrl(chainId) {
10
+ return `https://public.pimlico.io/v2/${chainId}/rpc`;
11
+ }
@@ -0,0 +1,13 @@
1
+ import { type Address } from 'viem';
2
+ import type { UserOperation } from 'viem/account-abstraction';
3
+ import type { WithdrawalIntent } from './shield.eip712.gen.js';
4
+ import type { ENTRYPOINT_VERSION } from './userop/types.js';
5
+ declare const WithdrawalSplitMismatch_base: new <Fields extends Record<string, unknown>>(fields: Fields) => Error & {
6
+ readonly _tag: "WithdrawalSplitMismatch";
7
+ } & Readonly<Fields>;
8
+ export declare class WithdrawalSplitMismatch extends WithdrawalSplitMismatch_base<{
9
+ readonly message: string;
10
+ }> {
11
+ }
12
+ export declare function assertWithdrawalSplitMatchesIntent(userOp: UserOperation<typeof ENTRYPOINT_VERSION>, signed: WithdrawalIntent, shieldAddress: Address): void;
13
+ export {};
@@ -0,0 +1,93 @@
1
+ import { decodeFunctionData, isAddressEqual } from 'viem';
2
+ import { incoShieldAbi, teeSmartAccountAbi } from '../contracts/abi.js';
3
+ import { TaggedError } from '../errors.js';
4
+ export class WithdrawalSplitMismatch extends TaggedError('WithdrawalSplitMismatch') {
5
+ }
6
+ export function assertWithdrawalSplitMatchesIntent(userOp, signed, shieldAddress) {
7
+ const outer = tryDecode(() => decodeFunctionData({ abi: teeSmartAccountAbi, data: userOp.callData }), 'settlement callData');
8
+ if (outer.functionName !== 'execute') {
9
+ throw mismatch(`settlement call is '${outer.functionName}', expected 'execute'`);
10
+ }
11
+ const [target, value, inner] = outer.args;
12
+ if (!isAddressEqual(target, shieldAddress)) {
13
+ throw mismatch(`execute target ${target} is not the shield ${shieldAddress}`);
14
+ }
15
+ if (value !== 0n) {
16
+ throw mismatch(`execute carries non-zero value ${value}`);
17
+ }
18
+ const call = tryDecode(() => decodeFunctionData({ abi: incoShieldAbi, data: inner }), 'inner withdraw call');
19
+ if (call.functionName !== 'withdraw') {
20
+ throw mismatch(`inner call is '${call.functionName}', expected 'withdraw'`);
21
+ }
22
+ const core = call.args[0].core;
23
+ const signedAsset = signed.op.core.asset;
24
+ if (core.asset.standard !== signedAsset.standard ||
25
+ !isAddressEqual(core.asset.token, signedAsset.token) ||
26
+ core.asset.tokenId !== signedAsset.tokenId) {
27
+ throw mismatch('withdrawal asset does not match the signed asset');
28
+ }
29
+ const signedMovements = signed.op.core.assetMovements;
30
+ const movements = core.assetMovements;
31
+ if (movements.length !== signedMovements.length) {
32
+ throw mismatch(`expected the ${signedMovements.length} signed movement(s), got ${movements.length}`);
33
+ }
34
+ signedMovements.forEach((signedMovement, i) => {
35
+ const movement = movements[i];
36
+ if (!isAddressEqual(movement.account, signedMovement.account)) {
37
+ throw mismatch(`movement ${i} account was changed`);
38
+ }
39
+ if (!isAddressEqual(movement.assetAdapter, signedMovement.assetAdapter)) {
40
+ throw mismatch(`movement ${i} asset adapter was changed`);
41
+ }
42
+ if (movement.movementArgs.toLowerCase() !==
43
+ signedMovement.movementArgs.toLowerCase()) {
44
+ throw mismatch(`movement ${i} args were changed`);
45
+ }
46
+ });
47
+ assertGasWithinSigned(userOp, signed.gas);
48
+ }
49
+ function assertGasWithinSigned(userOp, gas) {
50
+ const limits = [
51
+ ['callGasLimit', userOp.callGasLimit, gas.callGasLimit],
52
+ [
53
+ 'verificationGasLimit',
54
+ userOp.verificationGasLimit,
55
+ gas.verificationGasLimit,
56
+ ],
57
+ ['preVerificationGas', userOp.preVerificationGas, gas.preVerificationGas],
58
+ ['maxFeePerGas', userOp.maxFeePerGas, gas.maxFeePerGas],
59
+ [
60
+ 'maxPriorityFeePerGas',
61
+ userOp.maxPriorityFeePerGas,
62
+ gas.maxPriorityFeePerGas,
63
+ ],
64
+ [
65
+ 'paymasterVerificationGasLimit',
66
+ userOp.paymasterVerificationGasLimit ?? 0n,
67
+ gas.paymasterVerificationGasLimit,
68
+ ],
69
+ [
70
+ 'paymasterPostOpGasLimit',
71
+ userOp.paymasterPostOpGasLimit ?? 0n,
72
+ gas.paymasterPostOpGasLimit,
73
+ ],
74
+ ];
75
+ for (const [name, actual, limit] of limits) {
76
+ if (actual > limit) {
77
+ throw mismatch(`${name} ${actual} exceeds the signed limit ${limit}`);
78
+ }
79
+ }
80
+ }
81
+ function tryDecode(decode, what) {
82
+ try {
83
+ return decode();
84
+ }
85
+ catch (cause) {
86
+ throw mismatch(`could not decode ${what}: ${cause instanceof Error ? cause.message : String(cause)}`);
87
+ }
88
+ }
89
+ function mismatch(detail) {
90
+ return new WithdrawalSplitMismatch({
91
+ message: `enclave withdrawal UserOp does not match the signed intent: ${detail}`,
92
+ });
93
+ }
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@inco/shield-js",
3
- "version": "0.0.0-bootstrap.0",
3
+ "version": "0.2.0",
4
+ "description": "TypeScript SDK for interacting with Inco Shield.",
4
5
  "license": "Apache-2.0",
5
- "repository": "https://github.com/Inco-fhevm/shield",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Inco-fhevm/shield.git",
9
+ "directory": "sdk"
10
+ },
6
11
  "type": "module",
7
12
  "files": [
8
13
  "dist"
@@ -19,48 +24,34 @@
19
24
  ".": {
20
25
  "types": "./dist/index.d.ts",
21
26
  "import": "./dist/index.js"
22
- },
23
- "./uniswap": {
24
- "types": "./dist/uniswap-adapter/index.d.ts",
25
- "import": "./dist/uniswap-adapter/index.js"
26
27
  }
27
28
  },
28
29
  "scripts": {
29
30
  "generate:proto": "rm -rf src/generated && buf generate",
30
31
  "generate:abi": "cd ../contracts && bun run generate && cd ../sdk && bun run scripts/extract-abi.ts",
31
32
  "generate": "bun run generate:proto && bun run generate:abi",
32
- "build": "bun run generate && tsc -p tsconfig.dist.json",
33
- "typecheck": "tsc --noEmit",
34
- "test": "bun run generate && vitest run",
33
+ "clean": "rm -rf dist",
34
+ "compile": "bun run clean && tsc -p tsconfig.dist.json",
35
+ "build": "bun run generate && bun run compile",
36
+ "typecheck": "tsc --noEmit -p tsconfig.dist.json",
37
+ "test": "bun run generate && bun run test:unit",
38
+ "test:unit": "vitest run",
35
39
  "lint": "biome check --write . && eslint .",
36
40
  "test:lint": "biome check . && eslint .",
37
- "uniswap": "bun run scripts/uniswap/uniswap.ts"
41
+ "publish:npm": "npm publish"
38
42
  },
39
43
  "dependencies": {
40
- "@bufbuild/protobuf": "^1.10.0",
41
- "@connectrpc/connect": "^1.7.0",
44
+ "@bufbuild/protobuf": "^2.12.0",
45
+ "@connectrpc/connect": "^2.1.2",
42
46
  "@hpke/chacha20poly1305": "^1.8.0",
43
47
  "@hpke/core": "^1.9.0",
44
48
  "@hpke/hybridkem-x-wing": "^0.7.0",
45
49
  "@noble/ciphers": "^1.3.0",
46
50
  "viem": "^2.30.0"
47
51
  },
48
- "peerDependencies": {
49
- "@uniswap/v4-sdk": "^1.22.0",
50
- "ethers": "^5"
51
- },
52
- "peerDependenciesMeta": {
53
- "@uniswap/v4-sdk": {
54
- "optional": true
55
- },
56
- "ethers": {
57
- "optional": true
58
- }
59
- },
60
52
  "devDependencies": {
61
- "@bufbuild/buf": "^1.34.0",
62
- "@bufbuild/protoc-gen-es": "^1.10.0",
63
- "@connectrpc/protoc-gen-connect-es": "^1.7.0",
53
+ "@bufbuild/buf": "^1.70.0",
54
+ "@bufbuild/protoc-gen-es": "^2.12.0",
64
55
  "@types/node": "^22.8.5",
65
56
  "@uniswap/v4-sdk": "^1.22.0",
66
57
  "bun-types": "^1.3.8",