@cmdoss/suipay-mcp 0.2.2-dev.5 → 0.2.2-dev.7
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/README.md +17 -1
- package/dist/bin/suipay.js +2 -2
- package/dist/{chunk-THLDEMGS.js → chunk-6ZSOXII5.js} +1790 -273
- package/dist/{chunk-P54ZYBPN.js → chunk-KR6WRUDY.js} +2 -2
- package/dist/{http-B4YTVw0k.d.ts → http-DggnqM91.d.ts} +247 -4
- package/dist/http.d.ts +4 -1
- package/dist/http.js +1 -1
- package/dist/index.d.ts +14 -210
- package/dist/index.js +2 -2
- package/package.json +4 -3
|
@@ -103,7 +103,7 @@ function wrapFailover(clients) {
|
|
|
103
103
|
|
|
104
104
|
// ../sdk/dist/buyer-control/chain.js
|
|
105
105
|
import { Transaction as Transaction2, coinWithBalance } from "@mysten/sui/transactions";
|
|
106
|
-
import { normalizeStructTag as normalizeStructTag3, normalizeSuiAddress as normalizeSuiAddress2, toBase64 } from "@mysten/sui/utils";
|
|
106
|
+
import { fromBase64 as fromBase642, normalizeStructTag as normalizeStructTag3, normalizeSuiAddress as normalizeSuiAddress2, toBase64 } from "@mysten/sui/utils";
|
|
107
107
|
|
|
108
108
|
// ../core/dist/chain/address.js
|
|
109
109
|
function hexOf(address) {
|
|
@@ -636,7 +636,7 @@ function createSpendAccountChain(options) {
|
|
|
636
636
|
const tx = new Transaction2();
|
|
637
637
|
tx.setSender(input.owner);
|
|
638
638
|
tx.moveCall({
|
|
639
|
-
target: target("
|
|
639
|
+
target: target("withdraw_to_owner"),
|
|
640
640
|
typeArguments: [input.coinType],
|
|
641
641
|
arguments: [
|
|
642
642
|
tx.object(input.poolObjectId),
|
|
@@ -823,24 +823,27 @@ function createSpendAccountChain(options) {
|
|
|
823
823
|
},
|
|
824
824
|
async buildSettleOpenRecipientKind(input) {
|
|
825
825
|
if (!input.termsHash || input.termsHash.length === 0) {
|
|
826
|
-
throw new Error("termsHash must be non-empty for spend_account::
|
|
826
|
+
throw new Error("termsHash must be non-empty for spend_account::settle_any_recipient");
|
|
827
827
|
}
|
|
828
828
|
if (!input.paymentIdHash || input.paymentIdHash.length === 0) {
|
|
829
|
-
throw new Error("paymentIdHash must be non-empty for spend_account::
|
|
829
|
+
throw new Error("paymentIdHash must be non-empty for spend_account::settle_any_recipient");
|
|
830
830
|
}
|
|
831
831
|
if (!input.recipient || isZeroAddress(input.recipient)) {
|
|
832
|
-
throw new Error("
|
|
832
|
+
throw new Error("settle_any_recipient recipient must be a non-zero address");
|
|
833
833
|
}
|
|
834
834
|
if (!input.amount || BigInt(input.amount) <= 0n) {
|
|
835
|
-
throw new Error("
|
|
835
|
+
throw new Error("settle_any_recipient amount must be > 0");
|
|
836
836
|
}
|
|
837
|
+
const guarded = resolveGuardedInputs(input, "settle_any_recipient");
|
|
837
838
|
const hash = targetHashBytes(input.targetHash);
|
|
838
839
|
const tx = new Transaction2();
|
|
839
840
|
tx.setSender(input.delegate);
|
|
840
841
|
tx.moveCall({
|
|
841
|
-
target: target("
|
|
842
|
+
target: target("settle_any_recipient"),
|
|
842
843
|
typeArguments: [input.coinType],
|
|
843
844
|
arguments: [
|
|
845
|
+
tx.object(guarded.exclusionDirectoryId),
|
|
846
|
+
tx.object(guarded.recipientRegistryId),
|
|
844
847
|
tx.object(input.poolObjectId),
|
|
845
848
|
tx.object(input.grantObjectId),
|
|
846
849
|
tx.pure.address(input.recipient),
|
|
@@ -848,6 +851,7 @@ function createSpendAccountChain(options) {
|
|
|
848
851
|
tx.pure.vector("u8", Array.from(input.paymentIdHash)),
|
|
849
852
|
tx.pure.vector("u8", Array.from(input.termsHash)),
|
|
850
853
|
tx.pure.vector("u8", Array.from(hash)),
|
|
854
|
+
tx.pure.u64(guarded.expiresAtMs),
|
|
851
855
|
tx.object(input.clockId ?? SUI_CLOCK)
|
|
852
856
|
]
|
|
853
857
|
});
|
|
@@ -858,6 +862,7 @@ function createSpendAccountChain(options) {
|
|
|
858
862
|
tx.setSender(input.owner);
|
|
859
863
|
tx.moveCall({
|
|
860
864
|
target: target("pause_grant"),
|
|
865
|
+
typeArguments: [input.coinType],
|
|
861
866
|
arguments: [tx.object(input.grantObjectId)]
|
|
862
867
|
});
|
|
863
868
|
return kindBytes(tx);
|
|
@@ -867,6 +872,7 @@ function createSpendAccountChain(options) {
|
|
|
867
872
|
tx.setSender(input.owner);
|
|
868
873
|
tx.moveCall({
|
|
869
874
|
target: target("resume_grant"),
|
|
875
|
+
typeArguments: [input.coinType],
|
|
870
876
|
arguments: [tx.object(input.grantObjectId)]
|
|
871
877
|
});
|
|
872
878
|
return kindBytes(tx);
|
|
@@ -876,6 +882,7 @@ function createSpendAccountChain(options) {
|
|
|
876
882
|
tx.setSender(input.owner);
|
|
877
883
|
tx.moveCall({
|
|
878
884
|
target: target("revoke_grant"),
|
|
885
|
+
typeArguments: [input.coinType],
|
|
879
886
|
arguments: [tx.object(input.grantObjectId)]
|
|
880
887
|
});
|
|
881
888
|
return kindBytes(tx);
|
|
@@ -944,30 +951,57 @@ function createSpendAccountChain(options) {
|
|
|
944
951
|
const tx = new Transaction2();
|
|
945
952
|
tx.setSender(input.owner);
|
|
946
953
|
const expires = input.newExpiresAtMs === void 0 || input.newExpiresAtMs === null ? 0n : BigInt(input.newExpiresAtMs);
|
|
954
|
+
const totalBudget = input.newTotalBudget === null || input.newTotalBudget === void 0 ? null : BigInt(input.newTotalBudget);
|
|
947
955
|
tx.moveCall({
|
|
948
956
|
target: target("tighten_grant_limits"),
|
|
957
|
+
// Grant<T> is generic; every sibling grant builder passes the coin type.
|
|
958
|
+
typeArguments: [input.coinType],
|
|
949
959
|
arguments: [
|
|
950
960
|
tx.object(input.grantObjectId),
|
|
951
|
-
tx.pure.u64
|
|
961
|
+
tx.pure.option("u64", totalBudget),
|
|
952
962
|
tx.pure.u64(expires)
|
|
953
963
|
]
|
|
954
964
|
});
|
|
955
965
|
return kindBytes(tx);
|
|
956
966
|
},
|
|
967
|
+
async readGrantTotalSpent(grantObjectId) {
|
|
968
|
+
try {
|
|
969
|
+
const { object } = await client.core.getObject({
|
|
970
|
+
objectId: grantObjectId,
|
|
971
|
+
include: { json: true }
|
|
972
|
+
});
|
|
973
|
+
const json = object?.json;
|
|
974
|
+
if (!json || typeof json !== "object")
|
|
975
|
+
return null;
|
|
976
|
+
const spent = json.total_spent;
|
|
977
|
+
if (typeof spent === "string" && /^\d+$/.test(spent))
|
|
978
|
+
return BigInt(spent);
|
|
979
|
+
if (typeof spent === "number" && Number.isFinite(spent))
|
|
980
|
+
return BigInt(Math.trunc(spent));
|
|
981
|
+
if (typeof spent === "bigint")
|
|
982
|
+
return spent;
|
|
983
|
+
return null;
|
|
984
|
+
} catch {
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
},
|
|
957
988
|
async buildPayKind(input) {
|
|
958
989
|
if (!input.termsHash || input.termsHash.length === 0) {
|
|
959
|
-
throw new Error("termsHash must be non-empty for spend_account::
|
|
990
|
+
throw new Error("termsHash must be non-empty for spend_account::settle_policy");
|
|
960
991
|
}
|
|
961
992
|
if (!input.paymentIdHash || input.paymentIdHash.length === 0) {
|
|
962
|
-
throw new Error("paymentIdHash must be non-empty for spend_account::
|
|
993
|
+
throw new Error("paymentIdHash must be non-empty for spend_account::settle_policy");
|
|
963
994
|
}
|
|
995
|
+
const guarded = resolveGuardedInputs(input, "settle_policy");
|
|
964
996
|
const hash = targetHashBytes(input.targetHash);
|
|
965
997
|
const tx = new Transaction2();
|
|
966
998
|
tx.setSender(input.delegate);
|
|
967
999
|
tx.moveCall({
|
|
968
|
-
target: target("
|
|
1000
|
+
target: target("settle_policy"),
|
|
969
1001
|
typeArguments: [input.coinType],
|
|
970
1002
|
arguments: [
|
|
1003
|
+
tx.object(guarded.exclusionDirectoryId),
|
|
1004
|
+
tx.object(guarded.recipientRegistryId),
|
|
971
1005
|
tx.object(input.poolObjectId),
|
|
972
1006
|
tx.object(input.grantObjectId),
|
|
973
1007
|
tx.pure.u64(BigInt(input.policyId)),
|
|
@@ -975,6 +1009,7 @@ function createSpendAccountChain(options) {
|
|
|
975
1009
|
tx.pure.u64(BigInt(input.amount)),
|
|
976
1010
|
tx.pure.vector("u8", Array.from(input.paymentIdHash)),
|
|
977
1011
|
tx.pure.vector("u8", Array.from(input.termsHash)),
|
|
1012
|
+
tx.pure.u64(guarded.expiresAtMs),
|
|
978
1013
|
tx.object(input.clockId ?? SUI_CLOCK)
|
|
979
1014
|
]
|
|
980
1015
|
});
|
|
@@ -982,6 +1017,28 @@ function createSpendAccountChain(options) {
|
|
|
982
1017
|
}
|
|
983
1018
|
};
|
|
984
1019
|
}
|
|
1020
|
+
function resolveGuardedInputs(input, fn) {
|
|
1021
|
+
const present = [
|
|
1022
|
+
input.exclusionDirectoryId,
|
|
1023
|
+
input.recipientRegistryId,
|
|
1024
|
+
input.expiresAtMs
|
|
1025
|
+
].filter((v) => v !== void 0).length;
|
|
1026
|
+
if (present !== 3) {
|
|
1027
|
+
throw new Error(`${fn} on the fresh lineage requires exclusionDirectoryId, recipientRegistryId and expiresAtMs (mandatory guarded settlement \u2014 there is no unguarded twin)`);
|
|
1028
|
+
}
|
|
1029
|
+
const expires = BigInt(input.expiresAtMs);
|
|
1030
|
+
if (expires <= 0n) {
|
|
1031
|
+
throw new Error(`guarded ${fn} expiresAtMs must be a positive epoch-ms value`);
|
|
1032
|
+
}
|
|
1033
|
+
if (isZeroAddress(input.exclusionDirectoryId) || isZeroAddress(input.recipientRegistryId)) {
|
|
1034
|
+
throw new Error(`guarded ${fn} directory and registry ids must be non-zero`);
|
|
1035
|
+
}
|
|
1036
|
+
return {
|
|
1037
|
+
exclusionDirectoryId: input.exclusionDirectoryId,
|
|
1038
|
+
recipientRegistryId: input.recipientRegistryId,
|
|
1039
|
+
expiresAtMs: expires
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
985
1042
|
var SUI_NETWORKS = ["testnet", "devnet", "localnet", "mainnet"];
|
|
986
1043
|
function toGrpcNetwork(network) {
|
|
987
1044
|
const short = (network ?? "testnet").replace(/^sui:/, "");
|
|
@@ -990,18 +1047,132 @@ function toGrpcNetwork(network) {
|
|
|
990
1047
|
}
|
|
991
1048
|
return short;
|
|
992
1049
|
}
|
|
993
|
-
|
|
1050
|
+
var GUARDED_DELEGATE_ARG_LAYOUT = {
|
|
1051
|
+
settle_policy: {
|
|
1052
|
+
recipient: null,
|
|
1053
|
+
amount: 6,
|
|
1054
|
+
paymentIdHash: 7,
|
|
1055
|
+
expiresAtMs: 9
|
|
1056
|
+
},
|
|
1057
|
+
settle_any_recipient: {
|
|
1058
|
+
recipient: 4,
|
|
1059
|
+
amount: 5,
|
|
1060
|
+
paymentIdHash: 6,
|
|
1061
|
+
expiresAtMs: 9
|
|
1062
|
+
},
|
|
1063
|
+
settle_policy_to_refund_vault: {
|
|
1064
|
+
recipient: null,
|
|
1065
|
+
amount: 6,
|
|
1066
|
+
paymentIdHash: 7,
|
|
1067
|
+
expiresAtMs: 9
|
|
1068
|
+
},
|
|
1069
|
+
settle_any_recipient_to_refund_vault: {
|
|
1070
|
+
recipient: 4,
|
|
1071
|
+
amount: 5,
|
|
1072
|
+
paymentIdHash: 6,
|
|
1073
|
+
expiresAtMs: 9
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
function pureBytesOf(data, arg) {
|
|
1077
|
+
if (!arg || arg.$kind !== "Input" || arg.Input === void 0)
|
|
1078
|
+
return null;
|
|
1079
|
+
const input = data.inputs?.[arg.Input];
|
|
1080
|
+
if (!input || input.$kind !== "Pure" || !input.Pure)
|
|
1081
|
+
return null;
|
|
1082
|
+
return input.Pure.bytes;
|
|
1083
|
+
}
|
|
1084
|
+
function readPureU64(data, arg) {
|
|
1085
|
+
const raw = pureBytesOf(data, arg);
|
|
1086
|
+
if (raw === null)
|
|
1087
|
+
return null;
|
|
1088
|
+
try {
|
|
1089
|
+
return bcs.u64().parse(fromBase642(raw));
|
|
1090
|
+
} catch {
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
function readPureVectorU8Hex(data, arg) {
|
|
1095
|
+
const raw = pureBytesOf(data, arg);
|
|
1096
|
+
if (raw === null)
|
|
1097
|
+
return null;
|
|
1098
|
+
try {
|
|
1099
|
+
const bytes = bcs.vector(bcs.u8()).parse(fromBase642(raw));
|
|
1100
|
+
return Buffer.from(bytes).toString("hex");
|
|
1101
|
+
} catch {
|
|
1102
|
+
return null;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
function readPureAddress(data, arg) {
|
|
1106
|
+
const raw = pureBytesOf(data, arg);
|
|
1107
|
+
if (raw === null)
|
|
1108
|
+
return null;
|
|
1109
|
+
try {
|
|
1110
|
+
return normalizeSuiAddress2(bcs.Address.parse(fromBase642(raw)));
|
|
1111
|
+
} catch {
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
function recoverGuardedDelegateCalls(finalized) {
|
|
1116
|
+
const raw = finalized?.bcs;
|
|
1117
|
+
let bytes;
|
|
1118
|
+
if (raw instanceof Uint8Array) {
|
|
1119
|
+
bytes = raw;
|
|
1120
|
+
} else if (typeof raw === "string") {
|
|
1121
|
+
try {
|
|
1122
|
+
bytes = fromBase642(raw);
|
|
1123
|
+
} catch {
|
|
1124
|
+
return [];
|
|
1125
|
+
}
|
|
1126
|
+
} else {
|
|
1127
|
+
return [];
|
|
1128
|
+
}
|
|
1129
|
+
let data;
|
|
1130
|
+
try {
|
|
1131
|
+
data = Transaction2.from(bytes).getData();
|
|
1132
|
+
} catch {
|
|
1133
|
+
return [];
|
|
1134
|
+
}
|
|
1135
|
+
const calls = [];
|
|
1136
|
+
for (const command of data.commands) {
|
|
1137
|
+
if (command.$kind !== "MoveCall")
|
|
1138
|
+
continue;
|
|
1139
|
+
const mc = command.MoveCall;
|
|
1140
|
+
if (!mc || mc.module !== "spend_account")
|
|
1141
|
+
continue;
|
|
1142
|
+
if (!(mc.function in GUARDED_DELEGATE_ARG_LAYOUT))
|
|
1143
|
+
continue;
|
|
1144
|
+
const fn = mc.function;
|
|
1145
|
+
const layout = GUARDED_DELEGATE_ARG_LAYOUT[fn];
|
|
1146
|
+
const args = mc.arguments ?? [];
|
|
1147
|
+
calls.push({
|
|
1148
|
+
packageId: normalizeSuiAddress2(mc.package),
|
|
1149
|
+
function: fn,
|
|
1150
|
+
paymentIdHash: readPureVectorU8Hex(data, args[layout.paymentIdHash]),
|
|
1151
|
+
amount: readPureU64(data, args[layout.amount]),
|
|
1152
|
+
recipient: layout.recipient === null ? null : readPureAddress(data, args[layout.recipient]),
|
|
1153
|
+
expiresAtMs: readPureU64(data, args[layout.expiresAtMs])
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
return calls;
|
|
1157
|
+
}
|
|
1158
|
+
function createSpendAccountSettledTxReader(options) {
|
|
994
1159
|
const network = toGrpcNetwork(options.network);
|
|
995
1160
|
const client = options.client ?? createFailoverGrpcClient({
|
|
996
1161
|
network,
|
|
997
1162
|
url: options.url ?? `https://fullnode.${network}.sui.io:443`
|
|
998
1163
|
});
|
|
999
1164
|
return {
|
|
1000
|
-
async
|
|
1165
|
+
async getSettledTx(digest) {
|
|
1001
1166
|
const result = await fetchGrpcTransaction(client, digest, {
|
|
1002
1167
|
events: true,
|
|
1003
1168
|
effects: true,
|
|
1004
|
-
transaction: true
|
|
1169
|
+
transaction: true,
|
|
1170
|
+
// The guarded delegate recovery below decodes the authoritative
|
|
1171
|
+
// serialized transaction. Those bytes are a separate read-mask path
|
|
1172
|
+
// from the parsed `transaction` projection — without asking for them
|
|
1173
|
+
// the response carries none, recovery finds no guarded call, and every
|
|
1174
|
+
// delegate payment is refused after the funds already moved.
|
|
1175
|
+
bcs: true
|
|
1005
1176
|
});
|
|
1006
1177
|
if (!result)
|
|
1007
1178
|
return null;
|
|
@@ -1037,26 +1208,28 @@ function createSpendAccountFinalityReader(options) {
|
|
|
1037
1208
|
type: e.eventType,
|
|
1038
1209
|
kind: e.eventType.split("::").pop() ?? e.eventType
|
|
1039
1210
|
}));
|
|
1211
|
+
const guardedDelegateCalls = recoverGuardedDelegateCalls(finalized);
|
|
1040
1212
|
return {
|
|
1041
1213
|
status: "success",
|
|
1042
1214
|
txDigest: digest,
|
|
1043
1215
|
sender,
|
|
1044
1216
|
moveCalls,
|
|
1045
1217
|
objectIds,
|
|
1046
|
-
events
|
|
1218
|
+
events,
|
|
1219
|
+
guardedDelegateCalls
|
|
1047
1220
|
};
|
|
1048
1221
|
}
|
|
1049
1222
|
};
|
|
1050
1223
|
}
|
|
1051
1224
|
|
|
1052
1225
|
// ../sdk/dist/delegated-signer/payer.js
|
|
1053
|
-
import { bcs as
|
|
1226
|
+
import { bcs as bcs5 } from "@mysten/sui/bcs";
|
|
1054
1227
|
import { Transaction as Transaction4 } from "@mysten/sui/transactions";
|
|
1055
|
-
import { fromBase64 as
|
|
1228
|
+
import { fromBase64 as fromBase646, normalizeStructTag as normalizeStructTag6, normalizeSuiAddress as normalizeSuiAddress6, toBase64 as toBase645 } from "@mysten/sui/utils";
|
|
1056
1229
|
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
|
|
1057
1230
|
|
|
1058
1231
|
// ../sdk/dist/pay-operation/operation.js
|
|
1059
|
-
import { normalizeStructTag as
|
|
1232
|
+
import { normalizeStructTag as normalizeStructTag5, normalizeSuiAddress as normalizeSuiAddress3 } from "@mysten/sui/utils";
|
|
1060
1233
|
|
|
1061
1234
|
// ../core/dist/policy/grant-target.js
|
|
1062
1235
|
function normalizeHash(hex) {
|
|
@@ -1139,6 +1312,9 @@ function resolveGrantTarget(snapshot, offerTargetHash) {
|
|
|
1139
1312
|
};
|
|
1140
1313
|
}
|
|
1141
1314
|
|
|
1315
|
+
// ../sdk/dist/protocol/carrier.js
|
|
1316
|
+
import { fromBase64 as fromBase644, toBase64 as toBase643 } from "@mysten/sui/utils";
|
|
1317
|
+
|
|
1142
1318
|
// ../sdk/dist/protocol/auth-params.js
|
|
1143
1319
|
var TOKEN = /^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/;
|
|
1144
1320
|
function formatAuthParams(params) {
|
|
@@ -1230,8 +1406,11 @@ function bytesToBase64Url(bytes) {
|
|
|
1230
1406
|
var SETTLEMENT_RAILS = [
|
|
1231
1407
|
"direct",
|
|
1232
1408
|
"delegate",
|
|
1233
|
-
"
|
|
1409
|
+
"recurring"
|
|
1234
1410
|
];
|
|
1411
|
+
function mintLiveRail(rail) {
|
|
1412
|
+
return rail;
|
|
1413
|
+
}
|
|
1235
1414
|
function isDelegateRail(rail) {
|
|
1236
1415
|
return rail === "delegate";
|
|
1237
1416
|
}
|
|
@@ -1239,6 +1418,29 @@ var DIALECT_ORDER = ["mpp", "x402"];
|
|
|
1239
1418
|
function isSettlementRail(value) {
|
|
1240
1419
|
return typeof value === "string" && SETTLEMENT_RAILS.includes(value);
|
|
1241
1420
|
}
|
|
1421
|
+
function decodeWireRail(value) {
|
|
1422
|
+
if (isSettlementRail(value))
|
|
1423
|
+
return value;
|
|
1424
|
+
if (value === "subscription")
|
|
1425
|
+
return "recurring";
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
function decodeAcceptedRails(rails) {
|
|
1429
|
+
if (rails.length === 0)
|
|
1430
|
+
return null;
|
|
1431
|
+
const out = [];
|
|
1432
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1433
|
+
for (const r of rails) {
|
|
1434
|
+
const decoded = decodeWireRail(r);
|
|
1435
|
+
if (!decoded)
|
|
1436
|
+
return null;
|
|
1437
|
+
if (seen.has(decoded))
|
|
1438
|
+
continue;
|
|
1439
|
+
seen.add(decoded);
|
|
1440
|
+
out.push(decoded);
|
|
1441
|
+
}
|
|
1442
|
+
return out;
|
|
1443
|
+
}
|
|
1242
1444
|
function assertSettlementRail(value) {
|
|
1243
1445
|
if (!isSettlementRail(value)) {
|
|
1244
1446
|
throw new Error(`invalid settlement rail: ${String(value)} (expected ${SETTLEMENT_RAILS.join(" | ")})`);
|
|
@@ -1249,7 +1451,7 @@ function isDialect(value) {
|
|
|
1249
1451
|
return value === "mpp" || value === "x402";
|
|
1250
1452
|
}
|
|
1251
1453
|
function isSettlementMode(value) {
|
|
1252
|
-
return value === "
|
|
1454
|
+
return value === "gateway-settled" || value === "self-settled";
|
|
1253
1455
|
}
|
|
1254
1456
|
function dialectsFromModes(modes) {
|
|
1255
1457
|
return DIALECT_ORDER.filter((d) => modes[d] != null);
|
|
@@ -1287,7 +1489,7 @@ function createSettlementRailAuthority(input) {
|
|
|
1287
1489
|
}
|
|
1288
1490
|
const dialectModes = Object.freeze({ ...input.dialectModes });
|
|
1289
1491
|
const supportedDialects = Object.freeze(dialectsFromModes(dialectModes));
|
|
1290
|
-
const proofMode = dialectModes.mpp ?? dialectModes.x402 ?? "
|
|
1492
|
+
const proofMode = dialectModes.mpp ?? dialectModes.x402 ?? "gateway-settled";
|
|
1291
1493
|
return Object.freeze({
|
|
1292
1494
|
rail: input.rail,
|
|
1293
1495
|
packageId: input.packageId.trim(),
|
|
@@ -1302,12 +1504,12 @@ function spendAccountDebitFunction(input) {
|
|
|
1302
1504
|
const open2 = input?.accessMode === "open";
|
|
1303
1505
|
const refundable = input?.refundable === true;
|
|
1304
1506
|
if (open2 && refundable)
|
|
1305
|
-
return "
|
|
1507
|
+
return "settle_any_recipient_to_refund_vault";
|
|
1306
1508
|
if (open2)
|
|
1307
|
-
return "
|
|
1509
|
+
return "settle_any_recipient";
|
|
1308
1510
|
if (refundable)
|
|
1309
|
-
return "
|
|
1310
|
-
return "
|
|
1511
|
+
return "settle_policy_to_refund_vault";
|
|
1512
|
+
return "settle_policy";
|
|
1311
1513
|
}
|
|
1312
1514
|
function defaultAuthorityForRail(rail, packageId, opts) {
|
|
1313
1515
|
switch (rail) {
|
|
@@ -1318,20 +1520,22 @@ function defaultAuthorityForRail(rail, packageId, opts) {
|
|
|
1318
1520
|
packageId,
|
|
1319
1521
|
module: "spend_account",
|
|
1320
1522
|
payFunction: spendAccountDebitFunction(),
|
|
1321
|
-
dialectModes: dual ? { mpp: "
|
|
1523
|
+
dialectModes: dual ? { mpp: "self-settled", x402: "gateway-settled" } : { mpp: "self-settled" }
|
|
1322
1524
|
});
|
|
1323
1525
|
}
|
|
1324
|
-
case "
|
|
1526
|
+
case "recurring":
|
|
1325
1527
|
return createSettlementRailAuthority({
|
|
1326
|
-
rail: "
|
|
1528
|
+
rail: "recurring",
|
|
1327
1529
|
packageId,
|
|
1328
1530
|
module: "subscription",
|
|
1329
1531
|
// Real Move entry function is `subscription::settle_period`; `charge`
|
|
1330
|
-
// never existed on-chain (a Move call to it would abort).
|
|
1532
|
+
// never existed on-chain (a Move call to it would abort). HTTP
|
|
1533
|
+
// `recurring` is the Move `RAIL_TAG`; the module name stays
|
|
1534
|
+
// `subscription`.
|
|
1331
1535
|
payFunction: "settle_period",
|
|
1332
1536
|
dialectModes: {
|
|
1333
|
-
mpp: "
|
|
1334
|
-
x402: "
|
|
1537
|
+
mpp: "gateway-settled",
|
|
1538
|
+
x402: "gateway-settled"
|
|
1335
1539
|
}
|
|
1336
1540
|
});
|
|
1337
1541
|
case "direct":
|
|
@@ -1339,10 +1543,12 @@ function defaultAuthorityForRail(rail, packageId, opts) {
|
|
|
1339
1543
|
rail: "direct",
|
|
1340
1544
|
packageId,
|
|
1341
1545
|
module: "payment",
|
|
1342
|
-
|
|
1546
|
+
// Fresh lineage (ADR-0048 Option B): the canonical guarded direct settle
|
|
1547
|
+
// is `payment::settle` (guard folded in; no `pay`/`settle_guarded` twin).
|
|
1548
|
+
payFunction: "settle",
|
|
1343
1549
|
dialectModes: {
|
|
1344
|
-
mpp: "
|
|
1345
|
-
x402: "
|
|
1550
|
+
mpp: "gateway-settled",
|
|
1551
|
+
x402: "gateway-settled"
|
|
1346
1552
|
}
|
|
1347
1553
|
});
|
|
1348
1554
|
default: {
|
|
@@ -1351,9 +1557,24 @@ function defaultAuthorityForRail(rail, packageId, opts) {
|
|
|
1351
1557
|
}
|
|
1352
1558
|
}
|
|
1353
1559
|
}
|
|
1560
|
+
function railSelfSettles(rail, opts) {
|
|
1561
|
+
const authority = defaultAuthorityForRail(rail, "0x0", opts);
|
|
1562
|
+
return Object.values(authority.dialectModes).some((mode) => mode === "self-settled");
|
|
1563
|
+
}
|
|
1354
1564
|
|
|
1355
1565
|
// ../sdk/dist/protocol/challenge.js
|
|
1356
1566
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
1567
|
+
|
|
1568
|
+
// ../sdk/dist/protocol/refund-approval.js
|
|
1569
|
+
import { createHash as createHash3 } from "crypto";
|
|
1570
|
+
import { bcs as bcs2 } from "@mysten/sui/bcs";
|
|
1571
|
+
import { fromHex } from "@mysten/sui/utils";
|
|
1572
|
+
|
|
1573
|
+
// ../sdk/dist/protocol/coin-type-hash.js
|
|
1574
|
+
import { createHash as createHash2 } from "crypto";
|
|
1575
|
+
import { normalizeStructTag as normalizeStructTag4 } from "@mysten/sui/utils";
|
|
1576
|
+
|
|
1577
|
+
// ../sdk/dist/protocol/challenge.js
|
|
1357
1578
|
var SIGNED_FIELDS_V1 = [
|
|
1358
1579
|
"challengeId",
|
|
1359
1580
|
"resource",
|
|
@@ -1402,6 +1623,12 @@ var SIGNED_FIELDS_V3 = [
|
|
|
1402
1623
|
"refundTo",
|
|
1403
1624
|
"settlementRef"
|
|
1404
1625
|
];
|
|
1626
|
+
var SIGNED_FIELDS_V4 = [
|
|
1627
|
+
...SIGNED_FIELDS_V3,
|
|
1628
|
+
"acceptedRails",
|
|
1629
|
+
"exclusionProtocolVersion",
|
|
1630
|
+
"registryIdentity"
|
|
1631
|
+
];
|
|
1405
1632
|
var V3_OMIT_WHEN_ABSENT = /* @__PURE__ */ new Set([
|
|
1406
1633
|
"intent",
|
|
1407
1634
|
"periodMs",
|
|
@@ -1419,6 +1646,15 @@ var V3_OMIT_WHEN_ABSENT = /* @__PURE__ */ new Set([
|
|
|
1419
1646
|
"sponsorGasPrice",
|
|
1420
1647
|
"sponsorGasPayment"
|
|
1421
1648
|
]);
|
|
1649
|
+
var V4_OMIT_WHEN_ABSENT = /* @__PURE__ */ new Set([
|
|
1650
|
+
"acceptedRails",
|
|
1651
|
+
"exclusionProtocolVersion",
|
|
1652
|
+
"registryIdentity"
|
|
1653
|
+
]);
|
|
1654
|
+
var OMIT_WHEN_ABSENT = /* @__PURE__ */ new Set([
|
|
1655
|
+
...V3_OMIT_WHEN_ABSENT,
|
|
1656
|
+
...V4_OMIT_WHEN_ABSENT
|
|
1657
|
+
]);
|
|
1422
1658
|
var BODY_HASH_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
1423
1659
|
var CONTENT_TYPE_RE = /^[a-z0-9!#$%&'*+.^_`|~-]+\/[a-z0-9!#$%&'*+.^_`|~-]+(?:; ?[a-z0-9!#$%&'*+.^_`|~-]+=(?:[a-zA-Z0-9!#$%&'*+.^_`|~-]+|"[^"\n\r]*"))*$/;
|
|
1424
1660
|
function isBodyHash(value) {
|
|
@@ -1446,10 +1682,43 @@ function presentV3OnlyFields(bearer) {
|
|
|
1446
1682
|
function carriesV3OnlyFields(bearer) {
|
|
1447
1683
|
return presentV3OnlyFields(bearer).length > 0;
|
|
1448
1684
|
}
|
|
1685
|
+
function presentV4OnlyFields(bearer) {
|
|
1686
|
+
return [
|
|
1687
|
+
bearer.acceptedRails !== void 0 ? "acceptedRails" : void 0,
|
|
1688
|
+
bearer.exclusionProtocolVersion !== void 0 ? "exclusionProtocolVersion" : void 0,
|
|
1689
|
+
bearer.registryIdentity !== void 0 ? "registryIdentity" : void 0
|
|
1690
|
+
].filter((f) => f !== void 0);
|
|
1691
|
+
}
|
|
1692
|
+
function carriesV4OnlyFields(bearer) {
|
|
1693
|
+
return presentV4OnlyFields(bearer).length > 0;
|
|
1694
|
+
}
|
|
1695
|
+
function offerRequiresGuardedSettle(offer) {
|
|
1696
|
+
const rails = [
|
|
1697
|
+
...offer.acceptedRails ?? [],
|
|
1698
|
+
...offer.settlementRail ? [offer.settlementRail] : []
|
|
1699
|
+
];
|
|
1700
|
+
if (!rails.length)
|
|
1701
|
+
return false;
|
|
1702
|
+
return [...new Set(rails)].some((r) => railSelfSettles(r, { x402DialectEnabled: false }));
|
|
1703
|
+
}
|
|
1704
|
+
function parseRegistryIdentity(registryIdentity) {
|
|
1705
|
+
if (!registryIdentity)
|
|
1706
|
+
return null;
|
|
1707
|
+
const idx = registryIdentity.indexOf("::");
|
|
1708
|
+
if (idx <= 0)
|
|
1709
|
+
return null;
|
|
1710
|
+
const packageId = registryIdentity.slice(0, idx);
|
|
1711
|
+
const directoryId = registryIdentity.slice(idx + 2);
|
|
1712
|
+
if (!packageId || !directoryId || directoryId.includes("::"))
|
|
1713
|
+
return null;
|
|
1714
|
+
return { packageId, directoryId };
|
|
1715
|
+
}
|
|
1449
1716
|
function resolveDigestVersion(offer) {
|
|
1717
|
+
if (carriesV4OnlyFields(offer))
|
|
1718
|
+
return 4;
|
|
1450
1719
|
if (carriesV3OnlyFields(offer))
|
|
1451
1720
|
return 3;
|
|
1452
|
-
if (offer.digestVersion === 3 || offer.digestVersion === 2 || offer.digestVersion === 1) {
|
|
1721
|
+
if (offer.digestVersion === 4 || offer.digestVersion === 3 || offer.digestVersion === 2 || offer.digestVersion === 1) {
|
|
1453
1722
|
return offer.digestVersion;
|
|
1454
1723
|
}
|
|
1455
1724
|
if (offer.settlementRail)
|
|
@@ -1457,6 +1726,8 @@ function resolveDigestVersion(offer) {
|
|
|
1457
1726
|
return 1;
|
|
1458
1727
|
}
|
|
1459
1728
|
function signedFieldsFor(version) {
|
|
1729
|
+
if (version === 4)
|
|
1730
|
+
return SIGNED_FIELDS_V4;
|
|
1460
1731
|
if (version === 3)
|
|
1461
1732
|
return SIGNED_FIELDS_V3;
|
|
1462
1733
|
if (version === 2)
|
|
@@ -1467,12 +1738,14 @@ function fieldValue(offer, key) {
|
|
|
1467
1738
|
const v = offer[key];
|
|
1468
1739
|
if (v === void 0 || v === null)
|
|
1469
1740
|
return "";
|
|
1741
|
+
if (Array.isArray(v))
|
|
1742
|
+
return v.join(",");
|
|
1470
1743
|
return String(v);
|
|
1471
1744
|
}
|
|
1472
1745
|
function canonicalOffer(offer, version = resolveDigestVersion(offer)) {
|
|
1473
1746
|
const lines = [];
|
|
1474
1747
|
for (const f of signedFieldsFor(version)) {
|
|
1475
|
-
if (
|
|
1748
|
+
if (OMIT_WHEN_ABSENT.has(f) && (offer[f] === void 0 || offer[f] === null))
|
|
1476
1749
|
continue;
|
|
1477
1750
|
lines.push(`${f}=${fieldValue(offer, f)}`);
|
|
1478
1751
|
}
|
|
@@ -1482,15 +1755,46 @@ function signOffer(offer, secret, version) {
|
|
|
1482
1755
|
const v = version ?? resolveDigestVersion(offer);
|
|
1483
1756
|
return createHmac("sha256", secret).update(canonicalOffer(offer, v)).digest("base64url");
|
|
1484
1757
|
}
|
|
1485
|
-
function
|
|
1758
|
+
function digestMatches(offer, secret) {
|
|
1486
1759
|
const version = resolveDigestVersion(offer);
|
|
1487
1760
|
const expected = Buffer.from(signOffer(offer, secret, version));
|
|
1488
1761
|
const actual = Buffer.from(offer.digest ?? "");
|
|
1489
1762
|
return expected.length === actual.length && timingSafeEqual(expected, actual);
|
|
1490
1763
|
}
|
|
1764
|
+
function withLegacySubscriptionSpelling(offer) {
|
|
1765
|
+
const railIsRecurring = offer.settlementRail === "recurring";
|
|
1766
|
+
const railsHaveRecurring = offer.acceptedRails?.includes("recurring") === true;
|
|
1767
|
+
if (!railIsRecurring && !railsHaveRecurring)
|
|
1768
|
+
return null;
|
|
1769
|
+
return {
|
|
1770
|
+
...offer,
|
|
1771
|
+
...railIsRecurring ? { settlementRail: "subscription" } : {},
|
|
1772
|
+
...railsHaveRecurring ? {
|
|
1773
|
+
acceptedRails: offer.acceptedRails.map((r) => r === "recurring" ? "subscription" : r)
|
|
1774
|
+
} : {}
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
function verifyOfferDigest(offer, secret) {
|
|
1778
|
+
if (digestMatches(offer, secret))
|
|
1779
|
+
return true;
|
|
1780
|
+
const legacy = withLegacySubscriptionSpelling(offer);
|
|
1781
|
+
return legacy !== null && digestMatches(legacy, secret);
|
|
1782
|
+
}
|
|
1491
1783
|
function isExpired(offer, now = Date.now()) {
|
|
1492
1784
|
return now >= offer.expiresAt;
|
|
1493
1785
|
}
|
|
1786
|
+
function decodeSponsorGasPayment(encoded) {
|
|
1787
|
+
if (typeof encoded !== "string" || !encoded.trim()) {
|
|
1788
|
+
throw new Error("sponsorGasPayment is required");
|
|
1789
|
+
}
|
|
1790
|
+
return encoded.split(",").map((part) => {
|
|
1791
|
+
const bits = part.split("@");
|
|
1792
|
+
if (bits.length !== 3 || !bits[0] || !bits[1] || !bits[2]) {
|
|
1793
|
+
throw new Error(`malformed sponsorGasPayment segment: ${part}`);
|
|
1794
|
+
}
|
|
1795
|
+
return { objectId: bits[0], version: bits[1], digest: bits[2] };
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1494
1798
|
|
|
1495
1799
|
// ../sdk/dist/protocol/mpp.js
|
|
1496
1800
|
function offerToBase64Url(offer) {
|
|
@@ -1501,7 +1805,66 @@ var MPP_METHOD = "sui.charge";
|
|
|
1501
1805
|
var MPP_CHALLENGE_HEADER = "WWW-Authenticate";
|
|
1502
1806
|
var MPP_CREDENTIAL_HEADER = "Authorization";
|
|
1503
1807
|
var MPP_RECEIPT_HEADER = "Payment-Receipt";
|
|
1504
|
-
function
|
|
1808
|
+
function splitChallenges(header) {
|
|
1809
|
+
const scheme = `${MPP_SCHEME} `;
|
|
1810
|
+
const value = header.trim();
|
|
1811
|
+
const starts = [];
|
|
1812
|
+
let inQuote = false;
|
|
1813
|
+
for (let i = 0; i < value.length; i++) {
|
|
1814
|
+
const ch = value[i];
|
|
1815
|
+
if (inQuote) {
|
|
1816
|
+
if (ch === "\\") {
|
|
1817
|
+
i++;
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
if (ch === '"')
|
|
1821
|
+
inQuote = false;
|
|
1822
|
+
continue;
|
|
1823
|
+
}
|
|
1824
|
+
if (ch === '"') {
|
|
1825
|
+
inQuote = true;
|
|
1826
|
+
continue;
|
|
1827
|
+
}
|
|
1828
|
+
if (value.startsWith(scheme, i)) {
|
|
1829
|
+
let j = i - 1;
|
|
1830
|
+
while (j >= 0 && (value[j] === " " || value[j] === " "))
|
|
1831
|
+
j--;
|
|
1832
|
+
if (j < 0 || value[j] === ",")
|
|
1833
|
+
starts.push(i);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
if (starts.length === 0)
|
|
1837
|
+
return [];
|
|
1838
|
+
const parts = [];
|
|
1839
|
+
for (let k = 0; k < starts.length; k++) {
|
|
1840
|
+
const from = starts[k];
|
|
1841
|
+
const to = k + 1 < starts.length ? starts[k + 1] : value.length;
|
|
1842
|
+
parts.push(value.slice(from, to).replace(/,\s*$/, "").trim());
|
|
1843
|
+
}
|
|
1844
|
+
return parts;
|
|
1845
|
+
}
|
|
1846
|
+
function decodeChallenge(header, opts) {
|
|
1847
|
+
const parts = splitChallenges(header);
|
|
1848
|
+
if (parts.length <= 1) {
|
|
1849
|
+
return decodeSingleChallenge(header);
|
|
1850
|
+
}
|
|
1851
|
+
if (opts?.rail !== void 0) {
|
|
1852
|
+
for (const part of parts) {
|
|
1853
|
+
const offer = decodeSingleChallenge(part);
|
|
1854
|
+
if (offer && mintLiveRail(offer.settlementRail ?? "direct") === mintLiveRail(opts.rail)) {
|
|
1855
|
+
return offer;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
return null;
|
|
1859
|
+
}
|
|
1860
|
+
for (const part of parts) {
|
|
1861
|
+
const offer = decodeSingleChallenge(part);
|
|
1862
|
+
if (offer)
|
|
1863
|
+
return offer;
|
|
1864
|
+
}
|
|
1865
|
+
return null;
|
|
1866
|
+
}
|
|
1867
|
+
function decodeSingleChallenge(header) {
|
|
1505
1868
|
const parsed = parseAuthHeader(header);
|
|
1506
1869
|
if (!parsed || parsed.scheme !== MPP_SCHEME)
|
|
1507
1870
|
return null;
|
|
@@ -1549,13 +1912,14 @@ function decodeChallenge(header) {
|
|
|
1549
1912
|
offer.platformFee = p.platform_fee;
|
|
1550
1913
|
if (p.digest_version !== void 0) {
|
|
1551
1914
|
const v = Number(p.digest_version);
|
|
1552
|
-
if (v === 1 || v === 2 || v === 3)
|
|
1915
|
+
if (v === 1 || v === 2 || v === 3 || v === 4)
|
|
1553
1916
|
offer.digestVersion = v;
|
|
1554
1917
|
}
|
|
1555
1918
|
if (p.settlement_rail !== void 0) {
|
|
1556
|
-
|
|
1919
|
+
const rail = decodeWireRail(p.settlement_rail);
|
|
1920
|
+
if (!rail)
|
|
1557
1921
|
return null;
|
|
1558
|
-
offer.settlementRail =
|
|
1922
|
+
offer.settlementRail = rail;
|
|
1559
1923
|
}
|
|
1560
1924
|
if (p.service_id !== void 0)
|
|
1561
1925
|
offer.serviceId = p.service_id;
|
|
@@ -1632,6 +1996,21 @@ function decodeChallenge(header) {
|
|
|
1632
1996
|
if (p.sponsor_gas_payment !== void 0) {
|
|
1633
1997
|
offer.sponsorGasPayment = p.sponsor_gas_payment;
|
|
1634
1998
|
}
|
|
1999
|
+
if (p.accepted_rails !== void 0) {
|
|
2000
|
+
const rails = decodeAcceptedRails(p.accepted_rails.split(","));
|
|
2001
|
+
if (!rails)
|
|
2002
|
+
return null;
|
|
2003
|
+
offer.acceptedRails = rails;
|
|
2004
|
+
}
|
|
2005
|
+
if (p.exclusion_protocol_version !== void 0) {
|
|
2006
|
+
const raw = p.exclusion_protocol_version;
|
|
2007
|
+
const v = Number(raw);
|
|
2008
|
+
if (raw.trim() === "" || !Number.isFinite(v))
|
|
2009
|
+
return null;
|
|
2010
|
+
offer.exclusionProtocolVersion = v;
|
|
2011
|
+
}
|
|
2012
|
+
if (p.registry_identity !== void 0)
|
|
2013
|
+
offer.registryIdentity = p.registry_identity;
|
|
1635
2014
|
return offer;
|
|
1636
2015
|
}
|
|
1637
2016
|
function encodeProof(proof) {
|
|
@@ -1675,12 +2054,128 @@ function decodeReceipt(header) {
|
|
|
1675
2054
|
}
|
|
1676
2055
|
|
|
1677
2056
|
// ../sdk/dist/protocol/x402.js
|
|
2057
|
+
import { fromBase64 as fromBase643, toBase64 as toBase642 } from "@mysten/sui/utils";
|
|
2058
|
+
function utf8ToBase64(text2) {
|
|
2059
|
+
return toBase642(new TextEncoder().encode(text2));
|
|
2060
|
+
}
|
|
2061
|
+
function base64ToUtf8(value) {
|
|
2062
|
+
return new TextDecoder().decode(fromBase643(value));
|
|
2063
|
+
}
|
|
2064
|
+
var X402_VERSION = 2;
|
|
1678
2065
|
var X402_SCHEME = "exact";
|
|
2066
|
+
var X402_PAYMENT_FLOW = "upfront";
|
|
2067
|
+
var X402_ERROR_REASON = {
|
|
2068
|
+
MALFORMED_CREDENTIAL: "invalid_payload",
|
|
2069
|
+
UNKNOWN_CHALLENGE: "invalid_payment_requirements",
|
|
2070
|
+
CHALLENGE_TAMPERED: "invalid_payment_requirements",
|
|
2071
|
+
CHALLENGE_EXPIRED: "invalid_payment_requirements",
|
|
2072
|
+
CHALLENGE_ALREADY_USED: "invalid_transaction_state",
|
|
2073
|
+
CHALLENGE_IN_FLIGHT: "invalid_transaction_state",
|
|
2074
|
+
TRANSACTION_INVALID: "invalid_payload",
|
|
2075
|
+
NETWORK_MISMATCH: "invalid_network",
|
|
2076
|
+
SIGNATURE_INVALID: "invalid_payload",
|
|
2077
|
+
INSUFFICIENT_FUNDS: "insufficient_funds",
|
|
2078
|
+
PAYER_MISMATCH: "invalid_payload",
|
|
2079
|
+
SETTLEMENT_REJECTED: "invalid_transaction_state",
|
|
2080
|
+
SETTLEMENT_MISMATCH: "unexpected_settle_error",
|
|
2081
|
+
AMBIGUOUS: "settlement_pending"
|
|
2082
|
+
};
|
|
1679
2083
|
var X402_CHALLENGE_HEADER = "PAYMENT-REQUIRED";
|
|
2084
|
+
var X402_PAYMENT_HEADER = "PAYMENT-SIGNATURE";
|
|
1680
2085
|
var X402_RESPONSE_HEADER = "PAYMENT-RESPONSE";
|
|
1681
2086
|
var X402_RECEIPT_HEADER = "X-PAYMENT-RESPONSE";
|
|
2087
|
+
function toAccept(offer, authority, opts) {
|
|
2088
|
+
const auth = authority ?? defaultAuthorityForRail(offer.settlementRail ?? "direct", offer.packageId);
|
|
2089
|
+
const payFunction = auth.payFunction;
|
|
2090
|
+
const suipay = {
|
|
2091
|
+
challengeId: offer.challengeId,
|
|
2092
|
+
packageId: offer.packageId,
|
|
2093
|
+
module: auth.module,
|
|
2094
|
+
function: payFunction,
|
|
2095
|
+
decimals: offer.decimals,
|
|
2096
|
+
issuedAt: offer.issuedAt,
|
|
2097
|
+
expiresAt: offer.expiresAt,
|
|
2098
|
+
digest: offer.digest
|
|
2099
|
+
};
|
|
2100
|
+
if (offer.netAmount !== void 0)
|
|
2101
|
+
suipay.netAmount = offer.netAmount;
|
|
2102
|
+
if (offer.platformFee !== void 0)
|
|
2103
|
+
suipay.platformFee = offer.platformFee;
|
|
2104
|
+
if (offer.digestVersion !== void 0)
|
|
2105
|
+
suipay.digestVersion = offer.digestVersion;
|
|
2106
|
+
if (offer.settlementRail !== void 0)
|
|
2107
|
+
suipay.settlementRail = offer.settlementRail;
|
|
2108
|
+
if (offer.serviceId !== void 0)
|
|
2109
|
+
suipay.serviceId = offer.serviceId;
|
|
2110
|
+
if (offer.serviceRevision !== void 0)
|
|
2111
|
+
suipay.serviceRevision = offer.serviceRevision;
|
|
2112
|
+
if (offer.targetHash !== void 0)
|
|
2113
|
+
suipay.targetHash = offer.targetHash;
|
|
2114
|
+
if (offer.authorizationEpoch !== void 0) {
|
|
2115
|
+
suipay.authorizationEpoch = offer.authorizationEpoch;
|
|
2116
|
+
}
|
|
2117
|
+
if (offer.method !== void 0)
|
|
2118
|
+
suipay.method = offer.method;
|
|
2119
|
+
if (offer.path !== void 0)
|
|
2120
|
+
suipay.path = offer.path;
|
|
2121
|
+
if (offer.bodyHash !== void 0)
|
|
2122
|
+
suipay.bodyHash = offer.bodyHash;
|
|
2123
|
+
if (offer.contentType !== void 0)
|
|
2124
|
+
suipay.contentType = offer.contentType;
|
|
2125
|
+
if (offer.intent !== void 0)
|
|
2126
|
+
suipay.intent = offer.intent;
|
|
2127
|
+
if (offer.periodMs !== void 0)
|
|
2128
|
+
suipay.periodMs = offer.periodMs;
|
|
2129
|
+
if (offer.periodCount !== void 0)
|
|
2130
|
+
suipay.periodCount = offer.periodCount;
|
|
2131
|
+
if (offer.refundable !== void 0)
|
|
2132
|
+
suipay.refundable = offer.refundable;
|
|
2133
|
+
if (offer.refundWindowMs !== void 0)
|
|
2134
|
+
suipay.refundWindowMs = offer.refundWindowMs;
|
|
2135
|
+
if (offer.decideWindowMs !== void 0)
|
|
2136
|
+
suipay.decideWindowMs = offer.decideWindowMs;
|
|
2137
|
+
if (offer.chainId !== void 0)
|
|
2138
|
+
suipay.chainId = offer.chainId;
|
|
2139
|
+
if (offer.paymentSalt !== void 0)
|
|
2140
|
+
suipay.paymentSalt = offer.paymentSalt;
|
|
2141
|
+
if (offer.refundTo !== void 0)
|
|
2142
|
+
suipay.refundTo = offer.refundTo;
|
|
2143
|
+
if (offer.sponsorGasOwner !== void 0) {
|
|
2144
|
+
suipay.sponsorGasOwner = offer.sponsorGasOwner;
|
|
2145
|
+
}
|
|
2146
|
+
if (offer.sponsorGasBudget !== void 0) {
|
|
2147
|
+
suipay.sponsorGasBudget = offer.sponsorGasBudget;
|
|
2148
|
+
}
|
|
2149
|
+
if (offer.sponsorGasPrice !== void 0) {
|
|
2150
|
+
suipay.sponsorGasPrice = offer.sponsorGasPrice;
|
|
2151
|
+
}
|
|
2152
|
+
if (offer.sponsorGasPayment !== void 0) {
|
|
2153
|
+
suipay.sponsorGasPayment = offer.sponsorGasPayment;
|
|
2154
|
+
}
|
|
2155
|
+
if (offer.acceptedRails !== void 0)
|
|
2156
|
+
suipay.acceptedRails = offer.acceptedRails;
|
|
2157
|
+
if (offer.exclusionProtocolVersion !== void 0) {
|
|
2158
|
+
suipay.exclusionProtocolVersion = offer.exclusionProtocolVersion;
|
|
2159
|
+
}
|
|
2160
|
+
if (offer.registryIdentity !== void 0)
|
|
2161
|
+
suipay.registryIdentity = offer.registryIdentity;
|
|
2162
|
+
return {
|
|
2163
|
+
scheme: X402_SCHEME,
|
|
2164
|
+
network: offer.network,
|
|
2165
|
+
amount: offer.amount,
|
|
2166
|
+
asset: offer.asset,
|
|
2167
|
+
payTo: offer.payTo,
|
|
2168
|
+
maxTimeoutSeconds: Math.max(1, Math.round((offer.expiresAt - offer.issuedAt) / 1e3)),
|
|
2169
|
+
extra: {
|
|
2170
|
+
paymentFlow: X402_PAYMENT_FLOW,
|
|
2171
|
+
...opts?.gasStation ? { gasStation: opts.gasStation } : {},
|
|
2172
|
+
suipay
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
1682
2176
|
function railOf(offer) {
|
|
1683
|
-
|
|
2177
|
+
const rail = offer.settlementRail ?? "direct";
|
|
2178
|
+
return mintLiveRail(rail);
|
|
1684
2179
|
}
|
|
1685
2180
|
function decodeChallenge2(body, opts) {
|
|
1686
2181
|
const b = body;
|
|
@@ -1691,7 +2186,7 @@ function decodeChallenge2(body, opts) {
|
|
|
1691
2186
|
if (a?.scheme !== X402_SCHEME || !a?.extra?.suipay)
|
|
1692
2187
|
continue;
|
|
1693
2188
|
const offer = fromAccept(a, b.resource);
|
|
1694
|
-
if (offer && railOf(offer) === opts.rail)
|
|
2189
|
+
if (offer && railOf(offer) === mintLiveRail(opts.rail))
|
|
1695
2190
|
return offer;
|
|
1696
2191
|
}
|
|
1697
2192
|
return null;
|
|
@@ -1726,13 +2221,14 @@ function fromAccept(accept, resource) {
|
|
|
1726
2221
|
offer.netAmount = s.netAmount;
|
|
1727
2222
|
if (s.platformFee !== void 0)
|
|
1728
2223
|
offer.platformFee = s.platformFee;
|
|
1729
|
-
if (s.digestVersion === 1 || s.digestVersion === 2 || s.digestVersion === 3) {
|
|
2224
|
+
if (s.digestVersion === 1 || s.digestVersion === 2 || s.digestVersion === 3 || s.digestVersion === 4) {
|
|
1730
2225
|
offer.digestVersion = s.digestVersion;
|
|
1731
2226
|
}
|
|
1732
2227
|
if (s.settlementRail !== void 0) {
|
|
1733
|
-
|
|
2228
|
+
const rail = decodeWireRail(s.settlementRail);
|
|
2229
|
+
if (!rail)
|
|
1734
2230
|
return null;
|
|
1735
|
-
offer.settlementRail =
|
|
2231
|
+
offer.settlementRail = rail;
|
|
1736
2232
|
}
|
|
1737
2233
|
if (s.serviceId !== void 0)
|
|
1738
2234
|
offer.serviceId = s.serviceId;
|
|
@@ -1784,11 +2280,71 @@ function fromAccept(accept, resource) {
|
|
|
1784
2280
|
if (s.sponsorGasPayment !== void 0) {
|
|
1785
2281
|
offer.sponsorGasPayment = s.sponsorGasPayment;
|
|
1786
2282
|
}
|
|
2283
|
+
if (s.acceptedRails !== void 0) {
|
|
2284
|
+
if (!Array.isArray(s.acceptedRails))
|
|
2285
|
+
return null;
|
|
2286
|
+
const rails = decodeAcceptedRails(s.acceptedRails);
|
|
2287
|
+
if (!rails)
|
|
2288
|
+
return null;
|
|
2289
|
+
offer.acceptedRails = rails;
|
|
2290
|
+
}
|
|
2291
|
+
if (s.exclusionProtocolVersion !== void 0) {
|
|
2292
|
+
const v = s.exclusionProtocolVersion;
|
|
2293
|
+
if (typeof v !== "number" || !Number.isFinite(v))
|
|
2294
|
+
return null;
|
|
2295
|
+
offer.exclusionProtocolVersion = v;
|
|
2296
|
+
}
|
|
2297
|
+
if (s.registryIdentity !== void 0)
|
|
2298
|
+
offer.registryIdentity = s.registryIdentity;
|
|
1787
2299
|
return offer;
|
|
1788
2300
|
}
|
|
2301
|
+
function encodeCredential(cred) {
|
|
2302
|
+
const payload = {
|
|
2303
|
+
x402Version: X402_VERSION,
|
|
2304
|
+
resource: {
|
|
2305
|
+
url: cred.offer.resource,
|
|
2306
|
+
description: cred.offer.description,
|
|
2307
|
+
mimeType: cred.offer.mimeType
|
|
2308
|
+
},
|
|
2309
|
+
accepted: toAccept(cred.offer),
|
|
2310
|
+
payload: {
|
|
2311
|
+
challengeId: cred.challengeId,
|
|
2312
|
+
payer: cred.payer ?? void 0,
|
|
2313
|
+
transaction: cred.txBytes,
|
|
2314
|
+
signature: cred.signature
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
return utf8ToBase64(JSON.stringify(payload));
|
|
2318
|
+
}
|
|
2319
|
+
var X402_ERROR_REASONS = new Set(Object.values(X402_ERROR_REASON));
|
|
2320
|
+
function decodeSettlementResponse(header) {
|
|
2321
|
+
const receipt = decodeReceipt2(header);
|
|
2322
|
+
if (receipt)
|
|
2323
|
+
return receipt;
|
|
2324
|
+
try {
|
|
2325
|
+
const r = JSON.parse(base64ToUtf8(header));
|
|
2326
|
+
if (!r || typeof r !== "object" || r.success !== false)
|
|
2327
|
+
return null;
|
|
2328
|
+
if (typeof r.errorReason !== "string" || !X402_ERROR_REASONS.has(r.errorReason))
|
|
2329
|
+
return null;
|
|
2330
|
+
if (typeof r.network !== "string" || typeof r.transaction !== "string")
|
|
2331
|
+
return null;
|
|
2332
|
+
const failure = {
|
|
2333
|
+
success: false,
|
|
2334
|
+
errorReason: r.errorReason,
|
|
2335
|
+
transaction: r.transaction,
|
|
2336
|
+
network: r.network
|
|
2337
|
+
};
|
|
2338
|
+
if (typeof r.payer === "string")
|
|
2339
|
+
failure.payer = r.payer;
|
|
2340
|
+
return failure;
|
|
2341
|
+
} catch {
|
|
2342
|
+
return null;
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
1789
2345
|
function decodeReceipt2(header) {
|
|
1790
2346
|
try {
|
|
1791
|
-
const r = JSON.parse(
|
|
2347
|
+
const r = JSON.parse(base64ToUtf8(header));
|
|
1792
2348
|
if (!r?.success || !r.transaction)
|
|
1793
2349
|
return null;
|
|
1794
2350
|
const receipt = {
|
|
@@ -1813,22 +2369,22 @@ function decodeReceipt2(header) {
|
|
|
1813
2369
|
}
|
|
1814
2370
|
|
|
1815
2371
|
// ../sdk/dist/protocol/carrier.js
|
|
1816
|
-
async function readChallenge(res, dialect) {
|
|
2372
|
+
async function readChallenge(res, dialect, opts) {
|
|
1817
2373
|
if (dialect === "mpp") {
|
|
1818
2374
|
const header = res.headers.get(MPP_CHALLENGE_HEADER);
|
|
1819
|
-
return header ? decodeChallenge(header) : null;
|
|
2375
|
+
return header ? decodeChallenge(header, opts) : null;
|
|
1820
2376
|
}
|
|
1821
2377
|
const headerVal = res.headers.get(X402_CHALLENGE_HEADER);
|
|
1822
2378
|
if (headerVal) {
|
|
1823
2379
|
try {
|
|
1824
|
-
const parsed = JSON.parse(
|
|
1825
|
-
const offer = decodeChallenge2(parsed);
|
|
2380
|
+
const parsed = JSON.parse(new TextDecoder().decode(fromBase644(headerVal)));
|
|
2381
|
+
const offer = decodeChallenge2(parsed, opts);
|
|
1826
2382
|
if (offer)
|
|
1827
2383
|
return offer;
|
|
1828
2384
|
} catch {
|
|
1829
2385
|
}
|
|
1830
2386
|
}
|
|
1831
|
-
return decodeChallenge2(await readBody(res));
|
|
2387
|
+
return decodeChallenge2(await readBody(res), opts);
|
|
1832
2388
|
}
|
|
1833
2389
|
function readReceipt(res, dialect) {
|
|
1834
2390
|
const header = dialect === "mpp" ? res.headers.get(MPP_RECEIPT_HEADER) : res.headers.get(X402_RESPONSE_HEADER) ?? res.headers.get(X402_RECEIPT_HEADER);
|
|
@@ -1871,7 +2427,7 @@ async function readBody(res) {
|
|
|
1871
2427
|
configurable: false
|
|
1872
2428
|
});
|
|
1873
2429
|
if (bytes.byteLength <= MAX_INLINE_BINARY_BYTES) {
|
|
1874
|
-
binary.base64 =
|
|
2430
|
+
binary.base64 = toBase643(bytes);
|
|
1875
2431
|
}
|
|
1876
2432
|
return binary;
|
|
1877
2433
|
}
|
|
@@ -1923,10 +2479,10 @@ function requestBindingViolation(offer, request) {
|
|
|
1923
2479
|
if (offer.intent === "subscription" && offer.bodyHash === void 0 && offer.contentType === void 0) {
|
|
1924
2480
|
return null;
|
|
1925
2481
|
}
|
|
1926
|
-
if (offer.digestVersion
|
|
2482
|
+
if ((offer.digestVersion ?? 1) < 3) {
|
|
1927
2483
|
return {
|
|
1928
2484
|
code: "OFFER_NOT_BODY_BOUND",
|
|
1929
|
-
reason: "a paid POST requires an offer digest
|
|
2485
|
+
reason: "a paid POST requires an offer digest that signs the request body"
|
|
1930
2486
|
};
|
|
1931
2487
|
}
|
|
1932
2488
|
if (offer.bodyHash !== request.bodyHash) {
|
|
@@ -2157,24 +2713,74 @@ var PAY_OPERATION_ERROR_CODES = {
|
|
|
2157
2713
|
/** The payment ledger reserve CAS lost (another attempt owns this id). */
|
|
2158
2714
|
PAYMENT_ATTEMPT_IN_FLIGHT: "PAYMENT_ATTEMPT_IN_FLIGHT",
|
|
2159
2715
|
/** The payment ledger could not persist the reservation. */
|
|
2160
|
-
PAYMENT_LEDGER_FAILED: "PAYMENT_LEDGER_FAILED"
|
|
2716
|
+
PAYMENT_LEDGER_FAILED: "PAYMENT_LEDGER_FAILED",
|
|
2717
|
+
/** The offer's HMAC digest failed verification — tampered or wrong secret. */
|
|
2718
|
+
DIGEST_INVALID: "DIGEST_INVALID",
|
|
2719
|
+
/** The offer's expiry is in the past (or issuedAt/expiresAt are incoherent). */
|
|
2720
|
+
OFFER_EXPIRED: "OFFER_EXPIRED",
|
|
2721
|
+
/**
|
|
2722
|
+
* The offer's pinned sponsor gas fields are incomplete or inconsistent
|
|
2723
|
+
* (partial pin, non-numeric budget/price, empty payment) — reject before
|
|
2724
|
+
* building a tx against a gas pin that cannot be honored.
|
|
2725
|
+
*/
|
|
2726
|
+
SPONSOR_GAS_INVALID: "SPONSOR_GAS_INVALID",
|
|
2727
|
+
/**
|
|
2728
|
+
* The recipient / amount / payment_id embedded in the built transaction does
|
|
2729
|
+
* not match the offer — the pre-sign verify-by-rebuild backstop. Nothing may
|
|
2730
|
+
* be signed once this fires.
|
|
2731
|
+
*/
|
|
2732
|
+
TX_OFFER_MISMATCH: "TX_OFFER_MISMATCH",
|
|
2733
|
+
/**
|
|
2734
|
+
* Open-grant x402 is refused. This is PERMANENT and architectural: an open
|
|
2735
|
+
* grant does not pre-bind a recipient, so a buyer cannot verify the recipient
|
|
2736
|
+
* before signing (ADR-0002 / support matrix `open × x402` = unsupported). Do
|
|
2737
|
+
* not conflate with a rail that is merely not built yet.
|
|
2738
|
+
*/
|
|
2739
|
+
UNSUPPORTED_SCOPE: "UNSUPPORTED_SCOPE",
|
|
2740
|
+
/**
|
|
2741
|
+
* A rail/dialect combination that is SUPPORTED by design but not yet
|
|
2742
|
+
* implemented in this build — currently scoped-grant spend_account x402
|
|
2743
|
+
* (the buyer credential-submit flow). Distinct from UNSUPPORTED_SCOPE so an
|
|
2744
|
+
* integrator reads "coming, not yet wired" rather than "permanently refused".
|
|
2745
|
+
* See docs/decisions/ADR-0047-buyer-side-x402-two-paths-one-entrypoint.md.
|
|
2746
|
+
*/
|
|
2747
|
+
RAIL_NOT_YET_IMPLEMENTED: "RAIL_NOT_YET_IMPLEMENTED",
|
|
2748
|
+
/**
|
|
2749
|
+
* Grant rail is configured but required verify-kind RPC context (getObjectJson)
|
|
2750
|
+
* is missing — distinct from RAIL_NOT_YET_IMPLEMENTED, which means the rail
|
|
2751
|
+
* itself is not wired.
|
|
2752
|
+
*/
|
|
2753
|
+
GRANT_VERIFY_MISCONFIGURED: "GRANT_VERIFY_MISCONFIGURED",
|
|
2754
|
+
/** The request URL or method is invalid and cannot be normalized. */
|
|
2755
|
+
REQUEST_INVALID: "REQUEST_INVALID",
|
|
2756
|
+
/** The 402 response body stream failed while reading the challenge. */
|
|
2757
|
+
CHALLENGE_READ_ERROR: "CHALLENGE_READ_ERROR",
|
|
2758
|
+
/**
|
|
2759
|
+
* Pre-sign transaction verification could not reach a verdict due to an RPC
|
|
2760
|
+
* or chain read outage (retryable, not tampering).
|
|
2761
|
+
*/
|
|
2762
|
+
VERIFICATION_UNAVAILABLE: "VERIFICATION_UNAVAILABLE"
|
|
2161
2763
|
};
|
|
2162
2764
|
var CODE_SET = new Set(Object.values(PAY_OPERATION_ERROR_CODES));
|
|
2163
2765
|
|
|
2164
2766
|
// ../sdk/dist/pay-operation/operation.js
|
|
2165
2767
|
async function resolvePayment(input, deps) {
|
|
2166
2768
|
const { url, gatewayUrl, packageId, network, authority, assertLive } = input;
|
|
2167
|
-
const { fetchImpl = fetch, preferredDialect = "mpp", validateOffer, buildPayKind } = deps;
|
|
2769
|
+
const { fetchImpl = fetch, preferredDialect = "mpp", validateOffer, buildPayKind, resolveExclusionRegistry: resolveExclusionRegistry2 } = deps;
|
|
2168
2770
|
const request = input.request ?? normalizePaidHttpRequest({ url, method: "GET" });
|
|
2169
|
-
const
|
|
2170
|
-
if (
|
|
2171
|
-
return failed(PAY_OPERATION_ERROR_CODES.ORIGIN_MISMATCH,
|
|
2771
|
+
const originViolation2 = validateOrigin(url, gatewayUrl);
|
|
2772
|
+
if (originViolation2) {
|
|
2773
|
+
return failed(PAY_OPERATION_ERROR_CODES.ORIGIN_MISMATCH, originViolation2);
|
|
2172
2774
|
}
|
|
2173
2775
|
let response;
|
|
2174
|
-
|
|
2175
|
-
response =
|
|
2176
|
-
}
|
|
2177
|
-
|
|
2776
|
+
if (deps.prefetchedResponse) {
|
|
2777
|
+
response = deps.prefetchedResponse;
|
|
2778
|
+
} else {
|
|
2779
|
+
try {
|
|
2780
|
+
response = await fetchImpl(url, paidRequestInit(request));
|
|
2781
|
+
} catch (e) {
|
|
2782
|
+
return failed(PAY_OPERATION_ERROR_CODES.RESOURCE_UNAVAILABLE, `fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
2783
|
+
}
|
|
2178
2784
|
}
|
|
2179
2785
|
if (response.status !== 402) {
|
|
2180
2786
|
if (response.status >= 200 && response.status < 300) {
|
|
@@ -2203,7 +2809,7 @@ async function resolvePayment(input, deps) {
|
|
|
2203
2809
|
return failed(PAY_OPERATION_ERROR_CODES.REQUEST_BINDING_VIOLATED, bindingViolation.reason, challengeId);
|
|
2204
2810
|
}
|
|
2205
2811
|
if (!isDelegateRail(offer.settlementRail)) {
|
|
2206
|
-
return failed(PAY_OPERATION_ERROR_CODES.UNSUPPORTED_RAIL, `rail ${offer.settlementRail ?? "none"} is not a
|
|
2812
|
+
return failed(PAY_OPERATION_ERROR_CODES.UNSUPPORTED_RAIL, `rail ${offer.settlementRail ?? "none"} is not a delegate rail`, challengeId);
|
|
2207
2813
|
}
|
|
2208
2814
|
let packageMatches;
|
|
2209
2815
|
try {
|
|
@@ -2248,6 +2854,41 @@ async function resolvePayment(input, deps) {
|
|
|
2248
2854
|
recipient: resolved.authority.recipient,
|
|
2249
2855
|
accessMode: resolved.authority.accessMode
|
|
2250
2856
|
};
|
|
2857
|
+
let guarded;
|
|
2858
|
+
if (offerRequiresGuardedSettle(offer)) {
|
|
2859
|
+
const identity = parseRegistryIdentity(offer.registryIdentity);
|
|
2860
|
+
if (!identity) {
|
|
2861
|
+
return failed(PAY_OPERATION_ERROR_CODES.BUILD_FAILED, "guarded offer carries no valid registry identity", challengeId);
|
|
2862
|
+
}
|
|
2863
|
+
if (normalizeSuiAddress3(identity.packageId) !== normalizeSuiAddress3(offer.packageId)) {
|
|
2864
|
+
return failed(PAY_OPERATION_ERROR_CODES.BUILD_FAILED, `guarded registry identity package ${identity.packageId} does not match offer package ${offer.packageId}`, challengeId);
|
|
2865
|
+
}
|
|
2866
|
+
if (!resolveExclusionRegistry2) {
|
|
2867
|
+
return failed(PAY_OPERATION_ERROR_CODES.BUILD_FAILED, "a guarded delegate offer requires an exclusion registry resolver", challengeId);
|
|
2868
|
+
}
|
|
2869
|
+
let registryResolution;
|
|
2870
|
+
try {
|
|
2871
|
+
registryResolution = await resolveExclusionRegistry2({
|
|
2872
|
+
directoryId: identity.directoryId,
|
|
2873
|
+
packageId: offer.packageId,
|
|
2874
|
+
recipient: resolved.authority.recipient
|
|
2875
|
+
});
|
|
2876
|
+
} catch (e) {
|
|
2877
|
+
return failed(PAY_OPERATION_ERROR_CODES.BUILD_FAILED, e instanceof Error ? e.message : String(e), challengeId);
|
|
2878
|
+
}
|
|
2879
|
+
kindInput.exclusionDirectoryId = identity.directoryId;
|
|
2880
|
+
kindInput.recipientRegistryId = registryResolution.recipientRegistryId;
|
|
2881
|
+
kindInput.expiresAtMs = offer.expiresAt;
|
|
2882
|
+
kindInput.exclusionDirectoryInitialSharedVersion = registryResolution.directoryInitialSharedVersion;
|
|
2883
|
+
kindInput.recipientRegistryInitialSharedVersion = registryResolution.registryInitialSharedVersion;
|
|
2884
|
+
guarded = {
|
|
2885
|
+
exclusionDirectoryId: identity.directoryId,
|
|
2886
|
+
recipientRegistryId: registryResolution.recipientRegistryId,
|
|
2887
|
+
expiresAtMs: offer.expiresAt,
|
|
2888
|
+
exclusionDirectoryInitialSharedVersion: registryResolution.directoryInitialSharedVersion,
|
|
2889
|
+
recipientRegistryInitialSharedVersion: registryResolution.registryInitialSharedVersion
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2251
2892
|
let kindBytes;
|
|
2252
2893
|
try {
|
|
2253
2894
|
const result = await buildPayKind(kindInput);
|
|
@@ -2264,7 +2905,8 @@ async function resolvePayment(input, deps) {
|
|
|
2264
2905
|
termsHash: tHash,
|
|
2265
2906
|
kindBytes,
|
|
2266
2907
|
paymentId: challengeId,
|
|
2267
|
-
request
|
|
2908
|
+
request,
|
|
2909
|
+
...guarded ? { guarded } : {}
|
|
2268
2910
|
};
|
|
2269
2911
|
}
|
|
2270
2912
|
function failed(code, detail, paymentId) {
|
|
@@ -2314,7 +2956,11 @@ function resolveAuthority(authority, offer) {
|
|
|
2314
2956
|
};
|
|
2315
2957
|
}
|
|
2316
2958
|
if (!offer.targetHash) {
|
|
2317
|
-
return {
|
|
2959
|
+
return {
|
|
2960
|
+
ok: false,
|
|
2961
|
+
code: PAY_OPERATION_ERROR_CODES.POLICY_REJECTED,
|
|
2962
|
+
reason: "offer has no targetHash for scoped resolution"
|
|
2963
|
+
};
|
|
2318
2964
|
}
|
|
2319
2965
|
const result = resolveGrantTarget(authority, offer.targetHash);
|
|
2320
2966
|
if (!result.ok) {
|
|
@@ -2337,8 +2983,8 @@ function resolveAuthority(authority, offer) {
|
|
|
2337
2983
|
};
|
|
2338
2984
|
}
|
|
2339
2985
|
function validateAmount(offer, resolved) {
|
|
2340
|
-
const offerCoinType =
|
|
2341
|
-
const grantCoinType =
|
|
2986
|
+
const offerCoinType = normalizeStructTag5(offer.asset);
|
|
2987
|
+
const grantCoinType = normalizeStructTag5(resolved.coinType);
|
|
2342
2988
|
if (offerCoinType !== grantCoinType) {
|
|
2343
2989
|
return {
|
|
2344
2990
|
code: PAY_OPERATION_ERROR_CODES.COIN_TYPE_MISMATCH,
|
|
@@ -2383,9 +3029,9 @@ function validateAmount(offer, resolved) {
|
|
|
2383
3029
|
// ../sdk/dist/sponsor/personal-message.js
|
|
2384
3030
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
2385
3031
|
import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
|
|
2386
|
-
import { bcs as
|
|
3032
|
+
import { bcs as bcs3 } from "@mysten/sui/bcs";
|
|
2387
3033
|
import { isValidPersonalMessageSignature, verifyPersonalMessageSignature } from "@mysten/sui/verify";
|
|
2388
|
-
import { fromBase64 as
|
|
3034
|
+
import { fromBase64 as fromBase645, normalizeSuiAddress as normalizeSuiAddress4, toBase64 as toBase644 } from "@mysten/sui/utils";
|
|
2389
3035
|
import { Transaction as Transaction3 } from "@mysten/sui/transactions";
|
|
2390
3036
|
var SPONSOR_PERSONAL_MESSAGE_DOMAIN = "suipay:sponsor-personal-message:v1";
|
|
2391
3037
|
function encodeSponsorPersonalMessagePayload(payload) {
|
|
@@ -2406,20 +3052,20 @@ function encodeSponsorPersonalMessagePayload(payload) {
|
|
|
2406
3052
|
throw new Error("network is required");
|
|
2407
3053
|
const parts = [];
|
|
2408
3054
|
const push = (u) => parts.push(u);
|
|
2409
|
-
push(
|
|
2410
|
-
push(
|
|
2411
|
-
push(
|
|
2412
|
-
push(
|
|
2413
|
-
push(
|
|
2414
|
-
push(
|
|
2415
|
-
push(
|
|
2416
|
-
push(
|
|
2417
|
-
push(
|
|
2418
|
-
push(
|
|
3055
|
+
push(bcs3.vector(bcs3.u8()).serialize([...Buffer.from(payload.domain, "utf8")]).toBytes());
|
|
3056
|
+
push(bcs3.vector(bcs3.u8()).serialize([...Buffer.from(payload.nonce, "utf8")]).toBytes());
|
|
3057
|
+
push(bcs3.u64().serialize(BigInt(payload.expiryMs)).toBytes());
|
|
3058
|
+
push(bcs3.Address.serialize(normalizeSuiAddress4(payload.delegate)).toBytes());
|
|
3059
|
+
push(bcs3.Address.serialize(normalizeSuiAddress4(payload.poolId)).toBytes());
|
|
3060
|
+
push(bcs3.Address.serialize(normalizeSuiAddress4(payload.grantId)).toBytes());
|
|
3061
|
+
push(bcs3.vector(bcs3.u8()).serialize([...Buffer.from(payload.network, "utf8")]).toBytes());
|
|
3062
|
+
push(bcs3.Address.serialize(normalizeSuiAddress4(payload.packageId)).toBytes());
|
|
3063
|
+
push(bcs3.vector(bcs3.u8()).serialize([...Buffer.from(payload.requestId, "utf8")]).toBytes());
|
|
3064
|
+
push(bcs3.vector(bcs3.u8()).serialize([...payload.txKindHash]).toBytes());
|
|
2419
3065
|
return Buffer.concat(parts.map((p) => Buffer.from(p)));
|
|
2420
3066
|
}
|
|
2421
3067
|
function hashTransactionKindBytes(kindBytes) {
|
|
2422
|
-
const raw = typeof kindBytes === "string" ?
|
|
3068
|
+
const raw = typeof kindBytes === "string" ? fromBase645(kindBytes) : kindBytes;
|
|
2423
3069
|
return sha2563(raw);
|
|
2424
3070
|
}
|
|
2425
3071
|
function buildSponsorPersonalMessagePayload(input) {
|
|
@@ -2444,7 +3090,7 @@ async function signSponsorPersonalMessagePayload(input) {
|
|
|
2444
3090
|
// ../sdk/dist/protocol/signed-request.js
|
|
2445
3091
|
import { sha256 as sha2564 } from "@noble/hashes/sha2.js";
|
|
2446
3092
|
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
|
|
2447
|
-
import { bcs as
|
|
3093
|
+
import { bcs as bcs4 } from "@mysten/sui/bcs";
|
|
2448
3094
|
import { messageWithIntent } from "@mysten/sui/cryptography";
|
|
2449
3095
|
import { isValidPersonalMessageSignature as isValidPersonalMessageSignature2, verifyPersonalMessageSignature as verifyPersonalMessageSignature2 } from "@mysten/sui/verify";
|
|
2450
3096
|
import { normalizeSuiAddress as normalizeSuiAddress5 } from "@mysten/sui/utils";
|
|
@@ -2478,8 +3124,8 @@ function encodeBootstrapRequest(payload) {
|
|
|
2478
3124
|
if (payload.domain !== BOOTSTRAP_REQUEST_DOMAIN) {
|
|
2479
3125
|
throw new Error(`domain must be ${BOOTSTRAP_REQUEST_DOMAIN}`);
|
|
2480
3126
|
}
|
|
2481
|
-
const utf8Vec = (s) =>
|
|
2482
|
-
const addr = (a) =>
|
|
3127
|
+
const utf8Vec = (s) => bcs4.vector(bcs4.u8()).serialize([...Buffer.from(s, "utf8")]).toBytes();
|
|
3128
|
+
const addr = (a) => bcs4.Address.serialize(normalizeSuiAddress5(a)).toBytes();
|
|
2483
3129
|
const parts = [
|
|
2484
3130
|
utf8Vec(payload.domain),
|
|
2485
3131
|
utf8Vec(payload.network),
|
|
@@ -2488,8 +3134,8 @@ function encodeBootstrapRequest(payload) {
|
|
|
2488
3134
|
utf8Vec(payload.method),
|
|
2489
3135
|
utf8Vec(payload.path),
|
|
2490
3136
|
utf8Vec(payload.query),
|
|
2491
|
-
|
|
2492
|
-
|
|
3137
|
+
bcs4.vector(bcs4.u8()).serialize([...hexToBytes32(payload.bodyHash)]).toBytes(),
|
|
3138
|
+
bcs4.u64().serialize(BigInt(payload.timestampMs)).toBytes(),
|
|
2493
3139
|
utf8Vec(payload.nonce)
|
|
2494
3140
|
];
|
|
2495
3141
|
return Buffer.concat(parts.map((p) => Buffer.from(p)));
|
|
@@ -2498,8 +3144,8 @@ function encodeSignedRequest(payload) {
|
|
|
2498
3144
|
if (payload.domain !== SIGNED_REQUEST_DOMAIN) {
|
|
2499
3145
|
throw new Error(`domain must be ${SIGNED_REQUEST_DOMAIN}`);
|
|
2500
3146
|
}
|
|
2501
|
-
const utf8Vec = (s) =>
|
|
2502
|
-
const addr = (a) =>
|
|
3147
|
+
const utf8Vec = (s) => bcs4.vector(bcs4.u8()).serialize([...Buffer.from(s, "utf8")]).toBytes();
|
|
3148
|
+
const addr = (a) => bcs4.Address.serialize(normalizeSuiAddress5(a)).toBytes();
|
|
2503
3149
|
const parts = [
|
|
2504
3150
|
utf8Vec(payload.domain),
|
|
2505
3151
|
utf8Vec(payload.network),
|
|
@@ -2509,8 +3155,8 @@ function encodeSignedRequest(payload) {
|
|
|
2509
3155
|
utf8Vec(payload.method),
|
|
2510
3156
|
utf8Vec(payload.path),
|
|
2511
3157
|
utf8Vec(payload.query),
|
|
2512
|
-
|
|
2513
|
-
|
|
3158
|
+
bcs4.vector(bcs4.u8()).serialize([...hexToBytes32(payload.bodyHash)]).toBytes(),
|
|
3159
|
+
bcs4.u64().serialize(BigInt(payload.timestampMs)).toBytes(),
|
|
2514
3160
|
utf8Vec(payload.nonce)
|
|
2515
3161
|
];
|
|
2516
3162
|
return Buffer.concat(parts.map((p) => Buffer.from(p)));
|
|
@@ -2869,7 +3515,7 @@ async function readPublicSessionState(getObjectJson, grantObjectId) {
|
|
|
2869
3515
|
const g = json;
|
|
2870
3516
|
return {
|
|
2871
3517
|
grantObjectId,
|
|
2872
|
-
// Field names are the Move struct's
|
|
3518
|
+
// Field names are the Move struct's; three were renamed:
|
|
2873
3519
|
// pool_id -> account_id, session_cap -> total_budget, session_spent ->
|
|
2874
3520
|
// total_spent. Reading the old names yielded undefined, which the grant
|
|
2875
3521
|
// verifier then read as "this grant names no pool" and refused every
|
|
@@ -2929,6 +3575,9 @@ var bootstrapPayerGrants = bootstrapPayerSessions;
|
|
|
2929
3575
|
// ../sdk/dist/delegated-signer/payer.js
|
|
2930
3576
|
var FINALIZATION_ATTEMPTS = 20;
|
|
2931
3577
|
var FINALIZATION_DELAY_MS = 250;
|
|
3578
|
+
var X402_EXPIRATION_EPOCHS = 32n;
|
|
3579
|
+
var X402_PRESENT_ATTEMPTS = 10;
|
|
3580
|
+
var X402_PRESENT_DELAY_MS = 1e3;
|
|
2932
3581
|
function delay(ms) {
|
|
2933
3582
|
return new Promise((resolve) => {
|
|
2934
3583
|
setTimeout(resolve, ms);
|
|
@@ -2996,7 +3645,16 @@ var DELEGATED_PAY_ERROR_CODES = {
|
|
|
2996
3645
|
* AGENT's own ceiling, and it is the check that does not need the gateway's
|
|
2997
3646
|
* secret to be meaningful.
|
|
2998
3647
|
*/
|
|
2999
|
-
SPEND_INTENT_VIOLATED: "SPEND_INTENT_VIOLATED"
|
|
3648
|
+
SPEND_INTENT_VIOLATED: "SPEND_INTENT_VIOLATED",
|
|
3649
|
+
/**
|
|
3650
|
+
* x402 was asked for and cannot be paid from the spend account: the 402
|
|
3651
|
+
* carried no x402 offer (the gateway strips it when it has no gas lease), a
|
|
3652
|
+
* sponsor gas pin is missing or unreadable, or no current epoch could be read
|
|
3653
|
+
* to bound the transaction. Nothing was reserved or signed.
|
|
3654
|
+
*/
|
|
3655
|
+
X402_UNAVAILABLE: "X402_UNAVAILABLE",
|
|
3656
|
+
/** The grant's scope cannot pay in the requested dialect (open-recipient over x402). */
|
|
3657
|
+
UNSUPPORTED_SCOPE: "UNSUPPORTED_SCOPE"
|
|
3000
3658
|
};
|
|
3001
3659
|
var DELEGATED_PAY_ERROR_CODE_SET = new Set(Object.values(DELEGATED_PAY_ERROR_CODES));
|
|
3002
3660
|
function isDelegatedPayErrorCode(value) {
|
|
@@ -3033,7 +3691,9 @@ var PUBLIC_DETAIL = {
|
|
|
3033
3691
|
FINALITY_UNAVAILABLE: "no finality reader configured; refusing to sign a pay whose settlement cannot be confirmed",
|
|
3034
3692
|
PREPARED_OFFER_DIVERGED: "settlement offer does not match the terms verified before signing",
|
|
3035
3693
|
PREPARED_VERIFY_UNAVAILABLE: "the pre-sign verification could not be completed; no signature was produced",
|
|
3036
|
-
SPEND_INTENT_VIOLATED: "offer does not match what this agent said it was buying"
|
|
3694
|
+
SPEND_INTENT_VIOLATED: "offer does not match what this agent said it was buying",
|
|
3695
|
+
X402_UNAVAILABLE: "offer cannot be paid over x402 from the spend account (no x402 offer, sponsor gas pin, or current epoch)",
|
|
3696
|
+
UNSUPPORTED_SCOPE: "open-recipient grants cannot pay over x402"
|
|
3037
3697
|
};
|
|
3038
3698
|
function classifyNon402Status(status) {
|
|
3039
3699
|
if (status >= 200 && status < 300)
|
|
@@ -3138,6 +3798,13 @@ function mapPayOpErrorToDelegated(code, detail) {
|
|
|
3138
3798
|
return { code: "RESOURCE_REJECTED", detail: PUBLIC_DETAIL.RESOURCE_REJECTED };
|
|
3139
3799
|
case PAY_OPERATION_ERROR_CODES.RESOURCE_UNAVAILABLE:
|
|
3140
3800
|
return { code: "RESOURCE_UNAVAILABLE", detail: PUBLIC_DETAIL.RESOURCE_UNAVAILABLE };
|
|
3801
|
+
case PAY_OPERATION_ERROR_CODES.DIGEST_INVALID:
|
|
3802
|
+
return { code: "CHALLENGE_TAMPERED", detail: PUBLIC_DETAIL.CHALLENGE_TAMPERED };
|
|
3803
|
+
case PAY_OPERATION_ERROR_CODES.OFFER_EXPIRED:
|
|
3804
|
+
return { code: "CHALLENGE_EXPIRED", detail: PUBLIC_DETAIL.CHALLENGE_EXPIRED };
|
|
3805
|
+
// SPONSOR_GAS_INVALID and TX_OFFER_MISMATCH fall through to POLICY_REJECTED
|
|
3806
|
+
// for now; the caller that surfaces them (the tx-build/sign path) picks
|
|
3807
|
+
// their final outward code when it lands.
|
|
3141
3808
|
// REQUEST_BINDING_VIOLATED, UNSUPPORTED_RAIL, PACKAGE_MISMATCH,
|
|
3142
3809
|
// NETWORK_MISMATCH, COIN_TYPE_MISMATCH, OVERPAY_REJECTED, UNDERPAY_REJECTED,
|
|
3143
3810
|
// ORIGIN_MISMATCH, and any unknown code all fall through to POLICY_REJECTED.
|
|
@@ -3145,7 +3812,7 @@ function mapPayOpErrorToDelegated(code, detail) {
|
|
|
3145
3812
|
return { code: "POLICY_REJECTED", detail: PUBLIC_DETAIL.POLICY_REJECTED };
|
|
3146
3813
|
}
|
|
3147
3814
|
}
|
|
3148
|
-
function
|
|
3815
|
+
function sameAddress2(a, b) {
|
|
3149
3816
|
try {
|
|
3150
3817
|
return normalizeSuiAddress6(a) === normalizeSuiAddress6(b);
|
|
3151
3818
|
} catch {
|
|
@@ -3161,7 +3828,7 @@ function atomicAmount(value) {
|
|
|
3161
3828
|
}
|
|
3162
3829
|
function sameStructTag(a, b) {
|
|
3163
3830
|
try {
|
|
3164
|
-
return
|
|
3831
|
+
return normalizeStructTag6(a) === normalizeStructTag6(b);
|
|
3165
3832
|
} catch {
|
|
3166
3833
|
return false;
|
|
3167
3834
|
}
|
|
@@ -3220,7 +3887,7 @@ async function resolveSigner(input) {
|
|
|
3220
3887
|
return {
|
|
3221
3888
|
address: kp.getPublicKey().toSuiAddress(),
|
|
3222
3889
|
async signTransaction(txBytes) {
|
|
3223
|
-
const { signature } = await kp.signTransaction(
|
|
3890
|
+
const { signature } = await kp.signTransaction(fromBase646(txBytes));
|
|
3224
3891
|
return signature;
|
|
3225
3892
|
},
|
|
3226
3893
|
async signPersonalMessage(message) {
|
|
@@ -3287,6 +3954,7 @@ async function buildSpendAccountPayKind(input) {
|
|
|
3287
3954
|
throw new Error("grantInitialSharedVersion is required");
|
|
3288
3955
|
}
|
|
3289
3956
|
const hash = targetHashBytes(input.targetHash);
|
|
3957
|
+
const guarded = resolveGuardedSettleOfflineInputs(input);
|
|
3290
3958
|
const target = payMoveCallTarget(input.packageId, "scoped");
|
|
3291
3959
|
const clockVersion = input.clockInitialSharedVersion?.trim() || SUI_CLOCK_INITIAL_SHARED_VERSION;
|
|
3292
3960
|
const tx = new Transaction4();
|
|
@@ -3295,6 +3963,16 @@ async function buildSpendAccountPayKind(input) {
|
|
|
3295
3963
|
target,
|
|
3296
3964
|
typeArguments: [input.coinType],
|
|
3297
3965
|
arguments: [
|
|
3966
|
+
tx.sharedObjectRef({
|
|
3967
|
+
objectId: guarded.exclusionDirectoryId,
|
|
3968
|
+
initialSharedVersion: guarded.exclusionDirectoryInitialSharedVersion,
|
|
3969
|
+
mutable: false
|
|
3970
|
+
}),
|
|
3971
|
+
tx.sharedObjectRef({
|
|
3972
|
+
objectId: guarded.recipientRegistryId,
|
|
3973
|
+
initialSharedVersion: guarded.recipientRegistryInitialSharedVersion,
|
|
3974
|
+
mutable: true
|
|
3975
|
+
}),
|
|
3298
3976
|
tx.sharedObjectRef({
|
|
3299
3977
|
objectId: input.poolObjectId,
|
|
3300
3978
|
initialSharedVersion: String(input.poolInitialSharedVersion),
|
|
@@ -3310,6 +3988,7 @@ async function buildSpendAccountPayKind(input) {
|
|
|
3310
3988
|
tx.pure.u64(BigInt(input.amount)),
|
|
3311
3989
|
tx.pure.vector("u8", Array.from(input.paymentIdHash)),
|
|
3312
3990
|
tx.pure.vector("u8", Array.from(input.termsHash)),
|
|
3991
|
+
tx.pure.u64(BigInt(guarded.expiresAtMs)),
|
|
3313
3992
|
tx.sharedObjectRef({
|
|
3314
3993
|
objectId: SUI_CLOCK2,
|
|
3315
3994
|
initialSharedVersion: clockVersion,
|
|
@@ -3318,7 +3997,7 @@ async function buildSpendAccountPayKind(input) {
|
|
|
3318
3997
|
]
|
|
3319
3998
|
});
|
|
3320
3999
|
const bytes = await tx.build({ onlyTransactionKind: true });
|
|
3321
|
-
return { kindBytes:
|
|
4000
|
+
return { kindBytes: toBase645(bytes) };
|
|
3322
4001
|
}
|
|
3323
4002
|
async function buildSpendAccountSettleOpenRecipientKind(input) {
|
|
3324
4003
|
if (!input.termsHash.length)
|
|
@@ -3333,9 +4012,10 @@ async function buildSpendAccountSettleOpenRecipientKind(input) {
|
|
|
3333
4012
|
throw new Error("grantInitialSharedVersion is required");
|
|
3334
4013
|
}
|
|
3335
4014
|
if (!input.recipient || /^0x0+$/i.test(input.recipient)) {
|
|
3336
|
-
throw new Error("
|
|
4015
|
+
throw new Error("settle_any_recipient recipient must be a non-zero address");
|
|
3337
4016
|
}
|
|
3338
4017
|
const hash = targetHashBytes(input.targetHash);
|
|
4018
|
+
const guarded = resolveGuardedSettleOfflineInputs(input);
|
|
3339
4019
|
const target = payMoveCallTarget(input.packageId, "open");
|
|
3340
4020
|
const clockVersion = input.clockInitialSharedVersion?.trim() || SUI_CLOCK_INITIAL_SHARED_VERSION;
|
|
3341
4021
|
const tx = new Transaction4();
|
|
@@ -3344,6 +4024,16 @@ async function buildSpendAccountSettleOpenRecipientKind(input) {
|
|
|
3344
4024
|
target,
|
|
3345
4025
|
typeArguments: [input.coinType],
|
|
3346
4026
|
arguments: [
|
|
4027
|
+
tx.sharedObjectRef({
|
|
4028
|
+
objectId: guarded.exclusionDirectoryId,
|
|
4029
|
+
initialSharedVersion: guarded.exclusionDirectoryInitialSharedVersion,
|
|
4030
|
+
mutable: false
|
|
4031
|
+
}),
|
|
4032
|
+
tx.sharedObjectRef({
|
|
4033
|
+
objectId: guarded.recipientRegistryId,
|
|
4034
|
+
initialSharedVersion: guarded.recipientRegistryInitialSharedVersion,
|
|
4035
|
+
mutable: true
|
|
4036
|
+
}),
|
|
3347
4037
|
tx.sharedObjectRef({
|
|
3348
4038
|
objectId: input.poolObjectId,
|
|
3349
4039
|
initialSharedVersion: String(input.poolInitialSharedVersion),
|
|
@@ -3359,6 +4049,7 @@ async function buildSpendAccountSettleOpenRecipientKind(input) {
|
|
|
3359
4049
|
tx.pure.vector("u8", Array.from(input.paymentIdHash)),
|
|
3360
4050
|
tx.pure.vector("u8", Array.from(input.termsHash)),
|
|
3361
4051
|
tx.pure.vector("u8", Array.from(hash)),
|
|
4052
|
+
tx.pure.u64(BigInt(guarded.expiresAtMs)),
|
|
3362
4053
|
tx.sharedObjectRef({
|
|
3363
4054
|
objectId: SUI_CLOCK2,
|
|
3364
4055
|
initialSharedVersion: clockVersion,
|
|
@@ -3367,14 +4058,14 @@ async function buildSpendAccountSettleOpenRecipientKind(input) {
|
|
|
3367
4058
|
]
|
|
3368
4059
|
});
|
|
3369
4060
|
const bytes = await tx.build({ onlyTransactionKind: true });
|
|
3370
|
-
return { kindBytes:
|
|
4061
|
+
return { kindBytes: toBase645(bytes) };
|
|
3371
4062
|
}
|
|
3372
4063
|
async function buildSpendAccountPayTx(input) {
|
|
3373
4064
|
if (!input.gas?.payment?.length) {
|
|
3374
4065
|
throw new Error("agent gas payment objects are required for executable pay");
|
|
3375
4066
|
}
|
|
3376
4067
|
const { kindBytes } = await buildSpendAccountPayKind(input);
|
|
3377
|
-
const tx = Transaction4.fromKind(
|
|
4068
|
+
const tx = Transaction4.fromKind(fromBase646(kindBytes));
|
|
3378
4069
|
tx.setSender(input.delegate);
|
|
3379
4070
|
tx.setGasOwner(input.gas.owner ?? input.delegate);
|
|
3380
4071
|
tx.setGasBudget(BigInt(input.gas.budget ?? DEFAULT_GAS_BUDGET));
|
|
@@ -3384,7 +4075,7 @@ async function buildSpendAccountPayTx(input) {
|
|
|
3384
4075
|
version: String(c.version),
|
|
3385
4076
|
digest: c.digest
|
|
3386
4077
|
})));
|
|
3387
|
-
const txBytes =
|
|
4078
|
+
const txBytes = toBase645(await tx.build());
|
|
3388
4079
|
return { txBytes, kindBytes };
|
|
3389
4080
|
}
|
|
3390
4081
|
async function buildSpendAccountSettleOpenRecipientTx(input) {
|
|
@@ -3392,7 +4083,7 @@ async function buildSpendAccountSettleOpenRecipientTx(input) {
|
|
|
3392
4083
|
throw new Error("agent gas payment objects are required for executable pay");
|
|
3393
4084
|
}
|
|
3394
4085
|
const { kindBytes } = await buildSpendAccountSettleOpenRecipientKind(input);
|
|
3395
|
-
const tx = Transaction4.fromKind(
|
|
4086
|
+
const tx = Transaction4.fromKind(fromBase646(kindBytes));
|
|
3396
4087
|
tx.setSender(input.delegate);
|
|
3397
4088
|
tx.setGasOwner(input.gas.owner ?? input.delegate);
|
|
3398
4089
|
tx.setGasBudget(BigInt(input.gas.budget ?? DEFAULT_GAS_BUDGET));
|
|
@@ -3402,23 +4093,59 @@ async function buildSpendAccountSettleOpenRecipientTx(input) {
|
|
|
3402
4093
|
version: String(c.version),
|
|
3403
4094
|
digest: c.digest
|
|
3404
4095
|
})));
|
|
3405
|
-
const txBytes =
|
|
4096
|
+
const txBytes = toBase645(await tx.build());
|
|
4097
|
+
return { txBytes, kindBytes };
|
|
4098
|
+
}
|
|
4099
|
+
function readX402GasPin(offer) {
|
|
4100
|
+
const owner = offer.sponsorGasOwner?.trim();
|
|
4101
|
+
const budget = atomicAmount(offer.sponsorGasBudget ?? "");
|
|
4102
|
+
const price = atomicAmount(offer.sponsorGasPrice ?? "");
|
|
4103
|
+
if (!owner || budget === null || price === null || budget <= 0n || price <= 0n) {
|
|
4104
|
+
return null;
|
|
4105
|
+
}
|
|
4106
|
+
try {
|
|
4107
|
+
return {
|
|
4108
|
+
owner,
|
|
4109
|
+
budget,
|
|
4110
|
+
price,
|
|
4111
|
+
payment: decodeSponsorGasPayment(offer.sponsorGasPayment ?? "")
|
|
4112
|
+
};
|
|
4113
|
+
} catch {
|
|
4114
|
+
return null;
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
async function buildSpendAccountX402PayTx(input) {
|
|
4118
|
+
const { kindBytes } = await buildSpendAccountPayKind(input);
|
|
4119
|
+
const tx = Transaction4.fromKind(fromBase646(kindBytes));
|
|
4120
|
+
tx.setSender(input.delegate);
|
|
4121
|
+
tx.setGasOwner(input.gas.owner);
|
|
4122
|
+
tx.setGasBudget(input.gas.budget);
|
|
4123
|
+
tx.setGasPrice(input.gas.price);
|
|
4124
|
+
tx.setGasPayment(input.gas.payment);
|
|
4125
|
+
tx.setExpiration({ Epoch: String(input.currentEpoch + X402_EXPIRATION_EPOCHS) });
|
|
4126
|
+
const txBytes = toBase645(await tx.build());
|
|
3406
4127
|
return { txBytes, kindBytes };
|
|
3407
4128
|
}
|
|
4129
|
+
function currentEpochReader(client) {
|
|
4130
|
+
return async () => {
|
|
4131
|
+
const { systemState } = await client.core.getCurrentSystemState();
|
|
4132
|
+
return BigInt(systemState.epoch);
|
|
4133
|
+
};
|
|
4134
|
+
}
|
|
3408
4135
|
function assertPayTransaction(txBytes, expected) {
|
|
3409
4136
|
let data;
|
|
3410
4137
|
try {
|
|
3411
|
-
data = Transaction4.from(
|
|
4138
|
+
data = Transaction4.from(fromBase646(txBytes)).getData();
|
|
3412
4139
|
} catch {
|
|
3413
4140
|
throw new Error("transaction is not valid Sui transaction bytes");
|
|
3414
4141
|
}
|
|
3415
|
-
if (!data.sender || !
|
|
4142
|
+
if (!data.sender || !sameAddress2(data.sender, expected.delegate)) {
|
|
3416
4143
|
throw new Error("transaction sender does not match delegate");
|
|
3417
4144
|
}
|
|
3418
4145
|
if (!data.commands || data.commands.length !== 1) {
|
|
3419
4146
|
throw new Error("transaction must contain exactly one command");
|
|
3420
4147
|
}
|
|
3421
|
-
const wanted = payMoveCallTarget(expected.packageId, expected.accessMode);
|
|
4148
|
+
const wanted = payMoveCallTarget(expected.packageId, expected.accessMode, expected.guarded === true);
|
|
3422
4149
|
const cmd = data.commands[0];
|
|
3423
4150
|
if (cmd.$kind !== "MoveCall" || !cmd.MoveCall) {
|
|
3424
4151
|
throw new Error("transaction is not a MoveCall");
|
|
@@ -3429,7 +4156,7 @@ function assertPayTransaction(txBytes, expected) {
|
|
|
3429
4156
|
}
|
|
3430
4157
|
let expectedKind;
|
|
3431
4158
|
try {
|
|
3432
|
-
expectedKind = Transaction4.fromKind(
|
|
4159
|
+
expectedKind = Transaction4.fromKind(fromBase646(expected.kindBytes)).getData();
|
|
3433
4160
|
} catch {
|
|
3434
4161
|
throw new Error("intended pay kind baseline is invalid");
|
|
3435
4162
|
}
|
|
@@ -3450,9 +4177,10 @@ function assertPayTransaction(txBytes, expected) {
|
|
|
3450
4177
|
if (!expected.paymentIdHash.length || !expected.termsHash.length) {
|
|
3451
4178
|
throw new Error("expected paymentIdHash and termsHash must be non-empty");
|
|
3452
4179
|
}
|
|
4180
|
+
const expectedArgc = 11;
|
|
3453
4181
|
const args = cmd.MoveCall.arguments;
|
|
3454
|
-
if (args && args.length !==
|
|
3455
|
-
throw new Error(`pay expects
|
|
4182
|
+
if (args && args.length !== expectedArgc) {
|
|
4183
|
+
throw new Error(`pay expects ${expectedArgc} arguments; got ${args.length}`);
|
|
3456
4184
|
}
|
|
3457
4185
|
}
|
|
3458
4186
|
function asValidatedReceipt(value) {
|
|
@@ -3635,13 +4363,13 @@ function spendIntentViolation(offer, intent) {
|
|
|
3635
4363
|
const ceiling = atomicAmount(intent.maxAmount);
|
|
3636
4364
|
if (offered === null || ceiling === null || offered > ceiling)
|
|
3637
4365
|
return "amount";
|
|
3638
|
-
if (intent.recipient !== void 0 && !
|
|
4366
|
+
if (intent.recipient !== void 0 && !sameAddress2(offer.payTo, intent.recipient)) {
|
|
3639
4367
|
return "recipient";
|
|
3640
4368
|
}
|
|
3641
4369
|
return null;
|
|
3642
4370
|
}
|
|
3643
4371
|
function preparedTermsDivergence(offer, terms) {
|
|
3644
|
-
if (!
|
|
4372
|
+
if (!sameAddress2(offer.payTo, terms.recipient))
|
|
3645
4373
|
return "recipient";
|
|
3646
4374
|
const offered = atomicAmount(offer.amount);
|
|
3647
4375
|
const verified = atomicAmount(terms.amount);
|
|
@@ -3724,6 +4452,10 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3724
4452
|
logInternal(logError, "resolve authority failed", err);
|
|
3725
4453
|
return failed2("GRANT_DENIED", PUBLIC_DETAIL.GRANT_DENIED);
|
|
3726
4454
|
}
|
|
4455
|
+
const x402Requested = args.dialect === "x402";
|
|
4456
|
+
if (x402Requested && snapshot.accessMode === "open") {
|
|
4457
|
+
return failed2("UNSUPPORTED_SCOPE", PUBLIC_DETAIL.UNSUPPORTED_SCOPE);
|
|
4458
|
+
}
|
|
3727
4459
|
let poolInitialSharedVersion;
|
|
3728
4460
|
let grantInitialSharedVersion;
|
|
3729
4461
|
let clockInitialSharedVersion;
|
|
@@ -3797,7 +4529,7 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3797
4529
|
throw new Error("POLICY:no_target");
|
|
3798
4530
|
}
|
|
3799
4531
|
try {
|
|
3800
|
-
|
|
4532
|
+
normalizeStructTag6(offer2.asset);
|
|
3801
4533
|
} catch {
|
|
3802
4534
|
throw new Error("POLICY:bad_asset");
|
|
3803
4535
|
}
|
|
@@ -3817,7 +4549,15 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3817
4549
|
amount: input.amount,
|
|
3818
4550
|
paymentIdHash: input.paymentIdHash,
|
|
3819
4551
|
termsHash: input.termsHash,
|
|
3820
|
-
recipient: input.recipient
|
|
4552
|
+
recipient: input.recipient,
|
|
4553
|
+
// Guarded settle inputs (undefined for an unguarded offer, which then
|
|
4554
|
+
// builds without the exclusion objects). resolvePayment set
|
|
4555
|
+
// these from the resolved exclusion registry.
|
|
4556
|
+
exclusionDirectoryId: input.exclusionDirectoryId,
|
|
4557
|
+
recipientRegistryId: input.recipientRegistryId,
|
|
4558
|
+
exclusionDirectoryInitialSharedVersion: input.exclusionDirectoryInitialSharedVersion,
|
|
4559
|
+
recipientRegistryInitialSharedVersion: input.recipientRegistryInitialSharedVersion,
|
|
4560
|
+
expiresAtMs: input.expiresAtMs
|
|
3821
4561
|
};
|
|
3822
4562
|
if (input.accessMode === "open") {
|
|
3823
4563
|
return buildSpendAccountSettleOpenRecipientKind(kindInput);
|
|
@@ -3825,7 +4565,7 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3825
4565
|
return buildSpendAccountPayKind(kindInput);
|
|
3826
4566
|
};
|
|
3827
4567
|
try {
|
|
3828
|
-
|
|
4568
|
+
normalizeStructTag6(snapshot.coinType);
|
|
3829
4569
|
} catch {
|
|
3830
4570
|
return failed2("POLICY_REJECTED", PUBLIC_DETAIL.POLICY_REJECTED);
|
|
3831
4571
|
}
|
|
@@ -3839,9 +4579,13 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3839
4579
|
};
|
|
3840
4580
|
const resolveDeps = {
|
|
3841
4581
|
fetchImpl: memoizedFetch,
|
|
3842
|
-
preferredDialect: "mpp",
|
|
4582
|
+
preferredDialect: x402Requested ? "x402" : "mpp",
|
|
3843
4583
|
validateOffer: validateOfferCallback,
|
|
3844
|
-
buildPayKind
|
|
4584
|
+
buildPayKind,
|
|
4585
|
+
// Guarded offers resolve the per-recipient exclusion registry
|
|
4586
|
+
// here; absent, resolvePayment fails closed (BUILD_FAILED) for such an offer
|
|
4587
|
+
// and never builds an unguarded settle. Unguarded offers never call it.
|
|
4588
|
+
resolveExclusionRegistry: deps.resolveExclusionRegistry
|
|
3845
4589
|
};
|
|
3846
4590
|
const resolution = await resolvePayment(resolveInput, resolveDeps);
|
|
3847
4591
|
if (resolution.status === "unpaid_ok") {
|
|
@@ -3853,8 +4597,15 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3853
4597
|
paymentId: resolution.paymentId
|
|
3854
4598
|
});
|
|
3855
4599
|
}
|
|
3856
|
-
const { offer, resolved: resolvedAuthority, kindBytes, paymentIdHash: payIdHash, termsHash: terms } = resolution;
|
|
4600
|
+
const { offer, resolved: resolvedAuthority, kindBytes, paymentIdHash: payIdHash, termsHash: terms, guarded: guardedResolution } = resolution;
|
|
3857
4601
|
const paymentId = resolution.paymentId;
|
|
4602
|
+
const guardedBuildFields = guardedResolution ? {
|
|
4603
|
+
exclusionDirectoryId: guardedResolution.exclusionDirectoryId,
|
|
4604
|
+
recipientRegistryId: guardedResolution.recipientRegistryId,
|
|
4605
|
+
exclusionDirectoryInitialSharedVersion: guardedResolution.exclusionDirectoryInitialSharedVersion,
|
|
4606
|
+
recipientRegistryInitialSharedVersion: guardedResolution.recipientRegistryInitialSharedVersion,
|
|
4607
|
+
expiresAtMs: guardedResolution.expiresAtMs
|
|
4608
|
+
} : {};
|
|
3858
4609
|
const isOpen = snapshot.accessMode === "open";
|
|
3859
4610
|
const resolved = {
|
|
3860
4611
|
poolObjectId: resolvedAuthority.poolObjectId,
|
|
@@ -3866,6 +4617,13 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3866
4617
|
recipient: resolvedAuthority.recipient
|
|
3867
4618
|
};
|
|
3868
4619
|
const offerDigest = offer.digest.trim();
|
|
4620
|
+
let x402Terms = null;
|
|
4621
|
+
if (x402Requested) {
|
|
4622
|
+
x402Terms = await x402Preflight(resolution.dialect, offer, deps, logError);
|
|
4623
|
+
if (!x402Terms) {
|
|
4624
|
+
return failed2("X402_UNAVAILABLE", PUBLIC_DETAIL.X402_UNAVAILABLE, { paymentId });
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
3869
4627
|
const ledger = deps.paymentLedger;
|
|
3870
4628
|
if (!ledger || typeof ledger.reserve !== "function" || typeof ledger.complete !== "function") {
|
|
3871
4629
|
return failed2("PAYMENT_LEDGER_REQUIRED", PUBLIC_DETAIL.PAYMENT_LEDGER_REQUIRED, { paymentId });
|
|
@@ -3882,7 +4640,9 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3882
4640
|
if (reserveOutcome.state === "done") {
|
|
3883
4641
|
const prior = asValidatedLedgerResult(reserveOutcome.prior, paymentId);
|
|
3884
4642
|
if (!prior) {
|
|
3885
|
-
return ambiguous("PAYMENT_ATTEMPT_IN_FLIGHT", PUBLIC_DETAIL.PAYMENT_ATTEMPT_IN_FLIGHT, {
|
|
4643
|
+
return ambiguous("PAYMENT_ATTEMPT_IN_FLIGHT", PUBLIC_DETAIL.PAYMENT_ATTEMPT_IN_FLIGHT, {
|
|
4644
|
+
paymentId
|
|
4645
|
+
});
|
|
3886
4646
|
}
|
|
3887
4647
|
return {
|
|
3888
4648
|
...prior,
|
|
@@ -3892,10 +4652,14 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3892
4652
|
};
|
|
3893
4653
|
}
|
|
3894
4654
|
if (reserveOutcome.state === "in_flight") {
|
|
3895
|
-
return ambiguous("PAYMENT_ATTEMPT_IN_FLIGHT", PUBLIC_DETAIL.PAYMENT_ATTEMPT_IN_FLIGHT, {
|
|
4655
|
+
return ambiguous("PAYMENT_ATTEMPT_IN_FLIGHT", PUBLIC_DETAIL.PAYMENT_ATTEMPT_IN_FLIGHT, {
|
|
4656
|
+
paymentId
|
|
4657
|
+
});
|
|
3896
4658
|
}
|
|
3897
4659
|
if (reserveOutcome.state !== "reserved") {
|
|
3898
|
-
logInternal(logError, "payment attempt ledger reserve returned unexpected state", {
|
|
4660
|
+
logInternal(logError, "payment attempt ledger reserve returned unexpected state", {
|
|
4661
|
+
state: reserveOutcome.state
|
|
4662
|
+
});
|
|
3899
4663
|
return failed2("PAYMENT_LEDGER_FAILED", PUBLIC_DETAIL.PAYMENT_LEDGER_FAILED, {
|
|
3900
4664
|
paymentId
|
|
3901
4665
|
});
|
|
@@ -3905,27 +4669,6 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3905
4669
|
await releaseReservation(ledger, ownedPaymentId, logError);
|
|
3906
4670
|
return failed2(code, detail, { paymentId });
|
|
3907
4671
|
};
|
|
3908
|
-
const gasMode = resolveGasMode(deps);
|
|
3909
|
-
const execute = deps.executeTx;
|
|
3910
|
-
if (gasMode === "none") {
|
|
3911
|
-
return abortBeforeSign("EXECUTE_UNAVAILABLE", PUBLIC_DETAIL.EXECUTE_UNAVAILABLE);
|
|
3912
|
-
}
|
|
3913
|
-
if (gasMode === "agent" && !execute) {
|
|
3914
|
-
return abortBeforeSign("EXECUTE_UNAVAILABLE", PUBLIC_DETAIL.EXECUTE_UNAVAILABLE);
|
|
3915
|
-
}
|
|
3916
|
-
if (typeof deps.getFinalizedTx !== "function") {
|
|
3917
|
-
return abortBeforeSign("FINALITY_UNAVAILABLE", PUBLIC_DETAIL.FINALITY_UNAVAILABLE);
|
|
3918
|
-
}
|
|
3919
|
-
let signer;
|
|
3920
|
-
try {
|
|
3921
|
-
signer = await resolveSigner(session.signer);
|
|
3922
|
-
} catch (err) {
|
|
3923
|
-
logInternal(logError, "resolve signer failed", err);
|
|
3924
|
-
return abortBeforeSign("SIGN_FAILED", PUBLIC_DETAIL.SIGN_FAILED);
|
|
3925
|
-
}
|
|
3926
|
-
if (!sameAddress(signer.address, resolved.delegateAddress)) {
|
|
3927
|
-
return abortBeforeSign("POLICY_REJECTED", PUBLIC_DETAIL.POLICY_REJECTED);
|
|
3928
|
-
}
|
|
3929
4672
|
const kindBuildInput = {
|
|
3930
4673
|
packageId: session.packageId,
|
|
3931
4674
|
delegate: resolved.delegateAddress,
|
|
@@ -3940,8 +4683,69 @@ async function payDelegatedSpendAccount(args) {
|
|
|
3940
4683
|
amount: offer.amount,
|
|
3941
4684
|
paymentIdHash: payIdHash,
|
|
3942
4685
|
termsHash: terms,
|
|
3943
|
-
recipient: resolved.recipient
|
|
4686
|
+
recipient: resolved.recipient,
|
|
4687
|
+
...guardedBuildFields
|
|
4688
|
+
};
|
|
4689
|
+
const expectedPay = {
|
|
4690
|
+
packageId: session.packageId,
|
|
4691
|
+
delegate: resolved.delegateAddress,
|
|
4692
|
+
poolObjectId: resolved.poolObjectId,
|
|
4693
|
+
grantObjectId: resolved.grantObjectId,
|
|
4694
|
+
policyId: resolved.onChainPolicyId,
|
|
4695
|
+
targetHash: resolved.targetHash,
|
|
4696
|
+
amount: offer.amount,
|
|
4697
|
+
paymentIdHash: payIdHash,
|
|
4698
|
+
termsHash: terms,
|
|
4699
|
+
coinType: resolved.coinType,
|
|
4700
|
+
kindBytes,
|
|
4701
|
+
accessMode: snapshot.accessMode,
|
|
4702
|
+
recipient: resolved.recipient,
|
|
4703
|
+
guarded: guardedResolution !== void 0
|
|
3944
4704
|
};
|
|
4705
|
+
if (x402Terms) {
|
|
4706
|
+
return payX402Reserved({
|
|
4707
|
+
session,
|
|
4708
|
+
deps,
|
|
4709
|
+
fetchImpl,
|
|
4710
|
+
resourceUrl,
|
|
4711
|
+
request,
|
|
4712
|
+
ledger,
|
|
4713
|
+
paymentId,
|
|
4714
|
+
offer,
|
|
4715
|
+
expected: expectedPay,
|
|
4716
|
+
buildInput: {
|
|
4717
|
+
...kindBuildInput,
|
|
4718
|
+
gas: x402Terms.gas,
|
|
4719
|
+
currentEpoch: x402Terms.currentEpoch
|
|
4720
|
+
},
|
|
4721
|
+
settlement: {
|
|
4722
|
+
status: "success",
|
|
4723
|
+
amount: offer.amount,
|
|
4724
|
+
recipient: resolved.recipient,
|
|
4725
|
+
coinType: resolved.coinType,
|
|
4726
|
+
asset: offer.asset,
|
|
4727
|
+
grantObjectId: resolved.grantObjectId,
|
|
4728
|
+
poolObjectId: resolved.poolObjectId
|
|
4729
|
+
},
|
|
4730
|
+
onProgress: args.onX402Progress,
|
|
4731
|
+
abort: abortBeforeSign
|
|
4732
|
+
});
|
|
4733
|
+
}
|
|
4734
|
+
const gasMode = resolveGasMode(deps);
|
|
4735
|
+
const execute = deps.executeTx;
|
|
4736
|
+
if (gasMode === "none") {
|
|
4737
|
+
return abortBeforeSign("EXECUTE_UNAVAILABLE", PUBLIC_DETAIL.EXECUTE_UNAVAILABLE);
|
|
4738
|
+
}
|
|
4739
|
+
if (gasMode === "agent" && !execute) {
|
|
4740
|
+
return abortBeforeSign("EXECUTE_UNAVAILABLE", PUBLIC_DETAIL.EXECUTE_UNAVAILABLE);
|
|
4741
|
+
}
|
|
4742
|
+
if (typeof deps.getSettledTx !== "function") {
|
|
4743
|
+
return abortBeforeSign("FINALITY_UNAVAILABLE", PUBLIC_DETAIL.FINALITY_UNAVAILABLE);
|
|
4744
|
+
}
|
|
4745
|
+
const signer = await resolveDelegateSigner(session.signer, resolved.delegateAddress, logError);
|
|
4746
|
+
if (typeof signer === "string") {
|
|
4747
|
+
return abortBeforeSign(signer, PUBLIC_DETAIL[signer]);
|
|
4748
|
+
}
|
|
3945
4749
|
let txBytes;
|
|
3946
4750
|
let sponsoredExecuteToken;
|
|
3947
4751
|
let sponsoredCreateDigest;
|
|
@@ -4015,7 +4819,7 @@ async function payDelegatedSpendAccount(args) {
|
|
|
4015
4819
|
return abortBeforeSign("GRANT_DENIED", grantDeniedDetail(err));
|
|
4016
4820
|
}
|
|
4017
4821
|
}
|
|
4018
|
-
const allowedPayTarget = payMoveCallTarget(session.packageId, snapshot.accessMode);
|
|
4822
|
+
const allowedPayTarget = payMoveCallTarget(session.packageId, snapshot.accessMode, guardedResolution !== void 0);
|
|
4019
4823
|
try {
|
|
4020
4824
|
const createRes = await fetchSponsor(`${base}/api/sponsor`, {
|
|
4021
4825
|
method: "POST",
|
|
@@ -4080,21 +4884,7 @@ async function payDelegatedSpendAccount(args) {
|
|
|
4080
4884
|
}
|
|
4081
4885
|
}
|
|
4082
4886
|
try {
|
|
4083
|
-
assertPayTransaction(txBytes,
|
|
4084
|
-
packageId: session.packageId,
|
|
4085
|
-
delegate: resolved.delegateAddress,
|
|
4086
|
-
poolObjectId: resolved.poolObjectId,
|
|
4087
|
-
grantObjectId: resolved.grantObjectId,
|
|
4088
|
-
policyId: resolved.onChainPolicyId,
|
|
4089
|
-
targetHash: resolved.targetHash,
|
|
4090
|
-
amount: offer.amount,
|
|
4091
|
-
paymentIdHash: payIdHash,
|
|
4092
|
-
termsHash: terms,
|
|
4093
|
-
coinType: resolved.coinType,
|
|
4094
|
-
kindBytes,
|
|
4095
|
-
accessMode: snapshot.accessMode,
|
|
4096
|
-
recipient: resolved.recipient
|
|
4097
|
-
});
|
|
4887
|
+
assertPayTransaction(txBytes, expectedPay);
|
|
4098
4888
|
} catch (err) {
|
|
4099
4889
|
logInternal(logError, "pre-sign pay validation failed", err);
|
|
4100
4890
|
return abortBeforeSign(gasMode === "sponsored" ? "SPONSORED_TX_MISMATCH" : "TX_MISMATCH", gasMode === "sponsored" ? PUBLIC_DETAIL.SPONSORED_TX_MISMATCH : PUBLIC_DETAIL.TX_MISMATCH);
|
|
@@ -4166,7 +4956,7 @@ async function payDelegatedSpendAccount(args) {
|
|
|
4166
4956
|
txDigest: errDigest ?? finalDigest
|
|
4167
4957
|
}), logError);
|
|
4168
4958
|
}
|
|
4169
|
-
const getFinalized = deps.
|
|
4959
|
+
const getFinalized = deps.getSettledTx;
|
|
4170
4960
|
let finalized = null;
|
|
4171
4961
|
try {
|
|
4172
4962
|
for (let attempt = 0; attempt < FINALIZATION_ATTEMPTS; attempt += 1) {
|
|
@@ -4177,7 +4967,7 @@ async function payDelegatedSpendAccount(args) {
|
|
|
4177
4967
|
await delay(FINALIZATION_DELAY_MS);
|
|
4178
4968
|
}
|
|
4179
4969
|
} catch (err) {
|
|
4180
|
-
logInternal(logError, "
|
|
4970
|
+
logInternal(logError, "getSettledTx threw after broadcast", err);
|
|
4181
4971
|
return completeAttempt(ledger, paymentId, ambiguous("FINALITY_AMBIGUOUS", PUBLIC_DETAIL.FINALITY_AMBIGUOUS, {
|
|
4182
4972
|
paymentId,
|
|
4183
4973
|
txDigest: finalDigest
|
|
@@ -4196,69 +4986,258 @@ async function payDelegatedSpendAccount(args) {
|
|
|
4196
4986
|
txDigest: finalDigest
|
|
4197
4987
|
});
|
|
4198
4988
|
}
|
|
4199
|
-
const settlement = {
|
|
4200
|
-
status: "success",
|
|
4201
|
-
txDigest: finalDigest,
|
|
4202
|
-
amount: offer.amount,
|
|
4203
|
-
recipient: resolved.recipient,
|
|
4204
|
-
coinType: resolved.coinType,
|
|
4205
|
-
asset: offer.asset,
|
|
4206
|
-
grantObjectId: resolved.grantObjectId,
|
|
4207
|
-
poolObjectId: resolved.poolObjectId
|
|
4208
|
-
};
|
|
4209
|
-
if (deps.awaitDelivery === false) {
|
|
4210
|
-
return completeAttempt(ledger, paymentId, {
|
|
4211
|
-
status: "ok",
|
|
4212
|
-
paid: true,
|
|
4213
|
-
txDigest: finalDigest,
|
|
4214
|
-
paymentId,
|
|
4215
|
-
settlement
|
|
4216
|
-
}, logError);
|
|
4989
|
+
const settlement = {
|
|
4990
|
+
status: "success",
|
|
4991
|
+
txDigest: finalDigest,
|
|
4992
|
+
amount: offer.amount,
|
|
4993
|
+
recipient: resolved.recipient,
|
|
4994
|
+
coinType: resolved.coinType,
|
|
4995
|
+
asset: offer.asset,
|
|
4996
|
+
grantObjectId: resolved.grantObjectId,
|
|
4997
|
+
poolObjectId: resolved.poolObjectId
|
|
4998
|
+
};
|
|
4999
|
+
if (deps.awaitDelivery === false) {
|
|
5000
|
+
return completeAttempt(ledger, paymentId, {
|
|
5001
|
+
status: "ok",
|
|
5002
|
+
paid: true,
|
|
5003
|
+
txDigest: finalDigest,
|
|
5004
|
+
paymentId,
|
|
5005
|
+
settlement
|
|
5006
|
+
}, logError);
|
|
5007
|
+
}
|
|
5008
|
+
let proof;
|
|
5009
|
+
try {
|
|
5010
|
+
proof = encodeProof({
|
|
5011
|
+
challengeId: offer.challengeId,
|
|
5012
|
+
offer,
|
|
5013
|
+
txDigest: finalDigest,
|
|
5014
|
+
payer: resolved.delegateAddress
|
|
5015
|
+
});
|
|
5016
|
+
} catch (err) {
|
|
5017
|
+
logInternal(logError, "encode payment proof failed", err);
|
|
5018
|
+
return completeAttempt(ledger, paymentId, ambiguous("PROOF_PRESENTATION_FAILED", PUBLIC_DETAIL.PROOF_PRESENTATION_FAILED, { paymentId, txDigest: finalDigest, settlement }), logError);
|
|
5019
|
+
}
|
|
5020
|
+
let second;
|
|
5021
|
+
try {
|
|
5022
|
+
second = await fetchImpl(resourceUrl, paidRequestInit(request, { [MPP_CREDENTIAL_HEADER]: proof }));
|
|
5023
|
+
} catch (err) {
|
|
5024
|
+
logInternal(logError, "proof presentation failed", err);
|
|
5025
|
+
return completeAttempt(ledger, paymentId, ambiguous("PROOF_PRESENTATION_FAILED", PUBLIC_DETAIL.PROOF_PRESENTATION_FAILED, {
|
|
5026
|
+
paymentId,
|
|
5027
|
+
txDigest: finalDigest,
|
|
5028
|
+
settlement
|
|
5029
|
+
}), logError);
|
|
5030
|
+
}
|
|
5031
|
+
if (second.status === 503) {
|
|
5032
|
+
return completeAttempt(ledger, paymentId, ambiguous(gatewayRefusedCode(second, logError), PUBLIC_DETAIL.PROOF_PRESENTATION_FAILED, {
|
|
5033
|
+
paymentId,
|
|
5034
|
+
txDigest: finalDigest,
|
|
5035
|
+
settlement
|
|
5036
|
+
}), logError);
|
|
5037
|
+
}
|
|
5038
|
+
let receipt = null;
|
|
5039
|
+
try {
|
|
5040
|
+
receipt = readReceipt(second, "mpp");
|
|
5041
|
+
} catch (err) {
|
|
5042
|
+
logInternal(logError, "read receipt failed", err);
|
|
5043
|
+
}
|
|
5044
|
+
if (second.status !== 200) {
|
|
5045
|
+
return completeAttempt(ledger, paymentId, ambiguous(gatewayRefusedCode(second, logError), PUBLIC_DETAIL.GATEWAY_REFUSED, {
|
|
5046
|
+
paymentId,
|
|
5047
|
+
txDigest: finalDigest,
|
|
5048
|
+
settlement
|
|
5049
|
+
}), logError);
|
|
5050
|
+
}
|
|
5051
|
+
let body;
|
|
5052
|
+
try {
|
|
5053
|
+
body = await readBody(second);
|
|
5054
|
+
} catch (err) {
|
|
5055
|
+
logInternal(logError, "read delivery body failed", err);
|
|
5056
|
+
return completeAttempt(ledger, paymentId, ambiguous("PROOF_PRESENTATION_FAILED", PUBLIC_DETAIL.PROOF_PRESENTATION_FAILED, {
|
|
5057
|
+
paymentId,
|
|
5058
|
+
txDigest: finalDigest,
|
|
5059
|
+
settlement
|
|
5060
|
+
}), logError);
|
|
5061
|
+
}
|
|
5062
|
+
return completeAttempt(ledger, paymentId, {
|
|
5063
|
+
status: "ok",
|
|
5064
|
+
paid: true,
|
|
5065
|
+
body,
|
|
5066
|
+
receipt,
|
|
5067
|
+
txDigest: finalDigest,
|
|
5068
|
+
paymentId,
|
|
5069
|
+
settlement
|
|
5070
|
+
}, logError);
|
|
5071
|
+
}
|
|
5072
|
+
async function resolveDelegateSigner(input, delegateAddress, logError) {
|
|
5073
|
+
let signer;
|
|
5074
|
+
try {
|
|
5075
|
+
signer = await resolveSigner(input);
|
|
5076
|
+
} catch (err) {
|
|
5077
|
+
logInternal(logError, "resolve signer failed", err);
|
|
5078
|
+
return "SIGN_FAILED";
|
|
5079
|
+
}
|
|
5080
|
+
return sameAddress2(signer.address, delegateAddress) ? signer : "POLICY_REJECTED";
|
|
5081
|
+
}
|
|
5082
|
+
async function x402Preflight(dialect, offer, deps, logError) {
|
|
5083
|
+
if (dialect !== "x402") {
|
|
5084
|
+
logInternal(logError, "x402 requested but the 402 carried no x402 offer");
|
|
5085
|
+
return null;
|
|
5086
|
+
}
|
|
5087
|
+
const gas = readX402GasPin(offer);
|
|
5088
|
+
if (!gas) {
|
|
5089
|
+
logInternal(logError, "x402 offer lacks readable sponsor gas pins");
|
|
5090
|
+
return null;
|
|
5091
|
+
}
|
|
5092
|
+
if (typeof deps.getCurrentEpoch !== "function") {
|
|
5093
|
+
logInternal(logError, "x402 needs a current-epoch reader");
|
|
5094
|
+
return null;
|
|
4217
5095
|
}
|
|
4218
|
-
let proof;
|
|
4219
5096
|
try {
|
|
4220
|
-
|
|
4221
|
-
challengeId: offer.challengeId,
|
|
4222
|
-
offer,
|
|
4223
|
-
txDigest: finalDigest,
|
|
4224
|
-
payer: resolved.delegateAddress
|
|
4225
|
-
});
|
|
5097
|
+
return { gas, currentEpoch: BigInt(await deps.getCurrentEpoch()) };
|
|
4226
5098
|
} catch (err) {
|
|
4227
|
-
logInternal(logError, "
|
|
4228
|
-
return
|
|
5099
|
+
logInternal(logError, "read current epoch failed", err);
|
|
5100
|
+
return null;
|
|
4229
5101
|
}
|
|
4230
|
-
|
|
5102
|
+
}
|
|
5103
|
+
function emitX402Progress(ctx, event) {
|
|
5104
|
+
if (!ctx.onProgress)
|
|
5105
|
+
return;
|
|
4231
5106
|
try {
|
|
4232
|
-
|
|
5107
|
+
ctx.onProgress(event);
|
|
4233
5108
|
} catch (err) {
|
|
4234
|
-
logInternal(logError, "
|
|
4235
|
-
return completeAttempt(ledger, paymentId, ambiguous("PROOF_PRESENTATION_FAILED", PUBLIC_DETAIL.PROOF_PRESENTATION_FAILED, { paymentId, txDigest: finalDigest, settlement }), logError);
|
|
5109
|
+
logInternal(ctx.deps.logError, "x402 progress callback threw", err);
|
|
4236
5110
|
}
|
|
4237
|
-
|
|
4238
|
-
|
|
5111
|
+
}
|
|
5112
|
+
async function payX402Reserved(ctx) {
|
|
5113
|
+
const logError = ctx.deps.logError;
|
|
5114
|
+
const signer = await resolveDelegateSigner(ctx.session.signer, ctx.expected.delegate, logError);
|
|
5115
|
+
if (typeof signer === "string")
|
|
5116
|
+
return ctx.abort(signer, PUBLIC_DETAIL[signer]);
|
|
5117
|
+
let txBytes;
|
|
5118
|
+
try {
|
|
5119
|
+
const build = ctx.deps.buildX402PayTx ?? buildSpendAccountX402PayTx;
|
|
5120
|
+
txBytes = (await build(ctx.buildInput)).txBytes;
|
|
5121
|
+
} catch (err) {
|
|
5122
|
+
logInternal(logError, "x402 pay build failed", err);
|
|
5123
|
+
return ctx.abort("BUILD_FAILED", PUBLIC_DETAIL.BUILD_FAILED);
|
|
4239
5124
|
}
|
|
4240
|
-
let receipt = null;
|
|
4241
5125
|
try {
|
|
4242
|
-
|
|
5126
|
+
assertPayTransaction(txBytes, ctx.expected);
|
|
4243
5127
|
} catch (err) {
|
|
4244
|
-
logInternal(logError, "
|
|
5128
|
+
logInternal(logError, "pre-sign x402 pay validation failed", err);
|
|
5129
|
+
return ctx.abort("TX_MISMATCH", PUBLIC_DETAIL.TX_MISMATCH);
|
|
4245
5130
|
}
|
|
4246
|
-
|
|
4247
|
-
|
|
5131
|
+
emitX402Progress(ctx, { stage: "built", txBytes });
|
|
5132
|
+
if (ctx.session.assertLive) {
|
|
5133
|
+
try {
|
|
5134
|
+
await ctx.session.assertLive();
|
|
5135
|
+
} catch (err) {
|
|
5136
|
+
logInternal(logError, "assertLive failed at sign", err);
|
|
5137
|
+
return ctx.abort("GRANT_DENIED", grantDeniedDetail(err));
|
|
5138
|
+
}
|
|
5139
|
+
}
|
|
5140
|
+
let signature;
|
|
5141
|
+
try {
|
|
5142
|
+
signature = await signer.signTransaction(txBytes);
|
|
5143
|
+
} catch (err) {
|
|
5144
|
+
logInternal(logError, "delegate sign failed", err);
|
|
5145
|
+
return ctx.abort("SIGN_FAILED", PUBLIC_DETAIL.SIGN_FAILED);
|
|
5146
|
+
}
|
|
5147
|
+
emitX402Progress(ctx, { stage: "signed", signature });
|
|
5148
|
+
let header;
|
|
5149
|
+
try {
|
|
5150
|
+
header = encodeCredential({
|
|
5151
|
+
challengeId: ctx.offer.challengeId,
|
|
5152
|
+
offer: ctx.offer,
|
|
5153
|
+
txBytes,
|
|
5154
|
+
signature,
|
|
5155
|
+
payer: ctx.expected.delegate
|
|
5156
|
+
});
|
|
5157
|
+
} catch (err) {
|
|
5158
|
+
logInternal(logError, "encode x402 credential failed", err);
|
|
5159
|
+
return ctx.abort("BUILD_FAILED", PUBLIC_DETAIL.BUILD_FAILED);
|
|
5160
|
+
}
|
|
5161
|
+
return recordX402Answer(ctx, await presentX402Credential(ctx, header));
|
|
5162
|
+
}
|
|
5163
|
+
function isX402Failure(value) {
|
|
5164
|
+
return value.success === false;
|
|
5165
|
+
}
|
|
5166
|
+
async function presentX402Credential(ctx, header) {
|
|
5167
|
+
const attempts = ctx.deps.x402PresentAttempts ?? X402_PRESENT_ATTEMPTS;
|
|
5168
|
+
const sleep = ctx.deps.sleep ?? delay;
|
|
5169
|
+
let txDigest;
|
|
5170
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
5171
|
+
emitX402Progress(ctx, { stage: "presented", header, attempt });
|
|
5172
|
+
let response;
|
|
5173
|
+
try {
|
|
5174
|
+
response = await ctx.fetchImpl(ctx.resourceUrl, paidRequestInit(ctx.request, { [X402_PAYMENT_HEADER]: header }));
|
|
5175
|
+
} catch (err) {
|
|
5176
|
+
logInternal(ctx.deps.logError, "x402 credential presentation failed", err);
|
|
5177
|
+
return { kind: "unresolved", txDigest };
|
|
5178
|
+
}
|
|
5179
|
+
const paymentResponse = response.headers.get(X402_RESPONSE_HEADER);
|
|
5180
|
+
emitX402Progress(ctx, {
|
|
5181
|
+
stage: "answered",
|
|
5182
|
+
status: response.status,
|
|
5183
|
+
paymentResponse,
|
|
5184
|
+
attempt
|
|
5185
|
+
});
|
|
5186
|
+
const decoded = paymentResponse ? decodeSettlementResponse(paymentResponse) : null;
|
|
5187
|
+
if (decoded && isX402Failure(decoded) && decoded.transaction.trim()) {
|
|
5188
|
+
txDigest = decoded.transaction.trim();
|
|
5189
|
+
}
|
|
5190
|
+
if (response.status !== 503) {
|
|
5191
|
+
return classifyX402Answer(response, decoded, {
|
|
5192
|
+
challengeId: ctx.offer.challengeId,
|
|
5193
|
+
txDigest,
|
|
5194
|
+
firstPresentation: attempt === 1
|
|
5195
|
+
});
|
|
5196
|
+
}
|
|
5197
|
+
if (attempt < attempts)
|
|
5198
|
+
await sleep(X402_PRESENT_DELAY_MS);
|
|
5199
|
+
}
|
|
5200
|
+
return { kind: "unresolved", txDigest };
|
|
5201
|
+
}
|
|
5202
|
+
function classifyX402Answer(response, decoded, seen) {
|
|
5203
|
+
const receipt = asValidatedReceipt(decoded);
|
|
5204
|
+
if (response.ok && receipt && receipt.challengeId === seen.challengeId) {
|
|
5205
|
+
return { kind: "settled", receipt, response };
|
|
5206
|
+
}
|
|
5207
|
+
if (seen.firstPresentation && response.status === 402 && decoded && isX402Failure(decoded) && !decoded.transaction.trim()) {
|
|
5208
|
+
return { kind: "refused", response };
|
|
5209
|
+
}
|
|
5210
|
+
return { kind: "unresolved", txDigest: seen.txDigest };
|
|
5211
|
+
}
|
|
5212
|
+
async function recordX402Answer(ctx, answer) {
|
|
5213
|
+
const { ledger, paymentId } = ctx;
|
|
5214
|
+
const logError = ctx.deps.logError;
|
|
5215
|
+
if (answer.kind === "unresolved") {
|
|
5216
|
+
return completeAttempt(ledger, paymentId, ambiguous("EXECUTE_AMBIGUOUS", PUBLIC_DETAIL.EXECUTE_AMBIGUOUS, {
|
|
5217
|
+
paymentId,
|
|
5218
|
+
txDigest: answer.txDigest
|
|
5219
|
+
}), logError);
|
|
5220
|
+
}
|
|
5221
|
+
if (answer.kind === "refused") {
|
|
5222
|
+
return ctx.abort(gatewayRefusedCode(answer.response, logError), PUBLIC_DETAIL.GATEWAY_REFUSED);
|
|
4248
5223
|
}
|
|
5224
|
+
const settlement = {
|
|
5225
|
+
...ctx.settlement,
|
|
5226
|
+
txDigest: answer.receipt.txDigest
|
|
5227
|
+
};
|
|
4249
5228
|
let body;
|
|
4250
5229
|
try {
|
|
4251
|
-
body = await readBody(
|
|
5230
|
+
body = await readBody(answer.response);
|
|
4252
5231
|
} catch (err) {
|
|
4253
5232
|
logInternal(logError, "read delivery body failed", err);
|
|
4254
|
-
return completeAttempt(ledger, paymentId, ambiguous("PROOF_PRESENTATION_FAILED", PUBLIC_DETAIL.PROOF_PRESENTATION_FAILED, { paymentId, txDigest:
|
|
5233
|
+
return completeAttempt(ledger, paymentId, ambiguous("PROOF_PRESENTATION_FAILED", PUBLIC_DETAIL.PROOF_PRESENTATION_FAILED, { paymentId, txDigest: settlement.txDigest, settlement }), logError);
|
|
4255
5234
|
}
|
|
4256
5235
|
return completeAttempt(ledger, paymentId, {
|
|
4257
5236
|
status: "ok",
|
|
4258
5237
|
paid: true,
|
|
4259
5238
|
body,
|
|
4260
|
-
receipt,
|
|
4261
|
-
txDigest:
|
|
5239
|
+
receipt: answer.receipt,
|
|
5240
|
+
txDigest: settlement.txDigest,
|
|
4262
5241
|
paymentId,
|
|
4263
5242
|
settlement
|
|
4264
5243
|
}, logError);
|
|
@@ -4279,9 +5258,35 @@ function settleOpenRecipientTarget(packageId) {
|
|
|
4279
5258
|
const fn = spendAccountDebitFunction({ accessMode: "open" });
|
|
4280
5259
|
return `${normalizeSuiAddress6(packageId)}::spend_account::${fn}`;
|
|
4281
5260
|
}
|
|
4282
|
-
function payMoveCallTarget(packageId, accessMode = "scoped") {
|
|
5261
|
+
function payMoveCallTarget(packageId, accessMode = "scoped", _guarded = false) {
|
|
4283
5262
|
return accessMode === "open" ? settleOpenRecipientTarget(packageId) : payTarget(packageId);
|
|
4284
5263
|
}
|
|
5264
|
+
function resolveGuardedSettleOfflineInputs(input) {
|
|
5265
|
+
const core = [input.exclusionDirectoryId, input.recipientRegistryId, input.expiresAtMs];
|
|
5266
|
+
const present = core.filter((v) => v !== void 0).length;
|
|
5267
|
+
if (present !== 3) {
|
|
5268
|
+
throw new Error("guarded settle requires exclusionDirectoryId, recipientRegistryId and expiresAtMs together (all or none)");
|
|
5269
|
+
}
|
|
5270
|
+
const expires = BigInt(input.expiresAtMs);
|
|
5271
|
+
if (expires <= 0n) {
|
|
5272
|
+
throw new Error("guarded settle expiresAtMs must be a positive epoch-ms value");
|
|
5273
|
+
}
|
|
5274
|
+
if (/^0x0+$/i.test(input.exclusionDirectoryId) || /^0x0+$/i.test(input.recipientRegistryId)) {
|
|
5275
|
+
throw new Error("guarded settle directory and registry ids must be non-zero");
|
|
5276
|
+
}
|
|
5277
|
+
const dirVer = input.exclusionDirectoryInitialSharedVersion;
|
|
5278
|
+
const regVer = input.recipientRegistryInitialSharedVersion;
|
|
5279
|
+
if (!dirVer || !String(dirVer).trim() || !regVer || !String(regVer).trim()) {
|
|
5280
|
+
throw new Error("guarded offline settle requires directory and registry initial shared versions");
|
|
5281
|
+
}
|
|
5282
|
+
return {
|
|
5283
|
+
exclusionDirectoryId: input.exclusionDirectoryId,
|
|
5284
|
+
recipientRegistryId: input.recipientRegistryId,
|
|
5285
|
+
exclusionDirectoryInitialSharedVersion: String(dirVer),
|
|
5286
|
+
recipientRegistryInitialSharedVersion: String(regVer),
|
|
5287
|
+
expiresAtMs: input.expiresAtMs
|
|
5288
|
+
};
|
|
5289
|
+
}
|
|
4285
5290
|
var PreparedVerificationUnavailableError = class extends Error {
|
|
4286
5291
|
code = DELEGATED_PAY_ERROR_CODES.PREPARED_VERIFY_UNAVAILABLE;
|
|
4287
5292
|
constructor(message, options) {
|
|
@@ -4299,17 +5304,42 @@ function preparedPureBytes(input) {
|
|
|
4299
5304
|
if (!input || input.$kind !== "Pure" || !input.Pure?.bytes)
|
|
4300
5305
|
return null;
|
|
4301
5306
|
try {
|
|
4302
|
-
return
|
|
5307
|
+
return fromBase646(input.Pure.bytes);
|
|
4303
5308
|
} catch {
|
|
4304
5309
|
return null;
|
|
4305
5310
|
}
|
|
4306
5311
|
}
|
|
5312
|
+
function preparedPureVectorU8(input) {
|
|
5313
|
+
const raw = preparedPureBytes(input);
|
|
5314
|
+
try {
|
|
5315
|
+
return Uint8Array.from(bcs5.vector(bcs5.u8()).parse(raw));
|
|
5316
|
+
} catch {
|
|
5317
|
+
}
|
|
5318
|
+
return null;
|
|
5319
|
+
}
|
|
5320
|
+
function sameBytes(a, b) {
|
|
5321
|
+
return Buffer.from(a).equals(Buffer.from(b));
|
|
5322
|
+
}
|
|
5323
|
+
function toHashBytes(val) {
|
|
5324
|
+
if (typeof val === "string") {
|
|
5325
|
+
const clean = val.startsWith("0x") ? val.slice(2) : val;
|
|
5326
|
+
return Uint8Array.from(Buffer.from(clean, "hex"));
|
|
5327
|
+
}
|
|
5328
|
+
return val;
|
|
5329
|
+
}
|
|
5330
|
+
function toTargetHashBytes(val) {
|
|
5331
|
+
if (typeof val === "string") {
|
|
5332
|
+
const clean = val.startsWith("0x") ? val.slice(2) : val;
|
|
5333
|
+
return targetHashBytes(clean);
|
|
5334
|
+
}
|
|
5335
|
+
return val;
|
|
5336
|
+
}
|
|
4307
5337
|
function preparedPureAddress(input) {
|
|
4308
5338
|
const raw = preparedPureBytes(input);
|
|
4309
5339
|
if (!raw)
|
|
4310
5340
|
return null;
|
|
4311
5341
|
try {
|
|
4312
|
-
return normalizeSuiAddress6(
|
|
5342
|
+
return normalizeSuiAddress6(bcs5.Address.parse(raw));
|
|
4313
5343
|
} catch {
|
|
4314
5344
|
return null;
|
|
4315
5345
|
}
|
|
@@ -4319,7 +5349,7 @@ function preparedPureU64(input) {
|
|
|
4319
5349
|
if (!raw)
|
|
4320
5350
|
return null;
|
|
4321
5351
|
try {
|
|
4322
|
-
return BigInt(
|
|
5352
|
+
return BigInt(bcs5.u64().parse(raw));
|
|
4323
5353
|
} catch {
|
|
4324
5354
|
return null;
|
|
4325
5355
|
}
|
|
@@ -4339,9 +5369,12 @@ function preparedSharedObjectId(input) {
|
|
|
4339
5369
|
}
|
|
4340
5370
|
async function verifyPreparedSettlementKind(input) {
|
|
4341
5371
|
const accessMode = input.expected.accessMode ?? "open";
|
|
5372
|
+
if (accessMode !== "scoped" && accessMode !== "open") {
|
|
5373
|
+
throw new Error(`unsupported accessMode: ${String(accessMode)}`);
|
|
5374
|
+
}
|
|
4342
5375
|
let data;
|
|
4343
5376
|
try {
|
|
4344
|
-
data = Transaction4.fromKind(
|
|
5377
|
+
data = Transaction4.fromKind(fromBase646(input.kindBytes)).getData();
|
|
4345
5378
|
} catch {
|
|
4346
5379
|
throw new Error("prepared transaction is not a valid settlement kind");
|
|
4347
5380
|
}
|
|
@@ -4365,16 +5398,25 @@ async function verifyPreparedSettlementKind(input) {
|
|
|
4365
5398
|
return void 0;
|
|
4366
5399
|
return inputs[idx];
|
|
4367
5400
|
};
|
|
4368
|
-
const
|
|
5401
|
+
const poolObjectId = preparedSharedObjectId(resolve(2));
|
|
5402
|
+
if (input.expected.poolObjectId) {
|
|
5403
|
+
if (!poolObjectId || !sameAddress2(poolObjectId, input.expected.poolObjectId)) {
|
|
5404
|
+
throw new Error(`prepared pool ${poolObjectId ?? "(missing)"} does not match expected pool ${input.expected.poolObjectId}`);
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5407
|
+
const grantObjectId = preparedSharedObjectId(resolve(3));
|
|
4369
5408
|
if (!grantObjectId) {
|
|
4370
5409
|
throw new Error("prepared transaction does not name a grant object");
|
|
4371
5410
|
}
|
|
5411
|
+
if (input.expected.grantObjectId && !sameAddress2(grantObjectId, input.expected.grantObjectId)) {
|
|
5412
|
+
throw new Error(`prepared grant ${grantObjectId} does not match expected grant ${input.expected.grantObjectId}`);
|
|
5413
|
+
}
|
|
4372
5414
|
if (accessMode === "open") {
|
|
4373
|
-
const recipient = preparedPureAddress(resolve(
|
|
4374
|
-
if (!recipient || !
|
|
5415
|
+
const recipient = preparedPureAddress(resolve(4));
|
|
5416
|
+
if (!recipient || !sameAddress2(recipient, input.expected.recipient)) {
|
|
4375
5417
|
throw new Error(`prepared recipient ${recipient ?? "(missing)"} does not match the requested recipient`);
|
|
4376
5418
|
}
|
|
4377
|
-
const amount = preparedPureU64(resolve(
|
|
5419
|
+
const amount = preparedPureU64(resolve(5));
|
|
4378
5420
|
let expectedAmount;
|
|
4379
5421
|
try {
|
|
4380
5422
|
expectedAmount = BigInt(input.expected.amount);
|
|
@@ -4385,7 +5427,21 @@ async function verifyPreparedSettlementKind(input) {
|
|
|
4385
5427
|
throw new Error(`prepared amount ${amount ?? "(missing)"} does not match the requested amount`);
|
|
4386
5428
|
}
|
|
4387
5429
|
} else {
|
|
4388
|
-
|
|
5430
|
+
if (input.expected.onChainPolicyId !== void 0) {
|
|
5431
|
+
const policyId = preparedPureU64(resolve(4));
|
|
5432
|
+
const expectedPolicyId = BigInt(input.expected.onChainPolicyId);
|
|
5433
|
+
if (policyId !== expectedPolicyId) {
|
|
5434
|
+
throw new Error(`prepared policy_id ${policyId ?? "(missing)"} does not match expected onChainPolicyId`);
|
|
5435
|
+
}
|
|
5436
|
+
}
|
|
5437
|
+
if (input.expected.targetHash) {
|
|
5438
|
+
const targetHash = preparedPureVectorU8(resolve(5));
|
|
5439
|
+
const expectedBytes = toTargetHashBytes(input.expected.targetHash);
|
|
5440
|
+
if (!targetHash || !sameBytes(targetHash, expectedBytes)) {
|
|
5441
|
+
throw new Error("prepared target_hash does not match expected targetHash");
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
const amount = preparedPureU64(resolve(6));
|
|
4389
5445
|
let expectedAmount;
|
|
4390
5446
|
try {
|
|
4391
5447
|
expectedAmount = BigInt(input.expected.amount);
|
|
@@ -4395,6 +5451,20 @@ async function verifyPreparedSettlementKind(input) {
|
|
|
4395
5451
|
if (amount === null || amount !== expectedAmount) {
|
|
4396
5452
|
throw new Error(`prepared amount ${amount ?? "(missing)"} does not match the requested amount`);
|
|
4397
5453
|
}
|
|
5454
|
+
if (input.expected.paymentIdHash) {
|
|
5455
|
+
const paymentIdHash2 = preparedPureVectorU8(resolve(7));
|
|
5456
|
+
const expectedBytes = toHashBytes(input.expected.paymentIdHash);
|
|
5457
|
+
if (!paymentIdHash2 || !sameBytes(paymentIdHash2, expectedBytes)) {
|
|
5458
|
+
throw new Error("prepared payment_id_hash does not match expected paymentIdHash");
|
|
5459
|
+
}
|
|
5460
|
+
}
|
|
5461
|
+
if (input.expected.termsHash) {
|
|
5462
|
+
const termsHash2 = preparedPureVectorU8(resolve(8));
|
|
5463
|
+
const expectedBytes = toHashBytes(input.expected.termsHash);
|
|
5464
|
+
if (!termsHash2 || !sameBytes(termsHash2, expectedBytes)) {
|
|
5465
|
+
throw new Error("prepared terms_hash does not match expected termsHash");
|
|
5466
|
+
}
|
|
5467
|
+
}
|
|
4398
5468
|
}
|
|
4399
5469
|
let grant;
|
|
4400
5470
|
try {
|
|
@@ -4405,9 +5475,12 @@ async function verifyPreparedSettlementKind(input) {
|
|
|
4405
5475
|
if (!grant) {
|
|
4406
5476
|
throw new Error("unsigned getObject could not read the named grant");
|
|
4407
5477
|
}
|
|
4408
|
-
if (!grant.delegateAddress || !
|
|
5478
|
+
if (!grant.delegateAddress || !sameAddress2(grant.delegateAddress, input.expected.delegateAddress)) {
|
|
4409
5479
|
throw new Error(`grant delegate ${grant.delegateAddress ?? "(missing)"} does not match the agent address`);
|
|
4410
5480
|
}
|
|
5481
|
+
if (input.expected.poolObjectId && grant.poolObjectId && !sameAddress2(grant.poolObjectId, input.expected.poolObjectId)) {
|
|
5482
|
+
throw new Error(`grant pool ${grant.poolObjectId} does not match expected pool ${input.expected.poolObjectId}`);
|
|
5483
|
+
}
|
|
4411
5484
|
}
|
|
4412
5485
|
function createMemoryLedger() {
|
|
4413
5486
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -4472,7 +5545,7 @@ import {
|
|
|
4472
5545
|
} from "fs";
|
|
4473
5546
|
import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
|
|
4474
5547
|
import { Ed25519Keypair as Ed25519Keypair2 } from "@mysten/sui/keypairs/ed25519";
|
|
4475
|
-
import { fromBase64 as
|
|
5548
|
+
import { fromBase64 as fromBase647 } from "@mysten/sui/utils";
|
|
4476
5549
|
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils.js";
|
|
4477
5550
|
|
|
4478
5551
|
// src/config.ts
|
|
@@ -4584,6 +5657,8 @@ function loadMcpConfig(env = process.env, argv = []) {
|
|
|
4584
5657
|
const preset = MCP_TARGET_PRESETS[target];
|
|
4585
5658
|
const gatewayUrl = env.SUIPAY_GATEWAY_URL?.trim() || preset.gatewayUrl;
|
|
4586
5659
|
const consoleUrl = env.SUIPAY_CONSOLE_URL?.trim() || preset.consoleUrl;
|
|
5660
|
+
const rawDir = env.SUIPAY_EXCLUSION_DIRECTORY_OBJECT_ID?.trim();
|
|
5661
|
+
if (rawDir) assertValidPackageId("SUIPAY_EXCLUSION_DIRECTORY_OBJECT_ID", rawDir);
|
|
4587
5662
|
return {
|
|
4588
5663
|
target,
|
|
4589
5664
|
consoleUrl,
|
|
@@ -4596,9 +5671,13 @@ function loadMcpConfig(env = process.env, argv = []) {
|
|
|
4596
5671
|
sponsorUrl: env.SUIPAY_SPONSOR_URL ?? `${gatewayUrl.replace(/\/$/, "")}/api/sponsor`,
|
|
4597
5672
|
minFunded: BigInt(env.SUIPAY_MIN_FUNDED ?? "0"),
|
|
4598
5673
|
minMaxPerPayment: BigInt(env.SUIPAY_MIN_MAX_PER_PAYMENT ?? "0"),
|
|
4599
|
-
label: env.SUIPAY_LABEL ?? "SuiPay MCP"
|
|
5674
|
+
label: env.SUIPAY_LABEL ?? "SuiPay MCP",
|
|
5675
|
+
exclusionDirectoryObjectId: rawDir || null
|
|
4600
5676
|
};
|
|
4601
5677
|
}
|
|
5678
|
+
function rpcClient(cfg) {
|
|
5679
|
+
return new SuiGrpcClient2({ network: cfg.network, baseUrl: cfg.rpcUrl });
|
|
5680
|
+
}
|
|
4602
5681
|
|
|
4603
5682
|
// src/crypto.ts
|
|
4604
5683
|
import { getPublicKeyAsync, utils } from "@noble/ed25519";
|
|
@@ -4774,7 +5853,7 @@ function walletFromSeedHex(seedHex) {
|
|
|
4774
5853
|
return {
|
|
4775
5854
|
address: kp.getPublicKey().toSuiAddress(),
|
|
4776
5855
|
async signTransaction(txBytes) {
|
|
4777
|
-
const { signature } = await kp.signTransaction(
|
|
5856
|
+
const { signature } = await kp.signTransaction(fromBase647(txBytes));
|
|
4778
5857
|
return signature;
|
|
4779
5858
|
}
|
|
4780
5859
|
};
|
|
@@ -4791,7 +5870,7 @@ function walletFromEnvSecret(raw) {
|
|
|
4791
5870
|
return {
|
|
4792
5871
|
address: kp.getPublicKey().toSuiAddress(),
|
|
4793
5872
|
async signTransaction(txBytes) {
|
|
4794
|
-
const { signature } = await kp.signTransaction(
|
|
5873
|
+
const { signature } = await kp.signTransaction(fromBase647(txBytes));
|
|
4795
5874
|
return signature;
|
|
4796
5875
|
}
|
|
4797
5876
|
};
|
|
@@ -4986,7 +6065,7 @@ function loadLocalSigner() {
|
|
|
4986
6065
|
return {
|
|
4987
6066
|
address: parsed.delegateAddress,
|
|
4988
6067
|
async signTransaction(txBytes) {
|
|
4989
|
-
const { signature } = await kp.signTransaction(
|
|
6068
|
+
const { signature } = await kp.signTransaction(fromBase647(txBytes));
|
|
4990
6069
|
return signature;
|
|
4991
6070
|
},
|
|
4992
6071
|
async signPersonalMessage(message) {
|
|
@@ -5072,7 +6151,7 @@ function objectWithOwner(object) {
|
|
|
5072
6151
|
}
|
|
5073
6152
|
return { ...object.json, owner: object.owner };
|
|
5074
6153
|
}
|
|
5075
|
-
function
|
|
6154
|
+
function sameAddress3(a, b) {
|
|
5076
6155
|
try {
|
|
5077
6156
|
return normalizeSuiAddress8(a) === normalizeSuiAddress8(b);
|
|
5078
6157
|
} catch {
|
|
@@ -5088,7 +6167,7 @@ function resolveAgentHeldSettlement(cfg, spendAccountPay, env = process.env) {
|
|
|
5088
6167
|
if (!secret) return null;
|
|
5089
6168
|
const local = loadLocalSigner();
|
|
5090
6169
|
if (!local) return null;
|
|
5091
|
-
if (!
|
|
6170
|
+
if (!sameAddress3(local.address, spendAccountPay.snapshot.delegateAddress)) {
|
|
5092
6171
|
return null;
|
|
5093
6172
|
}
|
|
5094
6173
|
const chain = createSpendAccountChain({
|
|
@@ -5096,7 +6175,7 @@ function resolveAgentHeldSettlement(cfg, spendAccountPay, env = process.env) {
|
|
|
5096
6175
|
network: cfg.network,
|
|
5097
6176
|
url: cfg.rpcUrl
|
|
5098
6177
|
});
|
|
5099
|
-
const finality =
|
|
6178
|
+
const finality = createSpendAccountSettledTxReader({ client: chain.client });
|
|
5100
6179
|
const getObjectJson = async (objectId) => {
|
|
5101
6180
|
const { object } = await chain.client.core.getObject({
|
|
5102
6181
|
objectId,
|
|
@@ -5119,7 +6198,7 @@ function resolveAgentHeldSettlement(cfg, spendAccountPay, env = process.env) {
|
|
|
5119
6198
|
sponsor: { sponsorUrl: cfg.gatewayUrl },
|
|
5120
6199
|
gasMode: "sponsored",
|
|
5121
6200
|
resolveSharedObjectVersions: (ids) => resolveSharedObjectVersionsFromGetObject(getObjectJson, ids),
|
|
5122
|
-
|
|
6201
|
+
getSettledTx: (digest) => finality.getSettledTx(digest)
|
|
5123
6202
|
}
|
|
5124
6203
|
};
|
|
5125
6204
|
}
|
|
@@ -5176,9 +6255,16 @@ function toolAllowedByScope(toolName, grantedScope) {
|
|
|
5176
6255
|
}
|
|
5177
6256
|
|
|
5178
6257
|
// src/tools.ts
|
|
5179
|
-
import { isValidSuiAddress, normalizeSuiAddress as
|
|
6258
|
+
import { isValidSuiAddress, normalizeSuiAddress as normalizeSuiAddress15 } from "@mysten/sui/utils";
|
|
5180
6259
|
|
|
5181
6260
|
// src/settle.ts
|
|
6261
|
+
function accessModeFromMoveTarget(target) {
|
|
6262
|
+
const fn = target.split("::").pop() ?? "";
|
|
6263
|
+
if (fn === spendAccountDebitFunction({ accessMode: "open" }) || fn === spendAccountDebitFunction({ accessMode: "open", refundable: true })) {
|
|
6264
|
+
return "open";
|
|
6265
|
+
}
|
|
6266
|
+
return "scoped";
|
|
6267
|
+
}
|
|
5182
6268
|
async function settlePreparedMcpPayment(args) {
|
|
5183
6269
|
try {
|
|
5184
6270
|
await verifyPreparedSettlementKind({
|
|
@@ -5188,9 +6274,9 @@ async function settlePreparedMcpPayment(args) {
|
|
|
5188
6274
|
delegateAddress: args.expected.delegateAddress,
|
|
5189
6275
|
amount: args.expected.amount,
|
|
5190
6276
|
recipient: args.expected.recipient,
|
|
5191
|
-
accessMode:
|
|
5192
|
-
|
|
5193
|
-
)
|
|
6277
|
+
accessMode: accessModeFromMoveTarget(
|
|
6278
|
+
args.signingRequest.submit.moveCallTarget
|
|
6279
|
+
)
|
|
5194
6280
|
},
|
|
5195
6281
|
getObjectJson: args.getObjectJson
|
|
5196
6282
|
});
|
|
@@ -5236,12 +6322,104 @@ async function settlePreparedMcpPayment(args) {
|
|
|
5236
6322
|
}
|
|
5237
6323
|
|
|
5238
6324
|
// ../sdk/dist/agent-sdk/delegate-agent.js
|
|
5239
|
-
import { normalizeSuiAddress as
|
|
6325
|
+
import { normalizeSuiAddress as normalizeSuiAddress11 } from "@mysten/sui/utils";
|
|
5240
6326
|
|
|
5241
|
-
// ../sdk/dist/
|
|
6327
|
+
// ../sdk/dist/buyer-control/exclusion-registry.js
|
|
6328
|
+
import { bcs as bcs6 } from "@mysten/sui/bcs";
|
|
5242
6329
|
import { normalizeSuiAddress as normalizeSuiAddress9 } from "@mysten/sui/utils";
|
|
6330
|
+
var ExclusionRegistryResolutionError = class extends Error {
|
|
6331
|
+
code;
|
|
6332
|
+
constructor(code, message) {
|
|
6333
|
+
super(message);
|
|
6334
|
+
this.code = code;
|
|
6335
|
+
this.name = "ExclusionRegistryResolutionError";
|
|
6336
|
+
}
|
|
6337
|
+
};
|
|
6338
|
+
async function resolveExclusionRegistry(input) {
|
|
6339
|
+
const directoryObjectId = typeof input.directoryObjectId === "string" ? input.directoryObjectId.trim() : "";
|
|
6340
|
+
if (!directoryObjectId) {
|
|
6341
|
+
throw new ExclusionRegistryResolutionError("NO_DIRECTORY", "no exclusion directory object id configured for a guarded offer");
|
|
6342
|
+
}
|
|
6343
|
+
let registryId;
|
|
6344
|
+
try {
|
|
6345
|
+
registryId = await input.reader.boundRegistryId(directoryObjectId, input.recipient);
|
|
6346
|
+
} catch (err) {
|
|
6347
|
+
if (err instanceof ExclusionRegistryResolutionError)
|
|
6348
|
+
throw err;
|
|
6349
|
+
throw new ExclusionRegistryResolutionError("REGISTRY_UNREADABLE", "could not read the exclusion directory binding for this recipient");
|
|
6350
|
+
}
|
|
6351
|
+
if (!registryId || !registryId.trim()) {
|
|
6352
|
+
throw new ExclusionRegistryResolutionError("REGISTRY_NOT_PROVISIONED", "no RecipientRegistry is bound for this recipient in the exclusion directory");
|
|
6353
|
+
}
|
|
6354
|
+
const directoryInitialSharedVersion = await input.reader.initialSharedVersion(directoryObjectId);
|
|
6355
|
+
if (!directoryInitialSharedVersion || !directoryInitialSharedVersion.trim()) {
|
|
6356
|
+
throw new ExclusionRegistryResolutionError("DIRECTORY_VERSION_UNRESOLVED", "could not resolve the exclusion directory initial shared version");
|
|
6357
|
+
}
|
|
6358
|
+
const registryInitialSharedVersion = await input.reader.initialSharedVersion(registryId);
|
|
6359
|
+
if (!registryInitialSharedVersion || !registryInitialSharedVersion.trim()) {
|
|
6360
|
+
throw new ExclusionRegistryResolutionError("REGISTRY_VERSION_UNRESOLVED", "could not resolve the recipient registry initial shared version");
|
|
6361
|
+
}
|
|
6362
|
+
return {
|
|
6363
|
+
recipientRegistryId: normalizeSuiAddress9(registryId),
|
|
6364
|
+
directoryInitialSharedVersion,
|
|
6365
|
+
registryInitialSharedVersion
|
|
6366
|
+
};
|
|
6367
|
+
}
|
|
6368
|
+
function initialSharedVersionFromOwner2(owner) {
|
|
6369
|
+
if (!owner || typeof owner !== "object")
|
|
6370
|
+
return null;
|
|
6371
|
+
const shared = owner.Shared;
|
|
6372
|
+
if (shared?.initialSharedVersion != null) {
|
|
6373
|
+
return String(shared.initialSharedVersion);
|
|
6374
|
+
}
|
|
6375
|
+
return null;
|
|
6376
|
+
}
|
|
6377
|
+
function isAbsentDynamicFieldError(err) {
|
|
6378
|
+
const text2 = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
6379
|
+
if (/timeout|timed out|unavailable|econnreset|econnrefused|network|fetch failed|socket| 502| 503| 429/.test(text2)) {
|
|
6380
|
+
return false;
|
|
6381
|
+
}
|
|
6382
|
+
return /\bnot[_ ]found\b|dynamic field not found|no such.*field/.test(text2);
|
|
6383
|
+
}
|
|
6384
|
+
function createGrpcExclusionRegistryReader(client) {
|
|
6385
|
+
return {
|
|
6386
|
+
async boundRegistryId(directoryObjectId, recipient) {
|
|
6387
|
+
let response;
|
|
6388
|
+
try {
|
|
6389
|
+
response = await client.core.getDynamicField({
|
|
6390
|
+
parentId: directoryObjectId,
|
|
6391
|
+
name: { type: "address", bcs: bcs6.Address.serialize(recipient).toBytes() }
|
|
6392
|
+
});
|
|
6393
|
+
} catch (err) {
|
|
6394
|
+
if (isAbsentDynamicFieldError(err))
|
|
6395
|
+
return null;
|
|
6396
|
+
throw new ExclusionRegistryResolutionError("REGISTRY_UNREADABLE", "could not read the exclusion directory binding for this recipient");
|
|
6397
|
+
}
|
|
6398
|
+
const raw = response?.dynamicField?.value?.bcs;
|
|
6399
|
+
if (!raw)
|
|
6400
|
+
return null;
|
|
6401
|
+
try {
|
|
6402
|
+
return normalizeSuiAddress9(bcs6.Address.parse(raw));
|
|
6403
|
+
} catch {
|
|
6404
|
+
return null;
|
|
6405
|
+
}
|
|
6406
|
+
},
|
|
6407
|
+
async initialSharedVersion(objectId) {
|
|
6408
|
+
let response;
|
|
6409
|
+
try {
|
|
6410
|
+
response = await client.core.getObject({ objectId });
|
|
6411
|
+
} catch {
|
|
6412
|
+
return null;
|
|
6413
|
+
}
|
|
6414
|
+
return initialSharedVersionFromOwner2(response?.object?.owner);
|
|
6415
|
+
}
|
|
6416
|
+
};
|
|
6417
|
+
}
|
|
6418
|
+
|
|
6419
|
+
// ../sdk/dist/agent-sdk/verify-prepared-settlement.js
|
|
6420
|
+
import { normalizeSuiAddress as normalizeSuiAddress10 } from "@mysten/sui/utils";
|
|
5243
6421
|
import { Transaction as Transaction5 } from "@mysten/sui/transactions";
|
|
5244
|
-
import { fromBase64 as
|
|
6422
|
+
import { fromBase64 as fromBase648 } from "@mysten/sui/utils";
|
|
5245
6423
|
|
|
5246
6424
|
// ../sdk/dist/protocol/prepared-settlement.js
|
|
5247
6425
|
var PREPARED_SETTLEMENT_VERSION = 1;
|
|
@@ -5269,6 +6447,11 @@ function asPreparedSettlement(value) {
|
|
|
5269
6447
|
"coinType",
|
|
5270
6448
|
"amount",
|
|
5271
6449
|
"recipient",
|
|
6450
|
+
"exclusionDirectoryId",
|
|
6451
|
+
"recipientRegistryId",
|
|
6452
|
+
"exclusionDirectoryInitialSharedVersion",
|
|
6453
|
+
"recipientRegistryInitialSharedVersion",
|
|
6454
|
+
"expiresAtMs",
|
|
5272
6455
|
"payUrl"
|
|
5273
6456
|
];
|
|
5274
6457
|
for (const key of strings) {
|
|
@@ -5293,6 +6476,11 @@ function asPreparedSettlement(value) {
|
|
|
5293
6476
|
coinType: v.coinType,
|
|
5294
6477
|
amount: v.amount,
|
|
5295
6478
|
recipient: v.recipient,
|
|
6479
|
+
exclusionDirectoryId: v.exclusionDirectoryId,
|
|
6480
|
+
recipientRegistryId: v.recipientRegistryId,
|
|
6481
|
+
exclusionDirectoryInitialSharedVersion: v.exclusionDirectoryInitialSharedVersion,
|
|
6482
|
+
recipientRegistryInitialSharedVersion: v.recipientRegistryInitialSharedVersion,
|
|
6483
|
+
expiresAtMs: v.expiresAtMs,
|
|
5296
6484
|
payUrl: v.payUrl
|
|
5297
6485
|
};
|
|
5298
6486
|
}
|
|
@@ -5311,9 +6499,9 @@ var PreparedSettlementRejected = class extends Error {
|
|
|
5311
6499
|
function reject(field, message) {
|
|
5312
6500
|
throw new PreparedSettlementRejected(field, message);
|
|
5313
6501
|
}
|
|
5314
|
-
function
|
|
6502
|
+
function sameAddress4(a, b) {
|
|
5315
6503
|
try {
|
|
5316
|
-
return
|
|
6504
|
+
return normalizeSuiAddress10(a) === normalizeSuiAddress10(b);
|
|
5317
6505
|
} catch {
|
|
5318
6506
|
return false;
|
|
5319
6507
|
}
|
|
@@ -5324,7 +6512,7 @@ function sameCoinType(a, b) {
|
|
|
5324
6512
|
if (pkg === void 0 || rest.length !== 2)
|
|
5325
6513
|
return value.trim();
|
|
5326
6514
|
try {
|
|
5327
|
-
return `${
|
|
6515
|
+
return `${normalizeSuiAddress10(pkg)}::${rest.join("::")}`;
|
|
5328
6516
|
} catch {
|
|
5329
6517
|
return value.trim();
|
|
5330
6518
|
}
|
|
@@ -5344,26 +6532,26 @@ async function verifyPreparedPayment(input) {
|
|
|
5344
6532
|
reject("shape", "the 402 carried no usable prepared settlement");
|
|
5345
6533
|
}
|
|
5346
6534
|
const { offer, delegateAddress } = input;
|
|
5347
|
-
if (!
|
|
6535
|
+
if (!sameAddress4(prepared.sender, delegateAddress)) {
|
|
5348
6536
|
reject("sender", `prepared sender ${prepared.sender} is not this agent's address`);
|
|
5349
6537
|
}
|
|
5350
6538
|
let data;
|
|
5351
6539
|
try {
|
|
5352
|
-
data = Transaction5.from(
|
|
6540
|
+
data = Transaction5.from(fromBase648(prepared.transactionBytes)).getData();
|
|
5353
6541
|
} catch {
|
|
5354
6542
|
reject("transactionBytes", "prepared transaction bytes are not a Sui transaction");
|
|
5355
6543
|
}
|
|
5356
6544
|
const gasOwner = data.gasData?.owner;
|
|
5357
|
-
if (!gasOwner ||
|
|
6545
|
+
if (!gasOwner || sameAddress4(gasOwner, delegateAddress)) {
|
|
5358
6546
|
reject("gasOwner", "prepared transaction is not sponsored: gas would be paid by this agent");
|
|
5359
6547
|
}
|
|
5360
|
-
if (input.expectedGrantObjectId && !
|
|
6548
|
+
if (input.expectedGrantObjectId && !sameAddress4(prepared.grantObjectId, input.expectedGrantObjectId)) {
|
|
5361
6549
|
reject("grantObjectId", `prepared settlement draws on grant ${prepared.grantObjectId}, not the one this agent resolved`);
|
|
5362
6550
|
}
|
|
5363
|
-
if (input.expectedPoolObjectId && !
|
|
6551
|
+
if (input.expectedPoolObjectId && !sameAddress4(prepared.poolObjectId, input.expectedPoolObjectId)) {
|
|
5364
6552
|
reject("poolObjectId", `prepared settlement draws on pool ${prepared.poolObjectId}, not the one this agent resolved`);
|
|
5365
6553
|
}
|
|
5366
|
-
if (!offer.packageId || !
|
|
6554
|
+
if (!offer.packageId || !sameAddress4(prepared.packageId, offer.packageId)) {
|
|
5367
6555
|
reject("packageId", "prepared package id does not match the offer");
|
|
5368
6556
|
}
|
|
5369
6557
|
const wantedTarget = payMoveCallTarget(offer.packageId, prepared.accessMode);
|
|
@@ -5376,9 +6564,19 @@ async function verifyPreparedPayment(input) {
|
|
|
5376
6564
|
if (!sameAmount(prepared.amount, offer.amount)) {
|
|
5377
6565
|
reject("amount", "prepared amount does not match the offer amount");
|
|
5378
6566
|
}
|
|
5379
|
-
if (!
|
|
6567
|
+
if (!sameAddress4(prepared.recipient, offer.payTo)) {
|
|
5380
6568
|
reject("recipient", "prepared recipient does not match the offer payTo");
|
|
5381
6569
|
}
|
|
6570
|
+
const identity = parseRegistryIdentity(offer.registryIdentity);
|
|
6571
|
+
if (!identity) {
|
|
6572
|
+
reject("registryIdentity", "offer carries no valid registry identity for a guarded settle");
|
|
6573
|
+
}
|
|
6574
|
+
if (!sameAddress4(prepared.exclusionDirectoryId, identity.directoryId)) {
|
|
6575
|
+
reject("exclusionDirectoryId", "prepared exclusion directory is not the one the offer committed to");
|
|
6576
|
+
}
|
|
6577
|
+
if (prepared.expiresAtMs !== String(offer.expiresAt)) {
|
|
6578
|
+
reject("expiresAtMs", "prepared guarded expiry does not match the offer expiry");
|
|
6579
|
+
}
|
|
5382
6580
|
const targetHash = offer.targetHash?.trim();
|
|
5383
6581
|
if (!targetHash) {
|
|
5384
6582
|
reject("targetHash", "offer carries no targetHash to bind the settlement to");
|
|
@@ -5398,7 +6596,14 @@ async function verifyPreparedPayment(input) {
|
|
|
5398
6596
|
targetHash,
|
|
5399
6597
|
amount: offer.amount,
|
|
5400
6598
|
paymentIdHash: paymentIdHash(offer.challengeId),
|
|
5401
|
-
termsHash: termsHash(offer)
|
|
6599
|
+
termsHash: termsHash(offer),
|
|
6600
|
+
// Guarded-settle inputs, rebuilt from the prepared bytes and validated
|
|
6601
|
+
// against the offer above.
|
|
6602
|
+
exclusionDirectoryId: prepared.exclusionDirectoryId,
|
|
6603
|
+
recipientRegistryId: prepared.recipientRegistryId,
|
|
6604
|
+
exclusionDirectoryInitialSharedVersion: prepared.exclusionDirectoryInitialSharedVersion,
|
|
6605
|
+
recipientRegistryInitialSharedVersion: prepared.recipientRegistryInitialSharedVersion,
|
|
6606
|
+
expiresAtMs: prepared.expiresAtMs
|
|
5402
6607
|
};
|
|
5403
6608
|
let rebuilt;
|
|
5404
6609
|
try {
|
|
@@ -5894,6 +7099,12 @@ function mergeObjectEnvelope(object) {
|
|
|
5894
7099
|
var SECRET_KEY_SET = new Set(FORBIDDEN_SECRET_KEYS.map((k) => k.toLowerCase()));
|
|
5895
7100
|
|
|
5896
7101
|
// ../sdk/dist/agent-sdk/delegate-agent.js
|
|
7102
|
+
function createExclusionRegistryResolver(client, exclusionDirectoryObjectId) {
|
|
7103
|
+
if (!exclusionDirectoryObjectId || !exclusionDirectoryObjectId.trim())
|
|
7104
|
+
return void 0;
|
|
7105
|
+
const reader = createGrpcExclusionRegistryReader(client);
|
|
7106
|
+
return async ({ directoryId, recipient }) => resolveExclusionRegistry({ directoryObjectId: directoryId, recipient, reader });
|
|
7107
|
+
}
|
|
5897
7108
|
async function verifyGrantOnChain(input) {
|
|
5898
7109
|
let state;
|
|
5899
7110
|
try {
|
|
@@ -5920,10 +7131,10 @@ async function verifyGrantOnChain(input) {
|
|
|
5920
7131
|
ok: false,
|
|
5921
7132
|
error: { code: MANIFEST_ERROR_CODES.GRANT_NOT_VERIFIED, detail }
|
|
5922
7133
|
});
|
|
5923
|
-
if (!state.delegateAddress ||
|
|
7134
|
+
if (!state.delegateAddress || normalizeSuiAddress11(state.delegateAddress) !== normalizeSuiAddress11(input.delegateAddress)) {
|
|
5924
7135
|
return notVerified(`grant ${input.grantObjectId} does not name this delegate on chain`);
|
|
5925
7136
|
}
|
|
5926
|
-
if (!state.poolObjectId ||
|
|
7137
|
+
if (!state.poolObjectId || normalizeSuiAddress11(state.poolObjectId) !== normalizeSuiAddress11(input.poolObjectId)) {
|
|
5927
7138
|
return notVerified(`grant ${input.grantObjectId} draws on a different pool than the one resolved`);
|
|
5928
7139
|
}
|
|
5929
7140
|
if (state.revoked === true) {
|
|
@@ -5987,7 +7198,7 @@ async function createPayer(options) {
|
|
|
5987
7198
|
}
|
|
5988
7199
|
};
|
|
5989
7200
|
}
|
|
5990
|
-
const finality =
|
|
7201
|
+
const finality = createSpendAccountSettledTxReader({ client: chain.client });
|
|
5991
7202
|
const getObjectJson = async (objectId) => {
|
|
5992
7203
|
const { object } = await chain.client.core.getObject({
|
|
5993
7204
|
objectId,
|
|
@@ -6010,7 +7221,18 @@ async function createPayer(options) {
|
|
|
6010
7221
|
sponsor: { sponsorUrl: options.gatewayUrl },
|
|
6011
7222
|
gasMode: "sponsored",
|
|
6012
7223
|
resolveSharedObjectVersions: (ids) => resolveSharedObjectVersionsFromGetObject(getObjectJson, ids),
|
|
6013
|
-
|
|
7224
|
+
getSettledTx: finality.getSettledTx,
|
|
7225
|
+
// A guarded offer resolves its per-recipient exclusion registry
|
|
7226
|
+
// through this dep, bound to the agent's own gRPC reader. Undefined when no
|
|
7227
|
+
// directory id was configured, so such an offer fails closed (BUILD_FAILED)
|
|
7228
|
+
// rather than building an unguarded settle. Unguarded offers never call it.
|
|
7229
|
+
...(() => {
|
|
7230
|
+
const resolve = createExclusionRegistryResolver(chain.client, options.exclusionDirectoryObjectId);
|
|
7231
|
+
return resolve ? { resolveExclusionRegistry: resolve } : {};
|
|
7232
|
+
})(),
|
|
7233
|
+
// x402 bounds the transaction's expiration by the current epoch, read from
|
|
7234
|
+
// the same client as every other chain read here.
|
|
7235
|
+
getCurrentEpoch: currentEpochReader(chain.client),
|
|
6014
7236
|
...options.offerSecret ? { offerSecret: options.offerSecret } : {},
|
|
6015
7237
|
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {},
|
|
6016
7238
|
...options.logError ? { logError: options.logError } : {}
|
|
@@ -6040,7 +7262,9 @@ async function createPayer(options) {
|
|
|
6040
7262
|
cfg: { gatewayUrl: options.gatewayUrl },
|
|
6041
7263
|
deps,
|
|
6042
7264
|
intent: input.intent,
|
|
6043
|
-
...input.preparedTerms ? { preparedTerms: input.preparedTerms } : {}
|
|
7265
|
+
...input.preparedTerms ? { preparedTerms: input.preparedTerms } : {},
|
|
7266
|
+
dialect: input.dialect,
|
|
7267
|
+
onX402Progress: input.onX402Progress
|
|
6044
7268
|
});
|
|
6045
7269
|
}
|
|
6046
7270
|
async function verifySettlement(prepared, offer) {
|
|
@@ -6346,12 +7570,26 @@ async function startStdioLogin(opts) {
|
|
|
6346
7570
|
}
|
|
6347
7571
|
|
|
6348
7572
|
// src/spend-account-pay.ts
|
|
6349
|
-
import { Transaction as
|
|
7573
|
+
import { Transaction as Transaction8 } from "@mysten/sui/transactions";
|
|
6350
7574
|
import {
|
|
6351
|
-
fromBase64 as
|
|
6352
|
-
normalizeSuiAddress as
|
|
6353
|
-
toBase64 as
|
|
7575
|
+
fromBase64 as fromBase6411,
|
|
7576
|
+
normalizeSuiAddress as normalizeSuiAddress14,
|
|
7577
|
+
toBase64 as toBase647
|
|
6354
7578
|
} from "@mysten/sui/utils";
|
|
7579
|
+
|
|
7580
|
+
// ../sdk/dist/pay-operation/verify-x402-offer.js
|
|
7581
|
+
import { normalizeStructTag as normalizeStructTag7, normalizeSuiAddress as normalizeSuiAddress12 } from "@mysten/sui/utils";
|
|
7582
|
+
|
|
7583
|
+
// ../sdk/dist/pay-operation/verify-direct-tx.js
|
|
7584
|
+
import { Transaction as Transaction6 } from "@mysten/sui/transactions";
|
|
7585
|
+
import { bcs as bcs7 } from "@mysten/sui/bcs";
|
|
7586
|
+
import { fromBase64 as fromBase649, normalizeStructTag as normalizeStructTag8, normalizeSuiAddress as normalizeSuiAddress13 } from "@mysten/sui/utils";
|
|
7587
|
+
|
|
7588
|
+
// ../sdk/dist/pay-operation/resolve-grant-x402.js
|
|
7589
|
+
import { Transaction as Transaction7 } from "@mysten/sui/transactions";
|
|
7590
|
+
import { fromBase64 as fromBase6410, toBase64 as toBase646 } from "@mysten/sui/utils";
|
|
7591
|
+
|
|
7592
|
+
// src/spend-account-pay.ts
|
|
6355
7593
|
var SUI_CLOCK3 = "0x6";
|
|
6356
7594
|
function traceEmitter(trace) {
|
|
6357
7595
|
if (!trace) return () => void 0;
|
|
@@ -6386,17 +7624,30 @@ function toHex(bytes) {
|
|
|
6386
7624
|
async function buildSpendAccountPayKind2(input) {
|
|
6387
7625
|
if (!input.termsHash.length) throw new Error("termsHash must be non-empty");
|
|
6388
7626
|
if (!input.paymentIdHash.length) throw new Error("paymentIdHash must be non-empty");
|
|
6389
|
-
const auth = defaultAuthorityForRail("delegate", input.packageId);
|
|
6390
|
-
const target = `${normalizeSuiAddress11(auth.packageId)}::${auth.module}::${auth.payFunction}`;
|
|
6391
7627
|
const hashHex = input.targetHash.replace(/^0x/i, "").toLowerCase();
|
|
6392
7628
|
if (!/^[0-9a-f]{64}$/.test(hashHex)) throw new Error("targetHash must be 32-byte hex");
|
|
6393
7629
|
const hash = Uint8Array.from(Buffer.from(hashHex, "hex"));
|
|
6394
|
-
const
|
|
7630
|
+
const guarded = resolveGuardedSettleOfflineInputs(input);
|
|
7631
|
+
const target = payMoveCallTarget(
|
|
7632
|
+
input.packageId,
|
|
7633
|
+
"scoped"
|
|
7634
|
+
);
|
|
7635
|
+
const tx = new Transaction8();
|
|
6395
7636
|
tx.setSender(input.delegate);
|
|
6396
7637
|
tx.moveCall({
|
|
6397
7638
|
target,
|
|
6398
7639
|
typeArguments: [input.coinType],
|
|
6399
7640
|
arguments: [
|
|
7641
|
+
tx.sharedObjectRef({
|
|
7642
|
+
objectId: guarded.exclusionDirectoryId,
|
|
7643
|
+
initialSharedVersion: guarded.exclusionDirectoryInitialSharedVersion,
|
|
7644
|
+
mutable: false
|
|
7645
|
+
}),
|
|
7646
|
+
tx.sharedObjectRef({
|
|
7647
|
+
objectId: guarded.recipientRegistryId,
|
|
7648
|
+
initialSharedVersion: guarded.recipientRegistryInitialSharedVersion,
|
|
7649
|
+
mutable: true
|
|
7650
|
+
}),
|
|
6400
7651
|
tx.sharedObjectRef({
|
|
6401
7652
|
objectId: input.poolObjectId,
|
|
6402
7653
|
initialSharedVersion: "1",
|
|
@@ -6412,6 +7663,7 @@ async function buildSpendAccountPayKind2(input) {
|
|
|
6412
7663
|
tx.pure.u64(BigInt(input.amount)),
|
|
6413
7664
|
tx.pure.vector("u8", Array.from(input.paymentIdHash)),
|
|
6414
7665
|
tx.pure.vector("u8", Array.from(input.termsHash)),
|
|
7666
|
+
tx.pure.u64(BigInt(guarded.expiresAtMs)),
|
|
6415
7667
|
tx.sharedObjectRef({
|
|
6416
7668
|
objectId: SUI_CLOCK3,
|
|
6417
7669
|
initialSharedVersion: "1",
|
|
@@ -6420,34 +7672,34 @@ async function buildSpendAccountPayKind2(input) {
|
|
|
6420
7672
|
]
|
|
6421
7673
|
});
|
|
6422
7674
|
const bytes = await tx.build({ onlyTransactionKind: true });
|
|
6423
|
-
return { kindBytes:
|
|
7675
|
+
return { kindBytes: toBase647(bytes) };
|
|
6424
7676
|
}
|
|
6425
7677
|
function verifySpendAccountPayReconstructs(bytes, expected) {
|
|
6426
7678
|
let data;
|
|
6427
7679
|
try {
|
|
6428
|
-
data =
|
|
7680
|
+
data = Transaction8.from(fromBase6411(bytes)).getData();
|
|
6429
7681
|
} catch {
|
|
6430
7682
|
throw new Error("sponsored transaction is not valid Sui transaction bytes");
|
|
6431
7683
|
}
|
|
6432
|
-
if (!data.sender ||
|
|
7684
|
+
if (!data.sender || normalizeSuiAddress14(data.sender) !== normalizeSuiAddress14(expected.delegate)) {
|
|
6433
7685
|
throw new Error("sponsored transaction sender does not match delegate");
|
|
6434
7686
|
}
|
|
6435
7687
|
if (!data.commands || data.commands.length !== 1) {
|
|
6436
7688
|
throw new Error("sponsored transaction must contain exactly one command");
|
|
6437
7689
|
}
|
|
6438
7690
|
const auth = defaultAuthorityForRail("delegate", expected.packageId);
|
|
6439
|
-
const wanted = `${
|
|
7691
|
+
const wanted = `${normalizeSuiAddress14(auth.packageId)}::${auth.module}::${auth.payFunction}`;
|
|
6440
7692
|
const cmd = data.commands[0];
|
|
6441
7693
|
if (cmd.$kind !== "MoveCall" || !cmd.MoveCall) {
|
|
6442
7694
|
throw new Error("sponsored transaction is not a MoveCall");
|
|
6443
7695
|
}
|
|
6444
|
-
const got = `${
|
|
7696
|
+
const got = `${normalizeSuiAddress14(String(cmd.MoveCall.package))}::${cmd.MoveCall.module}::${cmd.MoveCall.function}`;
|
|
6445
7697
|
if (got !== wanted) {
|
|
6446
7698
|
throw new Error(`sponsored transaction does not reconstruct ${wanted}`);
|
|
6447
7699
|
}
|
|
6448
7700
|
let expectedKind;
|
|
6449
7701
|
try {
|
|
6450
|
-
expectedKind =
|
|
7702
|
+
expectedKind = Transaction8.fromKind(fromBase6411(expected.kindBytes)).getData();
|
|
6451
7703
|
} catch {
|
|
6452
7704
|
throw new Error("sponsored transaction kind baseline invalid");
|
|
6453
7705
|
}
|
|
@@ -6479,6 +7731,13 @@ async function prepareSpendAccountPayment(args) {
|
|
|
6479
7731
|
const isOpen = session.snapshot.accessMode === "open";
|
|
6480
7732
|
const callerBuild = deps.buildPayKind;
|
|
6481
7733
|
const buildPayKind = async (input) => {
|
|
7734
|
+
const guardedFields = {
|
|
7735
|
+
exclusionDirectoryId: input.exclusionDirectoryId,
|
|
7736
|
+
recipientRegistryId: input.recipientRegistryId,
|
|
7737
|
+
expiresAtMs: input.expiresAtMs,
|
|
7738
|
+
exclusionDirectoryInitialSharedVersion: input.exclusionDirectoryInitialSharedVersion,
|
|
7739
|
+
recipientRegistryInitialSharedVersion: input.recipientRegistryInitialSharedVersion
|
|
7740
|
+
};
|
|
6482
7741
|
if (callerBuild) {
|
|
6483
7742
|
return callerBuild({
|
|
6484
7743
|
delegate: input.delegate,
|
|
@@ -6491,7 +7750,8 @@ async function prepareSpendAccountPayment(args) {
|
|
|
6491
7750
|
paymentIdHash: input.paymentIdHash,
|
|
6492
7751
|
termsHash: input.termsHash,
|
|
6493
7752
|
recipient: input.recipient,
|
|
6494
|
-
accessMode: input.accessMode
|
|
7753
|
+
accessMode: input.accessMode,
|
|
7754
|
+
...guardedFields
|
|
6495
7755
|
});
|
|
6496
7756
|
}
|
|
6497
7757
|
if (isOpen) {
|
|
@@ -6507,7 +7767,8 @@ async function prepareSpendAccountPayment(args) {
|
|
|
6507
7767
|
targetHash: input.targetHash,
|
|
6508
7768
|
amount: input.amount,
|
|
6509
7769
|
paymentIdHash: input.paymentIdHash,
|
|
6510
|
-
termsHash: input.termsHash
|
|
7770
|
+
termsHash: input.termsHash,
|
|
7771
|
+
...guardedFields
|
|
6511
7772
|
});
|
|
6512
7773
|
}
|
|
6513
7774
|
return buildSpendAccountPayKind2({
|
|
@@ -6520,7 +7781,8 @@ async function prepareSpendAccountPayment(args) {
|
|
|
6520
7781
|
targetHash: input.targetHash,
|
|
6521
7782
|
amount: input.amount,
|
|
6522
7783
|
paymentIdHash: input.paymentIdHash,
|
|
6523
|
-
termsHash: input.termsHash
|
|
7784
|
+
termsHash: input.termsHash,
|
|
7785
|
+
...guardedFields
|
|
6524
7786
|
});
|
|
6525
7787
|
};
|
|
6526
7788
|
const rawFetch = deps.fetchImpl ?? fetch;
|
|
@@ -6540,10 +7802,19 @@ async function prepareSpendAccountPayment(args) {
|
|
|
6540
7802
|
authority: session.snapshot,
|
|
6541
7803
|
assertLive: session.assertLive
|
|
6542
7804
|
};
|
|
7805
|
+
const resolveExclusionRegistry2 = deps.resolveExclusionRegistry ?? (cfg.exclusionDirectoryObjectId ? (() => {
|
|
7806
|
+
const reader = createGrpcExclusionRegistryReader(rpcClient(cfg));
|
|
7807
|
+
return async ({ directoryId, recipient }) => resolveExclusionRegistry({
|
|
7808
|
+
directoryObjectId: directoryId,
|
|
7809
|
+
recipient,
|
|
7810
|
+
reader
|
|
7811
|
+
});
|
|
7812
|
+
})() : void 0);
|
|
6543
7813
|
const resolveDeps = {
|
|
6544
7814
|
fetchImpl: wrappedFetch,
|
|
6545
7815
|
preferredDialect: preferred,
|
|
6546
|
-
buildPayKind
|
|
7816
|
+
buildPayKind,
|
|
7817
|
+
...resolveExclusionRegistry2 ? { resolveExclusionRegistry: resolveExclusionRegistry2 } : {}
|
|
6547
7818
|
};
|
|
6548
7819
|
const result = await resolvePayment(resolveInput, resolveDeps);
|
|
6549
7820
|
if (result.status === "unpaid_ok") {
|
|
@@ -6604,7 +7875,7 @@ async function prepareSpendAccountPayment(args) {
|
|
|
6604
7875
|
const signingRequest = {
|
|
6605
7876
|
unsignedKindBytes: kindBytes,
|
|
6606
7877
|
delegateAddress: resolved.delegateAddress,
|
|
6607
|
-
packageId:
|
|
7878
|
+
packageId: normalizeSuiAddress14(authority.packageId),
|
|
6608
7879
|
poolObjectId: resolved.poolObjectId,
|
|
6609
7880
|
grantObjectId: resolved.grantObjectId,
|
|
6610
7881
|
policyId: resolved.onChainPolicyId,
|
|
@@ -6686,11 +7957,11 @@ function mapErrorCode(code) {
|
|
|
6686
7957
|
}
|
|
6687
7958
|
|
|
6688
7959
|
// src/discover-afford.ts
|
|
6689
|
-
import { normalizeStructTag as
|
|
7960
|
+
import { normalizeStructTag as normalizeStructTag9 } from "@mysten/sui/utils";
|
|
6690
7961
|
function sameCoinType2(rowAsset, grantCoin) {
|
|
6691
7962
|
if (typeof rowAsset !== "string" || !grantCoin) return false;
|
|
6692
7963
|
try {
|
|
6693
|
-
return
|
|
7964
|
+
return normalizeStructTag9(rowAsset) === normalizeStructTag9(grantCoin);
|
|
6694
7965
|
} catch {
|
|
6695
7966
|
return false;
|
|
6696
7967
|
}
|
|
@@ -7002,6 +8273,205 @@ function settlePayResult(result) {
|
|
|
7002
8273
|
return error(result);
|
|
7003
8274
|
}
|
|
7004
8275
|
|
|
8276
|
+
// src/pay-file.ts
|
|
8277
|
+
import { isAbsolute, dirname as dirname3, sep } from "path";
|
|
8278
|
+
import {
|
|
8279
|
+
openSync as openSync2,
|
|
8280
|
+
closeSync as closeSync2,
|
|
8281
|
+
fstatSync,
|
|
8282
|
+
readFileSync as readFileSync2,
|
|
8283
|
+
realpathSync,
|
|
8284
|
+
constants
|
|
8285
|
+
} from "fs";
|
|
8286
|
+
var JSON_WRAP_SLACK_BYTES = 256;
|
|
8287
|
+
var MCP_MAX_FILE_FIELD_BYTES = Math.floor(
|
|
8288
|
+
(MAX_PAID_BODY_BYTES - JSON_WRAP_SLACK_BYTES) * 3 / 4
|
|
8289
|
+
);
|
|
8290
|
+
var FIELD = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
8291
|
+
var FORBIDDEN = /* @__PURE__ */ new Set(["constructor", "prototype"]);
|
|
8292
|
+
function fail2(code, detail) {
|
|
8293
|
+
return { code, detail };
|
|
8294
|
+
}
|
|
8295
|
+
function hasLocalFileArgs(input) {
|
|
8296
|
+
return input.file !== void 0 && input.file !== null || input.fileField !== void 0 && input.fileField !== null;
|
|
8297
|
+
}
|
|
8298
|
+
function parseField(value) {
|
|
8299
|
+
if (typeof value !== "string" || !FIELD.test(value) || FORBIDDEN.has(value)) {
|
|
8300
|
+
return fail2("INVALID_FILE_FIELD", "fileField must be a simple JSON key");
|
|
8301
|
+
}
|
|
8302
|
+
return value;
|
|
8303
|
+
}
|
|
8304
|
+
function parsePath(value) {
|
|
8305
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("\0")) {
|
|
8306
|
+
return fail2("INVALID_FILE", "file must be a non-empty absolute path");
|
|
8307
|
+
}
|
|
8308
|
+
if (!isAbsolute(value) || value.includes("://")) {
|
|
8309
|
+
return fail2("INVALID_FILE", "file must be an absolute filesystem path, not a URL");
|
|
8310
|
+
}
|
|
8311
|
+
return value;
|
|
8312
|
+
}
|
|
8313
|
+
function parseMethod(value) {
|
|
8314
|
+
if (value === void 0 || value === null) return "GET";
|
|
8315
|
+
if (typeof value !== "string") {
|
|
8316
|
+
return fail2("MALFORMED_REQUEST", "method must be GET or POST");
|
|
8317
|
+
}
|
|
8318
|
+
const upper = value.trim().toUpperCase();
|
|
8319
|
+
if (upper === "GET" || upper === "POST") return upper;
|
|
8320
|
+
return fail2("MALFORMED_REQUEST", "method must be GET or POST");
|
|
8321
|
+
}
|
|
8322
|
+
function inside(root, candidate) {
|
|
8323
|
+
const prefix = root.endsWith(sep) ? root : root + sep;
|
|
8324
|
+
return candidate === root || candidate.startsWith(prefix);
|
|
8325
|
+
}
|
|
8326
|
+
function resolveContainedPath(raw) {
|
|
8327
|
+
const rootRaw = process.env.SUIPAY_MCP_FILE_ROOT;
|
|
8328
|
+
if (typeof rootRaw !== "string" || rootRaw.trim() === "") {
|
|
8329
|
+
return fail2(
|
|
8330
|
+
"FILE_ROOT_UNSET",
|
|
8331
|
+
"file attach requires SUIPAY_MCP_FILE_ROOT to be an absolute directory"
|
|
8332
|
+
);
|
|
8333
|
+
}
|
|
8334
|
+
if (!isAbsolute(rootRaw) || rootRaw.includes("://")) {
|
|
8335
|
+
return fail2(
|
|
8336
|
+
"FILE_ROOT_UNSET",
|
|
8337
|
+
"SUIPAY_MCP_FILE_ROOT must be an absolute filesystem path"
|
|
8338
|
+
);
|
|
8339
|
+
}
|
|
8340
|
+
let rootReal;
|
|
8341
|
+
let fileReal;
|
|
8342
|
+
try {
|
|
8343
|
+
rootReal = realpathSync(rootRaw);
|
|
8344
|
+
} catch {
|
|
8345
|
+
return fail2("FILE_ROOT_UNSET", "SUIPAY_MCP_FILE_ROOT is not a readable directory");
|
|
8346
|
+
}
|
|
8347
|
+
try {
|
|
8348
|
+
fileReal = realpathSync(raw);
|
|
8349
|
+
} catch (err) {
|
|
8350
|
+
const code = err.code;
|
|
8351
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
8352
|
+
return fail2("FILE_NOT_FOUND", "file does not exist");
|
|
8353
|
+
}
|
|
8354
|
+
if (code === "EACCES") {
|
|
8355
|
+
return fail2("FILE_EACCES", "file is not readable");
|
|
8356
|
+
}
|
|
8357
|
+
return fail2(
|
|
8358
|
+
"INVALID_FILE",
|
|
8359
|
+
`file could not be read (${code ?? "unknown error"})`
|
|
8360
|
+
);
|
|
8361
|
+
}
|
|
8362
|
+
if (!inside(rootReal, fileReal)) {
|
|
8363
|
+
return fail2("FILE_OUTSIDE_ROOT", "file must be under SUIPAY_MCP_FILE_ROOT");
|
|
8364
|
+
}
|
|
8365
|
+
const credsDir2 = dirname3(mcpCredentialsPath());
|
|
8366
|
+
let credsReal = credsDir2;
|
|
8367
|
+
try {
|
|
8368
|
+
credsReal = realpathSync(credsDir2);
|
|
8369
|
+
} catch {
|
|
8370
|
+
}
|
|
8371
|
+
if (inside(credsReal, fileReal)) {
|
|
8372
|
+
return fail2("FILE_FORBIDDEN", "file must not be under ~/.suipay");
|
|
8373
|
+
}
|
|
8374
|
+
return fileReal;
|
|
8375
|
+
}
|
|
8376
|
+
function readRegularFile(path) {
|
|
8377
|
+
if (constants.O_NOFOLLOW === void 0 || constants.O_NONBLOCK === void 0) {
|
|
8378
|
+
return fail2("INVALID_FILE", "symlink-safe nonblocking open is unavailable on this platform");
|
|
8379
|
+
}
|
|
8380
|
+
let fd;
|
|
8381
|
+
try {
|
|
8382
|
+
const flags = constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
8383
|
+
fd = openSync2(path, flags);
|
|
8384
|
+
const st = fstatSync(fd);
|
|
8385
|
+
if (!st.isFile()) {
|
|
8386
|
+
return fail2("INVALID_FILE", "file must be a regular file");
|
|
8387
|
+
}
|
|
8388
|
+
if (st.size > MCP_MAX_FILE_FIELD_BYTES) {
|
|
8389
|
+
return fail2(
|
|
8390
|
+
"FILE_TOO_LARGE",
|
|
8391
|
+
`file is ${st.size} bytes; exceeds ${MCP_MAX_FILE_FIELD_BYTES} decoded bytes`
|
|
8392
|
+
);
|
|
8393
|
+
}
|
|
8394
|
+
const bytes = readFileSync2(fd);
|
|
8395
|
+
if (bytes.byteLength === 0) {
|
|
8396
|
+
return fail2("EMPTY_FILE", "file is empty");
|
|
8397
|
+
}
|
|
8398
|
+
if (bytes.byteLength > MCP_MAX_FILE_FIELD_BYTES) {
|
|
8399
|
+
return fail2(
|
|
8400
|
+
"FILE_TOO_LARGE",
|
|
8401
|
+
`file is ${bytes.byteLength} bytes; exceeds ${MCP_MAX_FILE_FIELD_BYTES} decoded bytes`
|
|
8402
|
+
);
|
|
8403
|
+
}
|
|
8404
|
+
return bytes;
|
|
8405
|
+
} catch (err) {
|
|
8406
|
+
const code = err.code;
|
|
8407
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
8408
|
+
return fail2("FILE_NOT_FOUND", "file does not exist");
|
|
8409
|
+
}
|
|
8410
|
+
if (code === "ELOOP" || code === "EMLINK" || code === "ENXIO") {
|
|
8411
|
+
return fail2("INVALID_FILE", "file must be a regular file");
|
|
8412
|
+
}
|
|
8413
|
+
if (code === "EACCES") return fail2("FILE_EACCES", "file is not readable");
|
|
8414
|
+
return fail2(
|
|
8415
|
+
"INVALID_FILE",
|
|
8416
|
+
`file could not be read (${code ?? "unknown error"})`
|
|
8417
|
+
);
|
|
8418
|
+
} finally {
|
|
8419
|
+
if (fd !== void 0) {
|
|
8420
|
+
try {
|
|
8421
|
+
closeSync2(fd);
|
|
8422
|
+
} catch {
|
|
8423
|
+
}
|
|
8424
|
+
}
|
|
8425
|
+
}
|
|
8426
|
+
}
|
|
8427
|
+
function toCanonicalBase64(bytes) {
|
|
8428
|
+
return Buffer.from(bytes).toString("base64");
|
|
8429
|
+
}
|
|
8430
|
+
function jsonWithLocalFile(input) {
|
|
8431
|
+
const hasFile = input.file !== void 0 && input.file !== null;
|
|
8432
|
+
const hasField = input.fileField !== void 0 && input.fileField !== null;
|
|
8433
|
+
if (!hasFile && !hasField) return { json: input.json };
|
|
8434
|
+
if (hasFile !== hasField) {
|
|
8435
|
+
return {
|
|
8436
|
+
error: fail2(
|
|
8437
|
+
"INVALID_FILE_ARGS",
|
|
8438
|
+
"file and fileField are required together"
|
|
8439
|
+
)
|
|
8440
|
+
};
|
|
8441
|
+
}
|
|
8442
|
+
const method = parseMethod(input.method);
|
|
8443
|
+
if (typeof method !== "string") return { error: method };
|
|
8444
|
+
if (method !== "POST") {
|
|
8445
|
+
return { error: fail2("INVALID_FILE_ARGS", "file is only valid on POST") };
|
|
8446
|
+
}
|
|
8447
|
+
const field = parseField(input.fileField);
|
|
8448
|
+
if (typeof field !== "string") return { error: field };
|
|
8449
|
+
const path = parsePath(input.file);
|
|
8450
|
+
if (typeof path !== "string") return { error: path };
|
|
8451
|
+
let json;
|
|
8452
|
+
if (input.json === void 0) {
|
|
8453
|
+
json = {};
|
|
8454
|
+
} else if (typeof input.json === "object" && input.json !== null && !Array.isArray(input.json)) {
|
|
8455
|
+
json = { ...input.json };
|
|
8456
|
+
} else {
|
|
8457
|
+
return { error: fail2("MALFORMED_REQUEST", "POST requires json object") };
|
|
8458
|
+
}
|
|
8459
|
+
if (Object.prototype.hasOwnProperty.call(json, field)) {
|
|
8460
|
+
return {
|
|
8461
|
+
error: fail2(
|
|
8462
|
+
"FILE_FIELD_EXISTS",
|
|
8463
|
+
"json already has fileField; refuse to overwrite"
|
|
8464
|
+
)
|
|
8465
|
+
};
|
|
8466
|
+
}
|
|
8467
|
+
const contained = resolveContainedPath(path);
|
|
8468
|
+
if (typeof contained !== "string") return { error: contained };
|
|
8469
|
+
const bytes = readRegularFile(contained);
|
|
8470
|
+
if (!(bytes instanceof Uint8Array)) return { error: bytes };
|
|
8471
|
+
json[field] = toCanonicalBase64(bytes);
|
|
8472
|
+
return { json };
|
|
8473
|
+
}
|
|
8474
|
+
|
|
7005
8475
|
// src/tools.ts
|
|
7006
8476
|
function payerTrace(deps) {
|
|
7007
8477
|
const hooks = deps.trace;
|
|
@@ -7038,7 +8508,7 @@ function normalizePayIntent(args) {
|
|
|
7038
8508
|
if (!/^0x[0-9a-fA-F]{1,64}$/.test(raw)) {
|
|
7039
8509
|
throw new Error("recipient must be a 0x-prefixed Sui address");
|
|
7040
8510
|
}
|
|
7041
|
-
const normalized =
|
|
8511
|
+
const normalized = normalizeSuiAddress15(raw);
|
|
7042
8512
|
if (!isValidSuiAddress(normalized) || /^0x0+$/.test(normalized)) {
|
|
7043
8513
|
throw new Error("recipient must be a non-zero Sui address");
|
|
7044
8514
|
}
|
|
@@ -7057,7 +8527,7 @@ function intentViolation(intent, prepared) {
|
|
|
7057
8527
|
if (intent.recipient) {
|
|
7058
8528
|
let preparedRecipient;
|
|
7059
8529
|
try {
|
|
7060
|
-
preparedRecipient =
|
|
8530
|
+
preparedRecipient = normalizeSuiAddress15(prepared.recipient);
|
|
7061
8531
|
} catch {
|
|
7062
8532
|
return "prepared payment does not name a valid recipient";
|
|
7063
8533
|
}
|
|
@@ -7080,18 +8550,34 @@ function intentViolation(intent, prepared) {
|
|
|
7080
8550
|
}
|
|
7081
8551
|
async function pay(args, cfg, deps = {}) {
|
|
7082
8552
|
if (!args.url) return error("url is required");
|
|
8553
|
+
if (hasLocalFileArgs(args) && deps.transport === "http") {
|
|
8554
|
+
return closedPayError(
|
|
8555
|
+
"FILE_STDIO_ONLY",
|
|
8556
|
+
"file is stdio only; hosted /mcp does not read the buyer disk"
|
|
8557
|
+
);
|
|
8558
|
+
}
|
|
7083
8559
|
let intent;
|
|
7084
8560
|
try {
|
|
7085
8561
|
intent = normalizePayIntent(args);
|
|
7086
8562
|
} catch {
|
|
7087
8563
|
return closedPayError("INVALID_INTENT", "payment intent is invalid");
|
|
7088
8564
|
}
|
|
8565
|
+
let attached;
|
|
8566
|
+
try {
|
|
8567
|
+
attached = jsonWithLocalFile(args);
|
|
8568
|
+
} catch {
|
|
8569
|
+
return closedPayError("INVALID_FILE", "file could not be read");
|
|
8570
|
+
}
|
|
8571
|
+
if ("error" in attached) {
|
|
8572
|
+
return closedPayError(attached.error.code, attached.error.detail);
|
|
8573
|
+
}
|
|
8574
|
+
const paidArgs = { ...args, json: attached.json };
|
|
7089
8575
|
let request;
|
|
7090
8576
|
try {
|
|
7091
|
-
request = normalizeMcpPaidHttpRequest(
|
|
8577
|
+
request = normalizeMcpPaidHttpRequest(paidArgs);
|
|
7092
8578
|
} catch (err) {
|
|
7093
8579
|
const raw = err instanceof Error ? err.message : "";
|
|
7094
|
-
const detail = /method/i.test(raw) ? "method must be GET or POST" : /json/i.test(raw) ? "POST requires json" : "request could not be normalized";
|
|
8580
|
+
const detail = /too large/i.test(raw) ? `paid request body exceeds ${MAX_PAID_BODY_BYTES} bytes` : /method/i.test(raw) ? "method must be GET or POST" : /json/i.test(raw) ? "POST requires json" : "request could not be normalized";
|
|
7095
8581
|
return closedPayError("MALFORMED_REQUEST", detail);
|
|
7096
8582
|
}
|
|
7097
8583
|
if (deps.auth?.spendAccountPay) {
|
|
@@ -7116,6 +8602,7 @@ async function pay(args, cfg, deps = {}) {
|
|
|
7116
8602
|
deps: {
|
|
7117
8603
|
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {},
|
|
7118
8604
|
...trace ? { trace } : {},
|
|
8605
|
+
...deps.resolveExclusionRegistry ? { resolveExclusionRegistry: deps.resolveExclusionRegistry } : {},
|
|
7119
8606
|
...deps.buildPayKind ? { buildPayKind: deps.buildPayKind } : liveKindBuilder ? {
|
|
7120
8607
|
buildPayKind: (input) => sp.snapshot.accessMode === "open" && input.recipient ? liveKindBuilder.buildSettleOpenRecipientKind({
|
|
7121
8608
|
...input,
|
|
@@ -7624,6 +9111,14 @@ var TOOLS = [
|
|
|
7624
9111
|
type: "object",
|
|
7625
9112
|
description: "JSON request body for a POST. Sent as application/json and bound to the payment."
|
|
7626
9113
|
},
|
|
9114
|
+
file: {
|
|
9115
|
+
type: "string",
|
|
9116
|
+
description: "Absolute path under SUIPAY_MCP_FILE_ROOT (unset disables attach). Stdio POST only; requires fileField. Regular non-empty file whose decoded size fits the paid-body ceiling. Not a URL, FIFO, device, or path under ~/.suipay. Bytes fill json[fileField] as canonical base64."
|
|
9117
|
+
},
|
|
9118
|
+
fileField: {
|
|
9119
|
+
type: "string",
|
|
9120
|
+
description: "JSON key to set from file (e.g. contentBase64). Required with file. Pattern [A-Za-z][A-Za-z0-9_]{0,63}. Must not already be present on json."
|
|
9121
|
+
},
|
|
7627
9122
|
recipient: {
|
|
7628
9123
|
type: "string",
|
|
7629
9124
|
description: "Sui address you expect to be paid. The payment is refused, unsigned, if the prepared transaction pays anyone else."
|
|
@@ -7731,6 +9226,7 @@ function toolError(value) {
|
|
|
7731
9226
|
}
|
|
7732
9227
|
function createServer2(cfg, auth, trace, options = {}) {
|
|
7733
9228
|
const resolveAgentHeld = options.resolveAgentHeld ?? resolveAgentHeldSettlement;
|
|
9229
|
+
const transport = options.transport ?? "stdio";
|
|
7734
9230
|
const server = new Server(
|
|
7735
9231
|
{ name: "suipay", version: "1.0.0" },
|
|
7736
9232
|
{
|
|
@@ -7757,19 +9253,32 @@ function createServer2(cfg, auth, trace, options = {}) {
|
|
|
7757
9253
|
return await accessContext({ auth }, cfg);
|
|
7758
9254
|
case "pay": {
|
|
7759
9255
|
const agentHeld = auth?.spendAccountPay ? resolveAgentHeld(cfg, auth.spendAccountPay) : null;
|
|
9256
|
+
const fileArgs = {
|
|
9257
|
+
...args.file !== void 0 ? { file: args.file } : {},
|
|
9258
|
+
...args.fileField !== void 0 ? { fileField: args.fileField } : {}
|
|
9259
|
+
};
|
|
9260
|
+
if (transport === "http" && hasLocalFileArgs(fileArgs)) {
|
|
9261
|
+
return closedPayError(
|
|
9262
|
+
"FILE_STDIO_ONLY",
|
|
9263
|
+
"file is stdio only; hosted /mcp does not read the buyer disk"
|
|
9264
|
+
);
|
|
9265
|
+
}
|
|
7760
9266
|
return await pay(
|
|
7761
9267
|
{
|
|
7762
9268
|
url: String(args.url ?? ""),
|
|
7763
9269
|
...args.method !== void 0 ? { method: args.method } : {},
|
|
7764
9270
|
...args.json !== void 0 ? { json: args.json } : {},
|
|
9271
|
+
...transport === "stdio" ? fileArgs : {},
|
|
7765
9272
|
...args.recipient !== void 0 ? { recipient: args.recipient } : {},
|
|
7766
9273
|
...args.maxAmount !== void 0 ? { maxAmount: args.maxAmount } : {}
|
|
7767
9274
|
},
|
|
7768
9275
|
cfg,
|
|
7769
9276
|
{
|
|
7770
9277
|
auth,
|
|
9278
|
+
transport,
|
|
7771
9279
|
...trace ? { trace } : {},
|
|
7772
|
-
...agentHeld ? { agentHeld } : {}
|
|
9280
|
+
...agentHeld ? { agentHeld } : {},
|
|
9281
|
+
...options.resolveExclusionRegistry ? { resolveExclusionRegistry: options.resolveExclusionRegistry } : {}
|
|
7773
9282
|
}
|
|
7774
9283
|
);
|
|
7775
9284
|
}
|
|
@@ -7856,6 +9365,11 @@ function canonicalMcpRequestId(id) {
|
|
|
7856
9365
|
function asRecord(value) {
|
|
7857
9366
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
7858
9367
|
}
|
|
9368
|
+
function redactPayFileArg(args) {
|
|
9369
|
+
const record = asRecord(args);
|
|
9370
|
+
if (!record || !("file" in record)) return args;
|
|
9371
|
+
return { ...record, file: "[redacted]" };
|
|
9372
|
+
}
|
|
7859
9373
|
function readEntry(message) {
|
|
7860
9374
|
const record = asRecord(message);
|
|
7861
9375
|
if (!record) return null;
|
|
@@ -7867,7 +9381,7 @@ function readEntry(message) {
|
|
|
7867
9381
|
jsonRpcId: typeof rawId === "string" || typeof rawId === "number" ? rawId : null,
|
|
7868
9382
|
requestId: canonicalMcpRequestId(rawId),
|
|
7869
9383
|
toolName: params && typeof params.name === "string" ? params.name : null,
|
|
7870
|
-
toolArguments: params ? params.arguments : void 0,
|
|
9384
|
+
toolArguments: params ? redactPayFileArg(params.arguments) : void 0,
|
|
7871
9385
|
clientName: clientInfo && typeof clientInfo.name === "string" ? clientInfo.name : null,
|
|
7872
9386
|
clientVersion: clientInfo && typeof clientInfo.version === "string" ? clientInfo.version : null,
|
|
7873
9387
|
protocolVersion: params && typeof params.protocolVersion === "string" ? params.protocolVersion : null
|
|
@@ -8297,7 +9811,10 @@ async function handleSuipayMcpHttpRequest(req, cfg, auth, trace, options) {
|
|
|
8297
9811
|
sessionIdGenerator: void 0,
|
|
8298
9812
|
enableJsonResponse: true
|
|
8299
9813
|
});
|
|
8300
|
-
const server = createServer2(cfg, auth, hooks,
|
|
9814
|
+
const server = createServer2(cfg, auth, hooks, {
|
|
9815
|
+
...options,
|
|
9816
|
+
transport: "http"
|
|
9817
|
+
});
|
|
8301
9818
|
await server.connect(transport);
|
|
8302
9819
|
return transport.handleRequest(request);
|
|
8303
9820
|
}
|