@atumlabs/payment-gateway-client 3.0.0 → 3.0.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/CHANGELOG.md +19 -0
- package/LICENSE +202 -83
- package/NOTICE +2 -0
- package/README.md +30 -13
- package/THIRD-PARTY-NOTICES.txt +4 -5
- package/dist/cli/approve-permit2.js +262 -37
- package/dist/cli/payment-status.js +78 -17
- package/dist/cli/quote-status.js +76 -15
- package/dist/cli/request-quote.js +76 -15
- package/dist/cli/send-payment.js +257 -42
- package/dist/index.d.ts +108 -1982
- package/dist/index.js +440 -149
- package/dist/sender-auth-verify-C-Q-zvAO.d.ts +2013 -0
- package/dist/signing.d.ts +223 -0
- package/dist/signing.js +7219 -0
- package/docs/idempotency.md +2 -1
- package/docs/signing.md +218 -0
- package/package.json +25 -6
- package/signing.d.ts +2 -0
- package/signing.js +11 -0
package/dist/cli/send-payment.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*
|
|
3
|
-
* Copyright
|
|
4
|
-
*
|
|
5
|
-
* Access and use are governed by the LICENSE file distributed with this package.
|
|
6
|
-
* Do not remove, alter, or obscure this notice.
|
|
3
|
+
* Copyright 2026 Atum Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
5
|
*/
|
|
8
6
|
"use strict";
|
|
9
7
|
var __create = Object.create;
|
|
@@ -12,11 +10,20 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
12
10
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
11
|
var __getProtoOf = Object.getPrototypeOf;
|
|
14
12
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
15
|
-
var __esm = (fn, res) => function __init() {
|
|
16
|
-
|
|
13
|
+
var __esm = (fn, res, err) => function __init() {
|
|
14
|
+
if (err) throw err[0];
|
|
15
|
+
try {
|
|
16
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
17
|
+
} catch (e) {
|
|
18
|
+
throw err = [e], e;
|
|
19
|
+
}
|
|
17
20
|
};
|
|
18
21
|
var __commonJS = (cb, mod) => function __require() {
|
|
19
|
-
|
|
22
|
+
try {
|
|
23
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
24
|
+
} catch (e) {
|
|
25
|
+
throw mod = 0, e;
|
|
26
|
+
}
|
|
20
27
|
};
|
|
21
28
|
var __export = (target, all) => {
|
|
22
29
|
for (var name in all)
|
|
@@ -2105,7 +2112,7 @@ var require_errors2 = __commonJS({
|
|
|
2105
2112
|
"../contracts/svm/escrow-encoding/typescript/dist/errors.js"(exports2) {
|
|
2106
2113
|
"use strict";
|
|
2107
2114
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2108
|
-
exports2.DestinationTooLongError = exports2.EmptyDestinationError = exports2.ZeroSourceAmountError = exports2.ZeroQuoteHashError = exports2.InvalidHexError = exports2.InvalidBase58Error = exports2.InvalidPubkeyLengthError = exports2.ZeroDepositHashError = exports2.ZeroRecipientError = exports2.ZeroReleaserError = exports2.ZeroReserverError = exports2.ZeroAmountError = exports2.ZeroMintError = exports2.ZeroDepositorError = exports2.ZeroDepositRequestHashError = exports2.EncodingError = void 0;
|
|
2115
|
+
exports2.InvalidEscrowDomainError = exports2.DestinationTooLongError = exports2.EmptyDestinationError = exports2.ZeroSourceAmountError = exports2.ZeroQuoteHashError = exports2.InvalidHexError = exports2.InvalidBase58Error = exports2.InvalidPubkeyLengthError = exports2.ZeroDepositHashError = exports2.ZeroRecipientError = exports2.ZeroReleaserError = exports2.ZeroReserverError = exports2.ZeroAmountError = exports2.ZeroMintError = exports2.ZeroDepositorError = exports2.ZeroDepositRequestHashError = exports2.EncodingError = void 0;
|
|
2109
2116
|
var EncodingError = class extends Error {
|
|
2110
2117
|
constructor(message) {
|
|
2111
2118
|
super(message);
|
|
@@ -2218,6 +2225,13 @@ var require_errors2 = __commonJS({
|
|
|
2218
2225
|
}
|
|
2219
2226
|
};
|
|
2220
2227
|
exports2.DestinationTooLongError = DestinationTooLongError;
|
|
2228
|
+
var InvalidEscrowDomainError = class extends EncodingError {
|
|
2229
|
+
constructor(message) {
|
|
2230
|
+
super(message);
|
|
2231
|
+
this.name = "InvalidEscrowDomainError";
|
|
2232
|
+
}
|
|
2233
|
+
};
|
|
2234
|
+
exports2.InvalidEscrowDomainError = InvalidEscrowDomainError;
|
|
2221
2235
|
}
|
|
2222
2236
|
});
|
|
2223
2237
|
|
|
@@ -2926,12 +2940,17 @@ var require_escrow_domain = __commonJS({
|
|
|
2926
2940
|
"use strict";
|
|
2927
2941
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2928
2942
|
exports2.escrowDomainPrefixBytes = escrowDomainPrefixBytes;
|
|
2943
|
+
exports2.newEscrowDomain = newEscrowDomain;
|
|
2944
|
+
var errors_1 = require_errors2();
|
|
2929
2945
|
function escrowDomainPrefixBytes(domain) {
|
|
2930
2946
|
if (domain.clusterId.length !== 32) {
|
|
2931
|
-
throw new
|
|
2947
|
+
throw new errors_1.InvalidEscrowDomainError(`EscrowDomain.clusterId must be 32 bytes, got ${domain.clusterId.length}`);
|
|
2948
|
+
}
|
|
2949
|
+
if (domain.programId.isZero()) {
|
|
2950
|
+
throw new errors_1.InvalidEscrowDomainError("EscrowDomain.programId cannot be zero");
|
|
2932
2951
|
}
|
|
2933
2952
|
if (!Number.isInteger(domain.signatureDomainVersion) || domain.signatureDomainVersion < 1 || domain.signatureDomainVersion > 255) {
|
|
2934
|
-
throw new
|
|
2953
|
+
throw new errors_1.InvalidEscrowDomainError(`EscrowDomain.signatureDomainVersion must be an integer in 1..=255, got ${domain.signatureDomainVersion}`);
|
|
2935
2954
|
}
|
|
2936
2955
|
return [
|
|
2937
2956
|
new TextEncoder().encode("escrow"),
|
|
@@ -2940,6 +2959,25 @@ var require_escrow_domain = __commonJS({
|
|
|
2940
2959
|
domain.programId.toBytes()
|
|
2941
2960
|
];
|
|
2942
2961
|
}
|
|
2962
|
+
function newEscrowDomain(clusterName, signatureDomainVersion, programId) {
|
|
2963
|
+
const trimmed = clusterName.trim();
|
|
2964
|
+
if (trimmed.length === 0) {
|
|
2965
|
+
throw new errors_1.InvalidEscrowDomainError("EscrowDomain cluster name cannot be empty");
|
|
2966
|
+
}
|
|
2967
|
+
const encoded = new TextEncoder().encode(trimmed);
|
|
2968
|
+
if (encoded.length > 32) {
|
|
2969
|
+
throw new errors_1.InvalidEscrowDomainError(`EscrowDomain cluster name "${trimmed}" exceeds 32 bytes`);
|
|
2970
|
+
}
|
|
2971
|
+
if (!Number.isInteger(signatureDomainVersion) || signatureDomainVersion < 1 || signatureDomainVersion > 255) {
|
|
2972
|
+
throw new errors_1.InvalidEscrowDomainError(`EscrowDomain.signatureDomainVersion must be an integer in 1..=255, got ${signatureDomainVersion}`);
|
|
2973
|
+
}
|
|
2974
|
+
if (programId.isZero()) {
|
|
2975
|
+
throw new errors_1.InvalidEscrowDomainError("EscrowDomain.programId cannot be zero");
|
|
2976
|
+
}
|
|
2977
|
+
const clusterId = new Uint8Array(32);
|
|
2978
|
+
clusterId.set(encoded);
|
|
2979
|
+
return { clusterId, signatureDomainVersion, programId };
|
|
2980
|
+
}
|
|
2943
2981
|
}
|
|
2944
2982
|
});
|
|
2945
2983
|
|
|
@@ -3138,7 +3176,7 @@ var require_dist4 = __commonJS({
|
|
|
3138
3176
|
"../contracts/svm/escrow-encoding/typescript/dist/index.js"(exports2) {
|
|
3139
3177
|
"use strict";
|
|
3140
3178
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3141
|
-
exports2.DEPOSIT_COMPUTE_UNIT_LIMIT = exports2.REPLAY_BUCKETS = exports2.MAX_VALID_SLOT = exports2.REPLAY_SLOTS = exports2.generateNonce = exports2.computeRefundHashV2 = exports2.computeRefundHash = exports2.computeReleaseHashV2 = exports2.computeReleaseHash = exports2.computeReserveHashV3 = exports2.computeReserveHashV2 = exports2.computeReserveHash = exports2.MAX_DESTINATION_LEN = exports2.computeDestinationHash = exports2.computeDepositHashV2 = exports2.computeDepositHash = exports2.InvalidHexError = exports2.InvalidBase58Error = exports2.InvalidPubkeyLengthError = exports2.ZeroDepositHashError = exports2.ZeroRecipientError = exports2.ZeroReleaserError = exports2.ZeroReserverError = exports2.ZeroAmountError = exports2.ZeroMintError = exports2.ZeroDepositorError = exports2.DestinationTooLongError = exports2.EmptyDestinationError = exports2.ZeroQuoteHashError = exports2.ZeroDepositRequestHashError = exports2.EncodingError = exports2.escrowDomainPrefixBytes = exports2.isZeroBytes32 = exports2.stringToBytes = exports2.keccak256 = exports2.encodeU64LE = exports2.Pubkey = void 0;
|
|
3179
|
+
exports2.DEPOSIT_COMPUTE_UNIT_LIMIT = exports2.REPLAY_BUCKETS = exports2.MAX_VALID_SLOT = exports2.REPLAY_SLOTS = exports2.generateNonce = exports2.computeRefundHashV2 = exports2.computeRefundHash = exports2.computeReleaseHashV2 = exports2.computeReleaseHash = exports2.computeReserveHashV3 = exports2.computeReserveHashV2 = exports2.computeReserveHash = exports2.MAX_DESTINATION_LEN = exports2.computeDestinationHash = exports2.computeDepositHashV2 = exports2.computeDepositHash = exports2.InvalidHexError = exports2.InvalidBase58Error = exports2.InvalidPubkeyLengthError = exports2.InvalidEscrowDomainError = exports2.ZeroSourceAmountError = exports2.ZeroDepositHashError = exports2.ZeroRecipientError = exports2.ZeroReleaserError = exports2.ZeroReserverError = exports2.ZeroAmountError = exports2.ZeroMintError = exports2.ZeroDepositorError = exports2.DestinationTooLongError = exports2.EmptyDestinationError = exports2.ZeroQuoteHashError = exports2.ZeroDepositRequestHashError = exports2.EncodingError = exports2.newEscrowDomain = exports2.escrowDomainPrefixBytes = exports2.isZeroBytes32 = exports2.stringToBytes = exports2.keccak256 = exports2.encodeU64LE = exports2.Pubkey = void 0;
|
|
3142
3180
|
var pubkey_1 = require_pubkey();
|
|
3143
3181
|
Object.defineProperty(exports2, "Pubkey", { enumerable: true, get: function() {
|
|
3144
3182
|
return pubkey_1.Pubkey;
|
|
@@ -3160,6 +3198,9 @@ var require_dist4 = __commonJS({
|
|
|
3160
3198
|
Object.defineProperty(exports2, "escrowDomainPrefixBytes", { enumerable: true, get: function() {
|
|
3161
3199
|
return escrow_domain_1.escrowDomainPrefixBytes;
|
|
3162
3200
|
} });
|
|
3201
|
+
Object.defineProperty(exports2, "newEscrowDomain", { enumerable: true, get: function() {
|
|
3202
|
+
return escrow_domain_1.newEscrowDomain;
|
|
3203
|
+
} });
|
|
3163
3204
|
var errors_1 = require_errors2();
|
|
3164
3205
|
Object.defineProperty(exports2, "EncodingError", { enumerable: true, get: function() {
|
|
3165
3206
|
return errors_1.EncodingError;
|
|
@@ -3197,6 +3238,12 @@ var require_dist4 = __commonJS({
|
|
|
3197
3238
|
Object.defineProperty(exports2, "ZeroDepositHashError", { enumerable: true, get: function() {
|
|
3198
3239
|
return errors_1.ZeroDepositHashError;
|
|
3199
3240
|
} });
|
|
3241
|
+
Object.defineProperty(exports2, "ZeroSourceAmountError", { enumerable: true, get: function() {
|
|
3242
|
+
return errors_1.ZeroSourceAmountError;
|
|
3243
|
+
} });
|
|
3244
|
+
Object.defineProperty(exports2, "InvalidEscrowDomainError", { enumerable: true, get: function() {
|
|
3245
|
+
return errors_1.InvalidEscrowDomainError;
|
|
3246
|
+
} });
|
|
3200
3247
|
Object.defineProperty(exports2, "InvalidPubkeyLengthError", { enumerable: true, get: function() {
|
|
3201
3248
|
return errors_1.InvalidPubkeyLengthError;
|
|
3202
3249
|
} });
|
|
@@ -5477,6 +5524,7 @@ var require_policies = __commonJS({
|
|
|
5477
5524
|
exports2.buildDenyApproveToNonAllowedSpenderTron = buildDenyApproveToNonAllowedSpenderTron;
|
|
5478
5525
|
exports2.listPolicies = listPolicies;
|
|
5479
5526
|
exports2.deletePolicy = deletePolicy;
|
|
5527
|
+
exports2.planPolicyChanges = planPolicyChanges;
|
|
5480
5528
|
exports2.applyPolicies = applyPolicies;
|
|
5481
5529
|
function derivePoliciesFromAllowlist(policy, chain, context) {
|
|
5482
5530
|
const policies = [];
|
|
@@ -5664,26 +5712,54 @@ var require_policies = __commonJS({
|
|
|
5664
5712
|
policyId
|
|
5665
5713
|
});
|
|
5666
5714
|
}
|
|
5715
|
+
function planPolicyChanges(existing, policies) {
|
|
5716
|
+
const existingByName = new Map(existing.map((p) => [p.policyName, p]));
|
|
5717
|
+
const drifted = [];
|
|
5718
|
+
for (const p of policies) {
|
|
5719
|
+
const prev = existingByName.get(p.policyName);
|
|
5720
|
+
if (!prev)
|
|
5721
|
+
continue;
|
|
5722
|
+
const matches = prev.effect === p.effect && prev.condition === p.condition && (prev.consensus ?? "") === (p.consensus ?? "");
|
|
5723
|
+
if (!matches)
|
|
5724
|
+
drifted.push({ name: p.policyName, diff: formatPolicyDiff(prev, p) });
|
|
5725
|
+
}
|
|
5726
|
+
const emittedNames = new Set(policies.map((p) => p.policyName));
|
|
5727
|
+
const stale = existing.filter((p) => !emittedNames.has(p.policyName));
|
|
5728
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5729
|
+
const duplicateNames = [
|
|
5730
|
+
...new Set(existing.filter((p) => !(seen.size === seen.add(p.policyName).size)).map((p) => p.policyName))
|
|
5731
|
+
];
|
|
5732
|
+
return { drifted, stale, duplicateNames };
|
|
5733
|
+
}
|
|
5667
5734
|
async function applyPolicies(client, policies, options) {
|
|
5668
5735
|
const existing = await listPolicies(client, options.organizationId);
|
|
5669
5736
|
const existingByName = new Map(existing.map((p) => [p.policyName, p]));
|
|
5670
5737
|
const created = [];
|
|
5671
5738
|
const skippedUnchanged = [];
|
|
5672
|
-
const drifted = [];
|
|
5673
5739
|
const replaced = [];
|
|
5740
|
+
const pruned = [];
|
|
5741
|
+
const { drifted, stale } = planPolicyChanges(existing, policies);
|
|
5742
|
+
if (stale.length > 0 && !options.prune) {
|
|
5743
|
+
const detail = stale.map((p) => ` - ${p.policyName} [${p.effect}] (${p.policyId})`).join("\n");
|
|
5744
|
+
throw new Error(`${stale.length} live polic${stale.length === 1 ? "y is" : "ies are"} absent from the emitted set:
|
|
5745
|
+
${detail}
|
|
5746
|
+
|
|
5747
|
+
A retired ALLOW left live keeps granting what it always granted, so a narrower replacement does not take effect until it is gone. Re-run with --prune to delete them, or reconcile the config if they are policies this set should still emit.`);
|
|
5748
|
+
}
|
|
5749
|
+
if (drifted.length > 0 && !options.update) {
|
|
5750
|
+
const detail = drifted.map((d) => ` - ${d.name}:
|
|
5751
|
+
${indent(d.diff, " ")}`).join("\n");
|
|
5752
|
+
throw new Error(`Content drift detected on ${drifted.length} polic${drifted.length === 1 ? "y" : "ies"}. Re-run with --update to delete-and-recreate, or fix the existing policy in the Turnkey console.
|
|
5753
|
+
${detail}`);
|
|
5754
|
+
}
|
|
5755
|
+
const driftedNames = new Set(drifted.map((d) => d.name));
|
|
5674
5756
|
for (const p of policies) {
|
|
5675
5757
|
const prev = existingByName.get(p.policyName);
|
|
5676
5758
|
if (prev) {
|
|
5677
|
-
|
|
5678
|
-
if (matches) {
|
|
5759
|
+
if (!driftedNames.has(p.policyName)) {
|
|
5679
5760
|
skippedUnchanged.push(p.policyName);
|
|
5680
5761
|
continue;
|
|
5681
5762
|
}
|
|
5682
|
-
const diff = formatPolicyDiff(prev, p);
|
|
5683
|
-
if (!options.update) {
|
|
5684
|
-
drifted.push({ name: p.policyName, diff });
|
|
5685
|
-
continue;
|
|
5686
|
-
}
|
|
5687
5763
|
await deletePolicy(client, prev.policyId);
|
|
5688
5764
|
const newId = await createPolicyRaw(client, p);
|
|
5689
5765
|
replaced.push({ name: p.policyName, oldPolicyId: prev.policyId, newPolicyId: newId });
|
|
@@ -5693,13 +5769,11 @@ var require_policies = __commonJS({
|
|
|
5693
5769
|
const id = await createPolicyRaw(client, p);
|
|
5694
5770
|
created.push(id);
|
|
5695
5771
|
}
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
throw new Error(`Content drift detected on ${drifted.length} polic${drifted.length === 1 ? "y" : "ies"}. Re-run with --update to delete-and-recreate, or fix the existing policy in the Turnkey console.
|
|
5700
|
-
${detail}`);
|
|
5772
|
+
for (const p of stale) {
|
|
5773
|
+
await deletePolicy(client, p.policyId);
|
|
5774
|
+
pruned.push({ name: p.policyName, policyId: p.policyId });
|
|
5701
5775
|
}
|
|
5702
|
-
return { created, skippedUnchanged, replaced };
|
|
5776
|
+
return { created, skippedUnchanged, replaced, pruned };
|
|
5703
5777
|
}
|
|
5704
5778
|
async function createPolicyRaw(client, p) {
|
|
5705
5779
|
const result = await client.executeActivity("ACTIVITY_TYPE_CREATE_POLICY_V3", "create_policy", {
|
|
@@ -5745,6 +5819,8 @@ var require_sender_policies = __commonJS({
|
|
|
5745
5819
|
"use strict";
|
|
5746
5820
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
5747
5821
|
exports2.TRON_PERMIT2 = exports2.EVM_PERMIT2 = void 0;
|
|
5822
|
+
exports2.tronTip712ChainId = tronTip712ChainId;
|
|
5823
|
+
exports2.toTronEvmHex = toTronEvmHex;
|
|
5748
5824
|
exports2.buildSenderPolicies = buildSenderPolicies;
|
|
5749
5825
|
exports2.tronBase58ToEvmHex = tronBase58ToEvmHex;
|
|
5750
5826
|
var node_crypto_1 = require("crypto");
|
|
@@ -5754,6 +5830,57 @@ var require_sender_policies = __commonJS({
|
|
|
5754
5830
|
shasta: "TPUqJPASUn1zLvbLBgRZ5pBYrx7WSe5ahp"
|
|
5755
5831
|
};
|
|
5756
5832
|
var APPROVE_SELECTOR = "0x095ea7b3";
|
|
5833
|
+
var PERMIT2_PRIMARY_TYPE = "PermitWitnessTransferFrom";
|
|
5834
|
+
var PERMIT2_DOMAIN_NAME = "Permit2";
|
|
5835
|
+
var TRON_TIP712_CHAIN_IDS = {
|
|
5836
|
+
mainnet: 728126428,
|
|
5837
|
+
shasta: 2494104990
|
|
5838
|
+
};
|
|
5839
|
+
function tronTip712ChainId(caip2) {
|
|
5840
|
+
const cluster = caip2.split(":")[1] ?? "";
|
|
5841
|
+
const id = Object.hasOwn(TRON_TIP712_CHAIN_IDS, cluster) ? TRON_TIP712_CHAIN_IDS[cluster] : void 0;
|
|
5842
|
+
if (id === void 0) {
|
|
5843
|
+
throw new Error(`Sender extras: unsupported Tron network "${caip2}". Known clusters: ${Object.keys(TRON_TIP712_CHAIN_IDS).join(", ")}.`);
|
|
5844
|
+
}
|
|
5845
|
+
return (id & 4294967295) >>> 0;
|
|
5846
|
+
}
|
|
5847
|
+
function evmNumericChainId(caip2) {
|
|
5848
|
+
const id = Number.parseInt(caip2.split(":")[1] ?? "", 10);
|
|
5849
|
+
if (!Number.isSafeInteger(id) || id <= 0) {
|
|
5850
|
+
throw new Error(`Sender extras: chain id "${caip2}" is not a usable eip155 CAIP-2 identifier.`);
|
|
5851
|
+
}
|
|
5852
|
+
return id;
|
|
5853
|
+
}
|
|
5854
|
+
var lower = (a) => a.toLowerCase();
|
|
5855
|
+
function assertEvmAddress(address, chainKey) {
|
|
5856
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
|
|
5857
|
+
throw new Error(`Sender extras: ${JSON.stringify(address)} on chain "${chainKey}" is not a 20-byte 0x hex address. A malformed literal matches nothing, which denies every payment on this chain.`);
|
|
5858
|
+
}
|
|
5859
|
+
return address;
|
|
5860
|
+
}
|
|
5861
|
+
function toTronEvmHex(address) {
|
|
5862
|
+
if (address.startsWith("T"))
|
|
5863
|
+
return `0x${tronBase58ToEvmHex(address)}`;
|
|
5864
|
+
if (/^0x[0-9a-fA-F]{40}$/.test(address))
|
|
5865
|
+
return lower(address);
|
|
5866
|
+
throw new Error(`Sender extras: Tron address ${JSON.stringify(address)} is neither a base58 (T\u2026) address nor a 20-byte 0x hex address, so its TIP-712 message form cannot be derived.`);
|
|
5867
|
+
}
|
|
5868
|
+
var quotedLower = (xs) => xs.map((x) => `'${lower(x)}'`).join(", ");
|
|
5869
|
+
function quotedExact(xs, chainKey) {
|
|
5870
|
+
for (const x of xs) {
|
|
5871
|
+
if (x.includes("'") || x.includes("\\")) {
|
|
5872
|
+
throw new Error(`Sender extras: destination ${JSON.stringify(x)} on chain "${chainKey}" contains a quote or backslash, which cannot appear in a policy condition literal.`);
|
|
5873
|
+
}
|
|
5874
|
+
if (x.length === 0 || !x.includes("@")) {
|
|
5875
|
+
throw new Error(`Sender extras: destination ${JSON.stringify(x)} on chain "${chainKey}" is not a composed destination (<caip19-asset>@<account>). Build it with buildEscrowDestination.`);
|
|
5876
|
+
}
|
|
5877
|
+
if (Buffer.byteLength(x, "utf8") > MAX_DESTINATION_BYTES) {
|
|
5878
|
+
throw new Error(`Sender extras: destination ${JSON.stringify(x)} on chain "${chainKey}" is longer than ${MAX_DESTINATION_BYTES} bytes, which the escrow rejects as DestinationTooLong, so no deposit could ever carry it.`);
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
return xs.map((x) => `'${x}'`).join(", ");
|
|
5882
|
+
}
|
|
5883
|
+
var MAX_DESTINATION_BYTES = 256;
|
|
5757
5884
|
var SOLANA_STANDARD_PROGRAMS = [
|
|
5758
5885
|
"11111111111111111111111111111111",
|
|
5759
5886
|
// System
|
|
@@ -5783,12 +5910,20 @@ var require_sender_policies = __commonJS({
|
|
|
5783
5910
|
}
|
|
5784
5911
|
function buildSenderPolicies(extras, chains) {
|
|
5785
5912
|
const policies = [];
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
}
|
|
5913
|
+
const permit2Evm = lower(extras.permit2 ?? exports2.EVM_PERMIT2);
|
|
5914
|
+
for (const chain of chains) {
|
|
5915
|
+
if (chain.kind !== "evm" && chain.kind !== "tron")
|
|
5916
|
+
continue;
|
|
5917
|
+
policies.push(permit2TypedDataAllow(chain, extras, permit2Evm));
|
|
5918
|
+
}
|
|
5919
|
+
if (chains.some((c) => c.kind === "solana")) {
|
|
5920
|
+
policies.push({
|
|
5921
|
+
policyName: "allowEd25519RawPayload",
|
|
5922
|
+
effect: "EFFECT_ALLOW",
|
|
5923
|
+
condition: `activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2' && activity.params.encoding in ['PAYLOAD_ENCODING_HEXADECIMAL', 'PAYLOAD_ENCODING_TEXT_UTF8'] && activity.params.hash_function == 'HASH_FUNCTION_NOT_APPLICABLE'`,
|
|
5924
|
+
notes: "Solana deposit inner signature (ed25519 over the deposit digest). Legal on secp256k1 only with EIP712, which the API enforces, so this stays an ed25519-only path."
|
|
5925
|
+
});
|
|
5926
|
+
}
|
|
5792
5927
|
for (const chain of chains) {
|
|
5793
5928
|
if (chain.kind === "evm") {
|
|
5794
5929
|
const evmChainId = parseInt(chain.chainId.split(":")[1], 10);
|
|
@@ -5797,7 +5932,7 @@ var require_sender_policies = __commonJS({
|
|
|
5797
5932
|
throw new Error(`Sender extras: chain "${chain.key}" has no allowedTokens. Add at least one ERC-20 contract address the sender's wallet is permitted to approve, or remove this chain.`);
|
|
5798
5933
|
}
|
|
5799
5934
|
const tokenList = tokens.map((t) => `'${t}'`).join(", ");
|
|
5800
|
-
const permit2Lower =
|
|
5935
|
+
const permit2Lower = permit2Evm.replace(/^0x/, "");
|
|
5801
5936
|
policies.push({
|
|
5802
5937
|
policyName: `allowApproveToPermit2 (${chain.chainId})`,
|
|
5803
5938
|
effect: "EFFECT_ALLOW",
|
|
@@ -5887,13 +6022,70 @@ var require_sender_policies = __commonJS({
|
|
|
5887
6022
|
}
|
|
5888
6023
|
}
|
|
5889
6024
|
policies.push({
|
|
5890
|
-
policyName: "
|
|
6025
|
+
policyName: "denyNonTypedDataHashFunctions",
|
|
5891
6026
|
effect: "EFFECT_DENY",
|
|
5892
|
-
condition: `activity.type in ['ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2', 'ACTIVITY_TYPE_SIGN_RAW_PAYLOADS'] && activity.params.hash_function
|
|
5893
|
-
notes: "
|
|
6027
|
+
condition: `activity.type in ['ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2', 'ACTIVITY_TYPE_SIGN_RAW_PAYLOADS'] && activity.params.hash_function in ['HASH_FUNCTION_NO_OP', 'HASH_FUNCTION_SHA256', 'HASH_FUNCTION_KECCAK256']`,
|
|
6028
|
+
notes: "NO_OP signs a caller-chosen digest as-is; KECCAK256 signs a caller-chosen preimage, which reproduces any EIP-712 digest and bypasses every typed-data condition."
|
|
6029
|
+
});
|
|
6030
|
+
policies.push({
|
|
6031
|
+
policyName: "denyAccountDelegationEncoding",
|
|
6032
|
+
effect: "EFFECT_DENY",
|
|
6033
|
+
condition: `activity.type in ['ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2', 'ACTIVITY_TYPE_SIGN_RAW_PAYLOADS'] && activity.params.encoding == 'PAYLOAD_ENCODING_EIP7702_AUTHORIZATION'`,
|
|
6034
|
+
notes: "One signature would delegate the sender EOA to an attacker contract."
|
|
6035
|
+
});
|
|
6036
|
+
policies.push({
|
|
6037
|
+
policyName: "denyBatchRawPayloads",
|
|
6038
|
+
effect: "EFFECT_DENY",
|
|
6039
|
+
condition: `activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOADS'`,
|
|
6040
|
+
notes: "Message conditions bind one payload of a batch, so a second could ride along unbound."
|
|
5894
6041
|
});
|
|
5895
6042
|
return policies;
|
|
5896
6043
|
}
|
|
6044
|
+
function permit2TypedDataAllow(chain, extras, permit2Evm) {
|
|
6045
|
+
const escrowsRaw = extras.escrows?.[chain.key] ?? [];
|
|
6046
|
+
if (escrowsRaw.length === 0) {
|
|
6047
|
+
throw new Error(`Sender extras: chain "${chain.key}" has no escrows. Permit2 leaves the transfer recipient out of the signed struct, so the escrow named as \`spender\` IS the funds destination \u2014 without it a stolen key can authorize a pull to anywhere. Read it from payment-gw /v1/defaults?chain_id=${chain.chainId} (escrow_contract).`);
|
|
6048
|
+
}
|
|
6049
|
+
const isTron = chain.kind === "tron";
|
|
6050
|
+
const tronCluster = isTron ? chain.chainId.split(":")[1] ?? "" : "";
|
|
6051
|
+
if (isTron) {
|
|
6052
|
+
const expected = extras.network === "mainnet" ? "mainnet" : "shasta";
|
|
6053
|
+
if (tronCluster !== expected) {
|
|
6054
|
+
throw new Error(`Sender extras: network "${extras.network}" selects the ${expected} Permit2 deployment, but chain "${chain.key}" is "${chain.chainId}". The domain would carry one cluster's Permit2 with another's chain id, which matches no signature the runtime produces.`);
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
const permit2 = isTron ? `0x${tronBase58ToEvmHex(exports2.TRON_PERMIT2[tronCluster])}` : permit2Evm;
|
|
6058
|
+
const chainIdValue = isTron ? tronTip712ChainId(chain.chainId) : evmNumericChainId(chain.chainId);
|
|
6059
|
+
const addr = (xs) => quotedLower(xs.map((x) => isTron ? toTronEvmHex(x) : assertEvmAddress(x, chain.key)));
|
|
6060
|
+
const tokens = extras.allowedTokens[chain.key] ?? [];
|
|
6061
|
+
if (tokens.length === 0) {
|
|
6062
|
+
throw new Error(`Sender extras: chain "${chain.key}" has no allowedTokens, so the typed-data allow would not bind which token the permit moves.`);
|
|
6063
|
+
}
|
|
6064
|
+
const tokenClause = ` && eth.eip_712.message['permitted']['token'] in [${addr(tokens)}]`;
|
|
6065
|
+
const reservers = extras.reservers?.[chain.key] ?? [];
|
|
6066
|
+
const releasers = extras.releasers?.[chain.key] ?? [];
|
|
6067
|
+
for (const [role, vals, field] of [
|
|
6068
|
+
["reservers", reservers, "quote_selector"],
|
|
6069
|
+
["releasers", releasers, "fulfillment_verifier.account"]
|
|
6070
|
+
]) {
|
|
6071
|
+
if (vals.length === 0) {
|
|
6072
|
+
throw new Error(`Sender extras: chain "${chain.key}" has no ${role}. Either witness role can divert funds at settlement and the payer pins neither client-side, so leaving this unset is a hole this policy set is meant to close. Read it from payment-gw /v1/defaults?chain_id=${chain.chainId} (${field}).`);
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
const reserverClause = ` && eth.eip_712.message['witness']['reserver'] in [${addr(reservers)}]`;
|
|
6076
|
+
const releaserClause = ` && eth.eip_712.message['witness']['releaser'] in [${addr(releasers)}]`;
|
|
6077
|
+
const destinations = extras.allowedDestinations?.[chain.key] ?? [];
|
|
6078
|
+
if (destinations.length === 0) {
|
|
6079
|
+
throw new Error(`Sender extras: chain "${chain.key}" has no allowedDestinations. Without them a stolen key can authorize a deposit whose payout instruction names the attacker, which is the hole this policy set exists to close. Compose each entry with buildEscrowDestination, or remove the chain.`);
|
|
6080
|
+
}
|
|
6081
|
+
const destinationClause = ` && eth.eip_712.message['witness']['destination'] in [${quotedExact(destinations, chain.key)}]`;
|
|
6082
|
+
return {
|
|
6083
|
+
policyName: `allowPermit2DepositTypedData (${chain.chainId})`,
|
|
6084
|
+
effect: "EFFECT_ALLOW",
|
|
6085
|
+
condition: `activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2' && activity.params.encoding == 'PAYLOAD_ENCODING_EIP712' && activity.params.hash_function == 'HASH_FUNCTION_NOT_APPLICABLE' && eth.eip_712.primary_type == '${PERMIT2_PRIMARY_TYPE}' && eth.eip_712.domain.name == '${PERMIT2_DOMAIN_NAME}' && eth.eip_712.domain.verifying_contract == '${permit2}' && eth.eip_712.domain.chain_id == ${chainIdValue} && eth.eip_712.message['spender'] in [${addr(escrowsRaw)}]` + tokenClause + reserverClause + releaserClause + destinationClause,
|
|
6086
|
+
notes: "The sender's only fund-moving signature: a Permit2 deposit authorization into an allowlisted escrow, for an allowlisted token, committing to an allowlisted destination."
|
|
6087
|
+
};
|
|
6088
|
+
}
|
|
5897
6089
|
function tronBase58ToEvmHex(b58) {
|
|
5898
6090
|
const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
5899
6091
|
let bn = 0n;
|
|
@@ -6197,7 +6389,7 @@ var require_admin = __commonJS({
|
|
|
6197
6389
|
"../key-management/providers/turnkey/typescript/dist/admin/index.js"(exports2) {
|
|
6198
6390
|
"use strict";
|
|
6199
6391
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
6200
|
-
exports2.validateExtras = exports2.buildExtrasPolicies = exports2.tronBase58ToEvmHex = exports2.TRON_PERMIT2 = exports2.EVM_PERMIT2 = exports2.buildSenderPolicies = exports2.listPolicies = exports2.derivePoliciesFromAllowlist = exports2.deletePolicy = exports2.buildDenyApproveToNonAllowedSpenderTron = exports2.buildDenyApproveToNonAllowedSpenderEvm = exports2.buildAllowSignRawPayloadPolicy = exports2.applyPolicies = exports2.createWalletWithChainAccounts = exports2.planRevocation = exports2.listUsers = exports2.isExpiredApiKey = exports2.isDurableApiKey = exports2.getUser = exports2.findApiKeyByPublicKey = exports2.deleteApiKeys = exports2.addApiKeys = exports2.UnsafeRevocationError = exports2.MAX_API_KEYS_PER_USER = exports2.listPendingActivities = exports2.getRootQuorum = exports2.getActivity = exports2.approvedVoterIds = exports2.approveActivity = exports2.provisionAgentUser = exports2.deleteSubOrganization = exports2.listSubOrganizations = exports2.createSubOrganization = void 0;
|
|
6392
|
+
exports2.validateExtras = exports2.buildExtrasPolicies = exports2.tronTip712ChainId = exports2.tronBase58ToEvmHex = exports2.toTronEvmHex = exports2.TRON_PERMIT2 = exports2.EVM_PERMIT2 = exports2.buildSenderPolicies = exports2.planPolicyChanges = exports2.listPolicies = exports2.derivePoliciesFromAllowlist = exports2.deletePolicy = exports2.buildDenyApproveToNonAllowedSpenderTron = exports2.buildDenyApproveToNonAllowedSpenderEvm = exports2.buildAllowSignRawPayloadPolicy = exports2.applyPolicies = exports2.createWalletWithChainAccounts = exports2.planRevocation = exports2.listUsers = exports2.isExpiredApiKey = exports2.isDurableApiKey = exports2.getUser = exports2.findApiKeyByPublicKey = exports2.deleteApiKeys = exports2.addApiKeys = exports2.UnsafeRevocationError = exports2.MAX_API_KEYS_PER_USER = exports2.listPendingActivities = exports2.getRootQuorum = exports2.getActivity = exports2.approvedVoterIds = exports2.approveActivity = exports2.provisionAgentUser = exports2.deleteSubOrganization = exports2.listSubOrganizations = exports2.createSubOrganization = void 0;
|
|
6201
6393
|
var sub_organizations_1 = require_sub_organizations();
|
|
6202
6394
|
Object.defineProperty(exports2, "createSubOrganization", { enumerable: true, get: function() {
|
|
6203
6395
|
return sub_organizations_1.createSubOrganization;
|
|
@@ -6287,6 +6479,9 @@ var require_admin = __commonJS({
|
|
|
6287
6479
|
Object.defineProperty(exports2, "listPolicies", { enumerable: true, get: function() {
|
|
6288
6480
|
return policies_1.listPolicies;
|
|
6289
6481
|
} });
|
|
6482
|
+
Object.defineProperty(exports2, "planPolicyChanges", { enumerable: true, get: function() {
|
|
6483
|
+
return policies_1.planPolicyChanges;
|
|
6484
|
+
} });
|
|
6290
6485
|
var sender_policies_1 = require_sender_policies();
|
|
6291
6486
|
Object.defineProperty(exports2, "buildSenderPolicies", { enumerable: true, get: function() {
|
|
6292
6487
|
return sender_policies_1.buildSenderPolicies;
|
|
@@ -6297,9 +6492,15 @@ var require_admin = __commonJS({
|
|
|
6297
6492
|
Object.defineProperty(exports2, "TRON_PERMIT2", { enumerable: true, get: function() {
|
|
6298
6493
|
return sender_policies_1.TRON_PERMIT2;
|
|
6299
6494
|
} });
|
|
6495
|
+
Object.defineProperty(exports2, "toTronEvmHex", { enumerable: true, get: function() {
|
|
6496
|
+
return sender_policies_1.toTronEvmHex;
|
|
6497
|
+
} });
|
|
6300
6498
|
Object.defineProperty(exports2, "tronBase58ToEvmHex", { enumerable: true, get: function() {
|
|
6301
6499
|
return sender_policies_1.tronBase58ToEvmHex;
|
|
6302
6500
|
} });
|
|
6501
|
+
Object.defineProperty(exports2, "tronTip712ChainId", { enumerable: true, get: function() {
|
|
6502
|
+
return sender_policies_1.tronTip712ChainId;
|
|
6503
|
+
} });
|
|
6303
6504
|
var extras_policies_1 = require_extras_policies();
|
|
6304
6505
|
Object.defineProperty(exports2, "buildExtrasPolicies", { enumerable: true, get: function() {
|
|
6305
6506
|
return extras_policies_1.buildExtrasPolicies;
|
|
@@ -7404,6 +7605,9 @@ var PaymentGatewayClient = class {
|
|
|
7404
7605
|
}
|
|
7405
7606
|
};
|
|
7406
7607
|
|
|
7608
|
+
// src/timeout-http-request.ts
|
|
7609
|
+
var import_axios2 = __toESM(require("axios"));
|
|
7610
|
+
|
|
7407
7611
|
// src/errors.ts
|
|
7408
7612
|
var GatewayTimeoutError = class extends Error {
|
|
7409
7613
|
constructor(baseUrl, timeoutMs) {
|
|
@@ -7467,6 +7671,12 @@ function isAxiosTimeout(error) {
|
|
|
7467
7671
|
if (failure.code === "ECONNABORTED" || failure.code === "ETIMEDOUT") return true;
|
|
7468
7672
|
return failure.code === "ERR_BAD_RESPONSE" && /stream has been aborted/i.test(failure.message ?? "");
|
|
7469
7673
|
}
|
|
7674
|
+
function dropPartialResponseOnTimeout(error) {
|
|
7675
|
+
if (isAxiosTimeout(error) && error !== null && typeof error === "object" && "response" in error) {
|
|
7676
|
+
delete error.response;
|
|
7677
|
+
}
|
|
7678
|
+
return error;
|
|
7679
|
+
}
|
|
7470
7680
|
function translateTimeout(error, baseUrl, fallbackTimeoutMs) {
|
|
7471
7681
|
if (!isAxiosTimeout(error)) return error;
|
|
7472
7682
|
const configured = error.config?.timeout;
|
|
@@ -7474,6 +7684,11 @@ function translateTimeout(error, baseUrl, fallbackTimeoutMs) {
|
|
|
7474
7684
|
}
|
|
7475
7685
|
|
|
7476
7686
|
// src/timeout-http-request.ts
|
|
7687
|
+
var timeoutAxios = import_axios2.default.create();
|
|
7688
|
+
timeoutAxios.interceptors.response.use(
|
|
7689
|
+
void 0,
|
|
7690
|
+
(error) => Promise.reject(dropPartialResponseOnTimeout(error))
|
|
7691
|
+
);
|
|
7477
7692
|
function withRequestTimeout(config2, timeouts) {
|
|
7478
7693
|
const request2 = new Interceptors();
|
|
7479
7694
|
for (const fn of config2?.interceptors?.request?._fns ?? []) request2.use(fn);
|
|
@@ -7489,7 +7704,7 @@ var TimeoutHttpRequest = class extends AxiosHttpRequest {
|
|
|
7489
7704
|
}
|
|
7490
7705
|
request(options) {
|
|
7491
7706
|
return new CancelablePromise((resolve2, reject, onCancel) => {
|
|
7492
|
-
const pending =
|
|
7707
|
+
const pending = request(this.config, options, timeoutAxios);
|
|
7493
7708
|
onCancel(() => pending.cancel());
|
|
7494
7709
|
pending.then(resolve2, (error) => {
|
|
7495
7710
|
const request2 = { method: options.method, url: options.url, wait: options.query?.wait };
|
|
@@ -7809,7 +8024,7 @@ var import_chain_address2 = __toESM(require_dist2());
|
|
|
7809
8024
|
|
|
7810
8025
|
// src/payment-tracking.ts
|
|
7811
8026
|
function isTerminalStatus(status) {
|
|
7812
|
-
return status === "completed" || status === "failed"
|
|
8027
|
+
return status === "completed" || status === "failed";
|
|
7813
8028
|
}
|
|
7814
8029
|
var DEFAULT_WAIT_SECONDS = 60;
|
|
7815
8030
|
var DEFAULT_WAIT_BUDGET_MS = DEFAULT_WAIT_SECONDS * 1e3;
|
|
@@ -8169,8 +8384,8 @@ var PaymentGatewayClient2 = class extends PaymentGatewayClient {
|
|
|
8169
8384
|
// reasonable against that type - would otherwise build an EIP-712 domain around a
|
|
8170
8385
|
// null verifyingContract.
|
|
8171
8386
|
permit2Contract: data.permit2_contract ?? void 0,
|
|
8172
|
-
// Which deposit witness the escrow on this chain pins
|
|
8173
|
-
// four-member
|
|
8387
|
+
// Which deposit witness the escrow on this chain pins. Absent means the
|
|
8388
|
+
// four-member witness — see ChainDefaults.depositWitnessVersion for why
|
|
8174
8389
|
// absence is the permissive default. EVM/Tron only; Solana uses the svm_* fields below.
|
|
8175
8390
|
depositWitnessVersion: data.deposit_witness_version ?? void 0,
|
|
8176
8391
|
// V3 Solana escrow domain separation. Present only for Solana
|
|
@@ -8385,7 +8600,7 @@ function runCli(program2, ...parseArgs) {
|
|
|
8385
8600
|
}
|
|
8386
8601
|
|
|
8387
8602
|
// src/version.ts
|
|
8388
|
-
var PACKAGE_VERSION = true ? "3.0.
|
|
8603
|
+
var PACKAGE_VERSION = true ? "3.0.1" : "0.0.0-dev";
|
|
8389
8604
|
|
|
8390
8605
|
// src/cli/payment-outcome.ts
|
|
8391
8606
|
var EXIT_COMPLETED = 0;
|
|
@@ -8402,7 +8617,7 @@ function describePaymentOutcome(input) {
|
|
|
8402
8617
|
summary: `completed: ${payment} settled.${replay}`
|
|
8403
8618
|
};
|
|
8404
8619
|
}
|
|
8405
|
-
if (status === "failed"
|
|
8620
|
+
if (status === "failed") {
|
|
8406
8621
|
const reason = errorCode ? ` (${errorCode})` : "";
|
|
8407
8622
|
return {
|
|
8408
8623
|
exitCode: EXIT_TERMINAL_FAILURE,
|