@prb/effect-evm-safe 1.0.0-beta.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/dist/constants/index.d.ts +5 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +6 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/react-hooks/index.d.ts +5 -0
- package/dist/react-hooks/index.d.ts.map +1 -0
- package/dist/react-hooks/index.js +6 -0
- package/dist/react-hooks/index.js.map +1 -0
- package/dist/react-hooks/safe-app-origins.d.ts +8 -0
- package/dist/react-hooks/safe-app-origins.d.ts.map +1 -0
- package/dist/react-hooks/safe-app-origins.js +124 -0
- package/dist/react-hooks/safe-app-origins.js.map +1 -0
- package/dist/react-hooks/use-is-host-safe-app.d.ts +2 -0
- package/dist/react-hooks/use-is-host-safe-app.d.ts.map +1 -0
- package/dist/react-hooks/use-is-host-safe-app.js +16 -0
- package/dist/react-hooks/use-is-host-safe-app.js.map +1 -0
- package/dist/react-hooks/use-is-safe-app-context.d.ts +2 -0
- package/dist/react-hooks/use-is-safe-app-context.d.ts.map +1 -0
- package/dist/react-hooks/use-is-safe-app-context.js +41 -0
- package/dist/react-hooks/use-is-safe-app-context.js.map +1 -0
- package/dist/react-hooks/use-is-safe-multisig-wallet.d.ts +2 -0
- package/dist/react-hooks/use-is-safe-multisig-wallet.d.ts.map +1 -0
- package/dist/react-hooks/use-is-safe-multisig-wallet.js +24 -0
- package/dist/react-hooks/use-is-safe-multisig-wallet.js.map +1 -0
- package/dist/safe/adapter.d.ts +7 -0
- package/dist/safe/adapter.d.ts.map +1 -0
- package/dist/safe/adapter.js +13 -0
- package/dist/safe/adapter.js.map +1 -0
- package/dist/safe/detection.d.ts +26 -0
- package/dist/safe/detection.d.ts.map +1 -0
- package/dist/safe/detection.js +102 -0
- package/dist/safe/detection.js.map +1 -0
- package/dist/safe/detection.test.integration.d.ts +2 -0
- package/dist/safe/detection.test.integration.d.ts.map +1 -0
- package/dist/safe/detection.test.integration.js +92 -0
- package/dist/safe/detection.test.integration.js.map +1 -0
- package/dist/safe/errors.d.ts +79 -0
- package/dist/safe/errors.d.ts.map +1 -0
- package/dist/safe/errors.js +34 -0
- package/dist/safe/errors.js.map +1 -0
- package/dist/safe/index.d.ts +8 -0
- package/dist/safe/index.d.ts.map +1 -0
- package/dist/safe/index.js +6 -0
- package/dist/safe/index.js.map +1 -0
- package/dist/safe/internal/poll.d.ts +6 -0
- package/dist/safe/internal/poll.d.ts.map +1 -0
- package/dist/safe/internal/poll.js +21 -0
- package/dist/safe/internal/poll.js.map +1 -0
- package/dist/safe/internal/sdk-types.d.ts +3 -0
- package/dist/safe/internal/sdk-types.d.ts.map +1 -0
- package/dist/safe/internal/sdk-types.js +2 -0
- package/dist/safe/internal/sdk-types.js.map +1 -0
- package/dist/safe/live.d.ts +7 -0
- package/dist/safe/live.d.ts.map +1 -0
- package/dist/safe/live.js +203 -0
- package/dist/safe/live.js.map +1 -0
- package/dist/safe/service.d.ts +26 -0
- package/dist/safe/service.d.ts.map +1 -0
- package/dist/safe/service.js +4 -0
- package/dist/safe/service.js.map +1 -0
- package/dist/safe/service.test.integration.d.ts +2 -0
- package/dist/safe/service.test.integration.d.ts.map +1 -0
- package/dist/safe/service.test.integration.js +171 -0
- package/dist/safe/service.test.integration.js.map +1 -0
- package/dist/safe/simulation/abis.d.ts +73 -0
- package/dist/safe/simulation/abis.d.ts.map +1 -0
- package/dist/safe/simulation/abis.js +61 -0
- package/dist/safe/simulation/abis.js.map +1 -0
- package/dist/safe/simulation/addresses.d.ts +4 -0
- package/dist/safe/simulation/addresses.d.ts.map +1 -0
- package/dist/safe/simulation/addresses.js +56 -0
- package/dist/safe/simulation/addresses.js.map +1 -0
- package/dist/safe/simulation/encoding.d.ts +16 -0
- package/dist/safe/simulation/encoding.d.ts.map +1 -0
- package/dist/safe/simulation/encoding.js +36 -0
- package/dist/safe/simulation/encoding.js.map +1 -0
- package/dist/safe/simulation/errors.d.ts +56 -0
- package/dist/safe/simulation/errors.d.ts.map +1 -0
- package/dist/safe/simulation/errors.js +37 -0
- package/dist/safe/simulation/errors.js.map +1 -0
- package/dist/safe/simulation/index.d.ts +7 -0
- package/dist/safe/simulation/index.d.ts.map +1 -0
- package/dist/safe/simulation/index.js +6 -0
- package/dist/safe/simulation/index.js.map +1 -0
- package/dist/safe/simulation/internal/calldata/calldata.d.ts +5 -0
- package/dist/safe/simulation/internal/calldata/calldata.d.ts.map +1 -0
- package/dist/safe/simulation/internal/calldata/calldata.js +17 -0
- package/dist/safe/simulation/internal/calldata/calldata.js.map +1 -0
- package/dist/safe/simulation/internal/calldata/index.d.ts +2 -0
- package/dist/safe/simulation/internal/calldata/index.d.ts.map +1 -0
- package/dist/safe/simulation/internal/calldata/index.js +2 -0
- package/dist/safe/simulation/internal/calldata/index.js.map +1 -0
- package/dist/safe/simulation/internal/contracts/contracts.d.ts +5 -0
- package/dist/safe/simulation/internal/contracts/contracts.d.ts.map +1 -0
- package/dist/safe/simulation/internal/contracts/contracts.js +25 -0
- package/dist/safe/simulation/internal/contracts/contracts.js.map +1 -0
- package/dist/safe/simulation/internal/contracts/index.d.ts +2 -0
- package/dist/safe/simulation/internal/contracts/index.d.ts.map +1 -0
- package/dist/safe/simulation/internal/contracts/index.js +2 -0
- package/dist/safe/simulation/internal/contracts/index.js.map +1 -0
- package/dist/safe/simulation/internal/evaluation/evaluation.d.ts +6 -0
- package/dist/safe/simulation/internal/evaluation/evaluation.d.ts.map +1 -0
- package/dist/safe/simulation/internal/evaluation/evaluation.js +20 -0
- package/dist/safe/simulation/internal/evaluation/evaluation.js.map +1 -0
- package/dist/safe/simulation/internal/evaluation/index.d.ts +2 -0
- package/dist/safe/simulation/internal/evaluation/index.d.ts.map +1 -0
- package/dist/safe/simulation/internal/evaluation/index.js +2 -0
- package/dist/safe/simulation/internal/evaluation/index.js.map +1 -0
- package/dist/safe/simulation/internal/execution/execution.d.ts +9 -0
- package/dist/safe/simulation/internal/execution/execution.d.ts.map +1 -0
- package/dist/safe/simulation/internal/execution/execution.js +65 -0
- package/dist/safe/simulation/internal/execution/execution.js.map +1 -0
- package/dist/safe/simulation/internal/execution/index.d.ts +2 -0
- package/dist/safe/simulation/internal/execution/index.d.ts.map +1 -0
- package/dist/safe/simulation/internal/execution/index.js +2 -0
- package/dist/safe/simulation/internal/execution/index.js.map +1 -0
- package/dist/safe/simulation/internal/limits/index.d.ts +2 -0
- package/dist/safe/simulation/internal/limits/index.d.ts.map +1 -0
- package/dist/safe/simulation/internal/limits/index.js +2 -0
- package/dist/safe/simulation/internal/limits/index.js.map +1 -0
- package/dist/safe/simulation/internal/limits/limits.d.ts +5 -0
- package/dist/safe/simulation/internal/limits/limits.d.ts.map +1 -0
- package/dist/safe/simulation/internal/limits/limits.js +18 -0
- package/dist/safe/simulation/internal/limits/limits.js.map +1 -0
- package/dist/safe/simulation/internal/types/index.d.ts +2 -0
- package/dist/safe/simulation/internal/types/index.d.ts.map +1 -0
- package/dist/safe/simulation/internal/types/index.js +2 -0
- package/dist/safe/simulation/internal/types/index.js.map +1 -0
- package/dist/safe/simulation/internal/types/types.d.ts +11 -0
- package/dist/safe/simulation/internal/types/types.d.ts.map +1 -0
- package/dist/safe/simulation/internal/types/types.js +2 -0
- package/dist/safe/simulation/internal/types/types.js.map +1 -0
- package/dist/safe/simulation/internal/validation/index.d.ts +2 -0
- package/dist/safe/simulation/internal/validation/index.d.ts.map +1 -0
- package/dist/safe/simulation/internal/validation/index.js +2 -0
- package/dist/safe/simulation/internal/validation/index.js.map +1 -0
- package/dist/safe/simulation/internal/validation/validation.d.ts +5 -0
- package/dist/safe/simulation/internal/validation/validation.d.ts.map +1 -0
- package/dist/safe/simulation/internal/validation/validation.js +27 -0
- package/dist/safe/simulation/internal/validation/validation.js.map +1 -0
- package/dist/safe/simulation/service.d.ts +14 -0
- package/dist/safe/simulation/service.d.ts.map +1 -0
- package/dist/safe/simulation/service.js +25 -0
- package/dist/safe/simulation/service.js.map +1 -0
- package/dist/safe/simulation/types.d.ts +20 -0
- package/dist/safe/simulation/types.d.ts.map +1 -0
- package/dist/safe/simulation/types.js +2 -0
- package/dist/safe/simulation/types.js.map +1 -0
- package/dist/safe/types.d.ts +61 -0
- package/dist/safe/types.d.ts.map +1 -0
- package/dist/safe/types.js +2 -0
- package/dist/safe/types.js.map +1 -0
- package/package.json +105 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, expect, it } from "@effect/vitest";
|
|
2
|
+
import { makePublicClientLayer } from "@prb/effect-evm/presets";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { mainnet } from "viem/chains";
|
|
5
|
+
import { isSafeMultisig } from "./detection.js";
|
|
6
|
+
const SAFE_V1_3_0_SINGLETON = "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552";
|
|
7
|
+
const SAFE_V1_4_1_SINGLETON = "0x41675C099F32341bf84BFc5382aF534df5C7461a";
|
|
8
|
+
const testLayer = makePublicClientLayer([
|
|
9
|
+
{
|
|
10
|
+
chain: mainnet,
|
|
11
|
+
chainId: 1,
|
|
12
|
+
rpcUrls: ["https://eth.llamarpc.com"],
|
|
13
|
+
},
|
|
14
|
+
]);
|
|
15
|
+
describe("isSafeMultisig", () => {
|
|
16
|
+
describe("Positive Cases - Known Safe Wallets", () => {
|
|
17
|
+
it.effect("detects CoW DAO Safe (v1.3.0)", () => Effect.gen(function* () {
|
|
18
|
+
const result = yield* isSafeMultisig({
|
|
19
|
+
address: "0xcA771eda0c70aA7d053aB1B25004559B918FE662",
|
|
20
|
+
chainId: 1,
|
|
21
|
+
});
|
|
22
|
+
expect(result.isSafe).toBe(true);
|
|
23
|
+
expect(result.proxyHash).toBeDefined();
|
|
24
|
+
expect(result.singletonAddress).toBe(SAFE_V1_3_0_SINGLETON);
|
|
25
|
+
expect(result.singletonHash).toBeDefined();
|
|
26
|
+
}).pipe(Effect.provide(testLayer)));
|
|
27
|
+
it.effect("detects Gnosis DAO Safe (v1.3.0)", () => Effect.gen(function* () {
|
|
28
|
+
const result = yield* isSafeMultisig({
|
|
29
|
+
address: "0x849D52316331967b6fF1198e5E32A0eB168D039d",
|
|
30
|
+
chainId: 1,
|
|
31
|
+
});
|
|
32
|
+
expect(result.isSafe).toBe(true);
|
|
33
|
+
expect(result.proxyHash).toBeDefined();
|
|
34
|
+
expect(result.singletonAddress).toBe(SAFE_V1_3_0_SINGLETON);
|
|
35
|
+
expect(result.singletonHash).toBeDefined();
|
|
36
|
+
}).pipe(Effect.provide(testLayer)));
|
|
37
|
+
it.effect("detects v1.4.1 Safe", () => Effect.gen(function* () {
|
|
38
|
+
const result = yield* isSafeMultisig({
|
|
39
|
+
address: "0x843ed9137c60772b30a71a7fbdb7f302f336ace7",
|
|
40
|
+
chainId: 1,
|
|
41
|
+
});
|
|
42
|
+
expect(result.isSafe).toBe(true);
|
|
43
|
+
expect(result.proxyHash).toBeDefined();
|
|
44
|
+
expect(result.singletonAddress).toBe(SAFE_V1_4_1_SINGLETON);
|
|
45
|
+
expect(result.singletonHash).toBeDefined();
|
|
46
|
+
}).pipe(Effect.provide(testLayer)));
|
|
47
|
+
});
|
|
48
|
+
describe("Negative Cases - Non-Safe Contracts", () => {
|
|
49
|
+
it.effect("identifies non-Safe contract (vitalik.eth with EIP-7702) as non-Safe", () => Effect.gen(function* () {
|
|
50
|
+
const result = yield* isSafeMultisig({
|
|
51
|
+
address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
|
|
52
|
+
chainId: 1,
|
|
53
|
+
});
|
|
54
|
+
expect(result.isSafe).toBe(false);
|
|
55
|
+
expect(result.singletonAddress).toBeUndefined();
|
|
56
|
+
expect(result.singletonHash).toBeUndefined();
|
|
57
|
+
}).pipe(Effect.provide(testLayer)));
|
|
58
|
+
it.effect("identifies Uniswap V2 Router as non-Safe", () => Effect.gen(function* () {
|
|
59
|
+
const result = yield* isSafeMultisig({
|
|
60
|
+
address: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
|
|
61
|
+
chainId: 1,
|
|
62
|
+
});
|
|
63
|
+
expect(result.isSafe).toBe(false);
|
|
64
|
+
expect(result.proxyHash).toBeDefined();
|
|
65
|
+
expect(result.singletonAddress).toBeUndefined();
|
|
66
|
+
expect(result.singletonHash).toBeUndefined();
|
|
67
|
+
}).pipe(Effect.provide(testLayer)));
|
|
68
|
+
it.effect("identifies USDC as non-Safe", () => Effect.gen(function* () {
|
|
69
|
+
const result = yield* isSafeMultisig({
|
|
70
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
71
|
+
chainId: 1,
|
|
72
|
+
});
|
|
73
|
+
expect(result.isSafe).toBe(false);
|
|
74
|
+
expect(result.proxyHash).toBeDefined();
|
|
75
|
+
expect(result.singletonAddress).toBeUndefined();
|
|
76
|
+
expect(result.singletonHash).toBeUndefined();
|
|
77
|
+
}).pipe(Effect.provide(testLayer)));
|
|
78
|
+
});
|
|
79
|
+
describe("Edge Cases", () => {
|
|
80
|
+
it.effect("handles precompile address gracefully", () => Effect.gen(function* () {
|
|
81
|
+
const result = yield* isSafeMultisig({
|
|
82
|
+
address: "0x0000000000000000000000000000000000000001",
|
|
83
|
+
chainId: 1,
|
|
84
|
+
});
|
|
85
|
+
expect(result.isSafe).toBe(false);
|
|
86
|
+
expect(result.proxyHash).toBeUndefined();
|
|
87
|
+
expect(result.singletonAddress).toBeUndefined();
|
|
88
|
+
expect(result.singletonHash).toBeUndefined();
|
|
89
|
+
}).pipe(Effect.provide(testLayer)));
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=detection.test.integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detection.test.integration.js","sourceRoot":"","sources":["../../src/safe/detection.test.integration.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from \"@effect/vitest\";\nimport { makePublicClientLayer } from \"@prb/effect-evm/presets\";\nimport { Effect } from \"effect\";\nimport type { Address } from \"viem\";\nimport { mainnet } from \"viem/chains\";\nimport { isSafeMultisig } from \"./detection.js\";\n\n// Known Safe singleton hashes for verification\nconst SAFE_V1_3_0_SINGLETON = \"0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552\" as Address;\nconst SAFE_V1_4_1_SINGLETON = \"0x41675C099F32341bf84BFc5382aF534df5C7461a\" as Address;\n\n// Test layer with Ethereum mainnet RPC\nconst testLayer = makePublicClientLayer([\n {\n chain: mainnet,\n chainId: 1,\n rpcUrls: [\"https://eth.llamarpc.com\"],\n },\n]);\n\ndescribe(\"isSafeMultisig\", () => {\n describe(\"Positive Cases - Known Safe Wallets\", () => {\n it.effect(\"detects CoW DAO Safe (v1.3.0)\", () =>\n Effect.gen(function* () {\n const result = yield* isSafeMultisig({\n address: \"0xcA771eda0c70aA7d053aB1B25004559B918FE662\",\n chainId: 1,\n });\n\n expect(result.isSafe).toBe(true);\n expect(result.proxyHash).toBeDefined();\n expect(result.singletonAddress).toBe(SAFE_V1_3_0_SINGLETON);\n expect(result.singletonHash).toBeDefined();\n }).pipe(Effect.provide(testLayer))\n );\n\n it.effect(\"detects Gnosis DAO Safe (v1.3.0)\", () =>\n Effect.gen(function* () {\n const result = yield* isSafeMultisig({\n address: \"0x849D52316331967b6fF1198e5E32A0eB168D039d\",\n chainId: 1,\n });\n\n expect(result.isSafe).toBe(true);\n expect(result.proxyHash).toBeDefined();\n expect(result.singletonAddress).toBe(SAFE_V1_3_0_SINGLETON);\n expect(result.singletonHash).toBeDefined();\n }).pipe(Effect.provide(testLayer))\n );\n\n it.effect(\"detects v1.4.1 Safe\", () =>\n Effect.gen(function* () {\n const result = yield* isSafeMultisig({\n address: \"0x843ed9137c60772b30a71a7fbdb7f302f336ace7\",\n chainId: 1,\n });\n\n expect(result.isSafe).toBe(true);\n expect(result.proxyHash).toBeDefined();\n expect(result.singletonAddress).toBe(SAFE_V1_4_1_SINGLETON);\n expect(result.singletonHash).toBeDefined();\n }).pipe(Effect.provide(testLayer))\n );\n });\n\n describe(\"Negative Cases - Non-Safe Contracts\", () => {\n it.effect(\"identifies non-Safe contract (vitalik.eth with EIP-7702) as non-Safe\", () =>\n Effect.gen(function* () {\n const result = yield* isSafeMultisig({\n address: \"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\",\n chainId: 1,\n });\n\n // vitalik.eth address has bytecode (EIP-7702 delegation) but is not a Safe multisig\n expect(result.isSafe).toBe(false);\n expect(result.singletonAddress).toBeUndefined();\n expect(result.singletonHash).toBeUndefined();\n }).pipe(Effect.provide(testLayer))\n );\n\n it.effect(\"identifies Uniswap V2 Router as non-Safe\", () =>\n Effect.gen(function* () {\n const result = yield* isSafeMultisig({\n address: \"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D\",\n chainId: 1,\n });\n\n expect(result.isSafe).toBe(false);\n expect(result.proxyHash).toBeDefined();\n expect(result.singletonAddress).toBeUndefined();\n expect(result.singletonHash).toBeUndefined();\n }).pipe(Effect.provide(testLayer))\n );\n\n it.effect(\"identifies USDC as non-Safe\", () =>\n Effect.gen(function* () {\n const result = yield* isSafeMultisig({\n address: \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\",\n chainId: 1,\n });\n\n expect(result.isSafe).toBe(false);\n expect(result.proxyHash).toBeDefined();\n expect(result.singletonAddress).toBeUndefined();\n expect(result.singletonHash).toBeUndefined();\n }).pipe(Effect.provide(testLayer))\n );\n });\n\n describe(\"Edge Cases\", () => {\n it.effect(\"handles precompile address gracefully\", () =>\n Effect.gen(function* () {\n const result = yield* isSafeMultisig({\n address: \"0x0000000000000000000000000000000000000001\",\n chainId: 1,\n });\n\n expect(result.isSafe).toBe(false);\n expect(result.proxyHash).toBeUndefined();\n expect(result.singletonAddress).toBeUndefined();\n expect(result.singletonHash).toBeUndefined();\n }).pipe(Effect.provide(testLayer))\n );\n });\n});\n"]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
declare const SafeAppsSdkUnavailableError_base: Schema.TaggedErrorClass<SafeAppsSdkUnavailableError, "SafeAppsSdkUnavailableError", {
|
|
3
|
+
readonly _tag: Schema.tag<"SafeAppsSdkUnavailableError">;
|
|
4
|
+
} & {
|
|
5
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
6
|
+
message: typeof Schema.String;
|
|
7
|
+
}>;
|
|
8
|
+
export declare class SafeAppsSdkUnavailableError extends SafeAppsSdkUnavailableError_base {
|
|
9
|
+
}
|
|
10
|
+
declare const NotInSafeAppContextError_base: Schema.TaggedErrorClass<NotInSafeAppContextError, "NotInSafeAppContextError", {
|
|
11
|
+
readonly _tag: Schema.tag<"NotInSafeAppContextError">;
|
|
12
|
+
} & {
|
|
13
|
+
message: typeof Schema.String;
|
|
14
|
+
}>;
|
|
15
|
+
export declare class NotInSafeAppContextError extends NotInSafeAppContextError_base {
|
|
16
|
+
}
|
|
17
|
+
declare const SafeMultisigInfoUnavailableError_base: Schema.TaggedErrorClass<SafeMultisigInfoUnavailableError, "SafeMultisigInfoUnavailableError", {
|
|
18
|
+
readonly _tag: Schema.tag<"SafeMultisigInfoUnavailableError">;
|
|
19
|
+
} & {
|
|
20
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
21
|
+
message: typeof Schema.String;
|
|
22
|
+
}>;
|
|
23
|
+
export declare class SafeMultisigInfoUnavailableError extends SafeMultisigInfoUnavailableError_base {
|
|
24
|
+
}
|
|
25
|
+
declare const SafeMultisigSettingsError_base: Schema.TaggedErrorClass<SafeMultisigSettingsError, "SafeMultisigSettingsError", {
|
|
26
|
+
readonly _tag: Schema.tag<"SafeMultisigSettingsError">;
|
|
27
|
+
} & {
|
|
28
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
29
|
+
message: typeof Schema.String;
|
|
30
|
+
}>;
|
|
31
|
+
export declare class SafeMultisigSettingsError extends SafeMultisigSettingsError_base {
|
|
32
|
+
}
|
|
33
|
+
declare const SafeMultisigTxSubmissionError_base: Schema.TaggedErrorClass<SafeMultisigTxSubmissionError, "SafeMultisigTxSubmissionError", {
|
|
34
|
+
readonly _tag: Schema.tag<"SafeMultisigTxSubmissionError">;
|
|
35
|
+
} & {
|
|
36
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
37
|
+
message: typeof Schema.String;
|
|
38
|
+
}>;
|
|
39
|
+
export declare class SafeMultisigTxSubmissionError extends SafeMultisigTxSubmissionError_base {
|
|
40
|
+
}
|
|
41
|
+
declare const SafeMultisigTxLookupError_base: Schema.TaggedErrorClass<SafeMultisigTxLookupError, "SafeMultisigTxLookupError", {
|
|
42
|
+
readonly _tag: Schema.tag<"SafeMultisigTxLookupError">;
|
|
43
|
+
} & {
|
|
44
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
45
|
+
message: typeof Schema.String;
|
|
46
|
+
retryable: typeof Schema.Boolean;
|
|
47
|
+
safeTxHash: typeof Schema.String;
|
|
48
|
+
}>;
|
|
49
|
+
export declare class SafeMultisigTxLookupError extends SafeMultisigTxLookupError_base {
|
|
50
|
+
}
|
|
51
|
+
declare const SafeMultisigTxExecutionTimeoutError_base: Schema.TaggedErrorClass<SafeMultisigTxExecutionTimeoutError, "SafeMultisigTxExecutionTimeoutError", {
|
|
52
|
+
readonly _tag: Schema.tag<"SafeMultisigTxExecutionTimeoutError">;
|
|
53
|
+
} & {
|
|
54
|
+
lastStatus: Schema.optional<typeof Schema.String>;
|
|
55
|
+
message: typeof Schema.String;
|
|
56
|
+
safeTxHash: typeof Schema.String;
|
|
57
|
+
timeout: typeof Schema.Number;
|
|
58
|
+
}>;
|
|
59
|
+
export declare class SafeMultisigTxExecutionTimeoutError extends SafeMultisigTxExecutionTimeoutError_base {
|
|
60
|
+
}
|
|
61
|
+
declare const SignTypedDataError_base: Schema.TaggedErrorClass<SignTypedDataError, "SignTypedDataError", {
|
|
62
|
+
readonly _tag: Schema.tag<"SignTypedDataError">;
|
|
63
|
+
} & {
|
|
64
|
+
cause: Schema.optional<typeof Schema.Unknown>;
|
|
65
|
+
message: typeof Schema.String;
|
|
66
|
+
}>;
|
|
67
|
+
export declare class SignTypedDataError extends SignTypedDataError_base {
|
|
68
|
+
}
|
|
69
|
+
declare const OffchainSignatureTimeoutError_base: Schema.TaggedErrorClass<OffchainSignatureTimeoutError, "OffchainSignatureTimeoutError", {
|
|
70
|
+
readonly _tag: Schema.tag<"OffchainSignatureTimeoutError">;
|
|
71
|
+
} & {
|
|
72
|
+
message: typeof Schema.String;
|
|
73
|
+
messageHash: typeof Schema.String;
|
|
74
|
+
timeout: typeof Schema.Number;
|
|
75
|
+
}>;
|
|
76
|
+
export declare class OffchainSignatureTimeoutError extends OffchainSignatureTimeoutError_base {
|
|
77
|
+
}
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/safe/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;;;;;;;AAGhC,qBAAa,2BAA4B,SAAQ,gCAGhD;CAAG;;;;;;AAEJ,qBAAa,wBAAyB,SAAQ,6BAG7C;CAAG;;;;;;;AAGJ,qBAAa,gCAAiC,SAAQ,qCAGrD;CAAG;;;;;;;AAEJ,qBAAa,yBAA0B,SAAQ,8BAG9C;CAAG;;;;;;;AAEJ,qBAAa,6BAA8B,SAAQ,kCAGlD;CAAG;;;;;;;;;AAEJ,qBAAa,yBAA0B,SAAQ,8BAQ9C;CAAG;;;;;;;;;AAEJ,qBAAa,mCAAoC,SAAQ,wCAQxD;CAAG;;;;;;;AAGJ,qBAAa,kBAAmB,SAAQ,uBAGvC;CAAG;;;;;;;;AAEJ,qBAAa,6BAA8B,SAAQ,kCAOlD;CAAG"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export class SafeAppsSdkUnavailableError extends Schema.TaggedError()("SafeAppsSdkUnavailableError", { cause: Schema.optional(Schema.Unknown), message: Schema.String }) {
|
|
3
|
+
}
|
|
4
|
+
export class NotInSafeAppContextError extends Schema.TaggedError()("NotInSafeAppContextError", { message: Schema.String }) {
|
|
5
|
+
}
|
|
6
|
+
export class SafeMultisigInfoUnavailableError extends Schema.TaggedError()("SafeMultisigInfoUnavailableError", { cause: Schema.optional(Schema.Unknown), message: Schema.String }) {
|
|
7
|
+
}
|
|
8
|
+
export class SafeMultisigSettingsError extends Schema.TaggedError()("SafeMultisigSettingsError", { cause: Schema.optional(Schema.Unknown), message: Schema.String }) {
|
|
9
|
+
}
|
|
10
|
+
export class SafeMultisigTxSubmissionError extends Schema.TaggedError()("SafeMultisigTxSubmissionError", { cause: Schema.optional(Schema.Unknown), message: Schema.String }) {
|
|
11
|
+
}
|
|
12
|
+
export class SafeMultisigTxLookupError extends Schema.TaggedError()("SafeMultisigTxLookupError", {
|
|
13
|
+
cause: Schema.optional(Schema.Unknown),
|
|
14
|
+
message: Schema.String,
|
|
15
|
+
retryable: Schema.Boolean,
|
|
16
|
+
safeTxHash: Schema.String,
|
|
17
|
+
}) {
|
|
18
|
+
}
|
|
19
|
+
export class SafeMultisigTxExecutionTimeoutError extends Schema.TaggedError()("SafeMultisigTxExecutionTimeoutError", {
|
|
20
|
+
lastStatus: Schema.optional(Schema.String),
|
|
21
|
+
message: Schema.String,
|
|
22
|
+
safeTxHash: Schema.String,
|
|
23
|
+
timeout: Schema.Number,
|
|
24
|
+
}) {
|
|
25
|
+
}
|
|
26
|
+
export class SignTypedDataError extends Schema.TaggedError()("SignTypedDataError", { cause: Schema.optional(Schema.Unknown), message: Schema.String }) {
|
|
27
|
+
}
|
|
28
|
+
export class OffchainSignatureTimeoutError extends Schema.TaggedError()("OffchainSignatureTimeoutError", {
|
|
29
|
+
message: Schema.String,
|
|
30
|
+
messageHash: Schema.String,
|
|
31
|
+
timeout: Schema.Number,
|
|
32
|
+
}) {
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/safe/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,MAAM,OAAO,2BAA4B,SAAQ,MAAM,CAAC,WAAW,EAA+B,CAChG,6BAA6B,EAC7B,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CACnE;CAAG;AAEJ,MAAM,OAAO,wBAAyB,SAAQ,MAAM,CAAC,WAAW,EAA4B,CAC1F,0BAA0B,EAC1B,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAC3B;CAAG;AAGJ,MAAM,OAAO,gCAAiC,SAAQ,MAAM,CAAC,WAAW,EAAoC,CAC1G,kCAAkC,EAClC,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CACnE;CAAG;AAEJ,MAAM,OAAO,yBAA0B,SAAQ,MAAM,CAAC,WAAW,EAA6B,CAC5F,2BAA2B,EAC3B,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CACnE;CAAG;AAEJ,MAAM,OAAO,6BAA8B,SAAQ,MAAM,CAAC,WAAW,EAAiC,CACpG,+BAA+B,EAC/B,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CACnE;CAAG;AAEJ,MAAM,OAAO,yBAA0B,SAAQ,MAAM,CAAC,WAAW,EAA6B,CAC5F,2BAA2B,EAC3B;IACE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,SAAS,EAAE,MAAM,CAAC,OAAO;IACzB,UAAU,EAAE,MAAM,CAAC,MAAM;CAC1B,CACF;CAAG;AAEJ,MAAM,OAAO,mCAAoC,SAAQ,MAAM,CAAC,WAAW,EAAuC,CAChH,qCAAqC,EACrC;IACE,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,UAAU,EAAE,MAAM,CAAC,MAAM;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CACF;CAAG;AAGJ,MAAM,OAAO,kBAAmB,SAAQ,MAAM,CAAC,WAAW,EAAsB,CAC9E,oBAAoB,EACpB,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CACnE;CAAG;AAEJ,MAAM,OAAO,6BAA8B,SAAQ,MAAM,CAAC,WAAW,EAAiC,CACpG,+BAA+B,EAC/B;IACE,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,WAAW,EAAE,MAAM,CAAC,MAAM;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CACF;CAAG","sourcesContent":["import { Schema } from \"effect\";\n\n// SDK availability errors\nexport class SafeAppsSdkUnavailableError extends Schema.TaggedError<SafeAppsSdkUnavailableError>()(\n \"SafeAppsSdkUnavailableError\",\n { cause: Schema.optional(Schema.Unknown), message: Schema.String }\n) {}\n\nexport class NotInSafeAppContextError extends Schema.TaggedError<NotInSafeAppContextError>()(\n \"NotInSafeAppContextError\",\n { message: Schema.String }\n) {}\n\n// Safe operation errors\nexport class SafeMultisigInfoUnavailableError extends Schema.TaggedError<SafeMultisigInfoUnavailableError>()(\n \"SafeMultisigInfoUnavailableError\",\n { cause: Schema.optional(Schema.Unknown), message: Schema.String }\n) {}\n\nexport class SafeMultisigSettingsError extends Schema.TaggedError<SafeMultisigSettingsError>()(\n \"SafeMultisigSettingsError\",\n { cause: Schema.optional(Schema.Unknown), message: Schema.String }\n) {}\n\nexport class SafeMultisigTxSubmissionError extends Schema.TaggedError<SafeMultisigTxSubmissionError>()(\n \"SafeMultisigTxSubmissionError\",\n { cause: Schema.optional(Schema.Unknown), message: Schema.String }\n) {}\n\nexport class SafeMultisigTxLookupError extends Schema.TaggedError<SafeMultisigTxLookupError>()(\n \"SafeMultisigTxLookupError\",\n {\n cause: Schema.optional(Schema.Unknown),\n message: Schema.String,\n retryable: Schema.Boolean,\n safeTxHash: Schema.String,\n }\n) {}\n\nexport class SafeMultisigTxExecutionTimeoutError extends Schema.TaggedError<SafeMultisigTxExecutionTimeoutError>()(\n \"SafeMultisigTxExecutionTimeoutError\",\n {\n lastStatus: Schema.optional(Schema.String),\n message: Schema.String,\n safeTxHash: Schema.String,\n timeout: Schema.Number,\n }\n) {}\n\n// Signing errors\nexport class SignTypedDataError extends Schema.TaggedError<SignTypedDataError>()(\n \"SignTypedDataError\",\n { cause: Schema.optional(Schema.Unknown), message: Schema.String }\n) {}\n\nexport class OffchainSignatureTimeoutError extends Schema.TaggedError<OffchainSignatureTimeoutError>()(\n \"OffchainSignatureTimeoutError\",\n {\n message: Schema.String,\n messageHash: Schema.String,\n timeout: Schema.Number,\n }\n) {}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { SafeDetectionParams, SafeDetectionResult } from "./detection.js";
|
|
2
|
+
export { isSafeMultisig, SafeMultisigDetectionError } from "./detection.js";
|
|
3
|
+
export { NotInSafeAppContextError, OffchainSignatureTimeoutError, SafeAppsSdkUnavailableError, SafeMultisigInfoUnavailableError, SafeMultisigSettingsError, SafeMultisigTxExecutionTimeoutError, SafeMultisigTxLookupError, SafeMultisigTxSubmissionError, SignTypedDataError, } from "./errors.js";
|
|
4
|
+
export { type SafeAppsServiceConfig, SafeAppsServiceLive } from "./live.js";
|
|
5
|
+
export { SafeAppsService, type SafeAppsServiceShape } from "./service.js";
|
|
6
|
+
export * from "./simulation/index.js";
|
|
7
|
+
export type { EIP712TypedData, OffchainSignaturePolicy, OffchainSignatureResult, SafeMultisigInfo, SafeMultisigTx, SafeMultisigTxResult, SafeMultisigTxSubmission, SafeWaitPolicy, SignTypedDataResult, } from "./types.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/safe/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE/E,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAG5E,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,2BAA2B,EAC3B,gCAAgC,EAChC,yBAAyB,EACzB,mCAAmC,EACnC,yBAAyB,EACzB,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE1E,cAAc,uBAAuB,CAAC;AAEtC,YAAY,EACV,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,cAAc,EACd,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { isSafeMultisig, SafeMultisigDetectionError } from "./detection.js";
|
|
2
|
+
export { NotInSafeAppContextError, OffchainSignatureTimeoutError, SafeAppsSdkUnavailableError, SafeMultisigInfoUnavailableError, SafeMultisigSettingsError, SafeMultisigTxExecutionTimeoutError, SafeMultisigTxLookupError, SafeMultisigTxSubmissionError, SignTypedDataError, } from "./errors.js";
|
|
3
|
+
export { SafeAppsServiceLive } from "./live.js";
|
|
4
|
+
export { SafeAppsService } from "./service.js";
|
|
5
|
+
export * from "./simulation/index.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/safe/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAG5E,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,2BAA2B,EAC3B,gCAAgC,EAChC,yBAAyB,EACzB,mCAAmC,EACnC,yBAAyB,EACzB,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAA8B,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,eAAe,EAA6B,MAAM,cAAc,CAAC;AAE1E,cAAc,uBAAuB,CAAC","sourcesContent":["// Service and layer\n\nexport type { SafeDetectionParams, SafeDetectionResult } from \"./detection.js\";\n// Detection\nexport { isSafeMultisig, SafeMultisigDetectionError } from \"./detection.js\";\n\n// Errors\nexport {\n NotInSafeAppContextError,\n OffchainSignatureTimeoutError,\n SafeAppsSdkUnavailableError,\n SafeMultisigInfoUnavailableError,\n SafeMultisigSettingsError,\n SafeMultisigTxExecutionTimeoutError,\n SafeMultisigTxLookupError,\n SafeMultisigTxSubmissionError,\n SignTypedDataError,\n} from \"./errors.js\";\nexport { type SafeAppsServiceConfig, SafeAppsServiceLive } from \"./live.js\";\nexport { SafeAppsService, type SafeAppsServiceShape } from \"./service.js\";\n// Simulation\nexport * from \"./simulation/index.js\";\n// Types\nexport type {\n EIP712TypedData,\n OffchainSignaturePolicy,\n OffchainSignatureResult,\n SafeMultisigInfo,\n SafeMultisigTx,\n SafeMultisigTxResult,\n SafeMultisigTxSubmission,\n SafeWaitPolicy,\n SignTypedDataResult,\n} from \"./types.js\";\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
export declare const pollUntil: <T, E1, E2, R>(attempt: Effect.Effect<Option.Option<T>, E1, R>, options: {
|
|
3
|
+
interval: number;
|
|
4
|
+
timeout: number;
|
|
5
|
+
}, onTimeout: (elapsed: number) => E2) => Effect.Effect<T, E1 | E2, R>;
|
|
6
|
+
//# sourceMappingURL=poll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../../src/safe/internal/poll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AASlD,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EACpC,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAC/C,SAAS;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC9C,WAAW,CAAC,OAAO,EAAE,MAAM,KAAK,EAAE,KACjC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CA2B1B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Duration, Effect, Option } from "effect";
|
|
2
|
+
export const pollUntil = (attempt, options, onTimeout) => Effect.gen(function* () {
|
|
3
|
+
let result = null;
|
|
4
|
+
const pollLoop = Effect.gen(function* () {
|
|
5
|
+
while (result === null) {
|
|
6
|
+
const maybeResult = yield* attempt;
|
|
7
|
+
if (Option.isSome(maybeResult)) {
|
|
8
|
+
result = maybeResult.value;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
yield* Effect.sleep(Duration.millis(options.interval));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
yield* pollLoop.pipe(Effect.timeout(Duration.millis(options.timeout)), Effect.catchTag("TimeoutException", () => Effect.fail(onTimeout(options.timeout))));
|
|
16
|
+
if (result === null) {
|
|
17
|
+
return yield* Effect.fail(onTimeout(options.timeout));
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=poll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll.js","sourceRoot":"","sources":["../../../src/safe/internal/poll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AASlD,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,OAA+C,EAC/C,OAA8C,EAC9C,SAAkC,EACJ,EAAE,CAChC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,IAAI,MAAM,GAAa,IAAI,CAAC;IAE5B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnC,OAAO,MAAM,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC;YACnC,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC/B,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBAEN,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAClB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAChD,MAAM,CAAC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CACnF,CAAC;IAGF,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC,CAAC","sourcesContent":["import { Duration, Effect, Option } from \"effect\";\n\n/**\n * Poll an effect until it returns Some, with timeout.\n *\n * @param attempt - Effect that returns Option.some when done, Option.none to continue polling\n * @param options - Polling configuration (interval/timeout in milliseconds)\n * @param onTimeout - Factory for timeout error, receives elapsed timeout\n */\nexport const pollUntil = <T, E1, E2, R>(\n attempt: Effect.Effect<Option.Option<T>, E1, R>,\n options: { interval: number; timeout: number },\n onTimeout: (elapsed: number) => E2\n): Effect.Effect<T, E1 | E2, R> =>\n Effect.gen(function* () {\n let result: T | null = null;\n\n const pollLoop = Effect.gen(function* () {\n while (result === null) {\n const maybeResult = yield* attempt;\n if (Option.isSome(maybeResult)) {\n result = maybeResult.value;\n } else {\n // Effect.sleep is interruptible\n yield* Effect.sleep(Duration.millis(options.interval));\n }\n }\n });\n\n yield* pollLoop.pipe(\n Effect.timeout(Duration.millis(options.timeout)),\n Effect.catchTag(\"TimeoutException\", () => Effect.fail(onTimeout(options.timeout)))\n );\n\n // After timeout handling, result should be set if we didn't fail\n if (result === null) {\n return yield* Effect.fail(onTimeout(options.timeout));\n }\n\n return result;\n });\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-types.d.ts","sourceRoot":"","sources":["../../../src/safe/internal/sdk-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,4BAA4B,CAAC;AAE1D,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-types.js","sourceRoot":"","sources":["../../../src/safe/internal/sdk-types.ts"],"names":[],"mappings":"","sourcesContent":["import type SafeAppsSDK from \"@safe-global/safe-apps-sdk\";\n\nexport type SafeAppsSDKInstance = SafeAppsSDK;\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TxManager } from "@prb/effect-evm/tx";
|
|
2
|
+
import { Layer } from "effect";
|
|
3
|
+
import type { SafeAppsSdkConfig } from "./adapter.js";
|
|
4
|
+
import { SafeAppsService } from "./service.js";
|
|
5
|
+
export type SafeAppsServiceConfig = SafeAppsSdkConfig;
|
|
6
|
+
export declare const SafeAppsServiceLive: (config?: SafeAppsServiceConfig) => Layer.Layer<SafeAppsService, never, TxManager>;
|
|
7
|
+
//# sourceMappingURL=live.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/safe/live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAU,KAAK,EAAe,MAAM,QAAQ,CAAC;AASpD,OAAO,KAAK,EAAuB,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAc3E,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,CAAC;AA4CtD,eAAO,MAAM,mBAAmB,GAAI,SAAS,qBAAqB,mDAwV/D,CAAC"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { TxManager } from "@prb/effect-evm/tx";
|
|
2
|
+
import { Effect, Layer, Option, Ref } from "effect";
|
|
3
|
+
import { isAddress, isHash, isHex } from "viem";
|
|
4
|
+
import { SAFE_EXECUTION_TIMEOUT, SAFE_POLL_INTERVAL, SAFE_SIGNATURE_POLL_INTERVAL, SAFE_SIGNATURE_TIMEOUT, } from "../constants/index.js";
|
|
5
|
+
import { loadSafeSdk } from "./adapter.js";
|
|
6
|
+
import { NotInSafeAppContextError, OffchainSignatureTimeoutError, SafeMultisigInfoUnavailableError, SafeMultisigSettingsError, SafeMultisigTxExecutionTimeoutError, SafeMultisigTxLookupError, SafeMultisigTxSubmissionError, SignTypedDataError, } from "./errors.js";
|
|
7
|
+
import { pollUntil } from "./internal/poll.js";
|
|
8
|
+
import { SafeAppsService } from "./service.js";
|
|
9
|
+
const makeValidator = (predicate, label) => (value, context) => predicate(value)
|
|
10
|
+
? Effect.succeed(value)
|
|
11
|
+
: Effect.fail(new SafeMultisigTxLookupError({
|
|
12
|
+
message: `Invalid ${label} from SDK in ${context}: ${value}`,
|
|
13
|
+
retryable: false,
|
|
14
|
+
safeTxHash: "",
|
|
15
|
+
}));
|
|
16
|
+
const validateAddress = makeValidator(isAddress, "address");
|
|
17
|
+
const validateHash = makeValidator(isHash, "hash");
|
|
18
|
+
const validateHex = makeValidator(isHex, "hex");
|
|
19
|
+
const withSdk = (getSdk, fn, mapError) => Effect.flatMap(Effect.mapError(getSdk, mapError), fn);
|
|
20
|
+
export const SafeAppsServiceLive = (config) => Layer.scoped(SafeAppsService, Effect.gen(function* () {
|
|
21
|
+
const infoRef = yield* Ref.make(null);
|
|
22
|
+
const txManager = yield* TxManager;
|
|
23
|
+
const sdkRef = yield* Ref.make({ _tag: "pending" });
|
|
24
|
+
const getSdk = Effect.gen(function* () {
|
|
25
|
+
const state = yield* Ref.get(sdkRef);
|
|
26
|
+
if (state._tag === "loaded") {
|
|
27
|
+
return state.sdk;
|
|
28
|
+
}
|
|
29
|
+
if (state._tag === "unavailable") {
|
|
30
|
+
return yield* Effect.fail(state.error);
|
|
31
|
+
}
|
|
32
|
+
if (typeof window === "undefined") {
|
|
33
|
+
const error = new NotInSafeAppContextError({
|
|
34
|
+
message: "Safe Apps SDK requires a browser environment (window is undefined)",
|
|
35
|
+
});
|
|
36
|
+
yield* Ref.set(sdkRef, { _tag: "unavailable", error });
|
|
37
|
+
return yield* Effect.fail(error);
|
|
38
|
+
}
|
|
39
|
+
const loadResult = yield* loadSafeSdk(config).pipe(Effect.map((sdk) => ({ _tag: "loaded", sdk })), Effect.catchTag("SafeAppsSdkUnavailableError", (error) => Effect.succeed({ _tag: "unavailable", error })));
|
|
40
|
+
yield* Ref.set(sdkRef, loadResult);
|
|
41
|
+
if (loadResult._tag === "unavailable") {
|
|
42
|
+
return yield* Effect.fail(loadResult.error);
|
|
43
|
+
}
|
|
44
|
+
return loadResult.sdk;
|
|
45
|
+
});
|
|
46
|
+
const getInfo = Effect.fn("SafeAppsService.getInfo")(function* () {
|
|
47
|
+
const cached = yield* Ref.get(infoRef);
|
|
48
|
+
if (cached) {
|
|
49
|
+
return cached;
|
|
50
|
+
}
|
|
51
|
+
const sdk = yield* withSdk(getSdk, (s) => Effect.tryPromise({
|
|
52
|
+
catch: (cause) => new SafeMultisigInfoUnavailableError({
|
|
53
|
+
cause,
|
|
54
|
+
message: "Failed to get Safe info from SDK",
|
|
55
|
+
}),
|
|
56
|
+
try: () => s.safe.getInfo(),
|
|
57
|
+
}), (e) => new SafeMultisigInfoUnavailableError({ cause: e, message: e.message }));
|
|
58
|
+
const safeAddress = yield* validateAddress(sdk.safeAddress, "getInfo").pipe(Effect.catchTag("SafeMultisigTxLookupError", (e) => Effect.fail(new SafeMultisigInfoUnavailableError({ cause: e, message: e.message }))));
|
|
59
|
+
const safeInfo = { chainId: sdk.chainId, safeAddress };
|
|
60
|
+
yield* Ref.set(infoRef, safeInfo);
|
|
61
|
+
return safeInfo;
|
|
62
|
+
});
|
|
63
|
+
const sendTxs = Effect.fn("SafeAppsService.sendTxs")(function* (txs, params) {
|
|
64
|
+
const sdkTxs = txs.map((tx) => ({
|
|
65
|
+
data: tx.data,
|
|
66
|
+
to: tx.to,
|
|
67
|
+
value: tx.value?.toString() ?? "0",
|
|
68
|
+
}));
|
|
69
|
+
const result = yield* withSdk(getSdk, (s) => Effect.tryPromise({
|
|
70
|
+
catch: (cause) => new SafeMultisigTxSubmissionError({
|
|
71
|
+
cause,
|
|
72
|
+
message: "Failed to submit txs to Safe",
|
|
73
|
+
}),
|
|
74
|
+
try: () => s.txs.send({ params, txs: sdkTxs }),
|
|
75
|
+
}), (e) => new SafeMultisigTxSubmissionError({ cause: e, message: e.message }));
|
|
76
|
+
const safeTxHash = yield* validateHash(result.safeTxHash, "sendTxs").pipe(Effect.catchTag("SafeMultisigTxLookupError", (e) => Effect.fail(new SafeMultisigTxSubmissionError({ cause: e, message: e.message }))));
|
|
77
|
+
const info = yield* getInfo().pipe(Effect.catchTag("SafeMultisigInfoUnavailableError", (error) => Effect.fail(new SafeMultisigTxSubmissionError({
|
|
78
|
+
cause: error,
|
|
79
|
+
message: "Failed to get Safe info after tx submission",
|
|
80
|
+
}))));
|
|
81
|
+
return { chainId: info.chainId, safeAddress: info.safeAddress, safeTxHash };
|
|
82
|
+
});
|
|
83
|
+
const getTx = Effect.fn("SafeAppsService.getTx")(function* (safeTxHash) {
|
|
84
|
+
const tx = yield* withSdk(getSdk, (s) => Effect.tryPromise({
|
|
85
|
+
catch: (cause) => new SafeMultisigTxLookupError({
|
|
86
|
+
cause,
|
|
87
|
+
message: `Failed to lookup Safe tx ${safeTxHash}`,
|
|
88
|
+
retryable: true,
|
|
89
|
+
safeTxHash,
|
|
90
|
+
}),
|
|
91
|
+
try: () => s.txs.getBySafeTxHash(safeTxHash),
|
|
92
|
+
}), (e) => new SafeMultisigTxLookupError({
|
|
93
|
+
cause: e,
|
|
94
|
+
message: e.message,
|
|
95
|
+
retryable: false,
|
|
96
|
+
safeTxHash,
|
|
97
|
+
}));
|
|
98
|
+
const txHash = tx.txHash ? yield* validateHash(tx.txHash, "getTx") : null;
|
|
99
|
+
return {
|
|
100
|
+
status: tx.txStatus ?? "AWAITING_CONFIRMATIONS",
|
|
101
|
+
txHash: txHash ? Option.some(txHash) : Option.none(),
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
const waitForTxReceipt = Effect.fn("SafeAppsService.waitForTxReceipt")(function* (safeTxHash, policy = {}) {
|
|
105
|
+
const info = yield* getInfo().pipe(Effect.catchTag("SafeMultisigInfoUnavailableError", (error) => Effect.fail(new SafeMultisigTxLookupError({
|
|
106
|
+
cause: error,
|
|
107
|
+
message: "Failed to get Safe info for receipt waiting",
|
|
108
|
+
retryable: true,
|
|
109
|
+
safeTxHash,
|
|
110
|
+
}))));
|
|
111
|
+
const pollInterval = policy.pollInterval ?? SAFE_POLL_INTERVAL;
|
|
112
|
+
const executionTimeout = policy.executionTimeout ?? SAFE_EXECUTION_TIMEOUT;
|
|
113
|
+
let lastStatus = "AWAITING_CONFIRMATIONS";
|
|
114
|
+
const onchainHash = yield* pollUntil(Effect.gen(function* () {
|
|
115
|
+
const tx = yield* getTx(safeTxHash);
|
|
116
|
+
lastStatus = tx.status;
|
|
117
|
+
return tx.txHash;
|
|
118
|
+
}), { interval: pollInterval, timeout: executionTimeout }, (timeout) => new SafeMultisigTxExecutionTimeoutError({
|
|
119
|
+
lastStatus,
|
|
120
|
+
message: `Safe tx ${safeTxHash} not executed within ${timeout}ms (last status: ${lastStatus})`,
|
|
121
|
+
safeTxHash,
|
|
122
|
+
timeout,
|
|
123
|
+
}));
|
|
124
|
+
const receipt = yield* txManager
|
|
125
|
+
.waitForReceipt(info.chainId, onchainHash, policy.receiptPolicy)
|
|
126
|
+
.pipe(Effect.catchTag("TxReplacedError", (error) => Effect.fail(new SafeMultisigTxLookupError({
|
|
127
|
+
cause: error,
|
|
128
|
+
message: `Transaction was replaced: ${error.message}`,
|
|
129
|
+
retryable: false,
|
|
130
|
+
safeTxHash,
|
|
131
|
+
}))));
|
|
132
|
+
return {
|
|
133
|
+
chainId: info.chainId,
|
|
134
|
+
onchainHash,
|
|
135
|
+
receipt,
|
|
136
|
+
safeAddress: info.safeAddress,
|
|
137
|
+
safeTxHash,
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
const signTypedData = Effect.fn("SafeAppsService.signTypedData")(function* (typedData) {
|
|
141
|
+
const result = yield* withSdk(getSdk, (s) => Effect.tryPromise({
|
|
142
|
+
catch: (cause) => new SignTypedDataError({ cause, message: "Failed to sign typed data via Safe" }),
|
|
143
|
+
try: () => s.txs.signTypedMessage(typedData),
|
|
144
|
+
}), (e) => new SignTypedDataError({ cause: e, message: e.message }));
|
|
145
|
+
if ("messageHash" in result) {
|
|
146
|
+
const messageHash = yield* validateHex(result.messageHash ?? "", "signTypedData").pipe(Effect.catchTag("SafeMultisigTxLookupError", (e) => Effect.fail(new SignTypedDataError({ cause: e, message: e.message }))));
|
|
147
|
+
return { _tag: "Offchain", messageHash };
|
|
148
|
+
}
|
|
149
|
+
const safeTxHash = yield* validateHash(result.safeTxHash, "signTypedData").pipe(Effect.catchTag("SafeMultisigTxLookupError", (e) => Effect.fail(new SignTypedDataError({ cause: e, message: e.message }))));
|
|
150
|
+
return { _tag: "Onchain", safeTxHash };
|
|
151
|
+
});
|
|
152
|
+
const getOffchainSignature = Effect.fn("SafeAppsService.getOffchainSignature")(function* (messageHash) {
|
|
153
|
+
const sig = yield* withSdk(getSdk, (s) => Effect.tryPromise({
|
|
154
|
+
catch: (cause) => new SafeMultisigTxLookupError({
|
|
155
|
+
cause,
|
|
156
|
+
message: `Failed to get off-chain signature for ${messageHash}`,
|
|
157
|
+
retryable: true,
|
|
158
|
+
safeTxHash: messageHash,
|
|
159
|
+
}),
|
|
160
|
+
try: () => s.safe.getOffChainSignature(messageHash),
|
|
161
|
+
}), (e) => new SafeMultisigTxLookupError({
|
|
162
|
+
cause: e,
|
|
163
|
+
message: e.message,
|
|
164
|
+
retryable: false,
|
|
165
|
+
safeTxHash: messageHash,
|
|
166
|
+
}));
|
|
167
|
+
if (!sig || sig === "" || sig === "0x") {
|
|
168
|
+
return Option.none();
|
|
169
|
+
}
|
|
170
|
+
const validatedSig = yield* validateHex(sig, "getOffchainSignature");
|
|
171
|
+
return Option.some(validatedSig);
|
|
172
|
+
});
|
|
173
|
+
const pollOffchainSignature = Effect.fn("SafeAppsService.pollOffchainSignature")(function* (messageHash, policy = {}) {
|
|
174
|
+
const pollInterval = policy.pollInterval ?? SAFE_SIGNATURE_POLL_INTERVAL;
|
|
175
|
+
const timeout = policy.timeout ?? SAFE_SIGNATURE_TIMEOUT;
|
|
176
|
+
const signature = yield* pollUntil(getOffchainSignature(messageHash), { interval: pollInterval, timeout }, (elapsed) => new OffchainSignatureTimeoutError({
|
|
177
|
+
message: `Off-chain signature for ${messageHash} not available within ${elapsed}ms`,
|
|
178
|
+
messageHash,
|
|
179
|
+
timeout: elapsed,
|
|
180
|
+
}));
|
|
181
|
+
return { messageHash, signature };
|
|
182
|
+
});
|
|
183
|
+
const enableOffchainSigning = Effect.fn("SafeAppsService.enableOffchainSigning")(function* () {
|
|
184
|
+
yield* withSdk(getSdk, (s) => Effect.tryPromise({
|
|
185
|
+
catch: (cause) => new SafeMultisigSettingsError({
|
|
186
|
+
cause,
|
|
187
|
+
message: "Failed to enable off-chain signing mode",
|
|
188
|
+
}),
|
|
189
|
+
try: () => s.eth.setSafeSettings([{ offChainSigning: true }]),
|
|
190
|
+
}), (e) => new SafeMultisigSettingsError({ cause: e, message: e.message }));
|
|
191
|
+
});
|
|
192
|
+
return SafeAppsService.of({
|
|
193
|
+
enableOffchainSigning,
|
|
194
|
+
getInfo,
|
|
195
|
+
getOffchainSignature,
|
|
196
|
+
getTx,
|
|
197
|
+
pollOffchainSignature,
|
|
198
|
+
sendTxs,
|
|
199
|
+
signTypedData,
|
|
200
|
+
waitForTxReceipt,
|
|
201
|
+
});
|
|
202
|
+
}));
|
|
203
|
+
//# sourceMappingURL=live.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/safe/live.ts"],"names":[],"mappings":"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","sourcesContent":["import { TxManager } from \"@prb/effect-evm/tx\";\nimport { Effect, Layer, Option, Ref } from \"effect\";\nimport type { Hash, Hex } from \"viem\";\nimport { isAddress, isHash, isHex } from \"viem\";\nimport {\n SAFE_EXECUTION_TIMEOUT,\n SAFE_POLL_INTERVAL,\n SAFE_SIGNATURE_POLL_INTERVAL,\n SAFE_SIGNATURE_TIMEOUT,\n} from \"@/src/constants/index.js\";\nimport type { SafeAppsSDKInstance, SafeAppsSdkConfig } from \"./adapter.js\";\nimport { loadSafeSdk } from \"./adapter.js\";\nimport type { SafeAppsSdkUnavailableError } from \"./errors.js\";\nimport {\n NotInSafeAppContextError,\n OffchainSignatureTimeoutError,\n SafeMultisigInfoUnavailableError,\n SafeMultisigSettingsError,\n SafeMultisigTxExecutionTimeoutError,\n SafeMultisigTxLookupError,\n SafeMultisigTxSubmissionError,\n SignTypedDataError,\n} from \"./errors.js\";\nimport { pollUntil } from \"./internal/poll.js\";\nimport { SafeAppsService } from \"./service.js\";\nimport type { EIP712TypedData, SafeMultisigInfo, SafeMultisigTx } from \"./types.js\";\n\nexport type SafeAppsServiceConfig = SafeAppsSdkConfig;\n\n// --- Validation Factory ---\n\ntype Predicate<T extends string> = (value: string) => value is T;\n\nconst makeValidator =\n <T extends string>(predicate: Predicate<T>, label: string) =>\n (value: string, context: string): Effect.Effect<T, SafeMultisigTxLookupError> =>\n predicate(value)\n ? Effect.succeed(value)\n : Effect.fail(\n new SafeMultisigTxLookupError({\n message: `Invalid ${label} from SDK in ${context}: ${value}`,\n retryable: false,\n safeTxHash: \"\",\n })\n );\n\nconst validateAddress = makeValidator(isAddress, \"address\");\nconst validateHash = makeValidator(isHash, \"hash\");\nconst validateHex = makeValidator(isHex, \"hex\");\n\n// --- SDK State ---\n\n/** SDK availability error - either SSR environment or SDK load failure */\ntype SdkUnavailableError = NotInSafeAppContextError | SafeAppsSdkUnavailableError;\n\n/** Internal SDK state for lazy loading */\ntype SdkState<T> =\n | { readonly _tag: \"pending\" }\n | { readonly _tag: \"loaded\"; readonly sdk: T }\n | { readonly _tag: \"unavailable\"; readonly error: SdkUnavailableError };\n\n// --- SDK Wrapper Helper ---\n\nconst withSdk = <A, E>(\n getSdk: Effect.Effect<SafeAppsSDKInstance, SdkUnavailableError>,\n fn: (sdk: SafeAppsSDKInstance) => Effect.Effect<A, E>,\n mapError: (e: SdkUnavailableError) => E\n): Effect.Effect<A, E> => Effect.flatMap(Effect.mapError(getSdk, mapError), fn);\n\n// --- Service Implementation ---\n\nexport const SafeAppsServiceLive = (config?: SafeAppsServiceConfig) =>\n Layer.scoped(\n SafeAppsService,\n Effect.gen(function* () {\n // Cache Safe info after first fetch\n const infoRef = yield* Ref.make<SafeMultisigInfo | null>(null);\n\n // Get TxManager for receipt waiting\n const txManager = yield* TxManager;\n\n const sdkRef = yield* Ref.make<SdkState<SafeAppsSDKInstance>>({ _tag: \"pending\" });\n\n /** Get SDK, loading lazily on first call. Fails if not in Safe App context. */\n const getSdk: Effect.Effect<SafeAppsSDKInstance, SdkUnavailableError> = Effect.gen(\n function* () {\n const state = yield* Ref.get(sdkRef);\n\n if (state._tag === \"loaded\") {\n return state.sdk;\n }\n if (state._tag === \"unavailable\") {\n return yield* Effect.fail(state.error);\n }\n\n // First call - check environment and load SDK\n if (typeof window === \"undefined\") {\n const error = new NotInSafeAppContextError({\n message: \"Safe Apps SDK requires a browser environment (window is undefined)\",\n });\n yield* Ref.set(sdkRef, { _tag: \"unavailable\", error });\n return yield* Effect.fail(error);\n }\n\n // Try to load SDK - catch SDK unavailable error and store it\n const loadResult = yield* loadSafeSdk(config).pipe(\n Effect.map((sdk) => ({ _tag: \"loaded\" as const, sdk })),\n Effect.catchTag(\"SafeAppsSdkUnavailableError\", (error) =>\n Effect.succeed({ _tag: \"unavailable\" as const, error } as const)\n )\n );\n\n yield* Ref.set(sdkRef, loadResult);\n\n if (loadResult._tag === \"unavailable\") {\n return yield* Effect.fail(loadResult.error);\n }\n\n return loadResult.sdk;\n }\n );\n\n // --- Service Methods ---\n\n const getInfo = Effect.fn(\"SafeAppsService.getInfo\")(function* () {\n const cached = yield* Ref.get(infoRef);\n if (cached) {\n return cached;\n }\n\n const sdk = yield* withSdk(\n getSdk,\n (s) =>\n Effect.tryPromise({\n catch: (cause) =>\n new SafeMultisigInfoUnavailableError({\n cause,\n message: \"Failed to get Safe info from SDK\",\n }),\n try: () => s.safe.getInfo(),\n }),\n (e) => new SafeMultisigInfoUnavailableError({ cause: e, message: e.message })\n );\n\n const safeAddress = yield* validateAddress(sdk.safeAddress, \"getInfo\").pipe(\n Effect.catchTag(\"SafeMultisigTxLookupError\", (e) =>\n Effect.fail(new SafeMultisigInfoUnavailableError({ cause: e, message: e.message }))\n )\n );\n\n const safeInfo: SafeMultisigInfo = { chainId: sdk.chainId, safeAddress };\n yield* Ref.set(infoRef, safeInfo);\n return safeInfo;\n });\n\n const sendTxs = Effect.fn(\"SafeAppsService.sendTxs\")(function* (\n txs: readonly SafeMultisigTx[],\n params?: { safeTxGas?: number }\n ) {\n // Convert bigint values to strings (Safe SDK expects decimal strings)\n const sdkTxs = txs.map((tx) => ({\n data: tx.data,\n to: tx.to,\n value: tx.value?.toString() ?? \"0\",\n }));\n\n const result = yield* withSdk(\n getSdk,\n (s) =>\n Effect.tryPromise({\n catch: (cause) =>\n new SafeMultisigTxSubmissionError({\n cause,\n message: \"Failed to submit txs to Safe\",\n }),\n try: () => s.txs.send({ params, txs: sdkTxs }),\n }),\n (e) => new SafeMultisigTxSubmissionError({ cause: e, message: e.message })\n );\n\n const safeTxHash = yield* validateHash(result.safeTxHash, \"sendTxs\").pipe(\n Effect.catchTag(\"SafeMultisigTxLookupError\", (e) =>\n Effect.fail(new SafeMultisigTxSubmissionError({ cause: e, message: e.message }))\n )\n );\n\n const info = yield* getInfo().pipe(\n Effect.catchTag(\"SafeMultisigInfoUnavailableError\", (error) =>\n Effect.fail(\n new SafeMultisigTxSubmissionError({\n cause: error,\n message: \"Failed to get Safe info after tx submission\",\n })\n )\n )\n );\n\n return { chainId: info.chainId, safeAddress: info.safeAddress, safeTxHash };\n });\n\n const getTx = Effect.fn(\"SafeAppsService.getTx\")(function* (safeTxHash: Hash) {\n const tx = yield* withSdk(\n getSdk,\n (s) =>\n Effect.tryPromise({\n catch: (cause) =>\n new SafeMultisigTxLookupError({\n cause,\n message: `Failed to lookup Safe tx ${safeTxHash}`,\n retryable: true,\n safeTxHash,\n }),\n try: () => s.txs.getBySafeTxHash(safeTxHash),\n }),\n (e) =>\n new SafeMultisigTxLookupError({\n cause: e,\n message: e.message,\n retryable: false,\n safeTxHash,\n })\n );\n\n const txHash = tx.txHash ? yield* validateHash(tx.txHash, \"getTx\") : null;\n\n return {\n status: tx.txStatus ?? \"AWAITING_CONFIRMATIONS\",\n txHash: txHash ? Option.some(txHash) : Option.none<Hash>(),\n };\n });\n\n const waitForTxReceipt = Effect.fn(\"SafeAppsService.waitForTxReceipt\")(function* (\n safeTxHash: Hash,\n policy: {\n pollInterval?: number;\n executionTimeout?: number;\n receiptPolicy?: { receiptTimeout?: number; pollingInterval?: number };\n } = {}\n ) {\n const info = yield* getInfo().pipe(\n Effect.catchTag(\"SafeMultisigInfoUnavailableError\", (error) =>\n Effect.fail(\n new SafeMultisigTxLookupError({\n cause: error,\n message: \"Failed to get Safe info for receipt waiting\",\n retryable: true,\n safeTxHash,\n })\n )\n )\n );\n\n const pollInterval = policy.pollInterval ?? SAFE_POLL_INTERVAL;\n const executionTimeout = policy.executionTimeout ?? SAFE_EXECUTION_TIMEOUT;\n\n // Track last status for timeout error message\n let lastStatus = \"AWAITING_CONFIRMATIONS\";\n\n const onchainHash = yield* pollUntil(\n Effect.gen(function* () {\n const tx = yield* getTx(safeTxHash);\n lastStatus = tx.status;\n return tx.txHash;\n }),\n { interval: pollInterval, timeout: executionTimeout },\n (timeout) =>\n new SafeMultisigTxExecutionTimeoutError({\n lastStatus,\n message: `Safe tx ${safeTxHash} not executed within ${timeout}ms (last status: ${lastStatus})`,\n safeTxHash,\n timeout,\n })\n );\n\n // Delegate to TxManager for on-chain receipt\n const receipt = yield* txManager\n .waitForReceipt(info.chainId, onchainHash, policy.receiptPolicy)\n .pipe(\n Effect.catchTag(\"TxReplacedError\", (error) =>\n Effect.fail(\n new SafeMultisigTxLookupError({\n cause: error,\n message: `Transaction was replaced: ${error.message}`,\n retryable: false,\n safeTxHash,\n })\n )\n )\n );\n\n return {\n chainId: info.chainId,\n onchainHash,\n receipt,\n safeAddress: info.safeAddress,\n safeTxHash,\n };\n });\n\n const signTypedData = Effect.fn(\"SafeAppsService.signTypedData\")(function* (\n typedData: EIP712TypedData\n ) {\n const result = yield* withSdk(\n getSdk,\n (s) =>\n Effect.tryPromise({\n catch: (cause) =>\n new SignTypedDataError({ cause, message: \"Failed to sign typed data via Safe\" }),\n try: () => s.txs.signTypedMessage(typedData),\n }),\n (e) => new SignTypedDataError({ cause: e, message: e.message })\n );\n\n // SDK returns { safeTxHash } for on-chain or { messageHash } for off-chain\n if (\"messageHash\" in result) {\n const messageHash = yield* validateHex(result.messageHash ?? \"\", \"signTypedData\").pipe(\n Effect.catchTag(\"SafeMultisigTxLookupError\", (e) =>\n Effect.fail(new SignTypedDataError({ cause: e, message: e.message }))\n )\n );\n return { _tag: \"Offchain\" as const, messageHash };\n }\n\n const safeTxHash = yield* validateHash(result.safeTxHash, \"signTypedData\").pipe(\n Effect.catchTag(\"SafeMultisigTxLookupError\", (e) =>\n Effect.fail(new SignTypedDataError({ cause: e, message: e.message }))\n )\n );\n return { _tag: \"Onchain\" as const, safeTxHash };\n });\n\n const getOffchainSignature = Effect.fn(\"SafeAppsService.getOffchainSignature\")(function* (\n messageHash: Hex\n ) {\n const sig = yield* withSdk(\n getSdk,\n (s) =>\n Effect.tryPromise({\n catch: (cause) =>\n new SafeMultisigTxLookupError({\n cause,\n message: `Failed to get off-chain signature for ${messageHash}`,\n retryable: true,\n safeTxHash: messageHash,\n }),\n try: () => s.safe.getOffChainSignature(messageHash),\n }),\n (e) =>\n new SafeMultisigTxLookupError({\n cause: e,\n message: e.message,\n retryable: false,\n safeTxHash: messageHash,\n })\n );\n\n // Empty string or \"0x\" means signature not yet available\n if (!sig || sig === \"\" || sig === \"0x\") {\n return Option.none<Hex>();\n }\n\n const validatedSig = yield* validateHex(sig, \"getOffchainSignature\");\n return Option.some(validatedSig);\n });\n\n const pollOffchainSignature = Effect.fn(\"SafeAppsService.pollOffchainSignature\")(function* (\n messageHash: Hex,\n policy: { pollInterval?: number; timeout?: number } = {}\n ) {\n const pollInterval = policy.pollInterval ?? SAFE_SIGNATURE_POLL_INTERVAL;\n const timeout = policy.timeout ?? SAFE_SIGNATURE_TIMEOUT;\n\n const signature = yield* pollUntil(\n getOffchainSignature(messageHash),\n { interval: pollInterval, timeout },\n (elapsed) =>\n new OffchainSignatureTimeoutError({\n message: `Off-chain signature for ${messageHash} not available within ${elapsed}ms`,\n messageHash,\n timeout: elapsed,\n })\n );\n\n return { messageHash, signature };\n });\n\n const enableOffchainSigning = Effect.fn(\"SafeAppsService.enableOffchainSigning\")(\n function* () {\n yield* withSdk(\n getSdk,\n (s) =>\n Effect.tryPromise({\n catch: (cause) =>\n new SafeMultisigSettingsError({\n cause,\n message: \"Failed to enable off-chain signing mode\",\n }),\n try: () => s.eth.setSafeSettings([{ offChainSigning: true }]),\n }),\n (e) => new SafeMultisigSettingsError({ cause: e, message: e.message })\n );\n }\n );\n\n return SafeAppsService.of({\n enableOffchainSigning,\n getInfo,\n getOffchainSignature,\n getTx,\n pollOffchainSignature,\n sendTxs,\n signTypedData,\n waitForTxReceipt,\n });\n })\n );\n"]}
|