@oobe-protocol-labs/synapse-sap-sdk 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/constants/index.js +9 -1
- package/dist/cjs/constants/index.js.map +1 -1
- package/dist/cjs/constants/payments.js +87 -0
- package/dist/cjs/constants/payments.js.map +1 -0
- package/dist/cjs/constants/seeds.js +9 -1
- package/dist/cjs/constants/seeds.js.map +1 -1
- package/dist/cjs/idl/synapse_agent_sap.json +270 -2
- package/dist/cjs/modules/escrow-v2.js +10 -0
- package/dist/cjs/modules/escrow-v2.js.map +1 -1
- package/dist/cjs/modules/escrow.js +29 -4
- package/dist/cjs/modules/escrow.js.map +1 -1
- package/dist/cjs/pda/index.js +31 -1
- package/dist/cjs/pda/index.js.map +1 -1
- package/dist/cjs/plugin/index.js +3 -1
- package/dist/cjs/plugin/index.js.map +1 -1
- package/dist/cjs/utils/hash.js +52 -1
- package/dist/cjs/utils/hash.js.map +1 -1
- package/dist/esm/constants/index.js +2 -0
- package/dist/esm/constants/index.js.map +1 -1
- package/dist/esm/constants/payments.js +82 -0
- package/dist/esm/constants/payments.js.map +1 -0
- package/dist/esm/constants/seeds.js +9 -1
- package/dist/esm/constants/seeds.js.map +1 -1
- package/dist/esm/idl/synapse_agent_sap.json +270 -2
- package/dist/esm/modules/escrow-v2.js +11 -1
- package/dist/esm/modules/escrow-v2.js.map +1 -1
- package/dist/esm/modules/escrow.js +30 -5
- package/dist/esm/modules/escrow.js.map +1 -1
- package/dist/esm/pda/index.js +29 -0
- package/dist/esm/pda/index.js.map +1 -1
- package/dist/esm/plugin/index.js +3 -1
- package/dist/esm/plugin/index.js.map +1 -1
- package/dist/esm/utils/hash.js +50 -0
- package/dist/esm/utils/hash.js.map +1 -1
- package/dist/types/constants/index.d.ts +1 -0
- package/dist/types/constants/index.d.ts.map +1 -1
- package/dist/types/constants/payments.d.ts +78 -0
- package/dist/types/constants/payments.d.ts.map +1 -0
- package/dist/types/constants/seeds.d.ts +9 -1
- package/dist/types/constants/seeds.d.ts.map +1 -1
- package/dist/types/modules/escrow-v2.d.ts.map +1 -1
- package/dist/types/modules/escrow.d.ts +9 -1
- package/dist/types/modules/escrow.d.ts.map +1 -1
- package/dist/types/pda/index.d.ts +21 -0
- package/dist/types/pda/index.d.ts.map +1 -1
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/utils/hash.d.ts +27 -0
- package/dist/types/utils/hash.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants/index.ts +11 -1
- package/src/constants/payments.ts +92 -0
- package/src/constants/seeds.ts +9 -1
- package/src/idl/synapse_agent_sap.json +270 -2
- package/src/modules/escrow-v2.ts +15 -0
- package/src/modules/escrow.ts +36 -3
- package/src/pda/index.ts +39 -0
- package/src/plugin/index.ts +2 -0
- package/src/utils/hash.ts +56 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"address": "SAPpUhsWLJG1FfkGRcXagEDMrMsWGjbky7AyhGpFETZ",
|
|
3
3
|
"metadata": {
|
|
4
4
|
"name": "synapse_agent_sap",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.2.0",
|
|
6
6
|
"spec": "0.1.0",
|
|
7
7
|
"description": "Created with Anchor"
|
|
8
8
|
},
|
|
@@ -2693,6 +2693,37 @@
|
|
|
2693
2693
|
"Agent to prepay for — anyone can create escrow"
|
|
2694
2694
|
]
|
|
2695
2695
|
},
|
|
2696
|
+
{
|
|
2697
|
+
"name": "agent_stake",
|
|
2698
|
+
"docs": [
|
|
2699
|
+
"v0.10 hardening: agent MUST hold at least `AgentStake::MIN_STAKE`",
|
|
2700
|
+
"in collateral before any new escrow can be opened. This guarantees",
|
|
2701
|
+
"every accepted client has a slashable bond to claim against in case",
|
|
2702
|
+
"of dispute."
|
|
2703
|
+
],
|
|
2704
|
+
"pda": {
|
|
2705
|
+
"seeds": [
|
|
2706
|
+
{
|
|
2707
|
+
"kind": "const",
|
|
2708
|
+
"value": [
|
|
2709
|
+
115,
|
|
2710
|
+
97,
|
|
2711
|
+
112,
|
|
2712
|
+
95,
|
|
2713
|
+
115,
|
|
2714
|
+
116,
|
|
2715
|
+
97,
|
|
2716
|
+
107,
|
|
2717
|
+
101
|
|
2718
|
+
]
|
|
2719
|
+
},
|
|
2720
|
+
{
|
|
2721
|
+
"kind": "account",
|
|
2722
|
+
"path": "agent"
|
|
2723
|
+
}
|
|
2724
|
+
]
|
|
2725
|
+
}
|
|
2726
|
+
},
|
|
2696
2727
|
{
|
|
2697
2728
|
"name": "escrow",
|
|
2698
2729
|
"writable": true,
|
|
@@ -2792,6 +2823,35 @@
|
|
|
2792
2823
|
{
|
|
2793
2824
|
"name": "agent"
|
|
2794
2825
|
},
|
|
2826
|
+
{
|
|
2827
|
+
"name": "agent_stake",
|
|
2828
|
+
"docs": [
|
|
2829
|
+
"v0.10 hardening: agent MUST have an active stake ≥ MIN_STAKE",
|
|
2830
|
+
"before any new escrow can be opened."
|
|
2831
|
+
],
|
|
2832
|
+
"pda": {
|
|
2833
|
+
"seeds": [
|
|
2834
|
+
{
|
|
2835
|
+
"kind": "const",
|
|
2836
|
+
"value": [
|
|
2837
|
+
115,
|
|
2838
|
+
97,
|
|
2839
|
+
112,
|
|
2840
|
+
95,
|
|
2841
|
+
115,
|
|
2842
|
+
116,
|
|
2843
|
+
97,
|
|
2844
|
+
107,
|
|
2845
|
+
101
|
|
2846
|
+
]
|
|
2847
|
+
},
|
|
2848
|
+
{
|
|
2849
|
+
"kind": "account",
|
|
2850
|
+
"path": "agent"
|
|
2851
|
+
}
|
|
2852
|
+
]
|
|
2853
|
+
}
|
|
2854
|
+
},
|
|
2795
2855
|
{
|
|
2796
2856
|
"name": "escrow",
|
|
2797
2857
|
"writable": true,
|
|
@@ -6956,7 +7016,9 @@
|
|
|
6956
7016
|
{
|
|
6957
7017
|
"name": "settle_batch",
|
|
6958
7018
|
"docs": [
|
|
6959
|
-
"Batch settle up to 10 settlements in one TX. Volume curve spans batch."
|
|
7019
|
+
"Batch settle up to 10 settlements in one TX. Volume curve spans batch.",
|
|
7020
|
+
"v0.10: requires `batch_root = sha256(s_0 || s_1 || ... || s_{N-1})`",
|
|
7021
|
+
"to seed the anti-replay receipt PDA."
|
|
6960
7022
|
],
|
|
6961
7023
|
"discriminator": [
|
|
6962
7024
|
22,
|
|
@@ -7056,6 +7118,44 @@
|
|
|
7056
7118
|
}
|
|
7057
7119
|
]
|
|
7058
7120
|
}
|
|
7121
|
+
},
|
|
7122
|
+
{
|
|
7123
|
+
"name": "settlement_receipt",
|
|
7124
|
+
"docs": [
|
|
7125
|
+
"v0.10 anti-replay: PDA bound to the batch_root (sha256 of all",
|
|
7126
|
+
"service_hashes in order). Replaying the same batch fails the",
|
|
7127
|
+
"`init` constraint."
|
|
7128
|
+
],
|
|
7129
|
+
"writable": true,
|
|
7130
|
+
"pda": {
|
|
7131
|
+
"seeds": [
|
|
7132
|
+
{
|
|
7133
|
+
"kind": "const",
|
|
7134
|
+
"value": [
|
|
7135
|
+
115,
|
|
7136
|
+
97,
|
|
7137
|
+
112,
|
|
7138
|
+
95,
|
|
7139
|
+
114,
|
|
7140
|
+
101,
|
|
7141
|
+
99,
|
|
7142
|
+
118
|
|
7143
|
+
]
|
|
7144
|
+
},
|
|
7145
|
+
{
|
|
7146
|
+
"kind": "account",
|
|
7147
|
+
"path": "escrow"
|
|
7148
|
+
},
|
|
7149
|
+
{
|
|
7150
|
+
"kind": "arg",
|
|
7151
|
+
"path": "batch_root"
|
|
7152
|
+
}
|
|
7153
|
+
]
|
|
7154
|
+
}
|
|
7155
|
+
},
|
|
7156
|
+
{
|
|
7157
|
+
"name": "system_program",
|
|
7158
|
+
"address": "11111111111111111111111111111111"
|
|
7059
7159
|
}
|
|
7060
7160
|
],
|
|
7061
7161
|
"args": [
|
|
@@ -7068,6 +7168,15 @@
|
|
|
7068
7168
|
}
|
|
7069
7169
|
}
|
|
7070
7170
|
}
|
|
7171
|
+
},
|
|
7172
|
+
{
|
|
7173
|
+
"name": "batch_root",
|
|
7174
|
+
"type": {
|
|
7175
|
+
"array": [
|
|
7176
|
+
"u8",
|
|
7177
|
+
32
|
|
7178
|
+
]
|
|
7179
|
+
}
|
|
7071
7180
|
}
|
|
7072
7181
|
]
|
|
7073
7182
|
},
|
|
@@ -7180,6 +7289,45 @@
|
|
|
7180
7289
|
}
|
|
7181
7290
|
]
|
|
7182
7291
|
}
|
|
7292
|
+
},
|
|
7293
|
+
{
|
|
7294
|
+
"name": "settlement_receipt",
|
|
7295
|
+
"docs": [
|
|
7296
|
+
"v0.10 anti-replay: an `init` of this PDA fails if the same",
|
|
7297
|
+
"`service_hash` was already used to settle this escrow.",
|
|
7298
|
+
"Receipt PDAs are intentionally NOT closeable to preserve the",
|
|
7299
|
+
"uniqueness invariant for the lifetime of the escrow."
|
|
7300
|
+
],
|
|
7301
|
+
"writable": true,
|
|
7302
|
+
"pda": {
|
|
7303
|
+
"seeds": [
|
|
7304
|
+
{
|
|
7305
|
+
"kind": "const",
|
|
7306
|
+
"value": [
|
|
7307
|
+
115,
|
|
7308
|
+
97,
|
|
7309
|
+
112,
|
|
7310
|
+
95,
|
|
7311
|
+
114,
|
|
7312
|
+
101,
|
|
7313
|
+
99,
|
|
7314
|
+
118
|
|
7315
|
+
]
|
|
7316
|
+
},
|
|
7317
|
+
{
|
|
7318
|
+
"kind": "account",
|
|
7319
|
+
"path": "escrow"
|
|
7320
|
+
},
|
|
7321
|
+
{
|
|
7322
|
+
"kind": "arg",
|
|
7323
|
+
"path": "service_hash"
|
|
7324
|
+
}
|
|
7325
|
+
]
|
|
7326
|
+
}
|
|
7327
|
+
},
|
|
7328
|
+
{
|
|
7329
|
+
"name": "system_program",
|
|
7330
|
+
"address": "11111111111111111111111111111111"
|
|
7183
7331
|
}
|
|
7184
7332
|
],
|
|
7185
7333
|
"args": [
|
|
@@ -7312,6 +7460,41 @@
|
|
|
7312
7460
|
]
|
|
7313
7461
|
}
|
|
7314
7462
|
},
|
|
7463
|
+
{
|
|
7464
|
+
"name": "settlement_receipt",
|
|
7465
|
+
"docs": [
|
|
7466
|
+
"v0.10 anti-replay: PDA bound to (escrow, service_hash).",
|
|
7467
|
+
"In CoSigned mode this gates the immediate transfer; in",
|
|
7468
|
+
"DisputeWindow mode this gates the pending-amount bump so the",
|
|
7469
|
+
"same `service_hash` cannot be re-applied to inflate pending."
|
|
7470
|
+
],
|
|
7471
|
+
"writable": true,
|
|
7472
|
+
"pda": {
|
|
7473
|
+
"seeds": [
|
|
7474
|
+
{
|
|
7475
|
+
"kind": "const",
|
|
7476
|
+
"value": [
|
|
7477
|
+
115,
|
|
7478
|
+
97,
|
|
7479
|
+
112,
|
|
7480
|
+
95,
|
|
7481
|
+
114,
|
|
7482
|
+
101,
|
|
7483
|
+
99,
|
|
7484
|
+
118
|
|
7485
|
+
]
|
|
7486
|
+
},
|
|
7487
|
+
{
|
|
7488
|
+
"kind": "account",
|
|
7489
|
+
"path": "escrow"
|
|
7490
|
+
},
|
|
7491
|
+
{
|
|
7492
|
+
"kind": "arg",
|
|
7493
|
+
"path": "service_hash"
|
|
7494
|
+
}
|
|
7495
|
+
]
|
|
7496
|
+
}
|
|
7497
|
+
},
|
|
7315
7498
|
{
|
|
7316
7499
|
"name": "system_program",
|
|
7317
7500
|
"address": "11111111111111111111111111111111"
|
|
@@ -8486,6 +8669,19 @@
|
|
|
8486
8669
|
179
|
|
8487
8670
|
]
|
|
8488
8671
|
},
|
|
8672
|
+
{
|
|
8673
|
+
"name": "SettlementReceipt",
|
|
8674
|
+
"discriminator": [
|
|
8675
|
+
52,
|
|
8676
|
+
249,
|
|
8677
|
+
252,
|
|
8678
|
+
121,
|
|
8679
|
+
4,
|
|
8680
|
+
232,
|
|
8681
|
+
187,
|
|
8682
|
+
4
|
|
8683
|
+
]
|
|
8684
|
+
},
|
|
8489
8685
|
{
|
|
8490
8686
|
"name": "Subscription",
|
|
8491
8687
|
"discriminator": [
|
|
@@ -9980,6 +10176,41 @@
|
|
|
9980
10176
|
"code": 6136,
|
|
9981
10177
|
"name": "InvalidReceiptProof",
|
|
9982
10178
|
"msg": "bad receipt proof"
|
|
10179
|
+
},
|
|
10180
|
+
{
|
|
10181
|
+
"code": 6137,
|
|
10182
|
+
"name": "SettlementReplay",
|
|
10183
|
+
"msg": "settlement replay"
|
|
10184
|
+
},
|
|
10185
|
+
{
|
|
10186
|
+
"code": 6138,
|
|
10187
|
+
"name": "PaymentTokenNotAllowed",
|
|
10188
|
+
"msg": "token not allowed"
|
|
10189
|
+
},
|
|
10190
|
+
{
|
|
10191
|
+
"code": 6139,
|
|
10192
|
+
"name": "AgentStakeRequired",
|
|
10193
|
+
"msg": "agent stake required"
|
|
10194
|
+
},
|
|
10195
|
+
{
|
|
10196
|
+
"code": 6140,
|
|
10197
|
+
"name": "DelegateExpiryInvalid",
|
|
10198
|
+
"msg": "delegate expiry invalid"
|
|
10199
|
+
},
|
|
10200
|
+
{
|
|
10201
|
+
"code": 6141,
|
|
10202
|
+
"name": "EscrowNotClosed",
|
|
10203
|
+
"msg": "escrow not closed"
|
|
10204
|
+
},
|
|
10205
|
+
{
|
|
10206
|
+
"code": 6142,
|
|
10207
|
+
"name": "VolumeCurveNotDescending",
|
|
10208
|
+
"msg": "curve not descending"
|
|
10209
|
+
},
|
|
10210
|
+
{
|
|
10211
|
+
"code": 6143,
|
|
10212
|
+
"name": "DuplicateServiceHash",
|
|
10213
|
+
"msg": "dup service hash"
|
|
9983
10214
|
}
|
|
9984
10215
|
],
|
|
9985
10216
|
"types": [
|
|
@@ -13125,6 +13356,43 @@
|
|
|
13125
13356
|
]
|
|
13126
13357
|
}
|
|
13127
13358
|
},
|
|
13359
|
+
{
|
|
13360
|
+
"name": "SettlementReceipt",
|
|
13361
|
+
"type": {
|
|
13362
|
+
"kind": "struct",
|
|
13363
|
+
"fields": [
|
|
13364
|
+
{
|
|
13365
|
+
"name": "bump",
|
|
13366
|
+
"type": "u8"
|
|
13367
|
+
},
|
|
13368
|
+
{
|
|
13369
|
+
"name": "escrow",
|
|
13370
|
+
"type": "pubkey"
|
|
13371
|
+
},
|
|
13372
|
+
{
|
|
13373
|
+
"name": "service_hash",
|
|
13374
|
+
"type": {
|
|
13375
|
+
"array": [
|
|
13376
|
+
"u8",
|
|
13377
|
+
32
|
|
13378
|
+
]
|
|
13379
|
+
}
|
|
13380
|
+
},
|
|
13381
|
+
{
|
|
13382
|
+
"name": "calls_settled",
|
|
13383
|
+
"type": "u64"
|
|
13384
|
+
},
|
|
13385
|
+
{
|
|
13386
|
+
"name": "amount",
|
|
13387
|
+
"type": "u64"
|
|
13388
|
+
},
|
|
13389
|
+
{
|
|
13390
|
+
"name": "settled_at",
|
|
13391
|
+
"type": "i64"
|
|
13392
|
+
}
|
|
13393
|
+
]
|
|
13394
|
+
}
|
|
13395
|
+
},
|
|
13128
13396
|
{
|
|
13129
13397
|
"name": "SettlementSecurity",
|
|
13130
13398
|
"docs": [
|
package/src/modules/escrow-v2.ts
CHANGED
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
deriveEscrowV2,
|
|
24
24
|
derivePendingSettlement as derivePendingPda,
|
|
25
25
|
deriveDispute as deriveDisputePda,
|
|
26
|
+
deriveStake,
|
|
27
|
+
deriveSettlementReceipt,
|
|
26
28
|
} from "../pda";
|
|
27
29
|
import type {
|
|
28
30
|
EscrowAccountV2Data,
|
|
@@ -35,6 +37,7 @@ import {
|
|
|
35
37
|
buildRpcOptions,
|
|
36
38
|
} from "../utils/priority-fee";
|
|
37
39
|
import type { SettleOptions } from "../utils/priority-fee";
|
|
40
|
+
import { isAcceptedPaymentToken } from "../constants/payments";
|
|
38
41
|
|
|
39
42
|
/**
|
|
40
43
|
* @name EscrowV2Module
|
|
@@ -83,8 +86,17 @@ export class EscrowV2Module extends BaseModule {
|
|
|
83
86
|
args: CreateEscrowV2Args,
|
|
84
87
|
splAccounts: AccountMeta[] = [],
|
|
85
88
|
): Promise<TransactionSignature> {
|
|
89
|
+
// v0.10.0: payment-token allowlist (SOL or USDC only).
|
|
90
|
+
if (!isAcceptedPaymentToken(args.tokenMint ?? null)) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"createEscrowV2: tokenMint must be null (SOL) or USDC (mainnet/devnet). " +
|
|
93
|
+
"On-chain will reject with PaymentTokenNotAllowed.",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
const [agentPda] = deriveAgent(agentWallet);
|
|
87
98
|
const [escrowPda] = this.deriveEscrow(agentPda, undefined, args.escrowNonce);
|
|
99
|
+
const [stakePda] = deriveStake(agentPda);
|
|
88
100
|
|
|
89
101
|
return this.methods
|
|
90
102
|
.createEscrowV2(
|
|
@@ -104,6 +116,7 @@ export class EscrowV2Module extends BaseModule {
|
|
|
104
116
|
.accounts({
|
|
105
117
|
depositor: this.walletPubkey,
|
|
106
118
|
agent: agentPda,
|
|
119
|
+
agentStake: stakePda,
|
|
107
120
|
escrow: escrowPda,
|
|
108
121
|
systemProgram: SystemProgram.programId,
|
|
109
122
|
})
|
|
@@ -142,6 +155,7 @@ export class EscrowV2Module extends BaseModule {
|
|
|
142
155
|
const [agentPda] = deriveAgent(this.walletPubkey);
|
|
143
156
|
const [escrowPda] = this.deriveEscrow(agentPda, depositorWallet, nonce);
|
|
144
157
|
const [statsPda] = deriveAgentStats(agentPda);
|
|
158
|
+
const [receiptPda] = deriveSettlementReceipt(escrowPda, serviceHash);
|
|
145
159
|
|
|
146
160
|
const preIxs = buildPriorityFeeIxs(opts);
|
|
147
161
|
const rpcOpts = buildRpcOptions(opts);
|
|
@@ -153,6 +167,7 @@ export class EscrowV2Module extends BaseModule {
|
|
|
153
167
|
agent: agentPda,
|
|
154
168
|
agentStats: statsPda,
|
|
155
169
|
escrow: escrowPda,
|
|
170
|
+
settlementReceipt: receiptPda,
|
|
156
171
|
systemProgram: SystemProgram.programId,
|
|
157
172
|
})
|
|
158
173
|
.remainingAccounts(splAccounts);
|
package/src/modules/escrow.ts
CHANGED
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
deriveAgent,
|
|
24
24
|
deriveAgentStats,
|
|
25
25
|
deriveEscrow,
|
|
26
|
+
deriveStake,
|
|
27
|
+
deriveSettlementReceipt,
|
|
26
28
|
} from "../pda";
|
|
27
29
|
import type {
|
|
28
30
|
EscrowAccountData,
|
|
@@ -34,6 +36,8 @@ import {
|
|
|
34
36
|
buildRpcOptions,
|
|
35
37
|
} from "../utils/priority-fee";
|
|
36
38
|
import type { SettleOptions } from "../utils/priority-fee";
|
|
39
|
+
import { computeBatchRoot, hashToArray } from "../utils/hash";
|
|
40
|
+
import { isAcceptedPaymentToken } from "../constants/payments";
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
43
|
* @name EscrowModule
|
|
@@ -105,9 +109,17 @@ export class EscrowModule extends BaseModule {
|
|
|
105
109
|
args: CreateEscrowArgs,
|
|
106
110
|
splAccounts: AccountMeta[] = [],
|
|
107
111
|
): Promise<TransactionSignature> {
|
|
112
|
+
// v0.10.0 client-side guard — surface the on-chain rejection earlier.
|
|
113
|
+
if (!isAcceptedPaymentToken(args.tokenMint ?? null)) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
"createEscrow: tokenMint must be null (SOL) or USDC (mainnet/devnet). " +
|
|
116
|
+
"On-chain will reject with PaymentTokenNotAllowed.",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
108
120
|
const [agentPda] = deriveAgent(agentWallet);
|
|
109
121
|
const [escrowPda] = this.deriveEscrow(agentPda);
|
|
110
|
-
const [
|
|
122
|
+
const [stakePda] = deriveStake(agentPda);
|
|
111
123
|
|
|
112
124
|
return this.methods
|
|
113
125
|
.createEscrow(
|
|
@@ -122,7 +134,7 @@ export class EscrowModule extends BaseModule {
|
|
|
122
134
|
.accounts({
|
|
123
135
|
depositor: this.walletPubkey,
|
|
124
136
|
agent: agentPda,
|
|
125
|
-
|
|
137
|
+
agentStake: stakePda,
|
|
126
138
|
escrow: escrowPda,
|
|
127
139
|
systemProgram: SystemProgram.programId,
|
|
128
140
|
})
|
|
@@ -181,6 +193,7 @@ export class EscrowModule extends BaseModule {
|
|
|
181
193
|
const [agentPda] = deriveAgent(this.walletPubkey);
|
|
182
194
|
const [escrowPda] = deriveEscrow(agentPda, depositorWallet);
|
|
183
195
|
const [statsPda] = deriveAgentStats(agentPda);
|
|
196
|
+
const [receiptPda] = deriveSettlementReceipt(escrowPda, serviceHash);
|
|
184
197
|
|
|
185
198
|
const preIxs = buildPriorityFeeIxs(opts);
|
|
186
199
|
const rpcOpts = buildRpcOptions(opts);
|
|
@@ -192,6 +205,7 @@ export class EscrowModule extends BaseModule {
|
|
|
192
205
|
agent: agentPda,
|
|
193
206
|
agentStats: statsPda,
|
|
194
207
|
escrow: escrowPda,
|
|
208
|
+
settlementReceipt: receiptPda,
|
|
195
209
|
systemProgram: SystemProgram.programId,
|
|
196
210
|
})
|
|
197
211
|
.remainingAccounts(splAccounts);
|
|
@@ -255,17 +269,26 @@ export class EscrowModule extends BaseModule {
|
|
|
255
269
|
* @name settleBatch
|
|
256
270
|
* @description Batch settlement — process up to 10 settlements in a single
|
|
257
271
|
* transaction. Must be called by the agent owner wallet.
|
|
272
|
+
*
|
|
258
273
|
* @param depositorWallet - The wallet of the client who funded the escrow.
|
|
259
274
|
* @param settlements - Array of settlement entries (up to 10).
|
|
275
|
+
* @param batchRoot - Optional pre-computed batch root
|
|
276
|
+
* (`sha256(s_0 || s_1 || ... || s_{N-1})`). When omitted the SDK
|
|
277
|
+
* computes it from `settlements[*].serviceHash`. **Required on-chain
|
|
278
|
+
* since v0.10.0** to seed the anti-replay receipt PDA.
|
|
260
279
|
* @param splAccounts - Remaining accounts for SPL token transfers. Defaults to `[]`.
|
|
261
280
|
* @param opts - Optional {@link SettleOptions} for priority fees and RPC tuning.
|
|
262
281
|
* @returns {Promise<TransactionSignature>} The transaction signature.
|
|
282
|
+
*
|
|
263
283
|
* @since v0.1.0
|
|
264
284
|
* @updated v0.6.2 — Added optional `opts` parameter for priority fees.
|
|
285
|
+
* @updated v0.10.0 — Added `batchRoot` argument and required
|
|
286
|
+
* `settlementReceipt` PDA on-chain (anti-replay).
|
|
265
287
|
*/
|
|
266
288
|
async settleBatch(
|
|
267
289
|
depositorWallet: PublicKey,
|
|
268
290
|
settlements: Settlement[],
|
|
291
|
+
batchRoot?: Uint8Array | number[] | null,
|
|
269
292
|
splAccounts: AccountMeta[] = [],
|
|
270
293
|
opts?: SettleOptions,
|
|
271
294
|
): Promise<TransactionSignature> {
|
|
@@ -273,16 +296,26 @@ export class EscrowModule extends BaseModule {
|
|
|
273
296
|
const [escrowPda] = deriveEscrow(agentPda, depositorWallet);
|
|
274
297
|
const [statsPda] = deriveAgentStats(agentPda);
|
|
275
298
|
|
|
299
|
+
const rootBytes =
|
|
300
|
+
batchRoot && batchRoot !== null
|
|
301
|
+
? batchRoot instanceof Uint8Array
|
|
302
|
+
? batchRoot
|
|
303
|
+
: Uint8Array.from(batchRoot)
|
|
304
|
+
: computeBatchRoot(settlements.map((s) => s.serviceHash));
|
|
305
|
+
const rootArr = hashToArray(rootBytes);
|
|
306
|
+
const [receiptPda] = deriveSettlementReceipt(escrowPda, rootBytes);
|
|
307
|
+
|
|
276
308
|
const preIxs = buildPriorityFeeIxs(opts);
|
|
277
309
|
const rpcOpts = buildRpcOptions(opts);
|
|
278
310
|
|
|
279
311
|
let builder = this.methods
|
|
280
|
-
.settleBatch(settlements)
|
|
312
|
+
.settleBatch(settlements, rootArr)
|
|
281
313
|
.accounts({
|
|
282
314
|
wallet: this.walletPubkey,
|
|
283
315
|
agent: agentPda,
|
|
284
316
|
agentStats: statsPda,
|
|
285
317
|
escrow: escrowPda,
|
|
318
|
+
settlementReceipt: receiptPda,
|
|
286
319
|
systemProgram: SystemProgram.programId,
|
|
287
320
|
})
|
|
288
321
|
.remainingAccounts(splAccounts);
|
package/src/pda/index.ts
CHANGED
|
@@ -878,3 +878,42 @@ export const deriveReceiptBatch = (
|
|
|
878
878
|
],
|
|
879
879
|
programId,
|
|
880
880
|
);
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Derive the **SettlementReceipt** PDA (v0.10.0 anti-replay).
|
|
884
|
+
*
|
|
885
|
+
* Seeds: `["sap_recv", escrow_pda, service_hash_or_batch_root]`
|
|
886
|
+
*
|
|
887
|
+
* Created via `init` on every `settle_calls`, `settle_batch`, and
|
|
888
|
+
* `settle_calls_v2`. The PDA's existence after the first call blocks
|
|
889
|
+
* any future replay of the same `service_hash` (or `batch_root`)
|
|
890
|
+
* against the same escrow.
|
|
891
|
+
*
|
|
892
|
+
* @name deriveSettlementReceipt
|
|
893
|
+
* @description Compute the receipt PDA that gates settlement replay.
|
|
894
|
+
* @param escrowPda - Parent escrow PDA (V1 `EscrowAccount` or V2 `EscrowAccountV2`).
|
|
895
|
+
* @param receiptKey - 32-byte `service_hash` (single settle / V2 settle)
|
|
896
|
+
* or `batch_root = sha256(s_0 || ... || s_{N-1})` (settle_batch).
|
|
897
|
+
* @param programId - Override program ID.
|
|
898
|
+
* @returns {PdaResult} `[pda, bump]` tuple.
|
|
899
|
+
* @category PDA
|
|
900
|
+
* @since v0.10.0
|
|
901
|
+
*/
|
|
902
|
+
export const deriveSettlementReceipt = (
|
|
903
|
+
escrowPda: PublicKey,
|
|
904
|
+
receiptKey: Uint8Array | number[] | Buffer,
|
|
905
|
+
programId = SAP_PROGRAM_ID,
|
|
906
|
+
): PdaResult => {
|
|
907
|
+
const buf = Buffer.isBuffer(receiptKey)
|
|
908
|
+
? receiptKey
|
|
909
|
+
: Buffer.from(receiptKey as Uint8Array);
|
|
910
|
+
if (buf.length !== 32) {
|
|
911
|
+
throw new Error(
|
|
912
|
+
`deriveSettlementReceipt: receiptKey must be 32 bytes, got ${buf.length}`,
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
return findPda(
|
|
916
|
+
[toSeedBuf(SEEDS.SETTLEMENT_RECEIPT), escrowPda.toBuffer(), buf],
|
|
917
|
+
programId,
|
|
918
|
+
);
|
|
919
|
+
};
|
package/src/plugin/index.ts
CHANGED
|
@@ -782,6 +782,8 @@ async function executeEscrow(client: SapClient, name: string, input: any) {
|
|
|
782
782
|
const tx = await client.escrow.settleBatch(
|
|
783
783
|
new PublicKey(input.depositorWallet),
|
|
784
784
|
settlements,
|
|
785
|
+
// batchRoot omitted \u2192 SDK auto-derives sha256(s_0 || ... || s_N)
|
|
786
|
+
undefined,
|
|
785
787
|
[],
|
|
786
788
|
batchOpts,
|
|
787
789
|
);
|
package/src/utils/hash.ts
CHANGED
|
@@ -55,3 +55,59 @@ export const sha256 = (input: string | Buffer | Uint8Array): Uint8Array => {
|
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
57
|
export const hashToArray = (hash: Uint8Array): number[] => Array.from(hash);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Compute the deterministic batch root used by `settle_batch` (v0.10.0).
|
|
61
|
+
*
|
|
62
|
+
* The on-chain program enforces:
|
|
63
|
+
* `batch_root == sha256(s_0 || s_1 || ... || s_{N-1})`
|
|
64
|
+
* where each `s_i` is a 32-byte service hash, in the same order as the
|
|
65
|
+
* `settlements: Settlement[]` array passed to the instruction.
|
|
66
|
+
*
|
|
67
|
+
* Use this helper to derive the seed for the {@link deriveSettlementReceipt}
|
|
68
|
+
* PDA when batching settlements.
|
|
69
|
+
*
|
|
70
|
+
* @name computeBatchRoot
|
|
71
|
+
* @param serviceHashes - Array of 32-byte service hashes (Buffer/Uint8Array/number[]).
|
|
72
|
+
* @returns {Uint8Array} 32-byte batch root.
|
|
73
|
+
* @throws If any `serviceHashes[i]` is not exactly 32 bytes long.
|
|
74
|
+
* @category Utils
|
|
75
|
+
* @since v0.10.0
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { computeBatchRoot, hashToArray } from "@synapse-sap/sdk/utils";
|
|
80
|
+
*
|
|
81
|
+
* const root = computeBatchRoot([h1, h2, h3]);
|
|
82
|
+
* await client.escrow.settleBatch(depositor, settlements, root);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export const computeBatchRoot = (
|
|
86
|
+
serviceHashes: ReadonlyArray<Uint8Array | Buffer | number[]>,
|
|
87
|
+
): Uint8Array => {
|
|
88
|
+
if (serviceHashes.length === 0) {
|
|
89
|
+
throw new Error("computeBatchRoot: serviceHashes must not be empty");
|
|
90
|
+
}
|
|
91
|
+
const hash = createHash("sha256");
|
|
92
|
+
for (let i = 0; i < serviceHashes.length; i++) {
|
|
93
|
+
const h = serviceHashes[i];
|
|
94
|
+
let buf: Buffer;
|
|
95
|
+
if (h instanceof Uint8Array) {
|
|
96
|
+
buf = Buffer.from(h);
|
|
97
|
+
} else if (Array.isArray(h)) {
|
|
98
|
+
buf = Buffer.from(h);
|
|
99
|
+
} else {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`computeBatchRoot: serviceHashes[${i}] is undefined or unsupported type`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (buf.length !== 32) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`computeBatchRoot: serviceHashes[${i}] must be 32 bytes, got ${buf.length}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
hash.update(buf);
|
|
110
|
+
}
|
|
111
|
+
return new Uint8Array(hash.digest());
|
|
112
|
+
};
|
|
113
|
+
|