@ton-agent-kit/plugin-escrow 1.0.3 → 1.1.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/src/index.ts CHANGED
@@ -4,16 +4,51 @@ import { depositToEscrowAction } from "./actions/deposit-to-escrow";
4
4
  import { releaseEscrowAction } from "./actions/release-escrow";
5
5
  import { refundEscrowAction } from "./actions/refund-escrow";
6
6
  import { getEscrowInfoAction } from "./actions/get-escrow-info";
7
+ import { confirmDeliveryAction } from "./actions/confirm-delivery";
8
+ import { autoReleaseEscrowAction } from "./actions/auto-release-escrow";
9
+ import { openDisputeAction } from "./actions/open-dispute";
10
+ import { joinDisputeAction } from "./actions/join-dispute";
11
+ import { voteReleaseAction } from "./actions/vote-release";
12
+ import { voteRefundAction } from "./actions/vote-refund";
13
+ import { claimRewardAction } from "./actions/claim-reward";
14
+ import { fallbackSettleAction } from "./actions/fallback-settle";
15
+ import { sellerStakeAction } from "./actions/seller-stake";
7
16
 
8
17
  /**
9
- * Escrow Plugin TON escrow deal management
18
+ * Escrow Plugin -- TON escrow with self-selecting arbiters, staking, and
19
+ * dispute-resolution voting.
20
+ *
21
+ * Manages the full lifecycle of on-chain escrow deals: creation, funding,
22
+ * delivery confirmation, release/refund, and a dispute flow where independent
23
+ * arbiters self-select by staking TON, then vote to release or refund.
10
24
  *
11
25
  * Actions:
12
- * - create_escrow: Create a new escrow deal
13
- * - deposit_to_escrow: Fund an escrow with TON
14
- * - release_escrow: Release funds to beneficiary
15
- * - refund_escrow: Refund funds to depositor
16
- * - get_escrow_info: Get escrow details or list all
26
+ * - `create_escrow` -- Create a new escrow deal (arbiters self-select during disputes)
27
+ * - `deposit_to_escrow` -- Fund an escrow with TON
28
+ * - `release_escrow` -- Release funds to beneficiary (depositor only, non-dispute)
29
+ * - `refund_escrow` -- Refund funds to depositor
30
+ * - `get_escrow_info` -- Get escrow details or list all escrows
31
+ * - `confirm_delivery` -- Confirm service delivery on-chain (buyer only)
32
+ * - `auto_release_escrow` -- Release after deadline (requires delivery confirmation)
33
+ * - `open_dispute` -- Open a dispute, freezing the escrow for arbiter voting
34
+ * - `join_dispute` -- Stake TON to join as an arbiter in a dispute
35
+ * - `vote_release` -- Arbiter votes to release funds during a dispute
36
+ * - `vote_refund` -- Arbiter votes to refund funds during a dispute
37
+ * - `claim_reward` -- Claim arbiter reward after settlement (winners profit, losers forfeit)
38
+ * - `fallback_settle` -- Settle after the 72 h voting deadline expires
39
+ * - `seller_stake` -- Seller stakes TON to signal commitment to the deal
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * import EscrowPlugin from "@ton-agent-kit/plugin-escrow";
44
+ * const agent = new TonAgentKit(wallet, rpcUrl).use(EscrowPlugin);
45
+ * const escrow = await agent.runAction("create_escrow", {
46
+ * beneficiary: "EQBx...",
47
+ * amount: "1.5",
48
+ * });
49
+ * ```
50
+ *
51
+ * @since 1.0.0
17
52
  */
