@layerzerolabs/lz-v2-stellar-sdk 0.2.121 ā 0.2.123
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/.turbo/turbo-build.log +235 -1
- package/.turbo/turbo-lint.log +8 -0
- package/.turbo/turbo-test.log +41 -1361
- package/dist/generated/dvn.d.ts +2 -2
- package/dist/generated/dvn.js +2 -2
- package/dist/generated/endpoint.d.ts +2 -2
- package/dist/generated/endpoint.js +2 -2
- package/dist/generated/executor.d.ts +3 -3
- package/dist/generated/executor.d.ts.map +1 -1
- package/dist/generated/executor.js +3 -3
- package/dist/generated/executor.js.map +1 -1
- package/dist/generated/price_feed.d.ts +2 -2
- package/dist/generated/price_feed.js +2 -2
- package/dist/generated/sml.d.ts +2 -2
- package/dist/generated/sml.js +2 -2
- package/dist/generated/uln302.d.ts +2 -2
- package/dist/generated/uln302.js +2 -2
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
- package/rust-toolchain.toml +4 -0
- package/src/index.ts +0 -5
- package/test/suites/constants.ts +30 -51
- package/test/suites/deploy.ts +13 -273
- package/test/suites/globalSetup.ts +3 -602
- package/test/upgrader.test.ts +1 -0
- package/test/utils.ts +11 -658
- package/ts-bindings-gen.toml +59 -0
- package/dist/generated/counter.d.ts +0 -1253
- package/dist/generated/counter.d.ts.map +0 -1
- package/dist/generated/counter.js +0 -391
- package/dist/generated/counter.js.map +0 -1
- package/dist/generated/oft.d.ts +0 -1638
- package/dist/generated/oft.d.ts.map +0 -1
- package/dist/generated/oft.js +0 -463
- package/dist/generated/oft.js.map +0 -1
- package/dist/generated/sac_manager.d.ts +0 -567
- package/dist/generated/sac_manager.d.ts.map +0 -1
- package/dist/generated/sac_manager.js +0 -198
- package/dist/generated/sac_manager.js.map +0 -1
- package/src/generated/bml.ts +0 -672
- package/src/generated/counter.ts +0 -1208
- package/src/generated/dvn.ts +0 -1508
- package/src/generated/dvn_fee_lib.ts +0 -595
- package/src/generated/endpoint.ts +0 -1181
- package/src/generated/executor.ts +0 -1521
- package/src/generated/executor_fee_lib.ts +0 -1027
- package/src/generated/executor_helper.ts +0 -833
- package/src/generated/layerzero_view.ts +0 -1119
- package/src/generated/oft.ts +0 -1609
- package/src/generated/price_feed.ts +0 -793
- package/src/generated/sac_manager.ts +0 -555
- package/src/generated/sml.ts +0 -921
- package/src/generated/treasury.ts +0 -887
- package/src/generated/uln302.ts +0 -1224
- package/src/generated/upgrader.ts +0 -276
- package/test/counter-sml.test.ts +0 -480
- package/test/counter-uln.test.ts +0 -584
- package/test/oft-sml.test.ts +0 -750
- package/test/sac-manager.test.ts +0 -464
- package/test/secp256k1.ts +0 -59
- package/test/suites/dummyContractClient.ts +0 -168
- package/test/suites/localnet.ts +0 -192
- package/test/suites/scan.ts +0 -202
- package/turbo.json +0 -15
package/test/oft-sml.test.ts
DELETED
|
@@ -1,750 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Address,
|
|
3
|
-
Asset,
|
|
4
|
-
BASE_FEE,
|
|
5
|
-
Keypair,
|
|
6
|
-
Operation,
|
|
7
|
-
rpc,
|
|
8
|
-
StrKey,
|
|
9
|
-
TransactionBuilder,
|
|
10
|
-
} from '@stellar/stellar-sdk';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { beforeAll, describe, expect, inject, it } from 'vitest';
|
|
13
|
-
|
|
14
|
-
import { getFullyQualifiedRepoRootPath } from '@layerzerolabs/common-node-utils';
|
|
15
|
-
import { PacketSerializer, PacketV1Codec } from '@layerzerolabs/lz-v2-utilities';
|
|
16
|
-
|
|
17
|
-
import { Client as EndpointClient } from '../src/generated/endpoint.js';
|
|
18
|
-
import { Client as ExecutorHelperClient } from '../src/generated/executor_helper.js';
|
|
19
|
-
import { Client as OFTClient, SendParam } from '../src/generated/oft.js';
|
|
20
|
-
import { Client as SACManagerClient } from '../src/generated/sac_manager.js';
|
|
21
|
-
import { Client as SMLClient } from '../src/generated/sml.js';
|
|
22
|
-
import {
|
|
23
|
-
DEFAULT_DEPLOYER,
|
|
24
|
-
EID_A,
|
|
25
|
-
EID_B,
|
|
26
|
-
EXECUTOR_ADMIN,
|
|
27
|
-
NETWORK_PASSPHRASE,
|
|
28
|
-
RPC_URL,
|
|
29
|
-
} from './suites/constants.js';
|
|
30
|
-
import { deployAssetSac, deployContract } from './suites/deploy.js';
|
|
31
|
-
import type { ChainAddresses } from './suites/globalSetup.js';
|
|
32
|
-
import { fundAccount } from './suites/localnet.js';
|
|
33
|
-
import { PacketSentEvent, scanPacketSentEvents } from './suites/scan.js';
|
|
34
|
-
import {
|
|
35
|
-
assertTransactionSucceeded,
|
|
36
|
-
createClient,
|
|
37
|
-
getTokenBalance,
|
|
38
|
-
signAndSendWithExecutorAuth,
|
|
39
|
-
} from './utils.js';
|
|
40
|
-
|
|
41
|
-
// Chain addresses (injected from globalSetup)
|
|
42
|
-
let chainA: ChainAddresses;
|
|
43
|
-
let chainB: ChainAddresses;
|
|
44
|
-
|
|
45
|
-
// OFT-specific addresses
|
|
46
|
-
let oftTokenAddress = '';
|
|
47
|
-
let sacManagerAddress = ''; // Mintable for Mint/Burn OFT (Chain B)
|
|
48
|
-
let lockUnlockOftAddress = ''; // Chain A
|
|
49
|
-
let mintBurnOftAddress = ''; // Chain B
|
|
50
|
-
|
|
51
|
-
// Chain A Clients
|
|
52
|
-
let endpointClientA: EndpointClient;
|
|
53
|
-
let smlClientA: SMLClient;
|
|
54
|
-
let executorHelperClientA: ExecutorHelperClient;
|
|
55
|
-
let lockUnlockOftClient: OFTClient;
|
|
56
|
-
|
|
57
|
-
// Chain B Clients
|
|
58
|
-
let endpointClientB: EndpointClient;
|
|
59
|
-
let smlClientB: SMLClient;
|
|
60
|
-
let executorHelperClientB: ExecutorHelperClient;
|
|
61
|
-
let mintBurnOftClient: OFTClient;
|
|
62
|
-
|
|
63
|
-
// Test accounts
|
|
64
|
-
const TOKEN_ISSUER = Keypair.random();
|
|
65
|
-
|
|
66
|
-
// Recipients for each direction
|
|
67
|
-
const RECIPIENT_A = Keypair.random(); // Receives tokens on Chain A (unlocked)
|
|
68
|
-
const RECIPIENT_B = Keypair.random(); // Receives tokens on Chain B (minted)
|
|
69
|
-
|
|
70
|
-
// OFT Token asset (custom token for testing)
|
|
71
|
-
const OFT_TOKEN_CODE = 'OFT';
|
|
72
|
-
let OFT_ASSET: Asset;
|
|
73
|
-
|
|
74
|
-
// Constants
|
|
75
|
-
const SHARED_DECIMALS = 6;
|
|
76
|
-
const INITIAL_TOKEN_AMOUNT = '1000'; // 1000 tokens
|
|
77
|
-
const SEND_AMOUNT = 100_0000000n; // 100 tokens in local decimals (7 decimals)
|
|
78
|
-
|
|
79
|
-
describe('OFT Cross-Chain E2E Testing with SAC (SML)', async () => {
|
|
80
|
-
const repoRoot = await getFullyQualifiedRepoRootPath();
|
|
81
|
-
const wasmDir = path.join(
|
|
82
|
-
repoRoot,
|
|
83
|
-
'contracts',
|
|
84
|
-
'protocol',
|
|
85
|
-
'stellar',
|
|
86
|
-
'target',
|
|
87
|
-
'wasm32v1-none',
|
|
88
|
-
'release',
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const OFT_WASM_PATH = path.join(wasmDir, 'oft.wasm');
|
|
92
|
-
const SAC_MANAGER_WASM_PATH = path.join(wasmDir, 'sac_manager.wasm');
|
|
93
|
-
|
|
94
|
-
beforeAll(async () => {
|
|
95
|
-
// Inject chain addresses from globalSetup
|
|
96
|
-
chainA = inject('chainA');
|
|
97
|
-
chainB = inject('chainB');
|
|
98
|
-
|
|
99
|
-
console.log('\nš Chain A addresses (EID: ' + EID_A + ')');
|
|
100
|
-
console.log(' Endpoint:', chainA.endpointV2);
|
|
101
|
-
console.log(' SML:', chainA.sml);
|
|
102
|
-
console.log(' Executor:', chainA.executor);
|
|
103
|
-
|
|
104
|
-
console.log('\nš Chain B addresses (EID: ' + EID_B + ')');
|
|
105
|
-
console.log(' Endpoint:', chainB.endpointV2);
|
|
106
|
-
console.log(' SML:', chainB.sml);
|
|
107
|
-
console.log(' Executor:', chainB.executor);
|
|
108
|
-
|
|
109
|
-
// Create clients for Chain A protocol contracts
|
|
110
|
-
endpointClientA = createClient(EndpointClient, chainA.endpointV2);
|
|
111
|
-
smlClientA = createClient(SMLClient, chainA.sml);
|
|
112
|
-
executorHelperClientA = createClient(ExecutorHelperClient, chainA.executorHelper);
|
|
113
|
-
|
|
114
|
-
// Create clients for Chain B protocol contracts
|
|
115
|
-
endpointClientB = createClient(EndpointClient, chainB.endpointV2);
|
|
116
|
-
smlClientB = createClient(SMLClient, chainB.sml);
|
|
117
|
-
executorHelperClientB = createClient(ExecutorHelperClient, chainB.executorHelper);
|
|
118
|
-
|
|
119
|
-
// Fund test accounts in parallel
|
|
120
|
-
await Promise.all([
|
|
121
|
-
fundAccount(TOKEN_ISSUER.publicKey()),
|
|
122
|
-
fundAccount(RECIPIENT_A.publicKey()),
|
|
123
|
-
fundAccount(RECIPIENT_B.publicKey()),
|
|
124
|
-
]);
|
|
125
|
-
|
|
126
|
-
// Create the OFT asset (TOKEN_ISSUER is the issuer)
|
|
127
|
-
OFT_ASSET = new Asset(OFT_TOKEN_CODE, TOKEN_ISSUER.publicKey());
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe('Deploy OFT Contracts on Both Chains', () => {
|
|
131
|
-
it('Deploy OFT Token SAC', async () => {
|
|
132
|
-
const server = new rpc.Server(RPC_URL, { allowHttp: true });
|
|
133
|
-
|
|
134
|
-
// Step 1: Issue the OFT token to DEFAULT_DEPLOYER and set up trustlines for recipients
|
|
135
|
-
const issuerAccount = await server.getAccount(TOKEN_ISSUER.publicKey());
|
|
136
|
-
const issueTx = new TransactionBuilder(issuerAccount, {
|
|
137
|
-
fee: BASE_FEE,
|
|
138
|
-
networkPassphrase: NETWORK_PASSPHRASE,
|
|
139
|
-
})
|
|
140
|
-
// Trustline for DEFAULT_DEPLOYER (sender)
|
|
141
|
-
.addOperation(
|
|
142
|
-
Operation.changeTrust({
|
|
143
|
-
asset: OFT_ASSET,
|
|
144
|
-
source: DEFAULT_DEPLOYER.publicKey(),
|
|
145
|
-
}),
|
|
146
|
-
)
|
|
147
|
-
// Trustline for RECIPIENT_A (receives unlocked tokens)
|
|
148
|
-
.addOperation(
|
|
149
|
-
Operation.changeTrust({
|
|
150
|
-
asset: OFT_ASSET,
|
|
151
|
-
source: RECIPIENT_A.publicKey(),
|
|
152
|
-
}),
|
|
153
|
-
)
|
|
154
|
-
// Trustline for RECIPIENT_B (receives minted tokens)
|
|
155
|
-
.addOperation(
|
|
156
|
-
Operation.changeTrust({
|
|
157
|
-
asset: OFT_ASSET,
|
|
158
|
-
source: RECIPIENT_B.publicKey(),
|
|
159
|
-
}),
|
|
160
|
-
)
|
|
161
|
-
// Issue tokens to DEFAULT_DEPLOYER
|
|
162
|
-
.addOperation(
|
|
163
|
-
Operation.payment({
|
|
164
|
-
asset: OFT_ASSET,
|
|
165
|
-
amount: INITIAL_TOKEN_AMOUNT,
|
|
166
|
-
destination: DEFAULT_DEPLOYER.publicKey(),
|
|
167
|
-
}),
|
|
168
|
-
)
|
|
169
|
-
.setTimeout(30)
|
|
170
|
-
.build();
|
|
171
|
-
|
|
172
|
-
issueTx.sign(TOKEN_ISSUER, DEFAULT_DEPLOYER, RECIPIENT_A, RECIPIENT_B);
|
|
173
|
-
const sendResult = await server.sendTransaction(issueTx);
|
|
174
|
-
if (sendResult.status !== 'PENDING') {
|
|
175
|
-
throw new Error(`Failed to issue OFT token: ${JSON.stringify(sendResult)}`);
|
|
176
|
-
}
|
|
177
|
-
const txResult = await server.pollTransaction(sendResult.hash);
|
|
178
|
-
if (txResult.status !== 'SUCCESS') {
|
|
179
|
-
throw new Error(`Failed to issue OFT token: ${JSON.stringify(txResult)}`);
|
|
180
|
-
}
|
|
181
|
-
console.log('ā
OFT token issued to DEFAULT_DEPLOYER');
|
|
182
|
-
|
|
183
|
-
// Step 2: Deploy the SAC for the OFT token
|
|
184
|
-
oftTokenAddress = await deployAssetSac(OFT_ASSET);
|
|
185
|
-
console.log('ā
OFT Token SAC deployed:', oftTokenAddress);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('Deploy SAC Manager and set SAC admin (mintable for Mint/Burn OFT)', async () => {
|
|
189
|
-
const sacManagerClient = await deployContract<SACManagerClient>(
|
|
190
|
-
SACManagerClient,
|
|
191
|
-
SAC_MANAGER_WASM_PATH,
|
|
192
|
-
{
|
|
193
|
-
sac_token: oftTokenAddress,
|
|
194
|
-
owner: DEFAULT_DEPLOYER.publicKey(),
|
|
195
|
-
},
|
|
196
|
-
DEFAULT_DEPLOYER,
|
|
197
|
-
);
|
|
198
|
-
sacManagerAddress = sacManagerClient.options.contractId;
|
|
199
|
-
console.log('ā
SAC Manager deployed:', sacManagerAddress);
|
|
200
|
-
|
|
201
|
-
const server = new rpc.Server(RPC_URL, { allowHttp: true });
|
|
202
|
-
const account = await server.getAccount(TOKEN_ISSUER.publicKey());
|
|
203
|
-
const setAdminTx = new TransactionBuilder(account, {
|
|
204
|
-
fee: BASE_FEE,
|
|
205
|
-
networkPassphrase: NETWORK_PASSPHRASE,
|
|
206
|
-
})
|
|
207
|
-
.addOperation(
|
|
208
|
-
Operation.invokeContractFunction({
|
|
209
|
-
contract: oftTokenAddress,
|
|
210
|
-
function: 'set_admin',
|
|
211
|
-
args: [Address.fromString(sacManagerAddress).toScVal()],
|
|
212
|
-
}),
|
|
213
|
-
)
|
|
214
|
-
.setTimeout(30)
|
|
215
|
-
.build();
|
|
216
|
-
|
|
217
|
-
const simulated = await server.simulateTransaction(setAdminTx);
|
|
218
|
-
if (rpc.Api.isSimulationError(simulated)) {
|
|
219
|
-
throw new Error(`Simulation failed: ${JSON.stringify(simulated)}`);
|
|
220
|
-
}
|
|
221
|
-
const preparedTx = rpc.assembleTransaction(setAdminTx, simulated).build();
|
|
222
|
-
preparedTx.sign(TOKEN_ISSUER);
|
|
223
|
-
|
|
224
|
-
const sendResult = await server.sendTransaction(preparedTx);
|
|
225
|
-
if (sendResult.status !== 'PENDING') {
|
|
226
|
-
throw new Error(`Failed to set admin: ${JSON.stringify(sendResult)}`);
|
|
227
|
-
}
|
|
228
|
-
const txResult = await server.pollTransaction(sendResult.hash);
|
|
229
|
-
if (txResult.status !== 'SUCCESS') {
|
|
230
|
-
throw new Error(`Failed to set admin: ${JSON.stringify(txResult)}`);
|
|
231
|
-
}
|
|
232
|
-
console.log('ā
SAC admin set to SAC Manager');
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('Deploy Lock/Unlock OFT on Chain A', async () => {
|
|
236
|
-
lockUnlockOftClient = await deployContract<OFTClient>(
|
|
237
|
-
OFTClient,
|
|
238
|
-
OFT_WASM_PATH,
|
|
239
|
-
{
|
|
240
|
-
token: oftTokenAddress,
|
|
241
|
-
owner: DEFAULT_DEPLOYER.publicKey(),
|
|
242
|
-
endpoint: chainA.endpointV2, // Chain A endpoint
|
|
243
|
-
delegate: DEFAULT_DEPLOYER.publicKey(),
|
|
244
|
-
shared_decimals: SHARED_DECIMALS,
|
|
245
|
-
oft_type: { tag: 'LockUnlock' },
|
|
246
|
-
},
|
|
247
|
-
DEFAULT_DEPLOYER,
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
lockUnlockOftAddress = lockUnlockOftClient.options.contractId;
|
|
251
|
-
console.log('ā
Lock/Unlock OFT deployed on Chain A:', lockUnlockOftAddress);
|
|
252
|
-
|
|
253
|
-
const { result: oftType } = await lockUnlockOftClient.oft_type();
|
|
254
|
-
expect(oftType.tag).toEqual('LockUnlock');
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('Deploy Mint/Burn OFT on Chain B', async () => {
|
|
258
|
-
mintBurnOftClient = await deployContract<OFTClient>(
|
|
259
|
-
OFTClient,
|
|
260
|
-
OFT_WASM_PATH,
|
|
261
|
-
{
|
|
262
|
-
token: oftTokenAddress,
|
|
263
|
-
owner: DEFAULT_DEPLOYER.publicKey(),
|
|
264
|
-
endpoint: chainB.endpointV2, // Chain B endpoint
|
|
265
|
-
delegate: DEFAULT_DEPLOYER.publicKey(),
|
|
266
|
-
shared_decimals: SHARED_DECIMALS,
|
|
267
|
-
oft_type: { tag: 'MintBurn', values: [sacManagerAddress] },
|
|
268
|
-
},
|
|
269
|
-
DEFAULT_DEPLOYER,
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
mintBurnOftAddress = mintBurnOftClient.options.contractId;
|
|
273
|
-
console.log('ā
Mint/Burn OFT deployed on Chain B:', mintBurnOftAddress);
|
|
274
|
-
|
|
275
|
-
const { result: oftType } = await mintBurnOftClient.oft_type();
|
|
276
|
-
expect(oftType.tag).toEqual('MintBurn');
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
describe('Wire OFT Contracts to use SML (Cross-Chain)', () => {
|
|
281
|
-
it('Set Lock/Unlock OFT (Chain A) Send Library to SML for Chain B', async () => {
|
|
282
|
-
const assembledTx = await endpointClientA.set_send_library({
|
|
283
|
-
caller: DEFAULT_DEPLOYER.publicKey(),
|
|
284
|
-
sender: lockUnlockOftAddress,
|
|
285
|
-
dst_eid: EID_B,
|
|
286
|
-
new_lib: chainA.sml,
|
|
287
|
-
});
|
|
288
|
-
await assembledTx.signAndSend();
|
|
289
|
-
|
|
290
|
-
const { result: sendLib } = await endpointClientA.get_send_library({
|
|
291
|
-
sender: lockUnlockOftAddress,
|
|
292
|
-
dst_eid: EID_B,
|
|
293
|
-
});
|
|
294
|
-
expect(sendLib.lib).toBe(chainA.sml);
|
|
295
|
-
console.log('ā
Lock/Unlock OFT (Chain A) send library set to SML for EID_B');
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('Set Lock/Unlock OFT (Chain A) Receive Library to SML for Chain B', async () => {
|
|
299
|
-
const assembledTx = await endpointClientA.set_receive_library({
|
|
300
|
-
caller: DEFAULT_DEPLOYER.publicKey(),
|
|
301
|
-
receiver: lockUnlockOftAddress,
|
|
302
|
-
src_eid: EID_B,
|
|
303
|
-
new_lib: chainA.sml,
|
|
304
|
-
grace_period: 0n,
|
|
305
|
-
});
|
|
306
|
-
await assembledTx.signAndSend();
|
|
307
|
-
|
|
308
|
-
const { result: receiveLib } = await endpointClientA.get_receive_library({
|
|
309
|
-
receiver: lockUnlockOftAddress,
|
|
310
|
-
src_eid: EID_B,
|
|
311
|
-
});
|
|
312
|
-
expect(receiveLib.lib).toBe(chainA.sml);
|
|
313
|
-
console.log('ā
Lock/Unlock OFT (Chain A) receive library set to SML for EID_B');
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it('Set Mint/Burn OFT (Chain B) Send Library to SML for Chain A', async () => {
|
|
317
|
-
const assembledTx = await endpointClientB.set_send_library({
|
|
318
|
-
caller: DEFAULT_DEPLOYER.publicKey(),
|
|
319
|
-
sender: mintBurnOftAddress,
|
|
320
|
-
dst_eid: EID_A,
|
|
321
|
-
new_lib: chainB.sml,
|
|
322
|
-
});
|
|
323
|
-
await assembledTx.signAndSend();
|
|
324
|
-
|
|
325
|
-
const { result: sendLib } = await endpointClientB.get_send_library({
|
|
326
|
-
sender: mintBurnOftAddress,
|
|
327
|
-
dst_eid: EID_A,
|
|
328
|
-
});
|
|
329
|
-
expect(sendLib.lib).toBe(chainB.sml);
|
|
330
|
-
console.log('ā
Mint/Burn OFT (Chain B) send library set to SML for EID_A');
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('Set Mint/Burn OFT (Chain B) Receive Library to SML for Chain A', async () => {
|
|
334
|
-
const assembledTx = await endpointClientB.set_receive_library({
|
|
335
|
-
caller: DEFAULT_DEPLOYER.publicKey(),
|
|
336
|
-
receiver: mintBurnOftAddress,
|
|
337
|
-
src_eid: EID_A,
|
|
338
|
-
new_lib: chainB.sml,
|
|
339
|
-
grace_period: 0n,
|
|
340
|
-
});
|
|
341
|
-
await assembledTx.signAndSend();
|
|
342
|
-
|
|
343
|
-
const { result: receiveLib } = await endpointClientB.get_receive_library({
|
|
344
|
-
receiver: mintBurnOftAddress,
|
|
345
|
-
src_eid: EID_A,
|
|
346
|
-
});
|
|
347
|
-
expect(receiveLib.lib).toBe(chainB.sml);
|
|
348
|
-
console.log('ā
Mint/Burn OFT (Chain B) receive library set to SML for EID_A');
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('Set Lock/Unlock OFT (Chain A) Peer to Mint/Burn OFT (Chain B)', async () => {
|
|
352
|
-
const mintBurnPeerBytes = StrKey.decodeContract(mintBurnOftAddress);
|
|
353
|
-
|
|
354
|
-
const assembledTx = await lockUnlockOftClient.set_peer({
|
|
355
|
-
eid: EID_B,
|
|
356
|
-
peer: Buffer.from(mintBurnPeerBytes),
|
|
357
|
-
operator: DEFAULT_DEPLOYER.publicKey(),
|
|
358
|
-
});
|
|
359
|
-
await assembledTx.signAndSend();
|
|
360
|
-
|
|
361
|
-
const { result: peer } = await lockUnlockOftClient.peer({
|
|
362
|
-
eid: EID_B,
|
|
363
|
-
});
|
|
364
|
-
expect(peer?.toString()).toBe(Buffer.from(mintBurnPeerBytes).toString());
|
|
365
|
-
console.log('ā
Lock/Unlock OFT (Chain A) peer set to Mint/Burn OFT for EID_B');
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it('Set Mint/Burn OFT (Chain B) Peer to Lock/Unlock OFT (Chain A)', async () => {
|
|
369
|
-
const lockUnlockPeerBytes = StrKey.decodeContract(lockUnlockOftAddress);
|
|
370
|
-
|
|
371
|
-
const assembledTx = await mintBurnOftClient.set_peer({
|
|
372
|
-
eid: EID_A,
|
|
373
|
-
peer: Buffer.from(lockUnlockPeerBytes),
|
|
374
|
-
operator: DEFAULT_DEPLOYER.publicKey(),
|
|
375
|
-
});
|
|
376
|
-
await assembledTx.signAndSend();
|
|
377
|
-
|
|
378
|
-
const { result: peer } = await mintBurnOftClient.peer({
|
|
379
|
-
eid: EID_A,
|
|
380
|
-
});
|
|
381
|
-
expect(peer?.toString()).toBe(Buffer.from(lockUnlockPeerBytes).toString());
|
|
382
|
-
console.log('ā
Mint/Burn OFT (Chain B) peer set to Lock/Unlock OFT for EID_A');
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it('Grant MINTER_ROLE to Mint/Burn OFT on SAC Manager (for receive/mint)', async () => {
|
|
386
|
-
const sacManagerClient = new SACManagerClient({
|
|
387
|
-
contractId: sacManagerAddress,
|
|
388
|
-
publicKey: DEFAULT_DEPLOYER.publicKey(),
|
|
389
|
-
signTransaction: async (tx: string) => {
|
|
390
|
-
const transaction = TransactionBuilder.fromXDR(tx, NETWORK_PASSPHRASE);
|
|
391
|
-
transaction.sign(DEFAULT_DEPLOYER);
|
|
392
|
-
return {
|
|
393
|
-
signedTxXdr: transaction.toXDR(),
|
|
394
|
-
signerAddress: DEFAULT_DEPLOYER.publicKey(),
|
|
395
|
-
};
|
|
396
|
-
},
|
|
397
|
-
rpcUrl: RPC_URL,
|
|
398
|
-
networkPassphrase: NETWORK_PASSPHRASE,
|
|
399
|
-
allowHttp: true,
|
|
400
|
-
});
|
|
401
|
-
const assembledTx = await sacManagerClient.grant_role({
|
|
402
|
-
account: mintBurnOftAddress,
|
|
403
|
-
role: 'MINTER_ROLE',
|
|
404
|
-
caller: DEFAULT_DEPLOYER.publicKey(),
|
|
405
|
-
});
|
|
406
|
-
await assembledTx.signAndSend();
|
|
407
|
-
console.log('ā
MINTER_ROLE granted to Mint/Burn OFT on SAC Manager');
|
|
408
|
-
});
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
describe('Send: Chain A (Lock) ā Chain B (Mint)', () => {
|
|
412
|
-
let sendLedger = 0;
|
|
413
|
-
let packetSentEvent: PacketSentEvent;
|
|
414
|
-
let guid: Buffer;
|
|
415
|
-
let message: Buffer;
|
|
416
|
-
|
|
417
|
-
it('Verify initial balances', async () => {
|
|
418
|
-
const [senderBalance, recipientBBalance] = await Promise.all([
|
|
419
|
-
getTokenBalance(DEFAULT_DEPLOYER.publicKey(), oftTokenAddress),
|
|
420
|
-
getTokenBalance(RECIPIENT_B.publicKey(), oftTokenAddress),
|
|
421
|
-
]);
|
|
422
|
-
console.log('š Initial Balances:');
|
|
423
|
-
console.log(` - Sender (DEFAULT_DEPLOYER): ${senderBalance} (expected: 10000000000)`);
|
|
424
|
-
console.log(` - RECIPIENT_B (Chain B): ${recipientBBalance} (expected: 0)`);
|
|
425
|
-
|
|
426
|
-
expect(senderBalance).toBe(10000000000n);
|
|
427
|
-
expect(recipientBBalance).toBe(0n);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
it('Quote OFT send (A ā B)', async () => {
|
|
431
|
-
const receiverBytes = StrKey.decodeEd25519PublicKey(RECIPIENT_B.publicKey());
|
|
432
|
-
const sendParam: SendParam = {
|
|
433
|
-
dst_eid: EID_B,
|
|
434
|
-
to: Buffer.from(receiverBytes),
|
|
435
|
-
amount_ld: SEND_AMOUNT,
|
|
436
|
-
min_amount_ld: SEND_AMOUNT,
|
|
437
|
-
extra_options: Buffer.from([]),
|
|
438
|
-
compose_msg: Buffer.from([]),
|
|
439
|
-
oft_cmd: Buffer.from([]),
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
const { result: quoteResult } = await lockUnlockOftClient.quote_oft({
|
|
443
|
-
from: DEFAULT_DEPLOYER.publicKey(),
|
|
444
|
-
send_param: sendParam,
|
|
445
|
-
});
|
|
446
|
-
const [limit, feeDetails, receipt] = quoteResult;
|
|
447
|
-
console.log('š OFT Quote (A ā B):');
|
|
448
|
-
console.log(' Limit:', limit);
|
|
449
|
-
console.log(' Fee Details:', feeDetails);
|
|
450
|
-
console.log(' Receipt:', receipt);
|
|
451
|
-
|
|
452
|
-
expect(receipt.amount_sent_ld).toBe(SEND_AMOUNT);
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
it('Send tokens from Chain A to Chain B (Lock ā Mint)', async () => {
|
|
456
|
-
const receiverBytes = StrKey.decodeEd25519PublicKey(RECIPIENT_B.publicKey());
|
|
457
|
-
const sendParam: SendParam = {
|
|
458
|
-
dst_eid: EID_B,
|
|
459
|
-
to: Buffer.from(receiverBytes),
|
|
460
|
-
amount_ld: SEND_AMOUNT,
|
|
461
|
-
min_amount_ld: SEND_AMOUNT,
|
|
462
|
-
extra_options: Buffer.from([]),
|
|
463
|
-
compose_msg: Buffer.from([]),
|
|
464
|
-
oft_cmd: Buffer.from([]),
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
const { result: fee } = await lockUnlockOftClient.quote_send({
|
|
468
|
-
from: DEFAULT_DEPLOYER.publicKey(),
|
|
469
|
-
send_param: sendParam,
|
|
470
|
-
pay_in_zro: true,
|
|
471
|
-
});
|
|
472
|
-
console.log('š Messaging Fee (A ā B):', fee);
|
|
473
|
-
|
|
474
|
-
const assembledTx = await lockUnlockOftClient.send({
|
|
475
|
-
from: DEFAULT_DEPLOYER.publicKey(),
|
|
476
|
-
send_param: sendParam,
|
|
477
|
-
fee: fee,
|
|
478
|
-
refund_address: DEFAULT_DEPLOYER.publicKey(),
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
const sentTx = await assembledTx.signAndSend();
|
|
482
|
-
|
|
483
|
-
const txResponse = sentTx.getTransactionResponse;
|
|
484
|
-
if (txResponse && 'ledger' in txResponse) {
|
|
485
|
-
sendLedger = txResponse.ledger;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (txResponse) {
|
|
489
|
-
assertTransactionSucceeded(txResponse, 'OFT Send (A ā B)');
|
|
490
|
-
}
|
|
491
|
-
console.log('ā
Tokens sent from Chain A, ledger:', sendLedger);
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
it('Scan PacketSent events (A ā B)', async () => {
|
|
495
|
-
const packetSentEvents = await scanPacketSentEvents(chainA.endpointV2, sendLedger);
|
|
496
|
-
expect(packetSentEvents.length).toBeGreaterThan(0);
|
|
497
|
-
packetSentEvent = packetSentEvents[0];
|
|
498
|
-
console.log(
|
|
499
|
-
`ā
PacketSent events scanned from Chain A. Found ${packetSentEvents.length} events`,
|
|
500
|
-
);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
it('Validate packet via SML on Chain B', async () => {
|
|
504
|
-
const packet = PacketSerializer.deserialize(packetSentEvent.encoded_packet);
|
|
505
|
-
guid = Buffer.from(packet.guid.replace('0x', ''), 'hex');
|
|
506
|
-
message = Buffer.from(packet.message.replace('0x', ''), 'hex');
|
|
507
|
-
const codec = PacketV1Codec.from(packet);
|
|
508
|
-
const packetHeader = codec.header();
|
|
509
|
-
const payloadHash = codec.payloadHash();
|
|
510
|
-
|
|
511
|
-
const assembledTx = await smlClientB.validate_packet({
|
|
512
|
-
header_bytes: Buffer.from(packetHeader.replace('0x', ''), 'hex'),
|
|
513
|
-
payload_hash: Buffer.from(payloadHash.replace('0x', ''), 'hex'),
|
|
514
|
-
});
|
|
515
|
-
await assembledTx.signAndSend();
|
|
516
|
-
console.log('ā
Packet validated via SML on Chain B');
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
it('Receive tokens on Chain B (mint)', async () => {
|
|
520
|
-
const lockUnlockPeerBytes = StrKey.decodeContract(lockUnlockOftAddress);
|
|
521
|
-
const origin = {
|
|
522
|
-
nonce: 1n,
|
|
523
|
-
sender: Buffer.from(lockUnlockPeerBytes),
|
|
524
|
-
src_eid: EID_A,
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
const assembledTx = await executorHelperClientB.execute(
|
|
528
|
-
{
|
|
529
|
-
executor: chainB.executor,
|
|
530
|
-
params: {
|
|
531
|
-
extra_data: Buffer.from([]),
|
|
532
|
-
gas_limit: 0n,
|
|
533
|
-
guid,
|
|
534
|
-
message,
|
|
535
|
-
origin,
|
|
536
|
-
receiver: mintBurnOftAddress,
|
|
537
|
-
value: 0n,
|
|
538
|
-
},
|
|
539
|
-
value_payer: EXECUTOR_ADMIN.publicKey(),
|
|
540
|
-
},
|
|
541
|
-
{
|
|
542
|
-
simulate: false,
|
|
543
|
-
},
|
|
544
|
-
);
|
|
545
|
-
|
|
546
|
-
const txResult = await signAndSendWithExecutorAuth(
|
|
547
|
-
chainB.executor,
|
|
548
|
-
EXECUTOR_ADMIN,
|
|
549
|
-
assembledTx,
|
|
550
|
-
NETWORK_PASSPHRASE,
|
|
551
|
-
);
|
|
552
|
-
|
|
553
|
-
assertTransactionSucceeded(txResult, 'LzReceive on Chain B (Mint)');
|
|
554
|
-
|
|
555
|
-
console.log('ā
Tokens received and minted on Chain B');
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
it('Verify balances after forward send (A ā B)', async () => {
|
|
559
|
-
const [senderBalance, lockUnlockOftBalance, recipientBBalance] = await Promise.all([
|
|
560
|
-
getTokenBalance(DEFAULT_DEPLOYER.publicKey(), oftTokenAddress),
|
|
561
|
-
getTokenBalance(lockUnlockOftAddress, oftTokenAddress),
|
|
562
|
-
getTokenBalance(RECIPIENT_B.publicKey(), oftTokenAddress),
|
|
563
|
-
]);
|
|
564
|
-
|
|
565
|
-
console.log('š Balances after forward send (A ā B):');
|
|
566
|
-
console.log(` - Sender (DEFAULT_DEPLOYER): ${senderBalance} (expected: 9000000000)`);
|
|
567
|
-
console.log(
|
|
568
|
-
` - Lock/Unlock OFT (Chain A, locked): ${lockUnlockOftBalance} (expected: 1000000000)`,
|
|
569
|
-
);
|
|
570
|
-
console.log(
|
|
571
|
-
` - RECIPIENT_B (Chain B, minted): ${recipientBBalance} (expected: 1000000000)`,
|
|
572
|
-
);
|
|
573
|
-
|
|
574
|
-
expect(senderBalance).toBe(9000000000n);
|
|
575
|
-
expect(lockUnlockOftBalance).toBe(1000000000n);
|
|
576
|
-
expect(recipientBBalance).toBe(1000000000n);
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
describe('Send: Chain B (Burn) ā Chain A (Unlock)', () => {
|
|
581
|
-
let sendLedger = 0;
|
|
582
|
-
let packetSentEvent: PacketSentEvent;
|
|
583
|
-
let guid: Buffer;
|
|
584
|
-
let message: Buffer;
|
|
585
|
-
const REVERSE_SEND_AMOUNT = 50_0000000n;
|
|
586
|
-
|
|
587
|
-
it('Quote OFT send (B ā A)', async () => {
|
|
588
|
-
const receiverBytes = StrKey.decodeEd25519PublicKey(RECIPIENT_A.publicKey());
|
|
589
|
-
const sendParam: SendParam = {
|
|
590
|
-
dst_eid: EID_A,
|
|
591
|
-
to: Buffer.from(receiverBytes),
|
|
592
|
-
amount_ld: REVERSE_SEND_AMOUNT,
|
|
593
|
-
min_amount_ld: REVERSE_SEND_AMOUNT,
|
|
594
|
-
extra_options: Buffer.from([]),
|
|
595
|
-
compose_msg: Buffer.from([]),
|
|
596
|
-
oft_cmd: Buffer.from([]),
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
const { result: quoteResult } = await mintBurnOftClient.quote_oft({
|
|
600
|
-
from: DEFAULT_DEPLOYER.publicKey(),
|
|
601
|
-
send_param: sendParam,
|
|
602
|
-
});
|
|
603
|
-
const [limit, feeDetails, receipt] = quoteResult;
|
|
604
|
-
console.log('š Reverse OFT Quote (B ā A):');
|
|
605
|
-
console.log(' Limit:', limit);
|
|
606
|
-
console.log(' Fee Details:', feeDetails);
|
|
607
|
-
console.log(' Receipt:', receipt);
|
|
608
|
-
|
|
609
|
-
expect(receipt.amount_sent_ld).toBe(REVERSE_SEND_AMOUNT);
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
it('Send tokens from Chain B to Chain A (Burn ā Unlock)', async () => {
|
|
613
|
-
const receiverBytes = StrKey.decodeEd25519PublicKey(RECIPIENT_A.publicKey());
|
|
614
|
-
const sendParam: SendParam = {
|
|
615
|
-
dst_eid: EID_A,
|
|
616
|
-
to: Buffer.from(receiverBytes),
|
|
617
|
-
amount_ld: REVERSE_SEND_AMOUNT,
|
|
618
|
-
min_amount_ld: REVERSE_SEND_AMOUNT,
|
|
619
|
-
extra_options: Buffer.from([]),
|
|
620
|
-
compose_msg: Buffer.from([]),
|
|
621
|
-
oft_cmd: Buffer.from([]),
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
const { result: fee } = await mintBurnOftClient.quote_send({
|
|
625
|
-
from: DEFAULT_DEPLOYER.publicKey(),
|
|
626
|
-
send_param: sendParam,
|
|
627
|
-
pay_in_zro: true,
|
|
628
|
-
});
|
|
629
|
-
console.log('š Reverse Messaging Fee (B ā A):', fee);
|
|
630
|
-
|
|
631
|
-
const assembledTx = await mintBurnOftClient.send({
|
|
632
|
-
from: DEFAULT_DEPLOYER.publicKey(),
|
|
633
|
-
send_param: sendParam,
|
|
634
|
-
fee: fee,
|
|
635
|
-
refund_address: DEFAULT_DEPLOYER.publicKey(),
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
const sentTx = await assembledTx.signAndSend();
|
|
639
|
-
|
|
640
|
-
const txResponse = sentTx.getTransactionResponse;
|
|
641
|
-
if (txResponse && 'ledger' in txResponse) {
|
|
642
|
-
sendLedger = txResponse.ledger;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
if (txResponse) {
|
|
646
|
-
assertTransactionSucceeded(txResponse, 'OFT Reverse Send (B ā A)');
|
|
647
|
-
}
|
|
648
|
-
console.log('ā
Tokens sent from Chain B (reverse), ledger:', sendLedger);
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
it('Scan PacketSent events (B ā A)', async () => {
|
|
652
|
-
const packetSentEvents = await scanPacketSentEvents(chainB.endpointV2, sendLedger);
|
|
653
|
-
expect(packetSentEvents.length).toBeGreaterThan(0);
|
|
654
|
-
packetSentEvent = packetSentEvents[0];
|
|
655
|
-
console.log(
|
|
656
|
-
`ā
PacketSent events scanned from Chain B (reverse). Found ${packetSentEvents.length} events`,
|
|
657
|
-
);
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it('Validate packet via SML on Chain A (reverse)', async () => {
|
|
661
|
-
const packet = PacketSerializer.deserialize(packetSentEvent.encoded_packet);
|
|
662
|
-
guid = Buffer.from(packet.guid.replace('0x', ''), 'hex');
|
|
663
|
-
message = Buffer.from(packet.message.replace('0x', ''), 'hex');
|
|
664
|
-
const codec = PacketV1Codec.from(packet);
|
|
665
|
-
const packetHeader = codec.header();
|
|
666
|
-
const payloadHash = codec.payloadHash();
|
|
667
|
-
|
|
668
|
-
const assembledTx = await smlClientA.validate_packet({
|
|
669
|
-
header_bytes: Buffer.from(packetHeader.replace('0x', ''), 'hex'),
|
|
670
|
-
payload_hash: Buffer.from(payloadHash.replace('0x', ''), 'hex'),
|
|
671
|
-
});
|
|
672
|
-
await assembledTx.signAndSend();
|
|
673
|
-
console.log('ā
Packet validated via SML on Chain A (reverse)');
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
it('Receive tokens on Chain A (unlock)', async () => {
|
|
677
|
-
const mintBurnPeerBytes = StrKey.decodeContract(mintBurnOftAddress);
|
|
678
|
-
const origin = {
|
|
679
|
-
nonce: 1n,
|
|
680
|
-
sender: Buffer.from(mintBurnPeerBytes),
|
|
681
|
-
src_eid: EID_B,
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
const assembledTx = await executorHelperClientA.execute(
|
|
685
|
-
{
|
|
686
|
-
executor: chainA.executor,
|
|
687
|
-
params: {
|
|
688
|
-
extra_data: Buffer.from([]),
|
|
689
|
-
gas_limit: 0n,
|
|
690
|
-
guid,
|
|
691
|
-
message,
|
|
692
|
-
origin,
|
|
693
|
-
receiver: lockUnlockOftAddress,
|
|
694
|
-
value: 0n,
|
|
695
|
-
},
|
|
696
|
-
value_payer: EXECUTOR_ADMIN.publicKey(),
|
|
697
|
-
},
|
|
698
|
-
{
|
|
699
|
-
simulate: false,
|
|
700
|
-
},
|
|
701
|
-
);
|
|
702
|
-
|
|
703
|
-
const txResult = await signAndSendWithExecutorAuth(
|
|
704
|
-
chainA.executor,
|
|
705
|
-
EXECUTOR_ADMIN,
|
|
706
|
-
assembledTx,
|
|
707
|
-
NETWORK_PASSPHRASE,
|
|
708
|
-
);
|
|
709
|
-
|
|
710
|
-
assertTransactionSucceeded(txResult, 'LzReceive on Chain A (Unlock)');
|
|
711
|
-
|
|
712
|
-
console.log('ā
Tokens received and unlocked on Chain A');
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
it('Verify final balances', async () => {
|
|
716
|
-
const [senderBalance, lockUnlockOftBalance, recipientABalance, recipientBBalance] =
|
|
717
|
-
await Promise.all([
|
|
718
|
-
getTokenBalance(DEFAULT_DEPLOYER.publicKey(), oftTokenAddress),
|
|
719
|
-
getTokenBalance(lockUnlockOftAddress, oftTokenAddress),
|
|
720
|
-
getTokenBalance(RECIPIENT_A.publicKey(), oftTokenAddress),
|
|
721
|
-
getTokenBalance(RECIPIENT_B.publicKey(), oftTokenAddress),
|
|
722
|
-
]);
|
|
723
|
-
|
|
724
|
-
console.log('\nš Final Balance Summary:');
|
|
725
|
-
console.log(` - Sender (DEFAULT_DEPLOYER): ${senderBalance} (expected: 8500000000)`);
|
|
726
|
-
console.log(
|
|
727
|
-
` - Lock/Unlock OFT (Chain A, locked): ${lockUnlockOftBalance} (expected: 500000000)`,
|
|
728
|
-
);
|
|
729
|
-
console.log(
|
|
730
|
-
` - RECIPIENT_A (Chain A, unlocked): ${recipientABalance} (expected: 500000000)`,
|
|
731
|
-
);
|
|
732
|
-
console.log(
|
|
733
|
-
` - RECIPIENT_B (Chain B, minted): ${recipientBBalance} (expected: 1000000000)`,
|
|
734
|
-
);
|
|
735
|
-
|
|
736
|
-
expect(senderBalance).toBe(8500000000n);
|
|
737
|
-
expect(lockUnlockOftBalance).toBe(500000000n);
|
|
738
|
-
expect(recipientABalance).toBe(500000000n);
|
|
739
|
-
expect(recipientBBalance).toBe(1000000000n);
|
|
740
|
-
|
|
741
|
-
console.log('\nš OFT Cross-Chain E2E test completed successfully!');
|
|
742
|
-
console.log(
|
|
743
|
-
' Chain A (Lock/Unlock) ā Chain B (Mint/Burn): 100 tokens locked, 100 minted',
|
|
744
|
-
);
|
|
745
|
-
console.log(
|
|
746
|
-
' Chain B (Mint/Burn) ā Chain A (Lock/Unlock): 50 tokens burned, 50 unlocked',
|
|
747
|
-
);
|
|
748
|
-
});
|
|
749
|
-
});
|
|
750
|
-
});
|