forked from Movalabs-crew/mova-store
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathevents.ts
More file actions
132 lines (122 loc) · 3.82 KB
/
Copy pathevents.ts
File metadata and controls
132 lines (122 loc) · 3.82 KB
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
import { rpc, StrKey, xdr } from "@stellar/stellar-sdk";
import { RPC_URL, TX_TIMEOUT_SECONDS, TX_POLL_INTERVAL_MS } from "./config";
import { bytesToHex, scValToString } from "./scval";
// ---------------------------------------------------------------------------
// Payment event decoding.
//
// The checkout contract emits (via #[contractevent]):
// topics: [Symbol("pay"), token, buyer, merchant, order_id]
// data: { amount: i128 }
// ---------------------------------------------------------------------------
export interface PaymentReceipt {
contractId?: string;
token?: string;
buyer?: string;
merchant?: string;
orderId?: string; // hex
amount?: string; // raw token units as decimal string
txHash: string;
ledger: number;
}
/**
* Poll `getTransaction` until the tx reaches a final state.
* Resolves with the successful transaction; throws on FAILED.
*/
export async function waitForTransaction(
hash: string
): Promise<rpc.Api.GetSuccessfulTransactionResponse> {
const server = new rpc.Server(RPC_URL);
const deadline = Date.now() + TX_TIMEOUT_SECONDS * 1000;
let last: rpc.Api.GetTransactionResponse | null = null;
while (Date.now() < deadline) {
last = await server.getTransaction(hash);
if (last.status === rpc.Api.GetTransactionStatus.SUCCESS) {
return last;
}
if (last.status === rpc.Api.GetTransactionStatus.FAILED) {
throw new Error(
`Transaction failed on ledger ${last.ledger} (hash: ${hash}). ` +
"See StellarExpert for details."
);
}
await sleep(TX_POLL_INTERVAL_MS);
}
throw new Error(
`Transaction did not reach a final state within ${TX_TIMEOUT_SECONDS}s ` +
`(hash: ${hash}). Check its status on StellarExpert.`
);
}
/**
* Find the `PaymentReceived` event emitted by the checkout contract inside a
* successful transaction's contract events.
*/
export function decodePaymentEvent(
tx: rpc.Api.GetSuccessfulTransactionResponse
): PaymentReceipt | null {
const contractEvents: xdr.ContractEvent[] = tx.events?.contractEventsXdr?.flat() ?? [];
for (const event of contractEvents) {
let v0: xdr.ContractEventV0;
try {
v0 = event.body().v0();
} catch {
continue;
}
const topics = v0.topics();
if (!topics || topics.length < 1) continue;
const first = topics[0];
if (first.switch() !== xdr.ScValType.scvSymbol()) continue;
if (first.sym().toString() !== "pay") continue;
const data = v0.data();
const receipt: PaymentReceipt = {
txHash: tx.txHash,
ledger: tx.ledger,
};
try {
if (event.contractId()) {
receipt.contractId = StrKey.encodeContract(
Buffer.from(event.contractId() as unknown as Uint8Array)
);
}
} catch {
// system events have no contract id
}
// topics[1..] = [token, buyer, merchant, order_id]
for (let i = 1; i < topics.length; i++) {
const str = scValToString(topics[i]);
switch (i) {
case 1:
receipt.token = str;
break;
case 2:
receipt.buyer = str;
break;
case 3:
receipt.merchant = str;
break;
case 4:
receipt.orderId = str;
break;
}
}
// data = Map { "amount": i128 }
if (data.switch() === xdr.ScValType.scvMap()) {
const entries = data.map();
for (const entry of entries ?? []) {
if (entry.key().switch() === xdr.ScValType.scvSymbol()) {
const key = entry.key().sym().toString();
if (key === "amount") {
receipt.amount = scValToString(entry.val());
}
}
}
} else {
receipt.amount = scValToString(data);
}
return receipt;
}
return null;
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export { bytesToHex };