lockup.js 27 KB

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