@antseed/node 0.1.0 → 0.1.2
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/LICENSE +674 -0
- package/README.md +7 -5
- package/dist/discovery/http-metadata-resolver.d.ts +6 -0
- package/dist/discovery/http-metadata-resolver.d.ts.map +1 -1
- package/dist/discovery/http-metadata-resolver.js +32 -4
- package/dist/discovery/http-metadata-resolver.js.map +1 -1
- package/dist/discovery/peer-lookup.d.ts +1 -0
- package/dist/discovery/peer-lookup.d.ts.map +1 -1
- package/dist/discovery/peer-lookup.js +10 -25
- package/dist/discovery/peer-lookup.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/seller-provider.d.ts +13 -1
- package/dist/interfaces/seller-provider.d.ts.map +1 -1
- package/dist/node.d.ts +13 -3
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +146 -21
- package/dist/node.js.map +1 -1
- package/dist/proxy/proxy-mux.d.ts +3 -1
- package/dist/proxy/proxy-mux.d.ts.map +1 -1
- package/dist/proxy/proxy-mux.js +9 -5
- package/dist/proxy/proxy-mux.js.map +1 -1
- package/dist/types/http.d.ts +1 -0
- package/dist/types/http.d.ts.map +1 -1
- package/dist/types/http.js +1 -1
- package/dist/types/http.js.map +1 -1
- package/package.json +14 -10
- package/contracts/AntseedEscrow.sol +0 -310
- package/contracts/MockUSDC.sol +0 -64
- package/contracts/README.md +0 -102
- package/src/config/encryption.test.ts +0 -49
- package/src/config/encryption.ts +0 -53
- package/src/config/plugin-config-manager.test.ts +0 -92
- package/src/config/plugin-config-manager.ts +0 -153
- package/src/config/plugin-loader.ts +0 -90
- package/src/discovery/announcer.ts +0 -169
- package/src/discovery/bootstrap.ts +0 -57
- package/src/discovery/default-metadata-resolver.ts +0 -18
- package/src/discovery/dht-health.ts +0 -136
- package/src/discovery/dht-node.ts +0 -191
- package/src/discovery/http-metadata-resolver.ts +0 -47
- package/src/discovery/index.ts +0 -15
- package/src/discovery/metadata-codec.ts +0 -453
- package/src/discovery/metadata-resolver.ts +0 -7
- package/src/discovery/metadata-server.ts +0 -73
- package/src/discovery/metadata-validator.ts +0 -172
- package/src/discovery/peer-lookup.ts +0 -122
- package/src/discovery/peer-metadata.ts +0 -34
- package/src/discovery/peer-selector.ts +0 -134
- package/src/discovery/profile-manager.ts +0 -131
- package/src/discovery/profile-search.ts +0 -100
- package/src/discovery/reputation-verifier.ts +0 -54
- package/src/index.ts +0 -61
- package/src/interfaces/buyer-router.ts +0 -21
- package/src/interfaces/plugin.ts +0 -36
- package/src/interfaces/seller-provider.ts +0 -81
- package/src/metering/index.ts +0 -6
- package/src/metering/receipt-generator.ts +0 -105
- package/src/metering/receipt-verifier.ts +0 -102
- package/src/metering/session-tracker.ts +0 -145
- package/src/metering/storage.ts +0 -600
- package/src/metering/token-counter.ts +0 -127
- package/src/metering/usage-aggregator.ts +0 -236
- package/src/node.ts +0 -1698
- package/src/p2p/connection-auth.ts +0 -152
- package/src/p2p/connection-manager.ts +0 -916
- package/src/p2p/handshake.ts +0 -162
- package/src/p2p/ice-config.ts +0 -59
- package/src/p2p/identity.ts +0 -110
- package/src/p2p/index.ts +0 -11
- package/src/p2p/keepalive.ts +0 -118
- package/src/p2p/message-protocol.ts +0 -171
- package/src/p2p/nat-traversal.ts +0 -169
- package/src/p2p/payment-codec.ts +0 -165
- package/src/p2p/payment-mux.ts +0 -153
- package/src/p2p/reconnect.ts +0 -117
- package/src/payments/balance-manager.ts +0 -77
- package/src/payments/buyer-payment-manager.ts +0 -414
- package/src/payments/disputes.ts +0 -72
- package/src/payments/evm/escrow-client.ts +0 -263
- package/src/payments/evm/keypair.ts +0 -31
- package/src/payments/evm/signatures.ts +0 -103
- package/src/payments/evm/wallet.ts +0 -42
- package/src/payments/index.ts +0 -50
- package/src/payments/settlement.ts +0 -40
- package/src/payments/types.ts +0 -79
- package/src/proxy/index.ts +0 -3
- package/src/proxy/provider-detection.ts +0 -78
- package/src/proxy/proxy-mux.ts +0 -173
- package/src/proxy/request-codec.ts +0 -294
- package/src/reputation/index.ts +0 -6
- package/src/reputation/rating-manager.ts +0 -118
- package/src/reputation/report-manager.ts +0 -91
- package/src/reputation/trust-engine.ts +0 -120
- package/src/reputation/trust-score.ts +0 -74
- package/src/reputation/uptime-tracker.ts +0 -155
- package/src/routing/default-router.ts +0 -75
- package/src/types/bittorrent-dht.d.ts +0 -19
- package/src/types/buyer.ts +0 -37
- package/src/types/capability.ts +0 -34
- package/src/types/connection.ts +0 -29
- package/src/types/http.ts +0 -20
- package/src/types/index.ts +0 -14
- package/src/types/metering.ts +0 -175
- package/src/types/nat-api.d.ts +0 -29
- package/src/types/peer-profile.ts +0 -25
- package/src/types/peer.ts +0 -62
- package/src/types/plugin-config.ts +0 -31
- package/src/types/protocol.ts +0 -162
- package/src/types/provider.ts +0 -40
- package/src/types/rating.ts +0 -23
- package/src/types/report.ts +0 -30
- package/src/types/seller.ts +0 -38
- package/src/types/staking.ts +0 -23
- package/src/utils/debug.ts +0 -30
- package/src/utils/hex.ts +0 -14
- package/tests/balance-manager.test.ts +0 -156
- package/tests/bootstrap.test.ts +0 -108
- package/tests/buyer-payment-manager.test.ts +0 -358
- package/tests/connection-auth.test.ts +0 -87
- package/tests/default-router.test.ts +0 -148
- package/tests/evm-keypair.test.ts +0 -173
- package/tests/identity.test.ts +0 -133
- package/tests/message-protocol.test.ts +0 -212
- package/tests/metadata-codec.test.ts +0 -165
- package/tests/metadata-validator.test.ts +0 -261
- package/tests/metering-storage.test.ts +0 -244
- package/tests/payment-codec.test.ts +0 -95
- package/tests/payment-mux.test.ts +0 -191
- package/tests/peer-selector.test.ts +0 -184
- package/tests/provider-detection.test.ts +0 -107
- package/tests/proxy-mux-security.test.ts +0 -38
- package/tests/receipt.test.ts +0 -215
- package/tests/reputation-integration.test.ts +0 -195
- package/tests/request-codec.test.ts +0 -144
- package/tests/token-counter.test.ts +0 -122
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -7
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity ^0.8.24;
|
|
3
|
-
|
|
4
|
-
interface IERC20 {
|
|
5
|
-
function transferFrom(address from, address to, uint256 value) external returns (bool);
|
|
6
|
-
function transfer(address to, uint256 value) external returns (bool);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @title AntseedEscrow
|
|
11
|
-
* @notice Session-scoped USDC escrow used by Antseed payment channels.
|
|
12
|
-
* @dev State mapping matches node/src/payments/crypto/escrow.ts STATE_MAP:
|
|
13
|
-
* 0=open, 1=active, 2=disputed, 3=settled, 4=closed.
|
|
14
|
-
*/
|
|
15
|
-
contract AntseedEscrow {
|
|
16
|
-
enum ChannelState {
|
|
17
|
-
Open,
|
|
18
|
-
Active,
|
|
19
|
-
Disputed,
|
|
20
|
-
Settled,
|
|
21
|
-
Closed
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
struct Channel {
|
|
25
|
-
address buyer;
|
|
26
|
-
address seller;
|
|
27
|
-
uint256 amount;
|
|
28
|
-
ChannelState state;
|
|
29
|
-
uint64 createdAt;
|
|
30
|
-
uint64 disputedAt;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
IERC20 public immutable usdc;
|
|
34
|
-
address public owner;
|
|
35
|
-
address public arbiter;
|
|
36
|
-
address public feeCollector;
|
|
37
|
-
uint64 public disputeTimeout;
|
|
38
|
-
|
|
39
|
-
mapping(bytes32 => Channel) private channels;
|
|
40
|
-
|
|
41
|
-
bool private locked;
|
|
42
|
-
|
|
43
|
-
event ChannelDeposited(bytes32 indexed sessionId, address indexed buyer, address indexed seller, uint256 amount);
|
|
44
|
-
event ChannelReleased(bytes32 indexed sessionId, address indexed seller, uint256 amount, address caller);
|
|
45
|
-
event ChannelSettled(
|
|
46
|
-
bytes32 indexed sessionId,
|
|
47
|
-
address indexed seller,
|
|
48
|
-
uint256 sellerAmount,
|
|
49
|
-
address indexed feeCollector,
|
|
50
|
-
uint256 platformAmount,
|
|
51
|
-
address buyer,
|
|
52
|
-
uint256 buyerRefund,
|
|
53
|
-
address caller
|
|
54
|
-
);
|
|
55
|
-
event ChannelRefunded(bytes32 indexed sessionId, address indexed buyer, uint256 amount, address caller);
|
|
56
|
-
event ChannelDisputed(bytes32 indexed sessionId, address indexed caller);
|
|
57
|
-
event ArbiterUpdated(address indexed previousArbiter, address indexed newArbiter);
|
|
58
|
-
event FeeCollectorUpdated(address indexed previousFeeCollector, address indexed newFeeCollector);
|
|
59
|
-
event DisputeTimeoutUpdated(uint64 previousTimeout, uint64 newTimeout);
|
|
60
|
-
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
|
61
|
-
|
|
62
|
-
error InvalidAddress();
|
|
63
|
-
error InvalidSession();
|
|
64
|
-
error InvalidAmount();
|
|
65
|
-
error SessionExists();
|
|
66
|
-
error InvalidState();
|
|
67
|
-
error NotAuthorized();
|
|
68
|
-
error NotOwner();
|
|
69
|
-
error NotArbiter();
|
|
70
|
-
error InvalidSplit();
|
|
71
|
-
error DisputeTimeoutNotReached();
|
|
72
|
-
error Reentrancy();
|
|
73
|
-
error TransferFailed();
|
|
74
|
-
|
|
75
|
-
modifier nonReentrant() {
|
|
76
|
-
if (locked) revert Reentrancy();
|
|
77
|
-
locked = true;
|
|
78
|
-
_;
|
|
79
|
-
locked = false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
modifier onlyOwner() {
|
|
83
|
-
if (msg.sender != owner) revert NotOwner();
|
|
84
|
-
_;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
constructor(address usdcToken, address initialArbiter) {
|
|
88
|
-
if (usdcToken == address(0)) revert InvalidAddress();
|
|
89
|
-
usdc = IERC20(usdcToken);
|
|
90
|
-
owner = msg.sender;
|
|
91
|
-
arbiter = initialArbiter == address(0) ? msg.sender : initialArbiter;
|
|
92
|
-
feeCollector = msg.sender;
|
|
93
|
-
disputeTimeout = 72 hours;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function setArbiter(address newArbiter) external onlyOwner {
|
|
97
|
-
if (newArbiter == address(0)) revert InvalidAddress();
|
|
98
|
-
emit ArbiterUpdated(arbiter, newArbiter);
|
|
99
|
-
arbiter = newArbiter;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function transferOwnership(address newOwner) external onlyOwner {
|
|
103
|
-
if (newOwner == address(0)) revert InvalidAddress();
|
|
104
|
-
emit OwnershipTransferred(owner, newOwner);
|
|
105
|
-
owner = newOwner;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function setFeeCollector(address newFeeCollector) external onlyOwner {
|
|
109
|
-
if (newFeeCollector == address(0)) revert InvalidAddress();
|
|
110
|
-
emit FeeCollectorUpdated(feeCollector, newFeeCollector);
|
|
111
|
-
feeCollector = newFeeCollector;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function setDisputeTimeout(uint64 newTimeout) external onlyOwner {
|
|
115
|
-
if (newTimeout == 0) revert InvalidAmount();
|
|
116
|
-
emit DisputeTimeoutUpdated(disputeTimeout, newTimeout);
|
|
117
|
-
disputeTimeout = newTimeout;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* @notice Lock buyer funds for a session.
|
|
122
|
-
* @param sessionId keccak256(sessionIdString) from ethers.id(sessionId)
|
|
123
|
-
* @param seller seller wallet address
|
|
124
|
-
* @param amount USDC amount in token base units (6 decimals)
|
|
125
|
-
*/
|
|
126
|
-
function deposit(bytes32 sessionId, address seller, uint256 amount) external nonReentrant {
|
|
127
|
-
if (sessionId == bytes32(0)) revert InvalidSession();
|
|
128
|
-
if (seller == address(0)) revert InvalidAddress();
|
|
129
|
-
if (amount == 0) revert InvalidAmount();
|
|
130
|
-
|
|
131
|
-
Channel storage channel = channels[sessionId];
|
|
132
|
-
if (channel.buyer != address(0)) revert SessionExists();
|
|
133
|
-
|
|
134
|
-
channel.buyer = msg.sender;
|
|
135
|
-
channel.seller = seller;
|
|
136
|
-
channel.amount = amount;
|
|
137
|
-
channel.state = ChannelState.Active;
|
|
138
|
-
channel.createdAt = uint64(block.timestamp);
|
|
139
|
-
channel.disputedAt = 0;
|
|
140
|
-
|
|
141
|
-
_safeTransferFrom(msg.sender, address(this), amount);
|
|
142
|
-
emit ChannelDeposited(sessionId, msg.sender, seller, amount);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* @notice Release escrow to seller.
|
|
147
|
-
* @dev Active channel: buyer/seller/arbiter can release.
|
|
148
|
-
* Disputed channel: only arbiter can release.
|
|
149
|
-
*/
|
|
150
|
-
function release(bytes32 sessionId) external nonReentrant {
|
|
151
|
-
Channel storage channel = channels[sessionId];
|
|
152
|
-
if (channel.state != ChannelState.Active && channel.state != ChannelState.Disputed) {
|
|
153
|
-
revert InvalidState();
|
|
154
|
-
}
|
|
155
|
-
_authorizeForSettlement(channel);
|
|
156
|
-
|
|
157
|
-
uint256 amount = channel.amount;
|
|
158
|
-
channel.amount = 0;
|
|
159
|
-
channel.state = ChannelState.Settled;
|
|
160
|
-
channel.disputedAt = 0;
|
|
161
|
-
|
|
162
|
-
_safeTransfer(channel.seller, amount);
|
|
163
|
-
emit ChannelReleased(sessionId, channel.seller, amount, msg.sender);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @notice Settle escrow by splitting funds between seller, platform fee collector, and buyer refund.
|
|
168
|
-
* @dev Active channel: buyer/seller/arbiter can settle.
|
|
169
|
-
* Disputed channel: only arbiter can settle.
|
|
170
|
-
*/
|
|
171
|
-
function settle(bytes32 sessionId, uint256 sellerAmount, uint256 platformAmount) external nonReentrant {
|
|
172
|
-
Channel storage channel = channels[sessionId];
|
|
173
|
-
if (channel.state != ChannelState.Active && channel.state != ChannelState.Disputed) {
|
|
174
|
-
revert InvalidState();
|
|
175
|
-
}
|
|
176
|
-
_authorizeForSettlement(channel);
|
|
177
|
-
|
|
178
|
-
uint256 amount = channel.amount;
|
|
179
|
-
if (sellerAmount > amount || platformAmount > amount - sellerAmount) {
|
|
180
|
-
revert InvalidSplit();
|
|
181
|
-
}
|
|
182
|
-
uint256 buyerRefund = amount - sellerAmount - platformAmount;
|
|
183
|
-
|
|
184
|
-
channel.amount = 0;
|
|
185
|
-
channel.state = ChannelState.Settled;
|
|
186
|
-
channel.disputedAt = 0;
|
|
187
|
-
|
|
188
|
-
if (sellerAmount > 0) {
|
|
189
|
-
_safeTransfer(channel.seller, sellerAmount);
|
|
190
|
-
}
|
|
191
|
-
if (platformAmount > 0) {
|
|
192
|
-
_safeTransfer(feeCollector, platformAmount);
|
|
193
|
-
}
|
|
194
|
-
if (buyerRefund > 0) {
|
|
195
|
-
_safeTransfer(channel.buyer, buyerRefund);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
emit ChannelSettled(
|
|
199
|
-
sessionId,
|
|
200
|
-
channel.seller,
|
|
201
|
-
sellerAmount,
|
|
202
|
-
feeCollector,
|
|
203
|
-
platformAmount,
|
|
204
|
-
channel.buyer,
|
|
205
|
-
buyerRefund,
|
|
206
|
-
msg.sender
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* @notice Mark an active channel as disputed.
|
|
212
|
-
*/
|
|
213
|
-
function dispute(bytes32 sessionId) external {
|
|
214
|
-
Channel storage channel = channels[sessionId];
|
|
215
|
-
if (channel.state != ChannelState.Active) revert InvalidState();
|
|
216
|
-
if (msg.sender != channel.buyer && msg.sender != channel.seller && msg.sender != arbiter) {
|
|
217
|
-
revert NotAuthorized();
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
channel.state = ChannelState.Disputed;
|
|
221
|
-
channel.disputedAt = uint64(block.timestamp);
|
|
222
|
-
emit ChannelDisputed(sessionId, msg.sender);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* @notice Refund escrow to buyer.
|
|
227
|
-
* @dev Active channel: buyer/arbiter can refund.
|
|
228
|
-
* Disputed channel: only arbiter can refund.
|
|
229
|
-
*/
|
|
230
|
-
function refund(bytes32 sessionId) external nonReentrant {
|
|
231
|
-
Channel storage channel = channels[sessionId];
|
|
232
|
-
if (channel.state != ChannelState.Active && channel.state != ChannelState.Disputed) {
|
|
233
|
-
revert InvalidState();
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (channel.state == ChannelState.Active) {
|
|
237
|
-
if (msg.sender != channel.buyer && msg.sender != arbiter) {
|
|
238
|
-
revert NotAuthorized();
|
|
239
|
-
}
|
|
240
|
-
} else if (msg.sender != arbiter) {
|
|
241
|
-
revert NotArbiter();
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
uint256 amount = channel.amount;
|
|
245
|
-
channel.amount = 0;
|
|
246
|
-
channel.state = ChannelState.Closed;
|
|
247
|
-
channel.disputedAt = 0;
|
|
248
|
-
|
|
249
|
-
_safeTransfer(channel.buyer, amount);
|
|
250
|
-
emit ChannelRefunded(sessionId, channel.buyer, amount, msg.sender);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* @notice Resolve stale disputes by refunding the buyer after the dispute timeout.
|
|
255
|
-
*/
|
|
256
|
-
function resolveDisputeTimeout(bytes32 sessionId) external nonReentrant {
|
|
257
|
-
Channel storage channel = channels[sessionId];
|
|
258
|
-
if (channel.state != ChannelState.Disputed) revert InvalidState();
|
|
259
|
-
if (uint64(block.timestamp) < channel.disputedAt + disputeTimeout) {
|
|
260
|
-
revert DisputeTimeoutNotReached();
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
uint256 amount = channel.amount;
|
|
264
|
-
channel.amount = 0;
|
|
265
|
-
channel.state = ChannelState.Closed;
|
|
266
|
-
channel.disputedAt = 0;
|
|
267
|
-
|
|
268
|
-
_safeTransfer(channel.buyer, amount);
|
|
269
|
-
emit ChannelRefunded(sessionId, channel.buyer, amount, msg.sender);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function getChannel(bytes32 sessionId)
|
|
273
|
-
external
|
|
274
|
-
view
|
|
275
|
-
returns (address buyer, address seller, uint256 amount, uint8 state)
|
|
276
|
-
{
|
|
277
|
-
Channel storage channel = channels[sessionId];
|
|
278
|
-
return (channel.buyer, channel.seller, channel.amount, uint8(channel.state));
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function _safeTransferFrom(address from, address to, uint256 value) private {
|
|
282
|
-
(bool ok, bytes memory ret) = address(usdc).call(
|
|
283
|
-
abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)
|
|
284
|
-
);
|
|
285
|
-
if (!ok || (ret.length > 0 && !abi.decode(ret, (bool)))) {
|
|
286
|
-
revert TransferFailed();
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function _safeTransfer(address to, uint256 value) private {
|
|
291
|
-
(bool ok, bytes memory ret) = address(usdc).call(
|
|
292
|
-
abi.encodeWithSelector(IERC20.transfer.selector, to, value)
|
|
293
|
-
);
|
|
294
|
-
if (!ok || (ret.length > 0 && !abi.decode(ret, (bool)))) {
|
|
295
|
-
revert TransferFailed();
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function _authorizeForSettlement(Channel storage channel) private view {
|
|
300
|
-
if (channel.state == ChannelState.Active) {
|
|
301
|
-
if (msg.sender != channel.buyer && msg.sender != channel.seller && msg.sender != arbiter) {
|
|
302
|
-
revert NotAuthorized();
|
|
303
|
-
}
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
if (msg.sender != arbiter) {
|
|
307
|
-
revert NotArbiter();
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
package/contracts/MockUSDC.sol
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity ^0.8.24;
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @title MockUSDC
|
|
6
|
-
* @notice Minimal ERC20 used for local escrow integration tests and examples.
|
|
7
|
-
*/
|
|
8
|
-
contract MockUSDC {
|
|
9
|
-
string public constant name = "Mock USD Coin";
|
|
10
|
-
string public constant symbol = "USDC";
|
|
11
|
-
uint8 public constant decimals = 6;
|
|
12
|
-
|
|
13
|
-
uint256 public totalSupply;
|
|
14
|
-
|
|
15
|
-
mapping(address => uint256) public balanceOf;
|
|
16
|
-
mapping(address => mapping(address => uint256)) public allowance;
|
|
17
|
-
|
|
18
|
-
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
19
|
-
event Approval(address indexed owner, address indexed spender, uint256 value);
|
|
20
|
-
|
|
21
|
-
error InsufficientBalance();
|
|
22
|
-
error InsufficientAllowance();
|
|
23
|
-
error InvalidAddress();
|
|
24
|
-
|
|
25
|
-
function mint(address to, uint256 amount) external {
|
|
26
|
-
if (to == address(0)) revert InvalidAddress();
|
|
27
|
-
totalSupply += amount;
|
|
28
|
-
balanceOf[to] += amount;
|
|
29
|
-
emit Transfer(address(0), to, amount);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function approve(address spender, uint256 amount) external returns (bool) {
|
|
33
|
-
allowance[msg.sender][spender] = amount;
|
|
34
|
-
emit Approval(msg.sender, spender, amount);
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function transfer(address to, uint256 amount) external returns (bool) {
|
|
39
|
-
_transfer(msg.sender, to, amount);
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
|
|
44
|
-
uint256 allowed = allowance[from][msg.sender];
|
|
45
|
-
if (allowed < amount) revert InsufficientAllowance();
|
|
46
|
-
|
|
47
|
-
if (allowed != type(uint256).max) {
|
|
48
|
-
allowance[from][msg.sender] = allowed - amount;
|
|
49
|
-
emit Approval(from, msg.sender, allowance[from][msg.sender]);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
_transfer(from, to, amount);
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function _transfer(address from, address to, uint256 amount) private {
|
|
57
|
-
if (to == address(0)) revert InvalidAddress();
|
|
58
|
-
if (balanceOf[from] < amount) revert InsufficientBalance();
|
|
59
|
-
|
|
60
|
-
balanceOf[from] -= amount;
|
|
61
|
-
balanceOf[to] += amount;
|
|
62
|
-
emit Transfer(from, to, amount);
|
|
63
|
-
}
|
|
64
|
-
}
|
package/contracts/README.md
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# Antseed Escrow Contract
|
|
2
|
-
|
|
3
|
-
`AntseedEscrow.sol` is the on-chain escrow contract used by the payment channel flow.
|
|
4
|
-
|
|
5
|
-
## Contract Paths
|
|
6
|
-
|
|
7
|
-
- `node/contracts/AntseedEscrow.sol` - production escrow contract
|
|
8
|
-
- `node/contracts/MockUSDC.sol` - test-only ERC20 used for local integration flows
|
|
9
|
-
|
|
10
|
-
## ABI Compatibility
|
|
11
|
-
|
|
12
|
-
The contract exposes the runtime methods expected by `BaseEscrowClient`:
|
|
13
|
-
|
|
14
|
-
- `deposit(bytes32 sessionId, address seller, uint256 amount)`
|
|
15
|
-
- `release(bytes32 sessionId)`
|
|
16
|
-
- `settle(bytes32 sessionId, uint256 sellerAmount, uint256 platformAmount)`
|
|
17
|
-
- `dispute(bytes32 sessionId)`
|
|
18
|
-
- `refund(bytes32 sessionId)`
|
|
19
|
-
- `resolveDisputeTimeout(bytes32 sessionId)`
|
|
20
|
-
- `getChannel(bytes32 sessionId) returns (address buyer, address seller, uint256 amount, uint8 state)`
|
|
21
|
-
|
|
22
|
-
Owner/admin methods:
|
|
23
|
-
|
|
24
|
-
- `setArbiter(address)`
|
|
25
|
-
- `setFeeCollector(address)`
|
|
26
|
-
- `setDisputeTimeout(uint64 seconds)`
|
|
27
|
-
|
|
28
|
-
Deployment uses the constructor signature:
|
|
29
|
-
|
|
30
|
-
- `constructor(address usdcToken, address initialArbiter)`
|
|
31
|
-
|
|
32
|
-
Channel states are encoded as:
|
|
33
|
-
|
|
34
|
-
- `0 = open`
|
|
35
|
-
- `1 = active`
|
|
36
|
-
- `2 = disputed`
|
|
37
|
-
- `3 = settled`
|
|
38
|
-
- `4 = closed`
|
|
39
|
-
|
|
40
|
-
## Compile
|
|
41
|
-
|
|
42
|
-
Example using Foundry (`forge`):
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
cd node
|
|
46
|
-
forge build --root . --contracts contracts --out contracts/out
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Example using `solc`:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
solc --optimize --bin --abi node/contracts/AntseedEscrow.sol -o node/contracts/out
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Deploy (TypeScript Helper)
|
|
56
|
-
|
|
57
|
-
Deploy the compiled contract using ethers.js or your preferred toolchain. The constructor expects `(address usdcToken, address initialArbiter)`.
|
|
58
|
-
|
|
59
|
-
For programmatic deployment, use `BaseEscrowClient` from `@antseed/node`:
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
import { BaseEscrowClient } from '@antseed/node';
|
|
63
|
-
|
|
64
|
-
const client = new BaseEscrowClient({
|
|
65
|
-
rpcUrl: process.env.RPC_URL!,
|
|
66
|
-
contractAddress: deployedAddress,
|
|
67
|
-
usdcAddress: process.env.USDC_ADDRESS!,
|
|
68
|
-
});
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## End-to-End Integration Test
|
|
72
|
-
|
|
73
|
-
A full on-chain test lives in:
|
|
74
|
-
|
|
75
|
-
- `node/tests/escrow-contract.integration.test.ts`
|
|
76
|
-
|
|
77
|
-
It compiles `AntseedEscrow.sol` + `MockUSDC.sol`, starts a local Anvil chain, deploys the contract, and exercises:
|
|
78
|
-
|
|
79
|
-
- `deposit -> release`
|
|
80
|
-
- `deposit -> dispute -> arbiter refund`
|
|
81
|
-
- `deposit -> settle(seller/platform/refund split)`
|
|
82
|
-
- `deposit -> dispute -> resolveDisputeTimeout`
|
|
83
|
-
|
|
84
|
-
Run only this suite:
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
cd node
|
|
88
|
-
npm test -- escrow-contract.integration.test.ts
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
The test auto-skips when `anvil` or `forge` is unavailable.
|
|
92
|
-
|
|
93
|
-
## Operational Notes
|
|
94
|
-
|
|
95
|
-
- `deposit` sets `buyer = msg.sender`; it expects an approved USDC `transferFrom`.
|
|
96
|
-
- `release` while active can be called by buyer/seller/arbiter.
|
|
97
|
-
- `release` while disputed requires arbiter.
|
|
98
|
-
- `settle` while active can be called by buyer/seller/arbiter.
|
|
99
|
-
- `settle` while disputed requires arbiter and supports explicit seller/platform payouts with automatic buyer refund remainder.
|
|
100
|
-
- `refund` while active can be called by buyer/arbiter.
|
|
101
|
-
- `refund` while disputed requires arbiter.
|
|
102
|
-
- `resolveDisputeTimeout` allows anyone to refund a stale disputed channel after `disputeTimeout`.
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { encryptValue, decryptValue, deriveMachineKey, generateSalt } from './encryption.js'
|
|
3
|
-
|
|
4
|
-
describe('encryption', () => {
|
|
5
|
-
it('should round-trip encrypt and decrypt', () => {
|
|
6
|
-
const salt = generateSalt()
|
|
7
|
-
const key = deriveMachineKey(salt)
|
|
8
|
-
const plaintext = 'sk-ant-api03-secret-key-value'
|
|
9
|
-
const encrypted = encryptValue(plaintext, key)
|
|
10
|
-
expect(encrypted).not.toBe(plaintext)
|
|
11
|
-
const decrypted = decryptValue(encrypted, key)
|
|
12
|
-
expect(decrypted).toBe(plaintext)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('should produce different ciphertexts for same input (random IV)', () => {
|
|
16
|
-
const salt = generateSalt()
|
|
17
|
-
const key = deriveMachineKey(salt)
|
|
18
|
-
const plaintext = 'test-secret'
|
|
19
|
-
const a = encryptValue(plaintext, key)
|
|
20
|
-
const b = encryptValue(plaintext, key)
|
|
21
|
-
expect(a).not.toBe(b)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('should fail to decrypt with wrong key', () => {
|
|
25
|
-
const salt1 = generateSalt()
|
|
26
|
-
const salt2 = generateSalt()
|
|
27
|
-
const key1 = deriveMachineKey(salt1)
|
|
28
|
-
const key2 = deriveMachineKey(salt2)
|
|
29
|
-
const encrypted = encryptValue('secret', key1)
|
|
30
|
-
expect(() => decryptValue(encrypted, key2)).toThrow()
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should handle empty string', () => {
|
|
34
|
-
const salt = generateSalt()
|
|
35
|
-
const key = deriveMachineKey(salt)
|
|
36
|
-
const encrypted = encryptValue('', key)
|
|
37
|
-
const decrypted = decryptValue(encrypted, key)
|
|
38
|
-
expect(decrypted).toBe('')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should handle unicode', () => {
|
|
42
|
-
const salt = generateSalt()
|
|
43
|
-
const key = deriveMachineKey(salt)
|
|
44
|
-
const plaintext = 'héllo wörld 🔑'
|
|
45
|
-
const encrypted = encryptValue(plaintext, key)
|
|
46
|
-
const decrypted = decryptValue(encrypted, key)
|
|
47
|
-
expect(decrypted).toBe(plaintext)
|
|
48
|
-
})
|
|
49
|
-
})
|
package/src/config/encryption.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto'
|
|
2
|
-
import { hostname, homedir } from 'node:os'
|
|
3
|
-
|
|
4
|
-
const ALGORITHM = 'aes-256-gcm'
|
|
5
|
-
const SALT_LENGTH = 32
|
|
6
|
-
const IV_LENGTH = 16
|
|
7
|
-
const TAG_LENGTH = 16
|
|
8
|
-
const KEY_LENGTH = 32
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Derive an encryption key from a machine-specific seed.
|
|
12
|
-
*/
|
|
13
|
-
export function deriveMachineKey(salt: Buffer): Buffer {
|
|
14
|
-
const seed = [
|
|
15
|
-
process.env['USER'] ?? process.env['USERNAME'] ?? '',
|
|
16
|
-
hostname(),
|
|
17
|
-
homedir(),
|
|
18
|
-
].join(':')
|
|
19
|
-
return scryptSync(seed, salt, KEY_LENGTH)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Encrypt a string value. Returns base64-encoded ciphertext with IV and auth tag prepended.
|
|
24
|
-
*/
|
|
25
|
-
export function encryptValue(plaintext: string, key: Buffer): string {
|
|
26
|
-
const iv = randomBytes(IV_LENGTH)
|
|
27
|
-
const cipher = createCipheriv(ALGORITHM, key, iv)
|
|
28
|
-
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf-8'), cipher.final()])
|
|
29
|
-
const tag = cipher.getAuthTag()
|
|
30
|
-
const combined = Buffer.concat([iv, tag, encrypted])
|
|
31
|
-
return combined.toString('base64')
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Decrypt a base64-encoded value produced by encryptValue.
|
|
36
|
-
*/
|
|
37
|
-
export function decryptValue(encoded: string, key: Buffer): string {
|
|
38
|
-
const combined = Buffer.from(encoded, 'base64')
|
|
39
|
-
const iv = combined.subarray(0, IV_LENGTH)
|
|
40
|
-
const tag = combined.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH)
|
|
41
|
-
const ciphertext = combined.subarray(IV_LENGTH + TAG_LENGTH)
|
|
42
|
-
const decipher = createDecipheriv(ALGORITHM, key, iv)
|
|
43
|
-
decipher.setAuthTag(tag)
|
|
44
|
-
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()])
|
|
45
|
-
return decrypted.toString('utf-8')
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Generate a fresh random salt for key derivation.
|
|
50
|
-
*/
|
|
51
|
-
export function generateSalt(): Buffer {
|
|
52
|
-
return randomBytes(SALT_LENGTH)
|
|
53
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
-
import { mkdtemp, rm } from 'node:fs/promises'
|
|
3
|
-
import { tmpdir } from 'node:os'
|
|
4
|
-
import { join } from 'node:path'
|
|
5
|
-
import { addInstance, removeInstance, getInstance, getInstances, updateInstanceConfig, loadPluginConfig } from './plugin-config-manager.js'
|
|
6
|
-
import type { ConfigField } from '../interfaces/plugin.js'
|
|
7
|
-
|
|
8
|
-
const schema: ConfigField[] = [
|
|
9
|
-
{ key: 'API_KEY', label: 'API Key', type: 'secret', required: true },
|
|
10
|
-
{ key: 'BASE_URL', label: 'Base URL', type: 'string', required: false },
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
describe('plugin-config-manager', () => {
|
|
14
|
-
let tmpDir: string
|
|
15
|
-
let configPath: string
|
|
16
|
-
|
|
17
|
-
beforeEach(async () => {
|
|
18
|
-
tmpDir = await mkdtemp(join(tmpdir(), 'antseed-test-'))
|
|
19
|
-
configPath = join(tmpDir, 'config.json')
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('should add and retrieve an instance', async () => {
|
|
23
|
-
await addInstance(configPath, {
|
|
24
|
-
id: 'test-provider',
|
|
25
|
-
package: '@antseed/provider-test',
|
|
26
|
-
type: 'provider',
|
|
27
|
-
config: { API_KEY: 'sk-secret-123', BASE_URL: 'https://api.test.com' },
|
|
28
|
-
}, schema)
|
|
29
|
-
|
|
30
|
-
const instance = await getInstance(configPath, 'test-provider')
|
|
31
|
-
expect(instance).not.toBeNull()
|
|
32
|
-
expect(instance!.id).toBe('test-provider')
|
|
33
|
-
expect(instance!.config['API_KEY']).toBe('sk-secret-123')
|
|
34
|
-
expect(instance!.config['BASE_URL']).toBe('https://api.test.com')
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('should encrypt secrets in the file', async () => {
|
|
38
|
-
await addInstance(configPath, {
|
|
39
|
-
id: 'test',
|
|
40
|
-
package: '@antseed/provider-test',
|
|
41
|
-
type: 'provider',
|
|
42
|
-
config: { API_KEY: 'sk-secret', BASE_URL: 'https://api.test.com' },
|
|
43
|
-
}, schema)
|
|
44
|
-
|
|
45
|
-
const raw = await loadPluginConfig(configPath)
|
|
46
|
-
const stored = raw.instances[0]!.config
|
|
47
|
-
// Secret should be encrypted (prefixed with enc:)
|
|
48
|
-
expect(typeof stored['API_KEY']).toBe('string')
|
|
49
|
-
expect((stored['API_KEY'] as string).startsWith('enc:')).toBe(true)
|
|
50
|
-
// Non-secret should be plain
|
|
51
|
-
expect(stored['BASE_URL']).toBe('https://api.test.com')
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('should remove an instance', async () => {
|
|
55
|
-
await addInstance(configPath, {
|
|
56
|
-
id: 'to-remove',
|
|
57
|
-
package: '@antseed/provider-test',
|
|
58
|
-
type: 'provider',
|
|
59
|
-
config: {},
|
|
60
|
-
})
|
|
61
|
-
await removeInstance(configPath, 'to-remove')
|
|
62
|
-
const instance = await getInstance(configPath, 'to-remove')
|
|
63
|
-
expect(instance).toBeNull()
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('should list all instances', async () => {
|
|
67
|
-
await addInstance(configPath, { id: 'a', package: 'pkg-a', type: 'provider', config: {} })
|
|
68
|
-
await addInstance(configPath, { id: 'b', package: 'pkg-b', type: 'router', config: {} })
|
|
69
|
-
const all = await getInstances(configPath)
|
|
70
|
-
expect(all).toHaveLength(2)
|
|
71
|
-
expect(all.map(i => i.id)).toEqual(['a', 'b'])
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('should update instance config', async () => {
|
|
75
|
-
await addInstance(configPath, {
|
|
76
|
-
id: 'updatable',
|
|
77
|
-
package: 'pkg',
|
|
78
|
-
type: 'provider',
|
|
79
|
-
config: { API_KEY: 'old-key' },
|
|
80
|
-
}, schema)
|
|
81
|
-
|
|
82
|
-
await updateInstanceConfig(configPath, 'updatable', { API_KEY: 'new-key' }, schema)
|
|
83
|
-
const updated = await getInstance(configPath, 'updatable')
|
|
84
|
-
expect(updated!.config['API_KEY']).toBe('new-key')
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('should reject duplicate instance IDs', async () => {
|
|
88
|
-
await addInstance(configPath, { id: 'dup', package: 'pkg', type: 'provider', config: {} })
|
|
89
|
-
await expect(addInstance(configPath, { id: 'dup', package: 'pkg', type: 'provider', config: {} }))
|
|
90
|
-
.rejects.toThrow('already exists')
|
|
91
|
-
})
|
|
92
|
-
})
|