18
53
  const EscrowPlugin = definePlugin({
19
54
  name: "escrow",
@@ -23,9 +58,19 @@ const EscrowPlugin = definePlugin({
23
58
  releaseEscrowAction,
24
59
  refundEscrowAction,
25
60
  getEscrowInfoAction,
61
+ confirmDeliveryAction,
62
+ autoReleaseEscrowAction,
63
+ openDisputeAction,
64
+ joinDisputeAction,
65
+ voteReleaseAction,
66
+ voteRefundAction,
67
+ claimRewardAction,
68
+ fallbackSettleAction,
69
+ sellerStakeAction,
26
70
  ],
27
71
  });
28
72
 
73
+ /** @since 1.0.0 */
29
74
  export default EscrowPlugin;
30
75
 
31
76
  export {
@@ -34,4 +79,13 @@ export {
34
79
  releaseEscrowAction,
35
80
  refundEscrowAction,
36
81
  getEscrowInfoAction,
82
+ confirmDeliveryAction,
83
+ autoReleaseEscrowAction,
84
+ openDisputeAction,
85
+ joinDisputeAction,
86
+ voteReleaseAction,
87
+ voteRefundAction,
88
+ claimRewardAction,
89
+ fallbackSettleAction,
90
+ sellerStakeAction,
37
91
  };
package/src/utils.ts CHANGED
@@ -8,22 +8,30 @@ import {
8
8
  storeDeposit,
9
9
  storeRelease,
10
10
  storeRefund,
11
+ storeDeliveryConfirmed,
12
+ storeAutoRelease,
13
+ storeOpenDispute,
14
+ storeJoinDispute,
15
+ storeVoteRelease,
16
+ storeVoteRefund,
17
+ storeClaimReward,
18
+ storeFallbackSettle,
19
+ storeSellerStake,
11
20
  } from "./contracts/Escrow_Escrow";
12
21
  import { sendTransaction } from "@ton-agent-kit/core";
13
22
 
14
23
  const ESCROW_FILE = ".escrow-store.json";
15
24
 
16
- // ── JSON index (escrowId → metadata + contract address) ───────────────────
17
-
18
25
  export interface EscrowRecord {
19
26
  id: string;
20
27
  contractAddress: string;
21
28
  depositor: string;
22
29
  beneficiary: string;
23
- arbiter: string;
24
30
  amount: string;
25
31
  deadline: number;
26
32
  deadlineISO: string;
33
+ minArbiters: number;
34
+ minStake: string;
27
35
  description: string;
28
36
  status: string;
29
37
  createdAt: string;
@@ -48,22 +56,23 @@ export function saveEscrows(escrows: Record<string, EscrowRecord>): void {
48
56
  }
49
57
  }
50
58
 
51
- // ── On-chain helpers ──────────────────────────────────────────────────────
59
+ // ── On-chain helpers ──
52
60
 
53
- /**
54
- * Deploy a new Escrow contract instance and return the contract address.
55
- */
56
61
  export async function deployEscrowContract(
57
62
  agent: AgentContext,
58
63
  depositor: Address,
59
64
  beneficiary: Address,
60
- arbiter: Address,
61
65
  deadline: bigint,
66
+ minArbiters: bigint,
67
+ minStake: bigint,
68
+ reputationContract: Address,
69
+ requireRepCollateral: boolean,
70
+ minRepScore: bigint,
71
+ baseSellerStake: bigint,
62
72
  ): Promise<Address> {
63
- const escrow = await Escrow.fromInit(depositor, beneficiary, arbiter, deadline);
64
-
73
+ const escrow = await Escrow.fromInit(depositor, beneficiary, deadline, minArbiters, minStake, reputationContract, requireRepCollateral, minRepScore, baseSellerStake);
65
74
  const deployBody = beginCell()
66
- .store(storeDeploy({ $$type: "Deploy", queryId: BigInt(0) }))
75
+ .store(storeDeploy({ $$type: "Deploy", queryId: 0n }))
67
76
  .endCell();
68
77
 
69
78
  await sendTransaction(agent, [
@@ -75,77 +84,70 @@ export async function deployEscrowContract(
75
84
  body: deployBody,
76
85
  }),
77
86
  ]);
78
-
79
87
  return escrow.address;
80
88
  }
81
89
 
82
- /**
83
- * Send a Deposit message (with TON attached) to an escrow contract.
84
- */
85
- export async function depositToContract(
86
- agent: AgentContext,
87
- contractAddress: Address,
88
- amount: string,
89
- ): Promise<void> {
90
- const body = beginCell()
91
- .store(storeDeposit({ $$type: "Deposit", queryId: BigInt(0) }))
92
- .endCell();
90
+ export async function depositToContract(agent: AgentContext, contractAddress: Address, amount: string): Promise<void> {
91
+ const body = beginCell().store(storeDeposit({ $$type: "Deposit", queryId: 0n })).endCell();
92
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano(amount), bounce: true, body })]);
93
+ }
93
94
 
94
- await sendTransaction(agent, [
95
- internal({
96
- to: contractAddress,
97
- value: toNano(amount),
98
- bounce: true,
99
- body,
100
- }),
101
- ]);
95
+ export async function releaseContract(agent: AgentContext, contractAddress: Address): Promise<void> {
96
+ const body = beginCell().store(storeRelease({ $$type: "Release", queryId: 0n })).endCell();
97
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.02"), bounce: true, body })]);
102
98
  }
103
99
 
