lockup.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. const assert = require("assert");
  2. const anchor = require('@project-serum/anchor');
  3. const serumCmn = require("@project-serum/common");
  4. const TokenInstructions = require("@project-serum/serum").TokenInstructions;
  5. const utils = require("./utils");
  6. describe("Lockup and Registry", () => {
  7. // Read the provider from the configured environmnet.
  8. const provider = anchor.Provider.env();
  9. // Configure the client to use the provider.
  10. anchor.setProvider(provider);
  11. const lockup = anchor.workspace.Lockup;
  12. const registry = anchor.workspace.Registry;
  13. let lockupAddress = null;
  14. let mint = null;
  15. let god = null;
  16. it("Sets up initial test state", async () => {
  17. const [_mint, _god] = await serumCmn.createMintAndVault(
  18. provider,
  19. new anchor.BN(1000000)
  20. );
  21. mint = _mint;
  22. god = _god;
  23. });
  24. it("Is initialized!", async () => {
  25. await lockup.state.rpc.new({
  26. accounts: {
  27. authority: provider.wallet.publicKey,
  28. },
  29. });
  30. lockupAddress = await lockup.state.address();
  31. const lockupAccount = await lockup.state();
  32. assert.ok(lockupAccount.authority.equals(provider.wallet.publicKey));
  33. assert.ok(lockupAccount.whitelist.length === 5);
  34. lockupAccount.whitelist.forEach((e) => {
  35. assert.ok(e.programId.equals(new anchor.web3.PublicKey()));
  36. });
  37. });
  38. it("Deletes the default whitelisted addresses", async () => {
  39. const defaultEntry = { programId: new anchor.web3.PublicKey() };
  40. await lockup.state.rpc.whitelistDelete(defaultEntry, {
  41. accounts: {
  42. authority: provider.wallet.publicKey,
  43. },
  44. });
  45. });
  46. it("Sets a new authority", async () => {
  47. const newAuthority = new anchor.web3.Account();
  48. await lockup.state.rpc.setAuthority(newAuthority.publicKey, {
  49. accounts: {
  50. authority: provider.wallet.publicKey,
  51. },
  52. });
  53. let lockupAccount = await lockup.state();
  54. assert.ok(lockupAccount.authority.equals(newAuthority.publicKey));
  55. await lockup.state.rpc.setAuthority(provider.wallet.publicKey, {
  56. accounts: {
  57. authority: newAuthority.publicKey,
  58. },
  59. signers: [newAuthority],
  60. });
  61. lockupAccount = await lockup.state();
  62. assert.ok(lockupAccount.authority.equals(provider.wallet.publicKey));
  63. });
  64. let e0 = null;
  65. let e1 = null;
  66. let e2 = null;
  67. let e3 = null;
  68. let e4 = null;
  69. it("Adds to the whitelist", async () => {
  70. const generateEntry = async () => {
  71. let programId = new anchor.web3.Account().publicKey;
  72. return {
  73. programId,
  74. };
  75. };
  76. e0 = await generateEntry();
  77. e1 = await generateEntry();
  78. e2 = await generateEntry();
  79. e3 = await generateEntry();
  80. e4 = await generateEntry();
  81. const e5 = await generateEntry();
  82. const accounts = {
  83. authority: provider.wallet.publicKey,
  84. };
  85. await lockup.state.rpc.whitelistAdd(e0, { accounts });
  86. let lockupAccount = await lockup.state();
  87. assert.ok(lockupAccount.whitelist.length === 1);
  88. assert.deepEqual(lockupAccount.whitelist, [e0]);
  89. await lockup.state.rpc.whitelistAdd(e1, { accounts });
  90. await lockup.state.rpc.whitelistAdd(e2, { accounts });
  91. await lockup.state.rpc.whitelistAdd(e3, { accounts });
  92. await lockup.state.rpc.whitelistAdd(e4, { accounts });
  93. lockupAccount = await lockup.state();
  94. assert.deepEqual(lockupAccount.whitelist, [e0, e1, e2, e3, e4]);
  95. await assert.rejects(
  96. async () => {
  97. await lockup.state.rpc.whitelistAdd(e5, { accounts });
  98. },
  99. (err) => {
  100. assert.equal(err.code, 108);
  101. assert.equal(err.msg, "Whitelist is full");
  102. return true;
  103. }
  104. );
  105. });
  106. it("Removes from the whitelist", async () => {
  107. await lockup.state.rpc.whitelistDelete(e0, {
  108. accounts: {
  109. authority: provider.wallet.publicKey,
  110. },
  111. });
  112. let lockupAccount = await lockup.state();
  113. assert.deepEqual(lockupAccount.whitelist, [e1, e2, e3, e4]);
  114. });
  115. const vesting = new anchor.web3.Account();
  116. let vestingAccount = null;
  117. let vestingSigner = null;
  118. it("Creates a vesting account", async () => {
  119. const beneficiary = provider.wallet.publicKey;
  120. const endTs = new anchor.BN(Date.now() / 1000 + 3);
  121. const periodCount = new anchor.BN(5);
  122. const depositAmount = new anchor.BN(100);
  123. const vault = new anchor.web3.Account();
  124. let [
  125. _vestingSigner,
  126. nonce,
  127. ] = await anchor.web3.PublicKey.findProgramAddress(
  128. [vesting.publicKey.toBuffer()],
  129. lockup.programId
  130. );
  131. vestingSigner = _vestingSigner;
  132. await lockup.rpc.createVesting(
  133. beneficiary,
  134. endTs,
  135. periodCount,
  136. depositAmount,
  137. nonce,
  138. {
  139. accounts: {
  140. vesting: vesting.publicKey,
  141. vault: vault.publicKey,
  142. depositor: god,
  143. depositorAuthority: provider.wallet.publicKey,
  144. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  145. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  146. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  147. },
  148. signers: [vesting, vault],
  149. instructions: [
  150. await lockup.account.vesting.createInstruction(vesting),
  151. ...(await serumCmn.createTokenAccountInstrs(
  152. provider,
  153. vault.publicKey,
  154. mint,
  155. vestingSigner
  156. )),
  157. ],
  158. }
  159. );
  160. vestingAccount = await lockup.account.vesting(vesting.publicKey);
  161. assert.ok(vestingAccount.beneficiary.equals(provider.wallet.publicKey));
  162. assert.ok(vestingAccount.mint.equals(mint));
  163. assert.ok(vestingAccount.grantor.equals(provider.wallet.publicKey));
  164. assert.ok(vestingAccount.outstanding.eq(depositAmount));
  165. assert.ok(vestingAccount.startBalance.eq(depositAmount));
  166. assert.ok(vestingAccount.endTs.eq(endTs));
  167. assert.ok(vestingAccount.periodCount.eq(periodCount));
  168. assert.ok(vestingAccount.whitelistOwned.eq(new anchor.BN(0)));
  169. assert.equal(vestingAccount.nonce, nonce);
  170. assert.ok(endTs.gt(vestingAccount.startTs));
  171. });
  172. it("Fails to withdraw from a vesting account before vesting", async () => {
  173. await assert.rejects(
  174. async () => {
  175. await lockup.rpc.withdraw(new anchor.BN(100), {
  176. accounts: {
  177. vesting: vesting.publicKey,
  178. beneficiary: provider.wallet.publicKey,
  179. token: god,
  180. vault: vestingAccount.vault,
  181. vestingSigner: vestingSigner,
  182. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  183. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  184. },
  185. });
  186. },
  187. (err) => {
  188. assert.equal(err.code, 107);
  189. assert.equal(err.msg, "Insufficient withdrawal balance.");
  190. return true;
  191. }
  192. );
  193. });
  194. it("Waits for a vesting period to pass", async () => {
  195. await serumCmn.sleep(5 * 1000);
  196. });
  197. it("Withdraws from the vesting account", async () => {
  198. const token = await serumCmn.createTokenAccount(
  199. provider,
  200. mint,
  201. provider.wallet.publicKey
  202. );
  203. await lockup.rpc.withdraw(new anchor.BN(100), {
  204. accounts: {
  205. vesting: vesting.publicKey,
  206. beneficiary: provider.wallet.publicKey,
  207. token,
  208. vault: vestingAccount.vault,
  209. vestingSigner,
  210. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  211. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  212. },
  213. });
  214. vestingAccount = await lockup.account.vesting(vesting.publicKey);
  215. assert.ok(vestingAccount.outstanding.eq(new anchor.BN(0)));
  216. const vaultAccount = await serumCmn.getTokenAccount(
  217. provider,
  218. vestingAccount.vault
  219. );
  220. assert.ok(vaultAccount.amount.eq(new anchor.BN(0)));
  221. const tokenAccount = await serumCmn.getTokenAccount(provider, token);
  222. assert.ok(tokenAccount.amount.eq(new anchor.BN(100)));
  223. });
  224. const registrar = new anchor.web3.Account();
  225. const rewardQ = new anchor.web3.Account();
  226. const withdrawalTimelock = new anchor.BN(4);
  227. const stakeRate = new anchor.BN(2);
  228. const rewardQLen = 100;
  229. let registrarAccount = null;
  230. let registrarSigner = null;
  231. let nonce = null;
  232. let poolMint = null;
  233. it("Creates registry genesis", async () => {
  234. const [
  235. _registrarSigner,
  236. _nonce,
  237. ] = await anchor.web3.PublicKey.findProgramAddress(
  238. [registrar.publicKey.toBuffer()],
  239. registry.programId
  240. );
  241. registrarSigner = _registrarSigner;
  242. nonce = _nonce;
  243. poolMint = await serumCmn.createMint(provider, registrarSigner);
  244. });
  245. it("Initializes registry's global state", async () => {
  246. await registry.state.rpc.new({
  247. accounts: { lockupProgram: lockup.programId },
  248. });
  249. const state = await registry.state();
  250. assert.ok(state.lockupProgram.equals(lockup.programId));
  251. // Should not allow a second initializatoin.
  252. await assert.rejects(
  253. async () => {
  254. await registry.state.rpc.new(lockup.programId);
  255. },
  256. (err) => {
  257. return true;
  258. }
  259. );
  260. });
  261. it("Initializes the registrar", async () => {
  262. await registry.rpc.initialize(
  263. mint,
  264. provider.wallet.publicKey,
  265. nonce,
  266. withdrawalTimelock,
  267. stakeRate,
  268. rewardQLen,
  269. {
  270. accounts: {
  271. registrar: registrar.publicKey,
  272. poolMint,
  273. rewardEventQ: rewardQ.publicKey,
  274. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  275. },
  276. signers: [registrar, rewardQ],
  277. instructions: [
  278. await registry.account.registrar.createInstruction(registrar),
  279. await registry.account.rewardQueue.createInstruction(rewardQ, 8250),
  280. ],
  281. }
  282. );
  283. registrarAccount = await registry.account.registrar(registrar.publicKey);
  284. assert.ok(registrarAccount.authority.equals(provider.wallet.publicKey));
  285. assert.equal(registrarAccount.nonce, nonce);
  286. assert.ok(registrarAccount.mint.equals(mint));
  287. assert.ok(registrarAccount.poolMint.equals(poolMint));
  288. assert.ok(registrarAccount.stakeRate.eq(stakeRate));
  289. assert.ok(registrarAccount.rewardEventQ.equals(rewardQ.publicKey));
  290. assert.ok(registrarAccount.withdrawalTimelock.eq(withdrawalTimelock));
  291. });
  292. const member = new anchor.web3.Account();
  293. let memberAccount = null;
  294. let memberSigner = null;
  295. let balances = null;
  296. let balancesLocked = null;
  297. it("Creates a member", async () => {
  298. const [
  299. _memberSigner,
  300. nonce,
  301. ] = await anchor.web3.PublicKey.findProgramAddress(
  302. [registrar.publicKey.toBuffer(), member.publicKey.toBuffer()],
  303. registry.programId
  304. );
  305. memberSigner = _memberSigner;
  306. const [mainTx, _balances] = await utils.createBalanceSandbox(
  307. provider,
  308. registrarAccount,
  309. memberSigner
  310. );
  311. const [lockedTx, _balancesLocked] = await utils.createBalanceSandbox(
  312. provider,
  313. registrarAccount,
  314. memberSigner
  315. );
  316. balances = _balances;
  317. balancesLocked = _balancesLocked;
  318. const tx = registry.transaction.createMember(nonce, {
  319. accounts: {
  320. registrar: registrar.publicKey,
  321. member: member.publicKey,
  322. beneficiary: provider.wallet.publicKey,
  323. memberSigner,
  324. balances,
  325. balancesLocked,
  326. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  327. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  328. },
  329. instructions: [await registry.account.member.createInstruction(member)],
  330. });
  331. const signers = [member, provider.wallet.payer];
  332. const allTxs = [mainTx, lockedTx, { tx, signers }];
  333. let txSigs = await provider.sendAll(allTxs);
  334. memberAccount = await registry.account.member(member.publicKey);
  335. assert.ok(memberAccount.registrar.equals(registrar.publicKey));
  336. assert.ok(memberAccount.beneficiary.equals(provider.wallet.publicKey));
  337. assert.ok(memberAccount.metadata.equals(new anchor.web3.PublicKey()));
  338. assert.equal(
  339. JSON.stringify(memberAccount.balances),
  340. JSON.stringify(balances)
  341. );
  342. assert.equal(
  343. JSON.stringify(memberAccount.balancesLocked),
  344. JSON.stringify(balancesLocked)
  345. );
  346. assert.ok(memberAccount.rewardsCursor === 0);
  347. assert.ok(memberAccount.lastStakeTs.eq(new anchor.BN(0)));
  348. });
  349. it("Deposits (unlocked) to a member", async () => {
  350. const depositAmount = new anchor.BN(120);
  351. await registry.rpc.deposit(depositAmount, {
  352. accounts: {
  353. depositor: god,
  354. depositorAuthority: provider.wallet.publicKey,
  355. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  356. vault: memberAccount.balances.vault,
  357. beneficiary: provider.wallet.publicKey,
  358. member: member.publicKey,
  359. },
  360. });
  361. const memberVault = await serumCmn.getTokenAccount(
  362. provider,
  363. memberAccount.balances.vault
  364. );
  365. assert.ok(memberVault.amount.eq(depositAmount));
  366. });
  367. it("Stakes to a member (unlocked)", async () => {
  368. const stakeAmount = new anchor.BN(10);
  369. await registry.rpc.stake(stakeAmount, false, {
  370. accounts: {
  371. // Stake instance.
  372. registrar: registrar.publicKey,
  373. rewardEventQ: rewardQ.publicKey,
  374. poolMint,
  375. // Member.
  376. member: member.publicKey,
  377. beneficiary: provider.wallet.publicKey,
  378. balances,
  379. balancesLocked,
  380. // Program signers.
  381. memberSigner,
  382. registrarSigner,
  383. // Misc.
  384. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  385. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  386. },
  387. });
  388. const vault = await serumCmn.getTokenAccount(
  389. provider,
  390. memberAccount.balances.vault
  391. );
  392. const vaultStake = await serumCmn.getTokenAccount(
  393. provider,
  394. memberAccount.balances.vaultStake
  395. );
  396. const spt = await serumCmn.getTokenAccount(
  397. provider,
  398. memberAccount.balances.spt
  399. );
  400. assert.ok(vault.amount.eq(new anchor.BN(100)));
  401. assert.ok(vaultStake.amount.eq(new anchor.BN(20)));
  402. assert.ok(spt.amount.eq(new anchor.BN(10)));
  403. });
  404. const unlockedVendor = new anchor.web3.Account();
  405. const unlockedVendorVault = new anchor.web3.Account();
  406. let unlockedVendorSigner = null;
  407. it("Drops an unlocked reward", async () => {
  408. const rewardKind = {
  409. unlocked: {},
  410. };
  411. const rewardAmount = new anchor.BN(200);
  412. const expiry = new anchor.BN(Date.now() / 1000 + 5);
  413. const [
  414. _vendorSigner,
  415. nonce,
  416. ] = await anchor.web3.PublicKey.findProgramAddress(
  417. [registrar.publicKey.toBuffer(), unlockedVendor.publicKey.toBuffer()],
  418. registry.programId
  419. );
  420. unlockedVendorSigner = _vendorSigner;
  421. await registry.rpc.dropReward(
  422. rewardKind,
  423. rewardAmount,
  424. expiry,
  425. provider.wallet.publicKey,
  426. nonce,
  427. {
  428. accounts: {
  429. registrar: registrar.publicKey,
  430. rewardEventQ: rewardQ.publicKey,
  431. poolMint,
  432. vendor: unlockedVendor.publicKey,
  433. vendorVault: unlockedVendorVault.publicKey,
  434. depositor: god,
  435. depositorAuthority: provider.wallet.publicKey,
  436. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  437. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  438. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  439. },
  440. signers: [unlockedVendorVault, unlockedVendor],
  441. instructions: [
  442. ...(await serumCmn.createTokenAccountInstrs(
  443. provider,
  444. unlockedVendorVault.publicKey,
  445. mint,
  446. unlockedVendorSigner
  447. )),
  448. await registry.account.rewardVendor.createInstruction(unlockedVendor),
  449. ],
  450. }
  451. );
  452. const vendorAccount = await registry.account.rewardVendor(
  453. unlockedVendor.publicKey
  454. );
  455. assert.ok(vendorAccount.registrar.equals(registrar.publicKey));
  456. assert.ok(vendorAccount.vault.equals(unlockedVendorVault.publicKey));
  457. assert.ok(vendorAccount.nonce === nonce);
  458. assert.ok(vendorAccount.poolTokenSupply.eq(new anchor.BN(10)));
  459. assert.ok(vendorAccount.expiryTs.eq(expiry));
  460. assert.ok(vendorAccount.expiryReceiver.equals(provider.wallet.publicKey));
  461. assert.ok(vendorAccount.total.eq(rewardAmount));
  462. assert.ok(vendorAccount.expired === false);
  463. assert.ok(vendorAccount.rewardEventQCursor === 0);
  464. assert.deepEqual(vendorAccount.kind, rewardKind);
  465. const rewardQAccount = await registry.account.rewardQueue(
  466. rewardQ.publicKey
  467. );
  468. assert.ok(rewardQAccount.head === 1);
  469. assert.ok(rewardQAccount.tail === 0);
  470. const e = rewardQAccount.events[0];
  471. assert.ok(e.vendor.equals(unlockedVendor.publicKey));
  472. assert.equal(e.locked, false);
  473. });
  474. it("Collects an unlocked reward", async () => {
  475. const token = await serumCmn.createTokenAccount(
  476. provider,
  477. mint,
  478. provider.wallet.publicKey
  479. );
  480. await registry.rpc.claimReward({
  481. accounts: {
  482. to: token,
  483. cmn: {
  484. registrar: registrar.publicKey,
  485. member: member.publicKey,
  486. beneficiary: provider.wallet.publicKey,
  487. balances,
  488. balancesLocked,
  489. vendor: unlockedVendor.publicKey,
  490. vault: unlockedVendorVault.publicKey,
  491. vendorSigner: unlockedVendorSigner,
  492. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  493. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  494. },
  495. },
  496. });
  497. let tokenAccount = await serumCmn.getTokenAccount(provider, token);
  498. assert.ok(tokenAccount.amount.eq(new anchor.BN(200)));
  499. const memberAccount = await registry.account.member(member.publicKey);
  500. assert.ok(memberAccount.rewardsCursor == 1);
  501. });
  502. const lockedVendor = new anchor.web3.Account();
  503. const lockedVendorVault = new anchor.web3.Account();
  504. let lockedVendorSigner = null;
  505. let lockedRewardAmount = null;
  506. let lockedRewardKind = null;
  507. it("Drops a locked reward", async () => {
  508. lockedRewardKind = {
  509. locked: {
  510. endTs: new anchor.BN(Date.now() / 1000 + 70),
  511. periodCount: new anchor.BN(10),
  512. },
  513. };
  514. lockedRewardAmount = new anchor.BN(200);
  515. const expiry = new anchor.BN(Date.now() / 1000 + 5);
  516. const [
  517. _vendorSigner,
  518. nonce,
  519. ] = await anchor.web3.PublicKey.findProgramAddress(
  520. [registrar.publicKey.toBuffer(), lockedVendor.publicKey.toBuffer()],
  521. registry.programId
  522. );
  523. lockedVendorSigner = _vendorSigner;
  524. await registry.rpc.dropReward(
  525. lockedRewardKind,
  526. lockedRewardAmount,
  527. expiry,
  528. provider.wallet.publicKey,
  529. nonce,
  530. {
  531. accounts: {
  532. registrar: registrar.publicKey,
  533. rewardEventQ: rewardQ.publicKey,
  534. poolMint,
  535. vendor: lockedVendor.publicKey,
  536. vendorVault: lockedVendorVault.publicKey,
  537. depositor: god,
  538. depositorAuthority: provider.wallet.publicKey,
  539. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  540. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  541. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  542. },
  543. signers: [lockedVendorVault, lockedVendor],
  544. instructions: [
  545. ...(await serumCmn.createTokenAccountInstrs(
  546. provider,
  547. lockedVendorVault.publicKey,
  548. mint,
  549. lockedVendorSigner
  550. )),
  551. await registry.account.rewardVendor.createInstruction(lockedVendor),
  552. ],
  553. }
  554. );
  555. const vendorAccount = await registry.account.rewardVendor(
  556. lockedVendor.publicKey
  557. );
  558. assert.ok(vendorAccount.registrar.equals(registrar.publicKey));
  559. assert.ok(vendorAccount.vault.equals(lockedVendorVault.publicKey));
  560. assert.ok(vendorAccount.nonce === nonce);
  561. assert.ok(vendorAccount.poolTokenSupply.eq(new anchor.BN(10)));
  562. assert.ok(vendorAccount.expiryTs.eq(expiry));
  563. assert.ok(vendorAccount.expiryReceiver.equals(provider.wallet.publicKey));
  564. assert.ok(vendorAccount.total.eq(lockedRewardAmount));
  565. assert.ok(vendorAccount.expired === false);
  566. assert.ok(vendorAccount.rewardEventQCursor === 1);
  567. assert.equal(
  568. JSON.stringify(vendorAccount.kind),
  569. JSON.stringify(lockedRewardKind)
  570. );
  571. const rewardQAccount = await registry.account.rewardQueue(
  572. rewardQ.publicKey
  573. );
  574. assert.ok(rewardQAccount.head === 2);
  575. assert.ok(rewardQAccount.tail === 0);
  576. const e = rewardQAccount.events[1];
  577. assert.ok(e.vendor.equals(lockedVendor.publicKey));
  578. assert.ok(e.locked === true);
  579. });
  580. it("Collects a locked reward", async () => {
  581. const vendoredVesting = new anchor.web3.Account();
  582. const vendoredVestingVault = new anchor.web3.Account();
  583. let [
  584. vendoredVestingSigner,
  585. nonce,
  586. ] = await anchor.web3.PublicKey.findProgramAddress(
  587. [vendoredVesting.publicKey.toBuffer()],
  588. lockup.programId
  589. );
  590. const remainingAccounts = lockup.instruction.createVesting
  591. .accounts({
  592. vesting: vendoredVesting.publicKey,
  593. vault: vendoredVestingVault.publicKey,
  594. depositor: lockedVendorVault.publicKey,
  595. depositorAuthority: lockedVendorSigner,
  596. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  597. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  598. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  599. })
  600. // Change the signer status on the vendor signer since it's signed by the program, not the
  601. // client.
  602. .map((meta) =>
  603. meta.pubkey === lockedVendorSigner ? { ...meta, isSigner: false } : meta
  604. );
  605. await registry.rpc.claimRewardLocked(nonce, {
  606. accounts: {
  607. registry: await registry.state.address(),
  608. lockupProgram: lockup.programId,
  609. cmn: {
  610. registrar: registrar.publicKey,
  611. member: member.publicKey,
  612. beneficiary: provider.wallet.publicKey,
  613. balances,
  614. balancesLocked,
  615. vendor: lockedVendor.publicKey,
  616. vault: lockedVendorVault.publicKey,
  617. vendorSigner: lockedVendorSigner,
  618. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  619. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  620. },
  621. },
  622. remainingAccounts,
  623. signers: [vendoredVesting, vendoredVestingVault],
  624. instructions: [
  625. await lockup.account.vesting.createInstruction(vendoredVesting),
  626. ...(await serumCmn.createTokenAccountInstrs(
  627. provider,
  628. vendoredVestingVault.publicKey,
  629. mint,
  630. vendoredVestingSigner
  631. )),
  632. ],
  633. });
  634. const lockupAccount = await lockup.account.vesting(
  635. vendoredVesting.publicKey
  636. );
  637. assert.ok(lockupAccount.beneficiary.equals(provider.wallet.publicKey));
  638. assert.ok(lockupAccount.mint.equals(mint));
  639. assert.ok(lockupAccount.vault.equals(vendoredVestingVault.publicKey));
  640. assert.ok(lockupAccount.outstanding.eq(lockedRewardAmount));
  641. assert.ok(lockupAccount.startBalance.eq(lockedRewardAmount));
  642. assert.ok(lockupAccount.endTs.eq(lockedRewardKind.locked.endTs));
  643. assert.ok(
  644. lockupAccount.periodCount.eq(lockedRewardKind.locked.periodCount)
  645. );
  646. assert.ok(lockupAccount.whitelistOwned.eq(new anchor.BN(0)));
  647. });
  648. const pendingWithdrawal = new anchor.web3.Account();
  649. it("Unstakes (unlocked)", async () => {
  650. const unstakeAmount = new anchor.BN(10);
  651. await registry.rpc.startUnstake(unstakeAmount, false, {
  652. accounts: {
  653. registrar: registrar.publicKey,
  654. rewardEventQ: rewardQ.publicKey,
  655. poolMint,
  656. pendingWithdrawal: pendingWithdrawal.publicKey,
  657. member: member.publicKey,
  658. beneficiary: provider.wallet.publicKey,
  659. balances,
  660. balancesLocked,
  661. memberSigner,
  662. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  663. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  664. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  665. },
  666. signers: [pendingWithdrawal],
  667. instructions: [
  668. await registry.account.pendingWithdrawal.createInstruction(
  669. pendingWithdrawal
  670. ),
  671. ],
  672. });
  673. const vaultPw = await serumCmn.getTokenAccount(
  674. provider,
  675. memberAccount.balances.vaultPw
  676. );
  677. const vaultStake = await serumCmn.getTokenAccount(
  678. provider,
  679. memberAccount.balances.vaultStake
  680. );
  681. const spt = await serumCmn.getTokenAccount(
  682. provider,
  683. memberAccount.balances.spt
  684. );
  685. assert.ok(vaultPw.amount.eq(new anchor.BN(20)));
  686. assert.ok(vaultStake.amount.eq(new anchor.BN(0)));
  687. assert.ok(spt.amount.eq(new anchor.BN(0)));
  688. });
  689. const tryEndUnstake = async () => {
  690. await registry.rpc.endUnstake({
  691. accounts: {
  692. registrar: registrar.publicKey,
  693. member: member.publicKey,
  694. beneficiary: provider.wallet.publicKey,
  695. pendingWithdrawal: pendingWithdrawal.publicKey,
  696. vault: balances.vault,
  697. vaultPw: balances.vaultPw,
  698. memberSigner,
  699. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  700. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  701. },
  702. });
  703. };
  704. it("Fails to end unstaking before timelock", async () => {
  705. await assert.rejects(
  706. async () => {
  707. await tryEndUnstake();
  708. },
  709. (err) => {
  710. assert.equal(err.code, 109);
  711. assert.equal(err.msg, "The unstake timelock has not yet expired.");
  712. return true;
  713. }
  714. );
  715. });
  716. it("Waits for the unstake period to end", async () => {
  717. await serumCmn.sleep(5000);
  718. });
  719. it("Unstake finalizes (unlocked)", async () => {
  720. await tryEndUnstake();
  721. const vault = await serumCmn.getTokenAccount(
  722. provider,
  723. memberAccount.balances.vault
  724. );
  725. const vaultPw = await serumCmn.getTokenAccount(
  726. provider,
  727. memberAccount.balances.vaultPw
  728. );
  729. assert.ok(vault.amount.eq(new anchor.BN(120)));
  730. assert.ok(vaultPw.amount.eq(new anchor.BN(0)));
  731. });
  732. it("Withdraws deposits (unlocked)", async () => {
  733. const token = await serumCmn.createTokenAccount(
  734. provider,
  735. mint,
  736. provider.wallet.publicKey
  737. );
  738. const withdrawAmount = new anchor.BN(100);
  739. await registry.rpc.withdraw(withdrawAmount, {
  740. accounts: {
  741. registrar: registrar.publicKey,
  742. member: member.publicKey,
  743. beneficiary: provider.wallet.publicKey,
  744. vault: memberAccount.balances.vault,
  745. memberSigner,
  746. depositor: token,
  747. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  748. },
  749. });
  750. const tokenAccount = await serumCmn.getTokenAccount(provider, token);
  751. assert.ok(tokenAccount.amount.eq(withdrawAmount));
  752. });
  753. });