@bananapus/nana-sdk-core 2.14.0 → 2.15.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/cjs/safeService.d.ts +15 -0
- package/dist/cjs/safeService.d.ts.map +1 -1
- package/dist/cjs/safeService.js +58 -25
- package/dist/cjs/safeService.js.map +1 -1
- package/dist/esm/safeService.d.ts +15 -0
- package/dist/esm/safeService.d.ts.map +1 -1
- package/dist/esm/safeService.js +57 -25
- package/dist/esm/safeService.js.map +1 -1
- package/package.json +1 -1
|
@@ -20,6 +20,12 @@ export declare const SAFE_NONCE_GUIDANCE = "On Safe\u2019s confirmation screen,
|
|
|
20
20
|
/** The unix deadline for a swap/liquidity transaction proposed now. */
|
|
21
21
|
export declare function swapDeadline(isSafe: boolean, nowMs?: number): bigint;
|
|
22
22
|
export declare function safeQueueUrl(chainId: number, safe: Address): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Whether a WalletConnect peer is Safe{Wallet}. It proposes like the Safe
|
|
25
|
+
* app: the gas a dapp sends becomes the proposal's safeTxGas, and the reply is
|
|
26
|
+
* a safeTxHash unless the owner executes at once.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isSafeWalletPeer(url: string | undefined): boolean;
|
|
23
29
|
/**
|
|
24
30
|
* Resolve a Safe proposal identifier to its actual onchain execution hash.
|
|
25
31
|
* A safeTxHash is not a transaction hash and must never be receipt-polled.
|
|
@@ -28,9 +34,18 @@ export declare function safeQueueUrl(chainId: number, safe: Address): string | n
|
|
|
28
34
|
* map), NOT `SAFE_PREFIX`: OP Sepolia and Arbitrum Sepolia have Safe app
|
|
29
35
|
* URLs but no hosted transaction service, and polling a nonexistent service
|
|
30
36
|
* left every Safe write there pending forever.
|
|
37
|
+
*
|
|
38
|
+
* With the chain's `client`, a hash the chain already knows as a transaction
|
|
39
|
+
* is returned as the execution: over WalletConnect, Safe{Wallet} answers with
|
|
40
|
+
* the execution's own hash when the owner executes at once.
|
|
31
41
|
*/
|
|
32
42
|
export declare function waitForSafeExecutionHash(chainId: number, safeTxHash: Hex, options?: {
|
|
33
43
|
pollingIntervalMs?: number;
|
|
34
44
|
signal?: AbortSignal;
|
|
45
|
+
client?: {
|
|
46
|
+
getTransaction: (args: {
|
|
47
|
+
hash: Hex;
|
|
48
|
+
}) => Promise<unknown>;
|
|
49
|
+
};
|
|
35
50
|
}): Promise<Hex>;
|
|
36
51
|
//# sourceMappingURL=safeService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safeService.d.ts","sourceRoot":"","sources":["../../src/safeService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASvD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO/D,CAAC;AAEF;6EAC6E;AAC7E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED,eAAO,MAAM,mBAAmB,uJACiH,CAAC;AAUlJ,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,EACf,KAAK,GAAE,MAAmB,GACzB,MAAM,CAKR;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK1E;AAWD
|
|
1
|
+
{"version":3,"file":"safeService.d.ts","sourceRoot":"","sources":["../../src/safeService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASvD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO/D,CAAC;AAEF;6EAC6E;AAC7E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED,eAAO,MAAM,mBAAmB,uJACiH,CAAC;AAUlJ,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,EACf,KAAK,GAAE,MAAmB,GACzB,MAAM,CAKR;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK1E;AAWD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAMjE;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,GAAG,EACf,OAAO,GAAE;IACP,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE;QAAE,cAAc,EAAE,CAAC,IAAI,EAAE;YAAE,IAAI,EAAE,GAAG,CAAA;SAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;CACnE,GACL,OAAO,CAAC,GAAG,CAAC,CA+Ed"}
|
package/dist/cjs/safeService.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.SAFE_NONCE_GUIDANCE = exports.SAFE_SERVICE_PREFIX = exports.SAFE_PREFIX
|
|
|
4
4
|
exports.safeServiceBase = safeServiceBase;
|
|
5
5
|
exports.swapDeadline = swapDeadline;
|
|
6
6
|
exports.safeQueueUrl = safeQueueUrl;
|
|
7
|
+
exports.isSafeWalletPeer = isSafeWalletPeer;
|
|
7
8
|
exports.waitForSafeExecutionHash = waitForSafeExecutionHash;
|
|
8
9
|
/**
|
|
9
10
|
* Safe's per-chain app URL prefix. Wider than {@link SAFE_SERVICE_PREFIX}:
|
|
@@ -76,6 +77,22 @@ function safeQueueUrl(chainId, safe) {
|
|
|
76
77
|
* At the default 5s interval this is about a minute of patience.
|
|
77
78
|
*/
|
|
78
79
|
const SAFE_EXECUTION_NOT_FOUND_LIMIT = 12;
|
|
80
|
+
/**
|
|
81
|
+
* Whether a WalletConnect peer is Safe{Wallet}. It proposes like the Safe
|
|
82
|
+
* app: the gas a dapp sends becomes the proposal's safeTxGas, and the reply is
|
|
83
|
+
* a safeTxHash unless the owner executes at once.
|
|
84
|
+
*/
|
|
85
|
+
function isSafeWalletPeer(url) {
|
|
86
|
+
try {
|
|
87
|
+
return new URL(url ?? "").origin === "https://app.safe.global";
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function noHostedService(chainId) {
|
|
94
|
+
return new Error(`Safe does not host a transaction service on chain ${chainId}, so this proposal cannot be tracked here. Execute it from the Safe app; the action takes effect once it is executed there.`);
|
|
95
|
+
}
|
|
79
96
|
/**
|
|
80
97
|
* Resolve a Safe proposal identifier to its actual onchain execution hash.
|
|
81
98
|
* A safeTxHash is not a transaction hash and must never be receipt-polled.
|
|
@@ -84,46 +101,62 @@ const SAFE_EXECUTION_NOT_FOUND_LIMIT = 12;
|
|
|
84
101
|
* map), NOT `SAFE_PREFIX`: OP Sepolia and Arbitrum Sepolia have Safe app
|
|
85
102
|
* URLs but no hosted transaction service, and polling a nonexistent service
|
|
86
103
|
* left every Safe write there pending forever.
|
|
104
|
+
*
|
|
105
|
+
* With the chain's `client`, a hash the chain already knows as a transaction
|
|
106
|
+
* is returned as the execution: over WalletConnect, Safe{Wallet} answers with
|
|
107
|
+
* the execution's own hash when the owner executes at once.
|
|
87
108
|
*/
|
|
88
109
|
async function waitForSafeExecutionHash(chainId, safeTxHash, options = {}) {
|
|
89
110
|
const base = safeServiceBase(chainId);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
const { client } = options;
|
|
112
|
+
if (!base && !client)
|
|
113
|
+
throw noHostedService(chainId);
|
|
93
114
|
const interval = options.pollingIntervalMs ?? 5_000;
|
|
94
|
-
const endpoint = `${base}/api/v1/multisig-transactions/${safeTxHash}/`;
|
|
115
|
+
const endpoint = base && `${base}/api/v1/multisig-transactions/${safeTxHash}/`;
|
|
95
116
|
let consecutiveNotFound = 0;
|
|
96
117
|
for (;;) {
|
|
97
118
|
if (options.signal?.aborted) {
|
|
98
119
|
throw new DOMException("Safe execution wait aborted", "AbortError");
|
|
99
120
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
if (client &&
|
|
122
|
+
(await client.getTransaction({ hash: safeTxHash }).then(() => true, () => false))) {
|
|
123
|
+
return safeTxHash;
|
|
124
|
+
}
|
|
125
|
+
if (!endpoint) {
|
|
126
|
+
consecutiveNotFound += 1;
|
|
127
|
+
if (consecutiveNotFound >= SAFE_EXECUTION_NOT_FOUND_LIMIT) {
|
|
128
|
+
throw noHostedService(chainId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
try {
|
|
133
|
+
const response = await fetch(endpoint);
|
|
134
|
+
if (response.ok) {
|
|
135
|
+
consecutiveNotFound = 0;
|
|
136
|
+
const transaction = (await response.json());
|
|
137
|
+
if (transaction.isExecuted && transaction.isSuccessful === false) {
|
|
138
|
+
throw new Error("Safe executed the proposal, but the onchain transaction failed.");
|
|
139
|
+
}
|
|
140
|
+
if (transaction.isExecuted && transaction.transactionHash) {
|
|
141
|
+
return transaction.transactionHash;
|
|
142
|
+
}
|
|
107
143
|
}
|
|
108
|
-
if (
|
|
109
|
-
|
|
144
|
+
else if (response.status === 404) {
|
|
145
|
+
consecutiveNotFound += 1;
|
|
146
|
+
if (consecutiveNotFound >= SAFE_EXECUTION_NOT_FOUND_LIMIT) {
|
|
147
|
+
throw new Error("Safe’s transaction service has no record of this proposal. Tracking cannot continue here — check the proposal in the Safe app; if it exists there, it will still take effect once executed.");
|
|
148
|
+
}
|
|
110
149
|
}
|
|
111
150
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
throw
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (error instanceof Error &&
|
|
153
|
+
/executed the proposal.*failed|no record of this proposal/i.test(error.message)) {
|
|
154
|
+
throw error;
|
|
116
155
|
}
|
|
156
|
+
// Other service/network failures are transient. Keep the already-created
|
|
157
|
+
// proposal pending instead of inviting a duplicate submission.
|
|
117
158
|
}
|
|
118
159
|
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
if (error instanceof Error &&
|
|
121
|
-
/executed the proposal.*failed|no record of this proposal/i.test(error.message)) {
|
|
122
|
-
throw error;
|
|
123
|
-
}
|
|
124
|
-
// Other service/network failures are transient. Keep the already-created
|
|
125
|
-
// proposal pending instead of inviting a duplicate submission.
|
|
126
|
-
}
|
|
127
160
|
await new Promise((resolve, reject) => {
|
|
128
161
|
function onAbort() {
|
|
129
162
|
clearTimeout(timer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safeService.js","sourceRoot":"","sources":["../../src/safeService.ts"],"names":[],"mappings":";;;AAoCA,0CAWC;AAcD,oCAQC;AAED,oCAKC;
|
|
1
|
+
{"version":3,"file":"safeService.js","sourceRoot":"","sources":["../../src/safeService.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,12 @@ export declare const SAFE_NONCE_GUIDANCE = "On Safe\u2019s confirmation screen,
|
|
|
20
20
|
/** The unix deadline for a swap/liquidity transaction proposed now. */
|
|
21
21
|
export declare function swapDeadline(isSafe: boolean, nowMs?: number): bigint;
|
|
22
22
|
export declare function safeQueueUrl(chainId: number, safe: Address): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Whether a WalletConnect peer is Safe{Wallet}. It proposes like the Safe
|
|
25
|
+
* app: the gas a dapp sends becomes the proposal's safeTxGas, and the reply is
|
|
26
|
+
* a safeTxHash unless the owner executes at once.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isSafeWalletPeer(url: string | undefined): boolean;
|
|
23
29
|
/**
|
|
24
30
|
* Resolve a Safe proposal identifier to its actual onchain execution hash.
|
|
25
31
|
* A safeTxHash is not a transaction hash and must never be receipt-polled.
|
|
@@ -28,9 +34,18 @@ export declare function safeQueueUrl(chainId: number, safe: Address): string | n
|
|
|
28
34
|
* map), NOT `SAFE_PREFIX`: OP Sepolia and Arbitrum Sepolia have Safe app
|
|
29
35
|
* URLs but no hosted transaction service, and polling a nonexistent service
|
|
30
36
|
* left every Safe write there pending forever.
|
|
37
|
+
*
|
|
38
|
+
* With the chain's `client`, a hash the chain already knows as a transaction
|
|
39
|
+
* is returned as the execution: over WalletConnect, Safe{Wallet} answers with
|
|
40
|
+
* the execution's own hash when the owner executes at once.
|
|
31
41
|
*/
|
|
32
42
|
export declare function waitForSafeExecutionHash(chainId: number, safeTxHash: Hex, options?: {
|
|
33
43
|
pollingIntervalMs?: number;
|
|
34
44
|
signal?: AbortSignal;
|
|
45
|
+
client?: {
|
|
46
|
+
getTransaction: (args: {
|
|
47
|
+
hash: Hex;
|
|
48
|
+
}) => Promise<unknown>;
|
|
49
|
+
};
|
|
35
50
|
}): Promise<Hex>;
|
|
36
51
|
//# sourceMappingURL=safeService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safeService.d.ts","sourceRoot":"","sources":["../../src/safeService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASvD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO/D,CAAC;AAEF;6EAC6E;AAC7E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED,eAAO,MAAM,mBAAmB,uJACiH,CAAC;AAUlJ,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,EACf,KAAK,GAAE,MAAmB,GACzB,MAAM,CAKR;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK1E;AAWD
|
|
1
|
+
{"version":3,"file":"safeService.d.ts","sourceRoot":"","sources":["../../src/safeService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASvD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO/D,CAAC;AAEF;6EAC6E;AAC7E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED,eAAO,MAAM,mBAAmB,uJACiH,CAAC;AAUlJ,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,EACf,KAAK,GAAE,MAAmB,GACzB,MAAM,CAKR;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK1E;AAWD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAMjE;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,GAAG,EACf,OAAO,GAAE;IACP,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE;QAAE,cAAc,EAAE,CAAC,IAAI,EAAE;YAAE,IAAI,EAAE,GAAG,CAAA;SAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;CACnE,GACL,OAAO,CAAC,GAAG,CAAC,CA+Ed"}
|
package/dist/esm/safeService.js
CHANGED
|
@@ -69,6 +69,22 @@ export function safeQueueUrl(chainId, safe) {
|
|
|
69
69
|
* At the default 5s interval this is about a minute of patience.
|
|
70
70
|
*/
|
|
71
71
|
const SAFE_EXECUTION_NOT_FOUND_LIMIT = 12;
|
|
72
|
+
/**
|
|
73
|
+
* Whether a WalletConnect peer is Safe{Wallet}. It proposes like the Safe
|
|
74
|
+
* app: the gas a dapp sends becomes the proposal's safeTxGas, and the reply is
|
|
75
|
+
* a safeTxHash unless the owner executes at once.
|
|
76
|
+
*/
|
|
77
|
+
export function isSafeWalletPeer(url) {
|
|
78
|
+
try {
|
|
79
|
+
return new URL(url ?? "").origin === "https://app.safe.global";
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function noHostedService(chainId) {
|
|
86
|
+
return new Error(`Safe does not host a transaction service on chain ${chainId}, so this proposal cannot be tracked here. Execute it from the Safe app; the action takes effect once it is executed there.`);
|
|
87
|
+
}
|
|
72
88
|
/**
|
|
73
89
|
* Resolve a Safe proposal identifier to its actual onchain execution hash.
|
|
74
90
|
* A safeTxHash is not a transaction hash and must never be receipt-polled.
|
|
@@ -77,46 +93,62 @@ const SAFE_EXECUTION_NOT_FOUND_LIMIT = 12;
|
|
|
77
93
|
* map), NOT `SAFE_PREFIX`: OP Sepolia and Arbitrum Sepolia have Safe app
|
|
78
94
|
* URLs but no hosted transaction service, and polling a nonexistent service
|
|
79
95
|
* left every Safe write there pending forever.
|
|
96
|
+
*
|
|
97
|
+
* With the chain's `client`, a hash the chain already knows as a transaction
|
|
98
|
+
* is returned as the execution: over WalletConnect, Safe{Wallet} answers with
|
|
99
|
+
* the execution's own hash when the owner executes at once.
|
|
80
100
|
*/
|
|
81
101
|
export async function waitForSafeExecutionHash(chainId, safeTxHash, options = {}) {
|
|
82
102
|
const base = safeServiceBase(chainId);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
const { client } = options;
|
|
104
|
+
if (!base && !client)
|
|
105
|
+
throw noHostedService(chainId);
|
|
86
106
|
const interval = options.pollingIntervalMs ?? 5_000;
|
|
87
|
-
const endpoint = `${base}/api/v1/multisig-transactions/${safeTxHash}/`;
|
|
107
|
+
const endpoint = base && `${base}/api/v1/multisig-transactions/${safeTxHash}/`;
|
|
88
108
|
let consecutiveNotFound = 0;
|
|
89
109
|
for (;;) {
|
|
90
110
|
if (options.signal?.aborted) {
|
|
91
111
|
throw new DOMException("Safe execution wait aborted", "AbortError");
|
|
92
112
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
113
|
+
if (client &&
|
|
114
|
+
(await client.getTransaction({ hash: safeTxHash }).then(() => true, () => false))) {
|
|
115
|
+
return safeTxHash;
|
|
116
|
+
}
|
|
117
|
+
if (!endpoint) {
|
|
118
|
+
consecutiveNotFound += 1;
|
|
119
|
+
if (consecutiveNotFound >= SAFE_EXECUTION_NOT_FOUND_LIMIT) {
|
|
120
|
+
throw noHostedService(chainId);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
try {
|
|
125
|
+
const response = await fetch(endpoint);
|
|
126
|
+
if (response.ok) {
|
|
127
|
+
consecutiveNotFound = 0;
|
|
128
|
+
const transaction = (await response.json());
|
|
129
|
+
if (transaction.isExecuted && transaction.isSuccessful === false) {
|
|
130
|
+
throw new Error("Safe executed the proposal, but the onchain transaction failed.");
|
|
131
|
+
}
|
|
132
|
+
if (transaction.isExecuted && transaction.transactionHash) {
|
|
133
|
+
return transaction.transactionHash;
|
|
134
|
+
}
|
|
100
135
|
}
|
|
101
|
-
if (
|
|
102
|
-
|
|
136
|
+
else if (response.status === 404) {
|
|
137
|
+
consecutiveNotFound += 1;
|
|
138
|
+
if (consecutiveNotFound >= SAFE_EXECUTION_NOT_FOUND_LIMIT) {
|
|
139
|
+
throw new Error("Safe’s transaction service has no record of this proposal. Tracking cannot continue here — check the proposal in the Safe app; if it exists there, it will still take effect once executed.");
|
|
140
|
+
}
|
|
103
141
|
}
|
|
104
142
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
throw
|
|
143
|
+
catch (error) {
|
|
144
|
+
if (error instanceof Error &&
|
|
145
|
+
/executed the proposal.*failed|no record of this proposal/i.test(error.message)) {
|
|
146
|
+
throw error;
|
|
109
147
|
}
|
|
148
|
+
// Other service/network failures are transient. Keep the already-created
|
|
149
|
+
// proposal pending instead of inviting a duplicate submission.
|
|
110
150
|
}
|
|
111
151
|
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
if (error instanceof Error &&
|
|
114
|
-
/executed the proposal.*failed|no record of this proposal/i.test(error.message)) {
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
// Other service/network failures are transient. Keep the already-created
|
|
118
|
-
// proposal pending instead of inviting a duplicate submission.
|
|
119
|
-
}
|
|
120
152
|
await new Promise((resolve, reject) => {
|
|
121
153
|
function onAbort() {
|
|
122
154
|
clearTimeout(timer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safeService.js","sourceRoot":"","sources":["../../src/safeService.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAoC;IAC1D,CAAC,EAAE,KAAK;IACR,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;IACb,QAAQ,EAAE,KAAK;IACf,QAAQ,EAAE,WAAW;IACrB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,UAAU;CACnB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAoC;IAClE,CAAC,EAAE,KAAK;IACR,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;IACb,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,SAAS;CACjB,CAAC;AAEF;6EAC6E;AAC7E,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAC7B,CAAC;QACnC,IAAI,MAAM,EAAE,CAAC,OAAO,CAAC;YAAE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;IAClC,CAAC;IACD,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC,CAAC,CAAC,sCAAsC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACxE,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAC9B,+IAA+I,CAAC;AAElJ;qCACqC;AACrC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,CAAC;AACtC;;qEAEqE;AACrE,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAErD,uEAAuE;AACvE,MAAM,UAAU,YAAY,CAC1B,MAAe,EACf,QAAgB,IAAI,CAAC,GAAG,EAAE;IAE1B,OAAO,MAAM,CACX,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;QACtB,CAAC,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,IAAa;IACzD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,MAAM;QACX,CAAC,CAAC,mDAAmD,MAAM,IAAI,IAAI,EAAE;QACrE,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED;;;;;;GAMG;AACH,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAE1C
|
|
1
|
+
{"version":3,"file":"safeService.js","sourceRoot":"","sources":["../../src/safeService.ts"],"names":[],"mappings":"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"}
|