lockup.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  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. const provider = anchor.Provider.local();
  8. // Configure the client to use the local cluster.
  9. anchor.setProvider(provider);
  10. const lockup = anchor.workspace.Lockup;
  11. const registry = anchor.workspace.Registry;
  12. let lockupAddress = null;
  13. let mint = null;
  14. let god = null;
  15. it("Sets up initial test state", async () => {
  16. const [_mint, _god] = await serumCmn.createMintAndVault(
  17. provider,
  18. new anchor.BN(1000000)
  19. );
  20. mint = _mint;
  21. god = _god;
  22. });
  23. it("Is initialized!", async () => {
  24. await lockup.state.rpc.new(provider.wallet.publicKey);
  25. lockupAddress = await lockup.state.address();
  26. const lockupAccount = await lockup.state();
  27. assert.ok(lockupAccount.authority.equals(provider.wallet.publicKey));
  28. assert.ok(lockupAccount.whitelist.length === 5);
  29. lockupAccount.whitelist.forEach((e) => {
  30. assert.ok(e.programId.equals(new anchor.web3.PublicKey()));
  31. });
  32. });
  33. it("Deletes the default whitelisted addresses", async () => {
  34. const defaultEntry = { programId: new anchor.web3.PublicKey() };
  35. await lockup.rpc.whitelistDelete(defaultEntry, {
  36. accounts: {
  37. authority: provider.wallet.publicKey,
  38. lockup: lockupAddress,
  39. },
  40. });
  41. });
  42. it("Sets a new authority", async () => {
  43. const newAuthority = new anchor.web3.Account();
  44. await lockup.rpc.setAuthority(newAuthority.publicKey, {
  45. accounts: {
  46. authority: provider.wallet.publicKey,
  47. lockup: lockupAddress,
  48. },
  49. });
  50. let lockupAccount = await lockup.state();
  51. assert.ok(lockupAccount.authority.equals(newAuthority.publicKey));
  52. await lockup.rpc.setAuthority(provider.wallet.publicKey, {
  53. accounts: {
  54. authority: newAuthority.publicKey,
  55. lockup: lockupAddress,
  56. },
  57. signers: [newAuthority],
  58. });
  59. lockupAccount = await lockup.state();
  60. assert.ok(lockupAccount.authority.equals(provider.wallet.publicKey));
  61. });
  62. let e0 = null;
  63. let e1 = null;
  64. let e2 = null;
  65. let e3 = null;
  66. let e4 = null;
  67. it("Adds to the whitelist", async () => {
  68. const generateEntry = async () => {
  69. let programId = new anchor.web3.Account().publicKey;
  70. return {
  71. programId,
  72. };
  73. };
  74. e0 = await generateEntry();
  75. e1 = await generateEntry();
  76. e2 = await generateEntry();
  77. e3 = await generateEntry();
  78. e4 = await generateEntry();
  79. const e5 = await generateEntry();
  80. const accounts = {
  81. authority: provider.wallet.publicKey,
  82. lockup: lockupAddress,
  83. };
  84. await lockup.rpc.whitelistAdd(e0, { accounts });
  85. let lockupAccount = await lockup.state();
  86. assert.ok(lockupAccount.whitelist.length === 1);
  87. assert.deepEqual(lockupAccount.whitelist, [e0]);
  88. await lockup.rpc.whitelistAdd(e1, { accounts });
  89. await lockup.rpc.whitelistAdd(e2, { accounts });
  90. await lockup.rpc.whitelistAdd(e3, { accounts });
  91. await lockup.rpc.whitelistAdd(e4, { accounts });
  92. lockupAccount = await lockup.state();
  93. assert.deepEqual(lockupAccount.whitelist, [e0, e1, e2, e3, e4]);
  94. await assert.rejects(
  95. async () => {
  96. await lockup.rpc.whitelistAdd(e5, { accounts });
  97. },
  98. (err) => {
  99. assert.equal(err.code, 108);
  100. assert.equal(err.msg, "Whitelist is full");
  101. return true;
  102. }
  103. );
  104. });
  105. it("Removes from the whitelist", async () => {
  106. await lockup.rpc.whitelistDelete(e0, {
  107. accounts: {
  108. authority: provider.wallet.publicKey,
  109. lockup: lockupAddress,
  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(lockup.programId);
  247. const state = await registry.state();
  248. assert.ok(state.lockupProgram.equals(lockup.programId));
  249. // Should not allow a second initializatoin.
  250. await assert.rejects(
  251. async () => {
  252. await registry.state.rpc.new(lockup.programId);
  253. },
  254. (err) => {
  255. return true;
  256. }
  257. );
  258. });
  259. it("Initializes the registrar", async () => {
  260. await registry.rpc.initialize(
  261. mint,
  262. provider.wallet.publicKey,
  263. nonce,
  264. withdrawalTimelock,
  265. stakeRate,
  266. rewardQLen,
  267. {
  268. accounts: {
  269. registrar: registrar.publicKey,
  270. poolMint,
  271. rewardEventQ: rewardQ.publicKey,
  272. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  273. },
  274. signers: [registrar, rewardQ],
  275. instructions: [
  276. await registry.account.registrar.createInstruction(registrar),
  277. await registry.account.rewardQueue.createInstruction(rewardQ, 8250),
  278. ],
  279. }
  280. );
  281. registrarAccount = await registry.account.registrar(registrar.publicKey);
  282. assert.ok(registrarAccount.authority.equals(provider.wallet.publicKey));
  283. assert.equal(registrarAccount.nonce, nonce);
  284. assert.ok(registrarAccount.mint.equals(mint));
  285. assert.ok(registrarAccount.poolMint.equals(poolMint));
  286. assert.ok(registrarAccount.stakeRate.eq(stakeRate));
  287. assert.ok(registrarAccount.rewardEventQ.equals(rewardQ.publicKey));
  288. assert.ok(registrarAccount.withdrawalTimelock.eq(withdrawalTimelock));
  289. });
  290. const member = new anchor.web3.Account();
  291. let memberAccount = null;
  292. let memberSigner = null;
  293. let balances = null;
  294. let balancesLocked = null;
  295. it("Creates a member", async () => {
  296. const [
  297. _memberSigner,
  298. nonce,
  299. ] = await anchor.web3.PublicKey.findProgramAddress(
  300. [registrar.publicKey.toBuffer(), member.publicKey.toBuffer()],
  301. registry.programId
  302. );
  303. memberSigner = _memberSigner;
  304. const [mainTx, _balances] = await utils.createBalanceSandbox(
  305. provider,
  306. registrarAccount,
  307. memberSigner,
  308. provider.wallet.publicKey // Beneficiary,
  309. );
  310. const [lockedTx, _balancesLocked] = await utils.createBalanceSandbox(
  311. provider,
  312. registrarAccount,
  313. memberSigner,
  314. vesting.publicKey // Lockup.
  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. // Whitelist relay.
  354. vesting: anchor.web3.SYSVAR_RENT_PUBKEY,
  355. depositor: god,
  356. depositorAuthority: provider.wallet.publicKey,
  357. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  358. vault: memberAccount.balances.vault,
  359. memberSigner,
  360. // Program specific.
  361. registrar: registrar.publicKey,
  362. beneficiary: provider.wallet.publicKey,
  363. member: member.publicKey,
  364. },
  365. });
  366. const memberVault = await serumCmn.getTokenAccount(
  367. provider,
  368. memberAccount.balances.vault
  369. );
  370. assert.ok(memberVault.amount.eq(depositAmount));
  371. });
  372. it("Stakes to a member (unlocked)", async () => {
  373. const stakeAmount = new anchor.BN(10);
  374. await registry.rpc.stake(stakeAmount, provider.wallet.publicKey, {
  375. accounts: {
  376. // Stake instance.
  377. registrar: registrar.publicKey,
  378. rewardEventQ: rewardQ.publicKey,
  379. poolMint,
  380. // Member.
  381. member: member.publicKey,
  382. beneficiary: provider.wallet.publicKey,
  383. balances,
  384. balancesLocked,
  385. // Program signers.
  386. memberSigner,
  387. registrarSigner,
  388. // Misc.
  389. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  390. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  391. },
  392. });
  393. const vault = await serumCmn.getTokenAccount(
  394. provider,
  395. memberAccount.balances.vault
  396. );
  397. const vaultStake = await serumCmn.getTokenAccount(
  398. provider,
  399. memberAccount.balances.vaultStake
  400. );
  401. const spt = await serumCmn.getTokenAccount(
  402. provider,
  403. memberAccount.balances.spt
  404. );
  405. assert.ok(vault.amount.eq(new anchor.BN(100)));
  406. assert.ok(vaultStake.amount.eq(new anchor.BN(20)));
  407. assert.ok(spt.amount.eq(new anchor.BN(10)));
  408. });
  409. const unlockedVendor = new anchor.web3.Account();
  410. const unlockedVendorVault = new anchor.web3.Account();
  411. let unlockedVendorSigner = null;
  412. it("Drops an unlocked reward", async () => {
  413. const rewardKind = {
  414. unlocked: {},
  415. };
  416. const rewardAmount = new anchor.BN(200);
  417. const expiry = new anchor.BN(Date.now() / 1000 + 5);
  418. const [
  419. _vendorSigner,
  420. nonce,
  421. ] = await anchor.web3.PublicKey.findProgramAddress(
  422. [registrar.publicKey.toBuffer(), unlockedVendor.publicKey.toBuffer()],
  423. registry.programId
  424. );
  425. unlockedVendorSigner = _vendorSigner;
  426. await registry.rpc.dropReward(
  427. rewardKind,
  428. rewardAmount,
  429. expiry,
  430. provider.wallet.publicKey,
  431. nonce,
  432. {
  433. accounts: {
  434. registrar: registrar.publicKey,
  435. rewardEventQ: rewardQ.publicKey,
  436. poolMint,
  437. vendor: unlockedVendor.publicKey,
  438. vendorVault: unlockedVendorVault.publicKey,
  439. depositor: god,
  440. depositorAuthority: provider.wallet.publicKey,
  441. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  442. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  443. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  444. },
  445. signers: [unlockedVendorVault, unlockedVendor],
  446. instructions: [
  447. ...(await serumCmn.createTokenAccountInstrs(
  448. provider,
  449. unlockedVendorVault.publicKey,
  450. mint,
  451. unlockedVendorSigner
  452. )),
  453. await registry.account.rewardVendor.createInstruction(unlockedVendor),
  454. ],
  455. }
  456. );
  457. const vendorAccount = await registry.account.rewardVendor(
  458. unlockedVendor.publicKey
  459. );
  460. assert.ok(vendorAccount.registrar.equals(registrar.publicKey));
  461. assert.ok(vendorAccount.vault.equals(unlockedVendorVault.publicKey));
  462. assert.ok(vendorAccount.nonce === nonce);
  463. assert.ok(vendorAccount.poolTokenSupply.eq(new anchor.BN(10)));
  464. assert.ok(vendorAccount.expiryTs.eq(expiry));
  465. assert.ok(vendorAccount.expiryReceiver.equals(provider.wallet.publicKey));
  466. assert.ok(vendorAccount.total.eq(rewardAmount));
  467. assert.ok(vendorAccount.expired === false);
  468. assert.ok(vendorAccount.rewardEventQCursor === 0);
  469. assert.deepEqual(vendorAccount.kind, rewardKind);
  470. const rewardQAccount = await registry.account.rewardQueue(
  471. rewardQ.publicKey
  472. );
  473. assert.ok(rewardQAccount.head === 1);
  474. assert.ok(rewardQAccount.tail === 0);
  475. const e = rewardQAccount.events[0];
  476. assert.ok(e.vendor.equals(unlockedVendor.publicKey));
  477. assert.equal(e.locked, false);
  478. });
  479. it("Collects an unlocked reward", async () => {
  480. const token = await serumCmn.createTokenAccount(
  481. provider,
  482. mint,
  483. provider.wallet.publicKey
  484. );
  485. await registry.rpc.claimRewardUnlocked({
  486. accounts: {
  487. token,
  488. cmn: {
  489. registrar: registrar.publicKey,
  490. member: member.publicKey,
  491. beneficiary: provider.wallet.publicKey,
  492. balances,
  493. balancesLocked,
  494. vendor: unlockedVendor.publicKey,
  495. vault: unlockedVendorVault.publicKey,
  496. vendorSigner: unlockedVendorSigner,
  497. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  498. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  499. },
  500. },
  501. });
  502. let tokenAccount = await serumCmn.getTokenAccount(provider, token);
  503. assert.ok(tokenAccount.amount.eq(new anchor.BN(200)));
  504. const memberAccount = await registry.account.member(member.publicKey);
  505. assert.ok(memberAccount.rewardsCursor == 1);
  506. });
  507. const lockedVendor = new anchor.web3.Account();
  508. const lockedVendorVault = new anchor.web3.Account();
  509. let lockedVendorSigner = null;
  510. let lockedRewardAmount = null;
  511. let lockedRewardKind = null;
  512. it("Drops a locked reward", async () => {
  513. lockedRewardKind = {
  514. locked: {
  515. endTs: new anchor.BN(Date.now() / 1000 + 70),
  516. periodCount: new anchor.BN(10),
  517. },
  518. };
  519. lockedRewardAmount = new anchor.BN(200);
  520. const expiry = new anchor.BN(Date.now() / 1000 + 5);
  521. const [
  522. _vendorSigner,
  523. nonce,
  524. ] = await anchor.web3.PublicKey.findProgramAddress(
  525. [registrar.publicKey.toBuffer(), lockedVendor.publicKey.toBuffer()],
  526. registry.programId
  527. );
  528. lockedVendorSigner = _vendorSigner;
  529. await registry.rpc.dropReward(
  530. lockedRewardKind,
  531. lockedRewardAmount,
  532. expiry,
  533. provider.wallet.publicKey,
  534. nonce,
  535. {
  536. accounts: {
  537. registrar: registrar.publicKey,
  538. rewardEventQ: rewardQ.publicKey,
  539. poolMint,
  540. vendor: lockedVendor.publicKey,
  541. vendorVault: lockedVendorVault.publicKey,
  542. depositor: god,
  543. depositorAuthority: provider.wallet.publicKey,
  544. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  545. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  546. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  547. },
  548. signers: [lockedVendorVault, lockedVendor],
  549. instructions: [
  550. ...(await serumCmn.createTokenAccountInstrs(
  551. provider,
  552. lockedVendorVault.publicKey,
  553. mint,
  554. lockedVendorSigner
  555. )),
  556. await registry.account.rewardVendor.createInstruction(lockedVendor),
  557. ],
  558. }
  559. );
  560. const vendorAccount = await registry.account.rewardVendor(
  561. lockedVendor.publicKey
  562. );
  563. assert.ok(vendorAccount.registrar.equals(registrar.publicKey));
  564. assert.ok(vendorAccount.vault.equals(lockedVendorVault.publicKey));
  565. assert.ok(vendorAccount.nonce === nonce);
  566. assert.ok(vendorAccount.poolTokenSupply.eq(new anchor.BN(10)));
  567. assert.ok(vendorAccount.expiryTs.eq(expiry));
  568. assert.ok(vendorAccount.expiryReceiver.equals(provider.wallet.publicKey));
  569. assert.ok(vendorAccount.total.eq(lockedRewardAmount));
  570. assert.ok(vendorAccount.expired === false);
  571. assert.ok(vendorAccount.rewardEventQCursor === 1);
  572. assert.equal(
  573. JSON.stringify(vendorAccount.kind),
  574. JSON.stringify(lockedRewardKind)
  575. );
  576. const rewardQAccount = await registry.account.rewardQueue(
  577. rewardQ.publicKey
  578. );
  579. assert.ok(rewardQAccount.head === 2);
  580. assert.ok(rewardQAccount.tail === 0);
  581. const e = rewardQAccount.events[1];
  582. assert.ok(e.vendor.equals(lockedVendor.publicKey));
  583. assert.ok(e.locked === true);
  584. });
  585. it("Collects a locked reward", async () => {
  586. const vendoredVesting = new anchor.web3.Account();
  587. const vendoredVestingVault = new anchor.web3.Account();
  588. let [
  589. vendoredVestingSigner,
  590. nonce,
  591. ] = await anchor.web3.PublicKey.findProgramAddress(
  592. [vendoredVesting.publicKey.toBuffer()],
  593. lockup.programId
  594. );
  595. const remainingAccounts = lockup.instruction.createVesting
  596. .accounts({
  597. vesting: vendoredVesting.publicKey,
  598. vault: vendoredVestingVault.publicKey,
  599. depositor: lockedVendorVault.publicKey,
  600. depositorAuthority: lockedVendorSigner,
  601. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  602. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  603. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  604. })
  605. // Change the signer status on the vendor signer since it's signed by the program, not the
  606. // client.
  607. .map((meta) =>
  608. meta.pubkey === lockedVendorSigner ? { ...meta, isSigner: false } : meta
  609. );
  610. await registry.rpc.claimRewardLocked(nonce, {
  611. accounts: {
  612. lockupProgram: lockup.programId,
  613. cmn: {
  614. registrar: registrar.publicKey,
  615. member: member.publicKey,
  616. beneficiary: provider.wallet.publicKey,
  617. balances,
  618. balancesLocked,
  619. vendor: lockedVendor.publicKey,
  620. vault: lockedVendorVault.publicKey,
  621. vendorSigner: lockedVendorSigner,
  622. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  623. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  624. },
  625. },
  626. remainingAccounts,
  627. signers: [vendoredVesting, vendoredVestingVault],
  628. instructions: [
  629. await lockup.account.vesting.createInstruction(vendoredVesting),
  630. ...(await serumCmn.createTokenAccountInstrs(
  631. provider,
  632. vendoredVestingVault.publicKey,
  633. mint,
  634. vendoredVestingSigner
  635. )),
  636. ],
  637. });
  638. const lockupAccount = await lockup.account.vesting(
  639. vendoredVesting.publicKey
  640. );
  641. assert.ok(lockupAccount.beneficiary.equals(provider.wallet.publicKey));
  642. assert.ok(lockupAccount.mint.equals(mint));
  643. assert.ok(lockupAccount.vault.equals(vendoredVestingVault.publicKey));
  644. assert.ok(lockupAccount.outstanding.eq(lockedRewardAmount));
  645. assert.ok(lockupAccount.startBalance.eq(lockedRewardAmount));
  646. assert.ok(lockupAccount.endTs.eq(lockedRewardKind.locked.endTs));
  647. assert.ok(
  648. lockupAccount.periodCount.eq(lockedRewardKind.locked.periodCount)
  649. );
  650. assert.ok(lockupAccount.whitelistOwned.eq(new anchor.BN(0)));
  651. });
  652. const pendingWithdrawal = new anchor.web3.Account();
  653. it("Unstakes", async () => {
  654. const unstakeAmount = new anchor.BN(10);
  655. await registry.rpc.startUnstake(unstakeAmount, provider.wallet.publicKey, {
  656. accounts: {
  657. registrar: registrar.publicKey,
  658. rewardEventQ: rewardQ.publicKey,
  659. poolMint,
  660. pendingWithdrawal: pendingWithdrawal.publicKey,
  661. member: member.publicKey,
  662. beneficiary: provider.wallet.publicKey,
  663. balances,
  664. balancesLocked,
  665. memberSigner,
  666. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  667. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  668. rent: anchor.web3.SYSVAR_RENT_PUBKEY,
  669. },
  670. signers: [pendingWithdrawal],
  671. instructions: [
  672. await registry.account.pendingWithdrawal.createInstruction(
  673. pendingWithdrawal
  674. ),
  675. ],
  676. });
  677. const vaultPw = await serumCmn.getTokenAccount(
  678. provider,
  679. memberAccount.balances.vaultPw
  680. );
  681. const vaultStake = await serumCmn.getTokenAccount(
  682. provider,
  683. memberAccount.balances.vaultStake
  684. );
  685. const spt = await serumCmn.getTokenAccount(
  686. provider,
  687. memberAccount.balances.spt
  688. );
  689. assert.ok(vaultPw.amount.eq(new anchor.BN(20)));
  690. assert.ok(vaultStake.amount.eq(new anchor.BN(0)));
  691. assert.ok(spt.amount.eq(new anchor.BN(0)));
  692. });
  693. const tryEndUnstake = async () => {
  694. await registry.rpc.endUnstake({
  695. accounts: {
  696. registrar: registrar.publicKey,
  697. member: member.publicKey,
  698. beneficiary: provider.wallet.publicKey,
  699. pendingWithdrawal: pendingWithdrawal.publicKey,
  700. vault: balances.vault,
  701. vaultPw: balances.vaultPw,
  702. memberSigner,
  703. clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
  704. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  705. },
  706. });
  707. };
  708. it("Fails to end unstaking before timelock", async () => {
  709. await assert.rejects(
  710. async () => {
  711. await tryEndUnstake();
  712. },
  713. (err) => {
  714. assert.equal(err.code, 109);
  715. assert.equal(err.msg, "The unstake timelock has not yet expired.");
  716. return true;
  717. }
  718. );
  719. });
  720. it("Waits for the unstake period to end", async () => {
  721. await serumCmn.sleep(5000);
  722. });
  723. it("Unstake finalizes (unlocked)", async () => {
  724. await tryEndUnstake();
  725. const vault = await serumCmn.getTokenAccount(
  726. provider,
  727. memberAccount.balances.vault
  728. );
  729. const vaultPw = await serumCmn.getTokenAccount(
  730. provider,
  731. memberAccount.balances.vaultPw
  732. );
  733. assert.ok(vault.amount.eq(new anchor.BN(120)));
  734. assert.ok(vaultPw.amount.eq(new anchor.BN(0)));
  735. });
  736. it("Withdraws deposits (unlocked)", async () => {
  737. const token = await serumCmn.createTokenAccount(
  738. provider,
  739. mint,
  740. provider.wallet.publicKey
  741. );
  742. const withdrawAmount = new anchor.BN(100);
  743. await registry.rpc.withdraw(withdrawAmount, {
  744. accounts: {
  745. // Whitelist relay.
  746. vesting: anchor.web3.SYSVAR_RENT_PUBKEY,
  747. depositor: token,
  748. depositorAuthority: provider.wallet.publicKey,
  749. tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
  750. vault: memberAccount.balances.vault,
  751. memberSigner,
  752. // Program specific.
  753. registrar: registrar.publicKey,
  754. beneficiary: provider.wallet.publicKey,
  755. member: member.publicKey,
  756. },
  757. });
  758. const tokenAccount = await serumCmn.getTokenAccount(provider, token);
  759. assert.ok(tokenAccount.amount.eq(withdrawAmount));
  760. });
  761. });