@0xmonaco/contracts 1.0.48 → 1.0.50-develop.9c1b239

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type Hex, predictSweeperAddress, type SweeperAddressInput, sweeperCloneInitCode, sweeperImmutableArgs } from "./sweeper";
1
+ export { type DepositTarget, encodeDepositApplicationData, type Hex, predictSweeperAddress, type SweeperAddressInput, sweeperCloneInitCode, sweeperImmutableArgs, } from "./sweeper";
2
2
  /**
3
3
  * Contract ABIs for all protocol contracts.
4
4
  * These ABIs are used to interact with the deployed contracts.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * for all Monaco protocol contracts.
7
7
  */
8
8
  import { SWEEPER_ABI, SWEEPER_FACTORY_ABI, VAULT_ABI } from "./abis";
9
- export { predictSweeperAddress, sweeperCloneInitCode, sweeperImmutableArgs } from "./sweeper";
9
+ export { encodeDepositApplicationData, predictSweeperAddress, sweeperCloneInitCode, sweeperImmutableArgs, } from "./sweeper";
10
10
  /**
11
11
  * Contract ABIs for all protocol contracts.
12
12
  * These ABIs are used to interact with the deployed contracts.
package/dist/sweeper.d.ts CHANGED
@@ -2,17 +2,56 @@
2
2
  * Off-chain derivation of Monaco sweeper deposit addresses.
3
3
  *
4
4
  * A sweeper is an EIP-1167 clone deployed by `SweeperFactory` at a CREATE2
5
- * address determined by the (clientId, user) pair it is bound to. The address
6
- * can therefore be shown to a user — and funded — before any contract exists
7
- * there, without an RPC round trip.
5
+ * address determined by the (clientId, user, deposit target) triple it is
6
+ * bound to. The address can therefore be shown to a user — and funded — before
7
+ * any contract exists there, without an RPC round trip.
8
8
  *
9
9
  * The client id is the application's identifier as used for rollup deposits
10
10
  * (the string `sdk.login` takes). It is hashed **byte-for-byte**: two
11
11
  * spellings derive two different, both-valid addresses, so the exact string
12
12
  * of the registered application must be used — the backend rejects unknown
13
13
  * spellings at registration because the lookup is an exact match.
14
+ *
15
+ * The deposit target is hashed the same way, through the encoding in
16
+ * {@link encodeDepositApplicationData}, so `"spot"` and `"margin"` are two
17
+ * different addresses for one application and user, each carrying its own
18
+ * target. A `"spot"` address is never credited to margin.
19
+ *
20
+ * The reverse is not absolute: `"margin"` is a routing *request*. A deposit the
21
+ * backend cannot route to collateral — an unsupported collateral asset, or a
22
+ * margin account that fails validation — is credited to the spot wallet
23
+ * instead, so funds are never stranded. Derivation fixes which request the
24
+ * address carries, not which ledger the credit ultimately lands in.
14
25
  */
15
26
  export type Hex = `0x${string}`;
