-
Notifications
You must be signed in to change notification settings - Fork 0
/
woow.contract.ts
289 lines (231 loc) · 10.6 KB
/
woow.contract.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
import { Asset, Contract, Name, Symbol, TableStore, U256, Utils, check, currentTimeMs, printHex, requireAuth } from "proton-tsc";
import { PaymentsTable, StoreTable, BalancesTable } from "./tables";
import { AWAIT_PAYMENT, CANCELED_PAYMENT, FULLFILED_PAYMENT, PAYEDOUT_PAYMENT, REFUNDED_PAYMENT } from "./constants";
import { sendTransferToken } from "proton-tsc/token";
/**
* The main contract class for the `wookey` escrow contract.
*/
@contract
export class wookey extends Contract {
private storesTable: TableStore<StoreTable> = new TableStore<StoreTable>(this.receiver);
private paymentsTable: TableStore<PaymentsTable> = new TableStore<PaymentsTable>(this.receiver);
/**
* Handles token transfers.
* @param {Name} from - Sender's account name.
* @param {Name} to - Receiver's account name.
* @param {Asset} quantity - Amount of tokens to be transferred.
* @param {string} memo - Additional information about the transfer.
*/
@action('transfer', notify)
onTransfer(from:Name,to:Name,quantity:Asset,memo:string): void {
if (from == this.receiver) return;
if (memo == 'WOOKEY') return;
const memoToKey = Utils.hexToBytes(memo);
const u256Memo = U256.fromBytes(memoToKey)
const payment = this.paymentsTable.getBySecondaryU256(u256Memo, 0);
check(!!payment, 'Woow error: Payment not found');
if (!payment) return;
check(payment.status !== CANCELED_PAYMENT, 'Woow error: Payment has been previously canceled');
check(payment.status !== FULLFILED_PAYMENT, 'Woow error: Payment has been previously completed');
payment.status = FULLFILED_PAYMENT;
const now = currentTimeMs();
payment.updated = now;
const balanceTable: TableStore<BalancesTable> = new TableStore<BalancesTable>(this.receiver, payment.store);
if (balanceTable.exists(quantity.symbol.value)) {
const transferBalance = balanceTable.get(quantity.symbol.value);
check(!!transferBalance, 'Woow error: Error while fetching the balance')
if (!transferBalance) return;
transferBalance.amount.symbol = quantity.symbol;
transferBalance.amount.amount += quantity.amount;
balanceTable.update(transferBalance, this.receiver)
} else {
const newBalance = new BalancesTable(quantity.symbol)
newBalance.amount.symbol = quantity.symbol;
newBalance.amount.amount += quantity.amount;
newBalance.contract = payment.tokenContract;
balanceTable.store(newBalance,this.receiver)
}
this.paymentsTable.update(payment, this.receiver);
}
/**
* Registers a store.
* @param {Name} storeAccount - Store's account name.
*/
@action('store.reg')
registerStore(storeAccount: Name): void {
requireAuth(storeAccount);
const storeExists = this.storesTable.exists(storeAccount.N);
if (storeExists) return;
const newStore = new StoreTable(storeAccount, false);
this.storesTable.store(newStore, this.receiver);
}
/**
* Unregisters a store.
* @param {Name} storeAccount - Store's account name.
*/
@action('store.unreg')
unregisterStore(storeAccount: Name): void {
requireAuth(storeAccount);
const storeToDelete = this.storesTable.requireGet(storeAccount.N,'Store not registrated');
if (!storeToDelete) return;
this.storesTable.remove(storeToDelete);
}
/**
* Registers a payment.
* @param {Name} storeAccount - Store's account name.
* @param {Name} buyer - Buyer's account name.
* @param {string} paymentKey - Unique key used for payment reconciliation.
* @param {Asset} amount - Amount of the payment with symbol.
* @param {Name} tokenContract - Token contract name.
*/
@action("pay.reg")
registerPayment(storeAccount: Name, buyer: Name, paymentKey: string, amount: Asset,tokenContract:Name): void {
requireAuth(buyer);
const storeExists = this.storesTable.exists(storeAccount.N);
check(storeExists, 'Woow error: Store not exits');
if (!storeExists) return;
const paymentKeyToBytes = Utils.hexToBytes(paymentKey);
const u256Key = U256.fromBytes(paymentKeyToBytes)
const payment = this.paymentsTable.getBySecondaryU256(u256Key, 0);
check(!payment, 'Error: Duplicated Payment ');
const now = currentTimeMs();
const newPayment = new PaymentsTable(
this.paymentsTable.availablePrimaryKey,
storeAccount,
buyer,
u256Key,
amount,
tokenContract,
AWAIT_PAYMENT,
now,
0
);
this.paymentsTable.store(newPayment, this.receiver);
}
/**
* Refunds a payment if not already withdrawn.
* @param {Name} storeAccount - Store's account name.
* @param {string} paymentKey - Unique key for the payment.
*/
@action('pay.refund')
refundPayment(storeAccount: Name, paymentKey: string): void{
requireAuth(storeAccount);
const storeExists = this.storesTable.exists(storeAccount.N);
check(storeExists, 'Woow error: Store not exits');
if (!storeExists) return;
const paymentKeyToBytes = Utils.hexToBytes(paymentKey);
const u256Key = U256.fromBytes(paymentKeyToBytes)
const payment = this.paymentsTable.getBySecondaryU256(u256Key, 0);
check(!!payment, 'Woow error: Payment not exists');
if (!payment) return;
const now = currentTimeMs();
sendTransferToken(payment.tokenContract, this.receiver, payment.buyer, payment.amount, '');
payment.status = REFUNDED_PAYMENT;
this.paymentsTable.update(payment, this.receiver);
const balanceTable: TableStore<BalancesTable> = new TableStore<BalancesTable>(this.receiver, storeAccount);
const fromBalance = balanceTable.requireGet(payment.amount.symbol.value, 'Woow error: Balance not exists');
fromBalance.amount.amount -= payment.amount.amount
balanceTable.update(fromBalance, this.receiver);
}
/**
* Cancels a payment.
* @param {Name} storeAccount - Store's account name.
* @param {U256} paymentKey - Unique key for the payment.
*/
@action("pay.cancel")
cancelPayment(storeAccount:Name,paymentKey: U256): void {
requireAuth(storeAccount);
const payment = this.paymentsTable.getBySecondaryU256(paymentKey, 0);
check(!!payment, 'Error: Payment not exists ');
if (!payment) return;
check(payment.store == storeAccount, 'Error: unauthorized account');
if (payment.store != storeAccount) return;
payment.status = CANCELED_PAYMENT
this.paymentsTable.update(payment, this.receiver);
}
/**
* Claims balance.
* @param {Name} storeAccount - Store's account name.
* @param {Symbol} symbol - Symbol of the token.
*/
@action("bal.claim")
claimBalance(storeAccount: Name,symbol:Symbol):void {
requireAuth(storeAccount);
const storeExists = this.storesTable.exists(storeAccount.N);
check(storeExists, 'Woow error: Store not exists');
if (!storeExists) return;
const balanceTable: TableStore<BalancesTable> = new TableStore<BalancesTable>(this.receiver, storeAccount);
const claimBalance = balanceTable.requireGet(symbol.value, 'Woow error: Balance not exists');
if(claimBalance.amount.amount <= 0)return ;
sendTransferToken(claimBalance.contract, this.receiver, storeAccount, claimBalance.amount, `${symbol.getSymbolString()} payout`)
claimBalance.amount.amount = 0;
const now = currentTimeMs();
this.markPaymentsAsPayedOut(storeAccount, claimBalance.lastClaim,claimBalance.key);
claimBalance.lastClaim = now;
balanceTable.update(claimBalance,this.receiver)
}
@action("dev.clrpay")
clearPayments(): void {
while (!this.paymentsTable.isEmpty()) {
const paymentToRemove = this.paymentsTable.first();
if (paymentToRemove) {
this.paymentsTable.remove(paymentToRemove);
}
}
}
@action("dev.clrstore")
clearStore(): void {
while (!this.storesTable.isEmpty()) {
const storeToRemove = this.storesTable.first();
if (storeToRemove) {
this.storesTable.remove(storeToRemove);
}
}
}
@action("dev.clrbal")
clearBalance(storeAccount:Name): void {
const balanceTable: TableStore<BalancesTable> = new TableStore<BalancesTable>(this.receiver, storeAccount);
while (!balanceTable.isEmpty()) {
const balanceToRemove = balanceTable.first();
if (balanceToRemove) {
balanceTable.remove(balanceToRemove);
}
}
}
/**
* Marks payments as paid out.
* @param {Name} storeAccount - Store's account name.
* @param {i64} since - Timestamp indicating when the payments were updated.
* @param {Symbol} symbol - Symbol of the token.
* @private
*/
private markPaymentsAsPayedOut(storeAccount: Name, since: i64,symbol:Symbol): void {
let payment = this.paymentsTable.getBySecondaryU64(storeAccount.N, 1);
if (!payment) return;
payment = this.markSinglePaymentAsPayedOut(payment, since, symbol)
let res = '';
while (payment) {
res = `${res} ${payment.key} ${payment.amount.symbol.getSymbolString()}`
payment = this.paymentsTable.nextBySecondaryU64(payment, 1);
if (!payment) break ;
payment = this.markSinglePaymentAsPayedOut(payment,since,symbol)
if (!payment) break;
}
}
/**
* Marks a single payment as paid out.
* @param {PaymentsTable} payment - The payment to be marked.
* @param {i64} since - Timestamp indicating when the payment is updated.
* @param {Symbol} symbol - Symbol of the token.
* @returns {PaymentsTable} The marked payment.
* @private
*/
private markSinglePaymentAsPayedOut(payment:PaymentsTable, since: i64,symbol:Symbol): PaymentsTable {
if ( payment.created > since && payment.status == FULLFILED_PAYMENT && payment.amount.symbol.value == symbol.value ) {
payment.updated = since;
payment.status = PAYEDOUT_PAYMENT;
this.paymentsTable.update(payment, this.receiver);
}
return payment
}
}