@ton-agent-kit/plugin-escrow 1.0.2 → 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/package.json +1 -1
- package/src/actions/auto-release-escrow.ts +72 -0
- package/src/actions/claim-reward.ts +48 -0
- package/src/actions/confirm-delivery.ts +60 -0
- package/src/actions/create-escrow.ts +44 -26
- package/src/actions/fallback-settle.ts +51 -0
- package/src/actions/get-escrow-info.ts +22 -4
- package/src/actions/join-dispute.ts +56 -0
- package/src/actions/open-dispute.ts +56 -0
- package/src/actions/refund-escrow.ts +31 -0
- package/src/actions/release-escrow.ts +30 -0
- package/src/actions/seller-stake.ts +69 -0
- package/src/actions/vote-refund.ts +50 -0
- package/src/actions/vote-release.ts +50 -0
- package/src/contracts/Escrow_Escrow.code.boc +0 -0
- package/src/contracts/Escrow_Escrow.ts +887 -37
- package/src/index.ts +60 -6
- package/src/utils.ts +75 -84
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
|
|
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
|
|
13
|
-
* - deposit_to_escrow
|
|
14
|
-
* - release_escrow
|
|
15
|
-
* - refund_escrow
|
|
16
|
-
* - get_escrow_info
|
|
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,
|
|
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:
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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 {
|