27
+ /**
28
+ * Which ledger a deposit asks to be credited to.
29
+ *
30
+ * `"spot"` credits the spot wallet; `"margin"` asks for the parent margin
31
+ * account's collateral. The target is part of the sweeper address derivation,
32
+ * so the same pair has one deposit address per target.
33
+ *
34
+ * `"margin"` is a request: a deposit the backend cannot route to collateral
35
+ * falls back to the spot wallet rather than being stranded.
36
+ */
37
+ export type DepositTarget = "spot" | "margin";
38
+ /**
39
+ * Encodes the on-chain `applicationData` a deposit carries.
40
+ *
41
+ * Spot is the identity encoding — the bare client id, byte for byte. Not a
42
+ * shortcut but a compatibility requirement: the deposit address is a hash of
43
+ * this string, so encoding spot any other way would move every address already
44
+ * published to a user.
45
+ *
46
+ * Margin emits the canonical routing payload the backend decodes. It is built
47
+ * with `JSON.stringify` rather than string concatenation so a client id
48
+ * containing a quote, a backslash, or a brace is escaped instead of breaking
49
+ * out of the object. Key order is fixed and part of the derivation.
50
+ *
51
+ * The byte-for-byte counterpart of `encode_application_data` in
52
+ * `utils/src/deposit_routing.rs`.
53
+ */
54
+ export declare function encodeDepositApplicationData(clientId: string, target: DepositTarget): string;
16
55
  export interface SweeperAddressInput {
17
56
  /** Address of the deployed `SweeperFactory`. */
18
57
  factory: string;
@@ -25,14 +64,25 @@ export interface SweeperAddressInput {
25
64
  clientId: string;
26
65
  /** Address credited on the rollup when the sweeper sweeps. */
27
66
  user: string;
67
+ /**
68
+ * Which ledger swept deposits ask to be credited to. Defaults to `"spot"`,
69
+ * which reproduces the derivation from before deposit targets existed.
70
+ *
71
+ * Must match the `depositTarget` the pair was registered with: the target is
72
+ * inside the derivation, so a mismatch derives a real, fundable address that
73
+ * nothing is monitoring. Anything but `"spot"` or `"margin"` throws rather
74
+ * than defaulting, for the same reason.
75
+ */
76
+ depositTarget?: DepositTarget;
28
77
  }
29
78
  /**
30
- * Derives the deposit address for a (clientId, user) pair.
79
+ * Derives the deposit address for a (clientId, user, deposit target) triple.
31
80
  *
32
81
  * @returns The EIP-55 checksummed sweeper address, deployed or not.
33
- * @throws If any address is malformed or the client id is empty.
82
+ * @throws If any address is malformed, the client id is empty, or the deposit
83
+ * target is not exactly `"spot"` or `"margin"`.
34
84
  */
35
- export declare function predictSweeperAddress({ factory, implementation, clientId, user }: SweeperAddressInput): Hex;
85
+ export declare function predictSweeperAddress({ factory, implementation, clientId, user, depositTarget }: SweeperAddressInput): Hex;
36
86
  /**
37
87
  * The clone's immutable args: `abi.encode(address user, string clientId)`
38
88
  * (the contract calls the string `applicationId`, matching the rollup's
package/dist/sweeper.js CHANGED
@@ -14,17 +14,56 @@ const CLONE_RUNTIME_LENGTH = 45;
14
14
  */
15
15
  const SALT = new Uint8Array(32);
16
16
  /**
17
- * Derives the deposit address for a (clientId, user) pair.
17
+ * Encodes the on-chain `applicationData` a deposit carries.
18
+ *
19
+ * Spot is the identity encoding — the bare client id, byte for byte. Not a
20
+ * shortcut but a compatibility requirement: the deposit address is a hash of
21
+ * this string, so encoding spot any other way would move every address already
22
+ * published to a user.
23
+ *
24
+ * Margin emits the canonical routing payload the backend decodes. It is built
25
+ * with `JSON.stringify` rather than string concatenation so a client id
26
+ * containing a quote, a backslash, or a brace is escaped instead of breaking
27
+ * out of the object. Key order is fixed and part of the derivation.
28
+ *
29
+ * The byte-for-byte counterpart of `encode_application_data` in
30
+ * `utils/src/deposit_routing.rs`.
31
+ */
32
+ export function encodeDepositApplicationData(clientId, target) {
33
+ assertDepositTarget(target);
34
+ return target === "margin" ? JSON.stringify({ clientId, depositTarget: "MARGIN" }) : clientId;
35
+ }
36
+ /**
37
+ * Rejects anything that is not exactly `"spot"` or `"margin"`.
38
+ *
39
+ * Without this, `target === "margin" ? … : …` sends every other value down the
40
+ * spot branch, so an untyped JavaScript caller — or one who passed the wire
41
+ * form `"MARGIN"` copied from the API reference — silently derives a real,
42
+ * fundable **spot** address while believing it is a margin one. That is the
43
+ * same failure the backend refuses with a 400 rather than defaulting, and it is
44
+ * worse here: nothing round-trips through a server to reveal the mistake.
45
+ */
46
+ function assertDepositTarget(value) {
47
+ if (value !== "spot" && value !== "margin") {
48
+ throw new Error(`Invalid deposit target: ${JSON.stringify(value)}. ` + 'Expected "spot" or "margin" — lowercase, not the wire form "SPOT"/"MARGIN".');
49
+ }
50
+ }
51
+ /**
52
+ * Derives the deposit address for a (clientId, user, deposit target) triple.
18
53
  *
19
54
  * @returns The EIP-55 checksummed sweeper address, deployed or not.
20
- * @throws If any address is malformed or the client id is empty.
55
+ * @throws If any address is malformed, the client id is empty, or the deposit
56
+ * target is not exactly `"spot"` or `"margin"`.
21
57
  */
22
- export function predictSweeperAddress({ factory, implementation, clientId, user }) {
58
+ export function predictSweeperAddress({ factory, implementation, clientId, user, depositTarget = "spot" }) {
23
59
  const factoryAddress = assertAddress(factory, "factory");
24
60
  const implementationAddress = assertAddress(implementation, "implementation");
25
61
  const userAddress = assertNonZeroUser(assertAddress(user, "user"));
26
62
  assertClientId(clientId);
27
- const initCode = sweeperCloneInitCode(implementationAddress, sweeperImmutableArgs(userAddress, clientId));
63
+ // The bound string is the encoded application data, not the bare client id:
64
+ // it is what the clone carries and what a sweep puts on-chain.
65
+ const boundClientId = encodeDepositApplicationData(clientId, depositTarget);
66
+ const initCode = sweeperCloneInitCode(implementationAddress, sweeperImmutableArgs(userAddress, boundClientId));
28
67
  const preimage = concatBytes(Uint8Array.of(0xff), hexToBytes(factoryAddress), SALT, keccak_256(hexToBytes(initCode)));
29
68
  return toChecksumAddress(`0x${bytesToHex(keccak_256(preimage).slice(12))}`);
30
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/contracts",
3
- "version": "1.0.48",
3
+ "version": "1.0.50-develop.9c1b239",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.0xmonaco.com/sdk/typescript",
6
6
  "main": "./dist/index.js",