@polymarbot/shared 0.5.4 → 0.6.1
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/chunk-LHBGF66E.js +73 -0
- package/index.cjs +1 -0
- package/index.d.cts +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/markets/utils.cjs +10 -5
- package/markets/utils.js +10 -5
- package/package.json +14 -3
- package/safe-executor.cjs +191 -0
- package/safe-executor.d.cts +19 -0
- package/safe-executor.d.ts +19 -0
- package/safe-executor.js +138 -0
- package/trade-strategy/types.d.cts +1 -0
- package/trade-strategy/types.d.ts +1 -0
- package/wallet.js +10 -63
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/utils/wallet.ts
|
|
2
|
+
import { createPublicClient, http, formatUnits, parseAbi } from "viem";
|
|
3
|
+
import { polygon } from "viem/chains";
|
|
4
|
+
var USDC_ADDRESS = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
|
|
5
|
+
var USDC_DECIMALS = 6;
|
|
6
|
+
var USDC_ABI = parseAbi(["function balanceOf(address) view returns (uint256)"]);
|
|
7
|
+
var getPublicClient = /* @__PURE__ */ (() => {
|
|
8
|
+
let client = null;
|
|
9
|
+
let batchClient = null;
|
|
10
|
+
return (options) => {
|
|
11
|
+
const rpcUrl = process.env.POLYGON_RPC || polygon.rpcUrls.default.http[0];
|
|
12
|
+
if (options?.batch) {
|
|
13
|
+
if (!batchClient) {
|
|
14
|
+
batchClient = createPublicClient({
|
|
15
|
+
chain: polygon,
|
|
16
|
+
transport: http(rpcUrl, { batch: { batchSize: 100, wait: 0 } })
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return batchClient;
|
|
20
|
+
}
|
|
21
|
+
if (!client) {
|
|
22
|
+
client = createPublicClient({
|
|
23
|
+
chain: polygon,
|
|
24
|
+
transport: http(rpcUrl)
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return client;
|
|
28
|
+
};
|
|
29
|
+
})();
|
|
30
|
+
function formatBalance(rawBalance, decimals = USDC_DECIMALS) {
|
|
31
|
+
const num = parseFloat(formatUnits(BigInt(rawBalance), decimals));
|
|
32
|
+
return (Math.floor(num * 100) / 100).toFixed(2);
|
|
33
|
+
}
|
|
34
|
+
async function getUSDCBalance(address) {
|
|
35
|
+
const publicClient = getPublicClient();
|
|
36
|
+
return await publicClient.readContract({
|
|
37
|
+
address: USDC_ADDRESS,
|
|
38
|
+
abi: USDC_ABI,
|
|
39
|
+
functionName: "balanceOf",
|
|
40
|
+
args: [address]
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async function getUSDCBalancesBatch(addresses) {
|
|
44
|
+
if (addresses.length === 0) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
const publicClient = getPublicClient();
|
|
48
|
+
return await publicClient.multicall({
|
|
49
|
+
contracts: addresses.map((address) => ({
|
|
50
|
+
address: USDC_ADDRESS,
|
|
51
|
+
abi: USDC_ABI,
|
|
52
|
+
functionName: "balanceOf",
|
|
53
|
+
args: [address]
|
|
54
|
+
})),
|
|
55
|
+
allowFailure: true
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function isAddressEqual(a, b) {
|
|
59
|
+
if (!a) return false;
|
|
60
|
+
if (!b) return false;
|
|
61
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
USDC_ADDRESS,
|
|
66
|
+
USDC_DECIMALS,
|
|
67
|
+
USDC_ABI,
|
|
68
|
+
getPublicClient,
|
|
69
|
+
formatBalance,
|
|
70
|
+
getUSDCBalance,
|
|
71
|
+
getUSDCBalancesBatch,
|
|
72
|
+
isAddressEqual
|
|
73
|
+
};
|
package/index.cjs
CHANGED
|
@@ -7,6 +7,7 @@ module.exports = {
|
|
|
7
7
|
...require('./markets/types.cjs'),
|
|
8
8
|
...require('./markets/utils.cjs'),
|
|
9
9
|
...require('./relayer-client.cjs'),
|
|
10
|
+
...require('./safe-executor.cjs'),
|
|
10
11
|
...require('./trade-strategy/normalize.cjs'),
|
|
11
12
|
...require('./trade-strategy/types.cjs'),
|
|
12
13
|
...require('./wallet.cjs')
|
package/index.d.cts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './load-env.js'
|
|
|
6
6
|
export * from './markets/types.js'
|
|
7
7
|
export * from './markets/utils.js'
|
|
8
8
|
export * from './relayer-client.js'
|
|
9
|
+
export * from './safe-executor.js'
|
|
9
10
|
export * from './trade-strategy/normalize.js'
|
|
10
11
|
export * from './trade-strategy/types.js'
|
|
11
12
|
export * from './wallet.js'
|
package/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './load-env.js'
|
|
|
6
6
|
export * from './markets/types.js'
|
|
7
7
|
export * from './markets/utils.js'
|
|
8
8
|
export * from './relayer-client.js'
|
|
9
|
+
export * from './safe-executor.js'
|
|
9
10
|
export * from './trade-strategy/normalize.js'
|
|
10
11
|
export * from './trade-strategy/types.js'
|
|
11
12
|
export * from './wallet.js'
|
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from './load-env.js'
|
|
|
6
6
|
export * from './markets/types.js'
|
|
7
7
|
export * from './markets/utils.js'
|
|
8
8
|
export * from './relayer-client.js'
|
|
9
|
+
export * from './safe-executor.js'
|
|
9
10
|
export * from './trade-strategy/normalize.js'
|
|
10
11
|
export * from './trade-strategy/types.js'
|
|
11
12
|
export * from './wallet.js'
|
package/markets/utils.cjs
CHANGED
|
@@ -679,7 +679,12 @@ function generate1dSlug(timestamp, symbol) {
|
|
|
679
679
|
const date = new Date(timestamp * 1e3);
|
|
680
680
|
const month = MONTH_NAMES[date.getUTCMonth()];
|
|
681
681
|
const day = date.getUTCDate();
|
|
682
|
-
|
|
682
|
+
const YEAR_SLUG_CUTOFF = 1773532800;
|
|
683
|
+
if (timestamp < YEAR_SLUG_CUTOFF) {
|
|
684
|
+
return `${prefix}-up-or-down-on-${month}-${day}`;
|
|
685
|
+
}
|
|
686
|
+
const year = date.getUTCFullYear();
|
|
687
|
+
return `${prefix}-up-or-down-on-${month}-${day}-${year}`;
|
|
683
688
|
}
|
|
684
689
|
function calculatePeriodStartInET(intervalSeconds) {
|
|
685
690
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -724,7 +729,7 @@ function parseIntervalFromSlug(slug) {
|
|
|
724
729
|
if (lowerSlug.match(/-up-or-down-\w+-\d+-\d+(am|pm)-et$/)) {
|
|
725
730
|
return "1h" /* H1 */;
|
|
726
731
|
}
|
|
727
|
-
if (lowerSlug.match(/-up-or-down-on-\w+-\d
|
|
732
|
+
if (lowerSlug.match(/-up-or-down-on-\w+-\d+(-\d{4})?$/)) {
|
|
728
733
|
return "1d" /* D1 */;
|
|
729
734
|
}
|
|
730
735
|
return null;
|
|
@@ -778,12 +783,12 @@ function parseTimeRangeFromSlug(slug, referenceYear) {
|
|
|
778
783
|
}
|
|
779
784
|
}
|
|
780
785
|
} else if (interval === "1d" /* D1 */) {
|
|
781
|
-
const match = lowerSlug.match(/-up-or-down-on-(\w+)-(\d+)
|
|
786
|
+
const match = lowerSlug.match(/-up-or-down-on-(\w+)-(\d+)(?:-(\d{4}))?$/);
|
|
782
787
|
if (match) {
|
|
783
|
-
const [, monthName, day] = match;
|
|
788
|
+
const [, monthName, day, slugYear] = match;
|
|
784
789
|
const monthIndex = MONTH_NAMES.indexOf(monthName);
|
|
785
790
|
if (monthIndex !== -1) {
|
|
786
|
-
const year = referenceYear ?? (/* @__PURE__ */ new Date()).getFullYear();
|
|
791
|
+
const year = slugYear ? parseInt(slugYear, 10) : referenceYear ?? (/* @__PURE__ */ new Date()).getFullYear();
|
|
787
792
|
const utcTime = dayjs_default.utc(
|
|
788
793
|
`${year}-${String(monthIndex + 1).padStart(2, "0")}-${String(day).padStart(2, "0")} 00:00:00`
|
|
789
794
|
);
|
package/markets/utils.js
CHANGED
|
@@ -626,7 +626,12 @@ function generate1dSlug(timestamp, symbol) {
|
|
|
626
626
|
const date = new Date(timestamp * 1e3);
|
|
627
627
|
const month = MONTH_NAMES[date.getUTCMonth()];
|
|
628
628
|
const day = date.getUTCDate();
|
|
629
|
-
|
|
629
|
+
const YEAR_SLUG_CUTOFF = 1773532800;
|
|
630
|
+
if (timestamp < YEAR_SLUG_CUTOFF) {
|
|
631
|
+
return `${prefix}-up-or-down-on-${month}-${day}`;
|
|
632
|
+
}
|
|
633
|
+
const year = date.getUTCFullYear();
|
|
634
|
+
return `${prefix}-up-or-down-on-${month}-${day}-${year}`;
|
|
630
635
|
}
|
|
631
636
|
function calculatePeriodStartInET(intervalSeconds) {
|
|
632
637
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -671,7 +676,7 @@ function parseIntervalFromSlug(slug) {
|
|
|
671
676
|
if (lowerSlug.match(/-up-or-down-\w+-\d+-\d+(am|pm)-et$/)) {
|
|
672
677
|
return "1h" /* H1 */;
|
|
673
678
|
}
|
|
674
|
-
if (lowerSlug.match(/-up-or-down-on-\w+-\d
|
|
679
|
+
if (lowerSlug.match(/-up-or-down-on-\w+-\d+(-\d{4})?$/)) {
|
|
675
680
|
return "1d" /* D1 */;
|
|
676
681
|
}
|
|
677
682
|
return null;
|
|
@@ -725,12 +730,12 @@ function parseTimeRangeFromSlug(slug, referenceYear) {
|
|
|
725
730
|
}
|
|
726
731
|
}
|
|
727
732
|
} else if (interval === "1d" /* D1 */) {
|
|
728
|
-
const match = lowerSlug.match(/-up-or-down-on-(\w+)-(\d+)
|
|
733
|
+
const match = lowerSlug.match(/-up-or-down-on-(\w+)-(\d+)(?:-(\d{4}))?$/);
|
|
729
734
|
if (match) {
|
|
730
|
-
const [, monthName, day] = match;
|
|
735
|
+
const [, monthName, day, slugYear] = match;
|
|
731
736
|
const monthIndex = MONTH_NAMES.indexOf(monthName);
|
|
732
737
|
if (monthIndex !== -1) {
|
|
733
|
-
const year = referenceYear ?? (/* @__PURE__ */ new Date()).getFullYear();
|
|
738
|
+
const year = slugYear ? parseInt(slugYear, 10) : referenceYear ?? (/* @__PURE__ */ new Date()).getFullYear();
|
|
734
739
|
const utcTime = dayjs_default.utc(
|
|
735
740
|
`${year}-${String(monthIndex + 1).padStart(2, "0")}-${String(day).padStart(2, "0")} 00:00:00`
|
|
736
741
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polymarbot/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.cjs",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -96,6 +96,16 @@
|
|
|
96
96
|
"default": "./relayer-client.cjs"
|
|
97
97
|
}
|
|
98
98
|
},
|
|
99
|
+
"./safe-executor": {
|
|
100
|
+
"import": {
|
|
101
|
+
"types": "./safe-executor.d.ts",
|
|
102
|
+
"default": "./safe-executor.js"
|
|
103
|
+
},
|
|
104
|
+
"require": {
|
|
105
|
+
"types": "./safe-executor.d.cts",
|
|
106
|
+
"default": "./safe-executor.cjs"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
99
109
|
"./trade-strategy/normalize": {
|
|
100
110
|
"import": {
|
|
101
111
|
"types": "./trade-strategy/normalize.d.ts",
|
|
@@ -134,9 +144,10 @@
|
|
|
134
144
|
"**/*.d.cts"
|
|
135
145
|
],
|
|
136
146
|
"dependencies": {
|
|
137
|
-
"dotenv": "^17.3.1",
|
|
138
147
|
"viem": "^2.46.2",
|
|
148
|
+
"dotenv": "^17.3.1",
|
|
139
149
|
"@polymarket/builder-relayer-client": "^0.0.8",
|
|
140
|
-
"@polymarket/builder-signing-sdk": "^0.0.8"
|
|
150
|
+
"@polymarket/builder-signing-sdk": "^0.0.8",
|
|
151
|
+
"p-limit": "^7.3.0"
|
|
141
152
|
}
|
|
142
153
|
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/utils/safe-executor.ts
|
|
31
|
+
var safe_executor_exports = {};
|
|
32
|
+
__export(safe_executor_exports, {
|
|
33
|
+
execSafeTransaction: () => execSafeTransaction
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(safe_executor_exports);
|
|
36
|
+
var import_viem2 = require("viem");
|
|
37
|
+
var import_accounts = require("viem/accounts");
|
|
38
|
+
var import_chains2 = require("viem/chains");
|
|
39
|
+
var import_builder_relayer_client = require("@polymarket/builder-relayer-client");
|
|
40
|
+
|
|
41
|
+
// src/utils/wallet.ts
|
|
42
|
+
var import_viem = require("viem");
|
|
43
|
+
var import_chains = require("viem/chains");
|
|
44
|
+
var USDC_ABI = (0, import_viem.parseAbi)(["function balanceOf(address) view returns (uint256)"]);
|
|
45
|
+
var getPublicClient = /* @__PURE__ */ (() => {
|
|
46
|
+
let client = null;
|
|
47
|
+
let batchClient = null;
|
|
48
|
+
return (options) => {
|
|
49
|
+
const rpcUrl = process.env.POLYGON_RPC || import_chains.polygon.rpcUrls.default.http[0];
|
|
50
|
+
if (options?.batch) {
|
|
51
|
+
if (!batchClient) {
|
|
52
|
+
batchClient = (0, import_viem.createPublicClient)({
|
|
53
|
+
chain: import_chains.polygon,
|
|
54
|
+
transport: (0, import_viem.http)(rpcUrl, { batch: { batchSize: 100, wait: 0 } })
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return batchClient;
|
|
58
|
+
}
|
|
59
|
+
if (!client) {
|
|
60
|
+
client = (0, import_viem.createPublicClient)({
|
|
61
|
+
chain: import_chains.polygon,
|
|
62
|
+
transport: (0, import_viem.http)(rpcUrl)
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return client;
|
|
66
|
+
};
|
|
67
|
+
})();
|
|
68
|
+
|
|
69
|
+
// src/utils/safe-executor.ts
|
|
70
|
+
var import_p_limit = __toESM(require("p-limit"), 1);
|
|
71
|
+
var SAFE_MULTISEND = "0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761";
|
|
72
|
+
var SAFE_ABI = (0, import_viem2.parseAbi)([
|
|
73
|
+
"function execTransaction(address to, uint256 value, bytes data, uint8 operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, bytes signatures) payable returns (bool success)",
|
|
74
|
+
"function nonce() view returns (uint256)"
|
|
75
|
+
]);
|
|
76
|
+
var SAFE_TX_TYPES = {
|
|
77
|
+
SafeTx: [
|
|
78
|
+
{ name: "to", type: "address" },
|
|
79
|
+
{ name: "value", type: "uint256" },
|
|
80
|
+
{ name: "data", type: "bytes" },
|
|
81
|
+
{ name: "operation", type: "uint8" },
|
|
82
|
+
{ name: "safeTxGas", type: "uint256" },
|
|
83
|
+
{ name: "baseGas", type: "uint256" },
|
|
84
|
+
{ name: "gasPrice", type: "uint256" },
|
|
85
|
+
{ name: "gasToken", type: "address" },
|
|
86
|
+
{ name: "refundReceiver", type: "address" },
|
|
87
|
+
{ name: "nonce", type: "uint256" }
|
|
88
|
+
]
|
|
89
|
+
};
|
|
90
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
91
|
+
var gasPayerLimit = (0, import_p_limit.default)(1);
|
|
92
|
+
function aggregateTransaction(txns) {
|
|
93
|
+
if (txns.length === 0) {
|
|
94
|
+
throw new Error("No transactions to aggregate");
|
|
95
|
+
}
|
|
96
|
+
if (txns.length === 1) {
|
|
97
|
+
return txns[0];
|
|
98
|
+
}
|
|
99
|
+
return (0, import_builder_relayer_client.createSafeMultisendTransaction)(txns, SAFE_MULTISEND);
|
|
100
|
+
}
|
|
101
|
+
function createSafeTxHash(chainId, safeAddress, tx, nonce) {
|
|
102
|
+
return (0, import_viem2.hashTypedData)({
|
|
103
|
+
domain: {
|
|
104
|
+
chainId,
|
|
105
|
+
verifyingContract: safeAddress
|
|
106
|
+
},
|
|
107
|
+
types: SAFE_TX_TYPES,
|
|
108
|
+
primaryType: "SafeTx",
|
|
109
|
+
message: {
|
|
110
|
+
to: tx.to,
|
|
111
|
+
value: BigInt(tx.value),
|
|
112
|
+
data: tx.data,
|
|
113
|
+
operation: tx.operation,
|
|
114
|
+
safeTxGas: 0n,
|
|
115
|
+
baseGas: 0n,
|
|
116
|
+
gasPrice: 0n,
|
|
117
|
+
gasToken: ZERO_ADDRESS,
|
|
118
|
+
refundReceiver: ZERO_ADDRESS,
|
|
119
|
+
nonce
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async function signSafeTransaction(ownerPrivateKey, structHash) {
|
|
124
|
+
const account = (0, import_accounts.privateKeyToAccount)(ownerPrivateKey);
|
|
125
|
+
const signature = await account.signMessage({
|
|
126
|
+
message: { raw: (0, import_viem2.toBytes)(structHash) }
|
|
127
|
+
});
|
|
128
|
+
const r = signature.slice(0, 66);
|
|
129
|
+
const s = "0x" + signature.slice(66, 130);
|
|
130
|
+
const v = parseInt(signature.slice(130, 132), 16);
|
|
131
|
+
const adjustedV = v + 4;
|
|
132
|
+
return (0, import_viem2.encodePacked)(
|
|
133
|
+
["bytes32", "bytes32", "uint8"],
|
|
134
|
+
[r, s, adjustedV]
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
async function execSafeTransaction(params) {
|
|
138
|
+
const {
|
|
139
|
+
safeAddress,
|
|
140
|
+
ownerPrivateKey,
|
|
141
|
+
gasPayerPrivateKey,
|
|
142
|
+
transactions,
|
|
143
|
+
chainId = import_chains2.polygon.id
|
|
144
|
+
} = params;
|
|
145
|
+
const aggregatedTx = aggregateTransaction(transactions);
|
|
146
|
+
const publicClient = getPublicClient();
|
|
147
|
+
const safeNonce = await publicClient.readContract({
|
|
148
|
+
address: safeAddress,
|
|
149
|
+
abi: SAFE_ABI,
|
|
150
|
+
functionName: "nonce"
|
|
151
|
+
});
|
|
152
|
+
const safeTxHash = createSafeTxHash(chainId, safeAddress, aggregatedTx, safeNonce);
|
|
153
|
+
const signature = await signSafeTransaction(ownerPrivateKey, safeTxHash);
|
|
154
|
+
const txHash = await gasPayerLimit(async () => {
|
|
155
|
+
const rpcUrl = process.env.POLYGON_RPC || import_chains2.polygon.rpcUrls.default.http[0];
|
|
156
|
+
const gasPayerAccount = (0, import_accounts.privateKeyToAccount)(gasPayerPrivateKey);
|
|
157
|
+
const gasPayerWallet = (0, import_viem2.createWalletClient)({
|
|
158
|
+
account: gasPayerAccount,
|
|
159
|
+
chain: import_chains2.polygon,
|
|
160
|
+
transport: (0, import_viem2.http)(rpcUrl)
|
|
161
|
+
});
|
|
162
|
+
const hash = await gasPayerWallet.writeContract({
|
|
163
|
+
address: safeAddress,
|
|
164
|
+
abi: SAFE_ABI,
|
|
165
|
+
functionName: "execTransaction",
|
|
166
|
+
args: [
|
|
167
|
+
aggregatedTx.to,
|
|
168
|
+
BigInt(aggregatedTx.value),
|
|
169
|
+
aggregatedTx.data,
|
|
170
|
+
aggregatedTx.operation,
|
|
171
|
+
0n,
|
|
172
|
+
// safeTxGas
|
|
173
|
+
0n,
|
|
174
|
+
// baseGas
|
|
175
|
+
0n,
|
|
176
|
+
// gasPrice
|
|
177
|
+
ZERO_ADDRESS,
|
|
178
|
+
// gasToken
|
|
179
|
+
ZERO_ADDRESS,
|
|
180
|
+
// refundReceiver
|
|
181
|
+
signature
|
|
182
|
+
]
|
|
183
|
+
});
|
|
184
|
+
return hash;
|
|
185
|
+
});
|
|
186
|
+
return txHash;
|
|
187
|
+
}
|
|
188
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
189
|
+
0 && (module.exports = {
|
|
190
|
+
execSafeTransaction
|
|
191
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Address, Hex, Hash } from 'viem';
|
|
2
|
+
import { SafeTransaction } from '@polymarket/builder-relayer-client';
|
|
3
|
+
|
|
4
|
+
interface ExecSafeTransactionParams {
|
|
5
|
+
|
|
6
|
+
safeAddress: Address;
|
|
7
|
+
|
|
8
|
+
ownerPrivateKey: Hex;
|
|
9
|
+
|
|
10
|
+
gasPayerPrivateKey: Hex;
|
|
11
|
+
|
|
12
|
+
transactions: SafeTransaction[];
|
|
13
|
+
|
|
14
|
+
chainId?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare function execSafeTransaction(params: ExecSafeTransactionParams): Promise<Hash>;
|
|
18
|
+
|
|
19
|
+
export { type ExecSafeTransactionParams, execSafeTransaction };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Address, Hex, Hash } from 'viem';
|
|
2
|
+
import { SafeTransaction } from '@polymarket/builder-relayer-client';
|
|
3
|
+
|
|
4
|
+
interface ExecSafeTransactionParams {
|
|
5
|
+
|
|
6
|
+
safeAddress: Address;
|
|
7
|
+
|
|
8
|
+
ownerPrivateKey: Hex;
|
|
9
|
+
|
|
10
|
+
gasPayerPrivateKey: Hex;
|
|
11
|
+
|
|
12
|
+
transactions: SafeTransaction[];
|
|
13
|
+
|
|
14
|
+
chainId?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare function execSafeTransaction(params: ExecSafeTransactionParams): Promise<Hash>;
|
|
18
|
+
|
|
19
|
+
export { type ExecSafeTransactionParams, execSafeTransaction };
|
package/safe-executor.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getPublicClient
|
|
3
|
+
} from "./chunk-LHBGF66E.js";
|
|
4
|
+
import "./chunk-JSBRDJBE.js";
|
|
5
|
+
|
|
6
|
+
// src/utils/safe-executor.ts
|
|
7
|
+
import {
|
|
8
|
+
createWalletClient,
|
|
9
|
+
encodePacked,
|
|
10
|
+
hashTypedData,
|
|
11
|
+
http,
|
|
12
|
+
parseAbi,
|
|
13
|
+
toBytes
|
|
14
|
+
} from "viem";
|
|
15
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
16
|
+
import { polygon } from "viem/chains";
|
|
17
|
+
import { createSafeMultisendTransaction } from "@polymarket/builder-relayer-client";
|
|
18
|
+
import pLimit from "p-limit";
|
|
19
|
+
var SAFE_MULTISEND = "0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761";
|
|
20
|
+
var SAFE_ABI = parseAbi([
|
|
21
|
+
"function execTransaction(address to, uint256 value, bytes data, uint8 operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, bytes signatures) payable returns (bool success)",
|
|
22
|
+
"function nonce() view returns (uint256)"
|
|
23
|
+
]);
|
|
24
|
+
var SAFE_TX_TYPES = {
|
|
25
|
+
SafeTx: [
|
|
26
|
+
{ name: "to", type: "address" },
|
|
27
|
+
{ name: "value", type: "uint256" },
|
|
28
|
+
{ name: "data", type: "bytes" },
|
|
29
|
+
{ name: "operation", type: "uint8" },
|
|
30
|
+
{ name: "safeTxGas", type: "uint256" },
|
|
31
|
+
{ name: "baseGas", type: "uint256" },
|
|
32
|
+
{ name: "gasPrice", type: "uint256" },
|
|
33
|
+
{ name: "gasToken", type: "address" },
|
|
34
|
+
{ name: "refundReceiver", type: "address" },
|
|
35
|
+
{ name: "nonce", type: "uint256" }
|
|
36
|
+
]
|
|
37
|
+
};
|
|
38
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
39
|
+
var gasPayerLimit = pLimit(1);
|
|
40
|
+
function aggregateTransaction(txns) {
|
|
41
|
+
if (txns.length === 0) {
|
|
42
|
+
throw new Error("No transactions to aggregate");
|
|
43
|
+
}
|
|
44
|
+
if (txns.length === 1) {
|
|
45
|
+
return txns[0];
|
|
46
|
+
}
|
|
47
|
+
return createSafeMultisendTransaction(txns, SAFE_MULTISEND);
|
|
48
|
+
}
|
|
49
|
+
function createSafeTxHash(chainId, safeAddress, tx, nonce) {
|
|
50
|
+
return hashTypedData({
|
|
51
|
+
domain: {
|
|
52
|
+
chainId,
|
|
53
|
+
verifyingContract: safeAddress
|
|
54
|
+
},
|
|
55
|
+
types: SAFE_TX_TYPES,
|
|
56
|
+
primaryType: "SafeTx",
|
|
57
|
+
message: {
|
|
58
|
+
to: tx.to,
|
|
59
|
+
value: BigInt(tx.value),
|
|
60
|
+
data: tx.data,
|
|
61
|
+
operation: tx.operation,
|
|
62
|
+
safeTxGas: 0n,
|
|
63
|
+
baseGas: 0n,
|
|
64
|
+
gasPrice: 0n,
|
|
65
|
+
gasToken: ZERO_ADDRESS,
|
|
66
|
+
refundReceiver: ZERO_ADDRESS,
|
|
67
|
+
nonce
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async function signSafeTransaction(ownerPrivateKey, structHash) {
|
|
72
|
+
const account = privateKeyToAccount(ownerPrivateKey);
|
|
73
|
+
const signature = await account.signMessage({
|
|
74
|
+
message: { raw: toBytes(structHash) }
|
|
75
|
+
});
|
|
76
|
+
const r = signature.slice(0, 66);
|
|
77
|
+
const s = "0x" + signature.slice(66, 130);
|
|
78
|
+
const v = parseInt(signature.slice(130, 132), 16);
|
|
79
|
+
const adjustedV = v + 4;
|
|
80
|
+
return encodePacked(
|
|
81
|
+
["bytes32", "bytes32", "uint8"],
|
|
82
|
+
[r, s, adjustedV]
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
async function execSafeTransaction(params) {
|
|
86
|
+
const {
|
|
87
|
+
safeAddress,
|
|
88
|
+
ownerPrivateKey,
|
|
89
|
+
gasPayerPrivateKey,
|
|
90
|
+
transactions,
|
|
91
|
+
chainId = polygon.id
|
|
92
|
+
} = params;
|
|
93
|
+
const aggregatedTx = aggregateTransaction(transactions);
|
|
94
|
+
const publicClient = getPublicClient();
|
|
95
|
+
const safeNonce = await publicClient.readContract({
|
|
96
|
+
address: safeAddress,
|
|
97
|
+
abi: SAFE_ABI,
|
|
98
|
+
functionName: "nonce"
|
|
99
|
+
});
|
|
100
|
+
const safeTxHash = createSafeTxHash(chainId, safeAddress, aggregatedTx, safeNonce);
|
|
101
|
+
const signature = await signSafeTransaction(ownerPrivateKey, safeTxHash);
|
|
102
|
+
const txHash = await gasPayerLimit(async () => {
|
|
103
|
+
const rpcUrl = process.env.POLYGON_RPC || polygon.rpcUrls.default.http[0];
|
|
104
|
+
const gasPayerAccount = privateKeyToAccount(gasPayerPrivateKey);
|
|
105
|
+
const gasPayerWallet = createWalletClient({
|
|
106
|
+
account: gasPayerAccount,
|
|
107
|
+
chain: polygon,
|
|
108
|
+
transport: http(rpcUrl)
|
|
109
|
+
});
|
|
110
|
+
const hash = await gasPayerWallet.writeContract({
|
|
111
|
+
address: safeAddress,
|
|
112
|
+
abi: SAFE_ABI,
|
|
113
|
+
functionName: "execTransaction",
|
|
114
|
+
args: [
|
|
115
|
+
aggregatedTx.to,
|
|
116
|
+
BigInt(aggregatedTx.value),
|
|
117
|
+
aggregatedTx.data,
|
|
118
|
+
aggregatedTx.operation,
|
|
119
|
+
0n,
|
|
120
|
+
// safeTxGas
|
|
121
|
+
0n,
|
|
122
|
+
// baseGas
|
|
123
|
+
0n,
|
|
124
|
+
// gasPrice
|
|
125
|
+
ZERO_ADDRESS,
|
|
126
|
+
// gasToken
|
|
127
|
+
ZERO_ADDRESS,
|
|
128
|
+
// refundReceiver
|
|
129
|
+
signature
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
return hash;
|
|
133
|
+
});
|
|
134
|
+
return txHash;
|
|
135
|
+
}
|
|
136
|
+
export {
|
|
137
|
+
execSafeTransaction
|
|
138
|
+
};
|
package/wallet.js
CHANGED
|
@@ -1,67 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
USDC_ABI,
|
|
3
|
+
USDC_ADDRESS,
|
|
4
|
+
USDC_DECIMALS,
|
|
5
|
+
formatBalance,
|
|
6
|
+
getPublicClient,
|
|
7
|
+
getUSDCBalance,
|
|
8
|
+
getUSDCBalancesBatch,
|
|
9
|
+
isAddressEqual
|
|
10
|
+
} from "./chunk-LHBGF66E.js";
|
|
1
11
|
import "./chunk-JSBRDJBE.js";
|
|
2
|
-
|
|
3
|
-
// src/utils/wallet.ts
|
|
4
|
-
import { createPublicClient, http, formatUnits, parseAbi } from "viem";
|
|
5
|
-
import { polygon } from "viem/chains";
|
|
6
|
-
var USDC_ADDRESS = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
|
|
7
|
-
var USDC_DECIMALS = 6;
|
|
8
|
-
var USDC_ABI = parseAbi(["function balanceOf(address) view returns (uint256)"]);
|
|
9
|
-
var getPublicClient = /* @__PURE__ */ (() => {
|
|
10
|
-
let client = null;
|
|
11
|
-
let batchClient = null;
|
|
12
|
-
return (options) => {
|
|
13
|
-
const rpcUrl = process.env.POLYGON_RPC || polygon.rpcUrls.default.http[0];
|
|
14
|
-
if (options?.batch) {
|
|
15
|
-
if (!batchClient) {
|
|
16
|
-
batchClient = createPublicClient({
|
|
17
|
-
chain: polygon,
|
|
18
|
-
transport: http(rpcUrl, { batch: { batchSize: 100, wait: 0 } })
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
return batchClient;
|
|
22
|
-
}
|
|
23
|
-
if (!client) {
|
|
24
|
-
client = createPublicClient({
|
|
25
|
-
chain: polygon,
|
|
26
|
-
transport: http(rpcUrl)
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
return client;
|
|
30
|
-
};
|
|
31
|
-
})();
|
|
32
|
-
function formatBalance(rawBalance, decimals = USDC_DECIMALS) {
|
|
33
|
-
const num = parseFloat(formatUnits(BigInt(rawBalance), decimals));
|
|
34
|
-
return (Math.floor(num * 100) / 100).toFixed(2);
|
|
35
|
-
}
|
|
36
|
-
async function getUSDCBalance(address) {
|
|
37
|
-
const publicClient = getPublicClient();
|
|
38
|
-
return await publicClient.readContract({
|
|
39
|
-
address: USDC_ADDRESS,
|
|
40
|
-
abi: USDC_ABI,
|
|
41
|
-
functionName: "balanceOf",
|
|
42
|
-
args: [address]
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
async function getUSDCBalancesBatch(addresses) {
|
|
46
|
-
if (addresses.length === 0) {
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
const publicClient = getPublicClient();
|
|
50
|
-
return await publicClient.multicall({
|
|
51
|
-
contracts: addresses.map((address) => ({
|
|
52
|
-
address: USDC_ADDRESS,
|
|
53
|
-
abi: USDC_ABI,
|
|
54
|
-
functionName: "balanceOf",
|
|
55
|
-
args: [address]
|
|
56
|
-
})),
|
|
57
|
-
allowFailure: true
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
function isAddressEqual(a, b) {
|
|
61
|
-
if (!a) return false;
|
|
62
|
-
if (!b) return false;
|
|
63
|
-
return a.toLowerCase() === b.toLowerCase();
|
|
64
|
-
}
|
|
65
12
|
export {
|
|
66
13
|
USDC_ABI,
|
|
67
14
|
USDC_ADDRESS,
|