104
- /**
105
- * Send a Release message to an escrow contract.
106
- */
107
- export async function releaseContract(
108
- agent: AgentContext,
109
- contractAddress: Address,
110
- ): Promise<void> {
111
- const body = beginCell()
112
- .store(storeRelease({ $$type: "Release", queryId: BigInt(0) }))
113
- .endCell();
100
+ export async function refundContract(agent: AgentContext, contractAddress: Address): Promise<void> {
101
+ const body = beginCell().store(storeRefund({ $$type: "Refund", queryId: 0n })).endCell();
102
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.02"), bounce: true, body })]);
103
+ }
114
104
 
115
- await sendTransaction(agent, [
116
- internal({
117
- to: contractAddress,
118
- value: toNano("0.02"),
119
- bounce: true,
120
- body,
121
- }),
122
- ]);
105
+ export async function confirmDeliveryOnContract(agent: AgentContext, contractAddress: Address, x402TxHash: string): Promise<void> {
106
+ const body = beginCell().store(storeDeliveryConfirmed({ $$type: "DeliveryConfirmed", x402TxHash })).endCell();
107
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.02"), bounce: true, body })]);
123
108
  }
124
109
 
125
- /**
126
- * Send a Refund message to an escrow contract.
127
- */
128
- export async function refundContract(
129
- agent: AgentContext,
130
- contractAddress: Address,
131
- ): Promise<void> {
132
- const body = beginCell()
133
- .store(storeRefund({ $$type: "Refund", queryId: BigInt(0) }))
134
- .endCell();
110
+ export async function autoReleaseOnContract(agent: AgentContext, contractAddress: Address): Promise<void> {
111
+ const body = beginCell().store(storeAutoRelease({ $$type: "AutoRelease" })).endCell();
112
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.02"), bounce: true, body })]);
113
+ }
135
114
 
136
- await sendTransaction(agent, [
137
- internal({
138
- to: contractAddress,
139
- value: toNano("0.02"),
140
- bounce: true,
141
- body,
142
- }),
143
- ]);
115
+ export async function openDisputeOnContract(agent: AgentContext, contractAddress: Address): Promise<void> {
116
+ const body = beginCell().store(storeOpenDispute({ $$type: "OpenDispute" })).endCell();
117
+ // 0.05 TON: 0.02 escrow gas + 0.03 for cross-contract notification to reputation
118
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.05"), bounce: true, body })]);
119
+ }
120
+
121
+ export async function joinDisputeOnContract(agent: AgentContext, contractAddress: Address, stakeAmount: string): Promise<void> {
122
+ const body = beginCell().store(storeJoinDispute({ $$type: "JoinDispute" })).endCell();
123
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano(stakeAmount), bounce: true, body })]);
124
+ }
125
+
126
+ export async function voteReleaseOnContract(agent: AgentContext, contractAddress: Address): Promise<void> {
127
+ const body = beginCell().store(storeVoteRelease({ $$type: "VoteRelease" })).endCell();
128
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.02"), bounce: true, body })]);
129
+ }
130
+
131
+ export async function voteRefundOnContract(agent: AgentContext, contractAddress: Address): Promise<void> {
132
+ const body = beginCell().store(storeVoteRefund({ $$type: "VoteRefund" })).endCell();
133
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.02"), bounce: true, body })]);
134
+ }
135
+
136
+ export async function claimRewardOnContract(agent: AgentContext, contractAddress: Address): Promise<void> {
137
+ const body = beginCell().store(storeClaimReward({ $$type: "ClaimReward" })).endCell();
138
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.02"), bounce: true, body })]);
139
+ }
140
+
141
+ export async function fallbackSettleOnContract(agent: AgentContext, contractAddress: Address): Promise<void> {
142
+ const body = beginCell().store(storeFallbackSettle({ $$type: "FallbackSettle" })).endCell();
143
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano("0.02"), bounce: true, body })]);
144
+ }
145
+
146
+ export async function sellerStakeOnContract(agent: AgentContext, contractAddress: Address, stakeAmount: string): Promise<void> {
147
+ const body = beginCell().store(storeSellerStake({ $$type: "SellerStake" })).endCell();
148
+ await sendTransaction(agent, [internal({ to: contractAddress, value: toNano(stakeAmount), bounce: true, body })]);
144
149
  }
145
150
 
146
- /**
147
- * Read on-chain escrow state via get method.
148
- */
149
151
  export async function getContractState(agent: AgentContext, contractAddress: Address) {
150
152
  const client = new TonClient4({ endpoint: agent.rpcUrl });
151
153
  const contract = client.open(Escrow.fromAddress(contractAddress));
@@ -154,21 +156,10 @@ export async function getContractState(agent: AgentContext, contractAddress: Add
154
156
  return { ...data, balance };
155
157
  }
156
158
 
157
- /**
158
- * Look up the latest tx hash for an address via TonAPI.
159
- */
160
- export async function getLatestTxHash(
161
- address: string,
162
- network: "testnet" | "mainnet",
163
- ): Promise<string> {
164
- const apiBase =
165
- network === "testnet"
166
- ? "https://testnet.tonapi.io/v2"
167
- : "https://tonapi.io/v2";
159
+ export async function getLatestTxHash(address: string, network: "testnet" | "mainnet"): Promise<string> {
160
+ const apiBase = network === "testnet" ? "https://testnet.tonapi.io/v2" : "https://tonapi.io/v2";
168
161
  try {
169
- const res = await fetch(
170
- `${apiBase}/accounts/${encodeURIComponent(address)}/events?limit=1`,
171
- );
162
+ const res = await fetch(`${apiBase}/accounts/${encodeURIComponent(address)}/events?limit=1`);
172
163
  const data = await res.json();
173
164
  return data.events?.[0]?.event_id || "pending";
174
165
  } catch {