Skip to content

Commit

Permalink
Merge pull request #336 from energywebfoundation/DS-82-add-start-end-…
Browse files Browse the repository at this point in the history
…of-pool

feat(staking-pool): add partial withdraw
  • Loading branch information
hejkerooo committed Nov 30, 2021
2 parents ff91e59 + df7e98c commit 7fea051
Show file tree
Hide file tree
Showing 5 changed files with 303 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ Abstraction over staking pool smart contract

- [checkReward](modules_staking_staking_pool_service.StakingPoolService.md#checkreward)
- [getContributionLimit](modules_staking_staking_pool_service.StakingPoolService.md#getcontributionlimit)
- [getEnd](modules_staking_staking_pool_service.StakingPoolService.md#getend)
- [getHardCap](modules_staking_staking_pool_service.StakingPoolService.md#gethardcap)
- [getStake](modules_staking_staking_pool_service.StakingPoolService.md#getstake)
- [getStart](modules_staking_staking_pool_service.StakingPoolService.md#getstart)
- [partialWithdraw](modules_staking_staking_pool_service.StakingPoolService.md#partialwithdraw)
- [putStake](modules_staking_staking_pool_service.StakingPoolService.md#putstake)
- [withdraw](modules_staking_staking_pool_service.StakingPoolService.md#withdraw)

Expand Down Expand Up @@ -56,6 +59,16 @@ ___

___

### getEnd

**getEnd**(): `Promise`<`BigNumber`\>

#### Returns

`Promise`<`BigNumber`\>

___

### getHardCap

**getHardCap**(): `Promise`<`BigNumber`\>
Expand All @@ -78,6 +91,34 @@ Stake

___

### getStart

**getStart**(): `Promise`<`BigNumber`\>

#### Returns

`Promise`<`BigNumber`\>

___

### partialWithdraw

**partialWithdraw**(`value`): `Promise`<`ContractReceipt`\>

**`description`**

#### Parameters

| Name | Type |
| :------ | :------ |
| `value` | `number` \| `BigNumber` |

#### Returns

`Promise`<`ContractReceipt`\>

___

### putStake

**putStake**(`stake`): `Promise`<`void`\>
Expand Down
236 changes: 236 additions & 0 deletions e2e/staking-pool.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import { KeyTags } from "@ew-did-registry/did-resolver-interface";
import { BigNumber, providers, utils, Wallet } from "ethers";
import {
chainConfigs,
initWithPrivateKeySigner,
MessagingService,
ProviderType,
setChainConfig,
SignerService,
StakeStatus,
StakingFactoryService,
StakingPoolService,
} from "../src";
import { deployer, replenish, rpcUrl, setupENS } from "./utils/setup_contracts";
import { StakingPool__factory } from "../ethers-staking";

const provider = new providers.JsonRpcProvider(rpcUrl);
const rootOwner = Wallet.createRandom().connect(provider);
const orgOwner = Wallet.createRandom().connect(provider);

const patron = Wallet.createRandom().connect(provider);

MessagingService.create = (signerService: SignerService) => Promise.resolve(new MessagingService(signerService));
const mockPublish = jest.fn();
jest.mock("../src/modules/messaging/messaging.service", () => {
return {
MessagingService: jest.fn().mockImplementation(() => {
return { publish: mockPublish };
}),
};
});

const mockGetRoleDefinition = jest.fn();
const mockGetDidDocument = jest.fn().mockImplementation(({ did }: { did: string }) => {
return { publicKey: [{ id: `did:ethr:${did}-${KeyTags.OWNER}` }] };
});
const mockGetApplicationsByOrgNamespace = jest.fn();

jest.mock("../src/modules/cacheClient/cacheClient.service", () => {
return {
CacheClient: jest.fn().mockImplementation(() => {
return {
getRoleDefinition: mockGetRoleDefinition,
getDidDocument: mockGetDidDocument,
getApplicationsByOrganization: mockGetApplicationsByOrgNamespace,
init: jest.fn(),
login: jest.fn(),
};
}),
};
});

const duration = 3600 * 24 * 7;

const oneEWT = utils.parseUnits("1", "ether");
const hardCap = oneEWT.mul(20);
const contributionLimit = oneEWT.mul(10);
const zeroEwt = utils.parseUnits("0", "ether");

const ratio = 0.0002225;
const ratioInt = utils.parseUnits(ratio.toString(), 18);

const rewards = oneEWT.mul(11);

let poolStart;
let poolEnd;

export const setupStakingPoolFactory = async () => {
const { chainId } = await deployer.provider.getNetwork();
const { claimManagerAddress } = chainConfigs()[chainId];

const { timestamp } = await deployer.provider.getBlock("latest");

const start = timestamp + 10;
const end = start + duration;

const stakingPoolFactory = await (await new StakingPool__factory(deployer).deploy()).deployed();

poolStart = start;
poolEnd = end;

await stakingPoolFactory.init(claimManagerAddress, start, end, ratioInt, hardCap, contributionLimit, {
value: rewards,
});

setChainConfig(chainId, { stakingPoolFactoryAddress: stakingPoolFactory.address });
};

const putStake = async (pool: StakingPoolService, amount: number | BigNumber, withAmountCheck = true) => {
await pool.putStake(amount);

const currentStake = await pool.getStake();

if (withAmountCheck) {
expect(currentStake.amount).toStrictEqual(amount);
}

expect(currentStake.status).toBe(StakeStatus.STAKING);

const reward = await pool.checkReward();
expect(reward).toStrictEqual(zeroEwt);
};

const timeTravel = async (provider: providers.JsonRpcProvider, seconds: number) => {
await provider.send("evm_increaseTime", [seconds]);
await provider.send("evm_mine", []);
};

describe("StakingPool tests", () => {
// let domainsService: DomainsService;
let signerService: SignerService;
let stakingPoolService: StakingFactoryService;

beforeAll(async () => {
await replenish(orgOwner.address);
await replenish(patron.address);
await replenish(rootOwner.address);
await setupENS(rootOwner.address);
await setupStakingPoolFactory();
let connectToCacheServer;
({ connectToCacheServer, signerService } = await initWithPrivateKeySigner(rootOwner.privateKey, rpcUrl));

const { stakingPoolService: stakingPools } = await connectToCacheServer();
stakingPoolService = stakingPools;
});

beforeEach(async () => {
jest.clearAllMocks();
await setupStakingPoolFactory();
await signerService.connect(patron, ProviderType.PrivateKey);

await replenish(patron.address);
await timeTravel(provider, 20);
});

it("can stake", async () => {
const pool = await stakingPoolService.getPool();

await putStake(pool, oneEWT);
});

it("should reward", async () => {
const pool = await stakingPoolService.getPool();
await putStake(pool, oneEWT);

const periods = 24 * 90; // 3 months
const hour = 3600;

await timeTravel(provider, hour * periods);

const rewardAfterTimeTravel = await pool.checkReward();

expect(rewardAfterTimeTravel.gt(zeroEwt)).toBe(true);
});

it("should return all staked", async () => {
const pool = await stakingPoolService.getPool();

await putStake(pool, oneEWT);
await putStake(pool, oneEWT, false);

const currentStake = await pool.getStake();
expect(currentStake.amount).toStrictEqual(oneEWT.mul(2));

await pool.withdraw();
const stakeAfterWithdraw = await pool.getStake();

expect(stakeAfterWithdraw.amount).toStrictEqual(zeroEwt);

const { status } = await pool.getStake();
expect(status).toBe(StakeStatus.NONSTAKING);
});

it("should withdraw without reward", async () => {
const pool = await stakingPoolService.getPool();

await putStake(pool, oneEWT);

await pool.withdraw();

const currentStake = await pool.getStake();
expect(currentStake.amount).toStrictEqual(zeroEwt);
});

it("should return NONSTAKING status as staking pool is empty", async () => {
const pool = await stakingPoolService.getPool();
const stake = await pool.getStake();

expect(stake.status).toBe(StakeStatus.NONSTAKING);
});

it("should return hardcap", async () => {
const pool = await stakingPoolService.getPool();

const poolHardCap = await pool.getHardCap();

expect(poolHardCap.eq(hardCap)).toEqual(true);
});

it("should return contribution limit", async () => {
const pool = await stakingPoolService.getPool();

const poolContributionLimit = await pool.getContributionLimit();

expect(poolContributionLimit.eq(contributionLimit)).toEqual(true);
});

it("should be able to unstake", async () => {
const pool = await stakingPoolService.getPool();

await putStake(pool, oneEWT.mul(2));

await pool.partialWithdraw(oneEWT);
await provider.send("evm_mine", []);

const currentStake = await pool.getStake();
expect(currentStake.amount).toStrictEqual(oneEWT);
expect(currentStake.status).toBe(StakeStatus.STAKING);
});

it("should return start of the pool", async () => {
const pool = await stakingPoolService.getPool();

const start = await pool.getStart();

expect(start.toNumber()).toBe(poolStart);
});

it("should return end of the pool", async () => {
const pool = await stakingPoolService.getPool();

const end = await pool.getEnd();

expect(end.toNumber()).toBe(poolEnd);
});
});
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"@babel/runtime": "^7.12.5",
"@energyweb/ekc": "^0.6.5",
"@energyweb/iam-contracts": "^3.5.0",
"@energyweb/staking-pool": "^1.0.0-rc.2",
"@energyweb/staking-pool": "^1.0.0-rc.5",
"@ensdomains/ens": "^0.4.5",
"@ew-did-registry/claims": "^0.6.3-alpha.262.0",
"@ew-did-registry/did": "^0.6.3-alpha.262.0",
Expand Down
22 changes: 21 additions & 1 deletion src/modules/staking/staking-pool.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BigNumber, providers, utils } from "ethers";
import { BigNumber, ContractReceipt, providers, utils } from "ethers";
import { StakingPool as StakingPoolContract } from "../../../ethers-staking";
import { StakingPool__factory } from "../../../ethers-staking/factories/StakingPool__factory";
import { ERROR_MESSAGES } from "../../errors/ErrorMessages";
Expand Down Expand Up @@ -61,6 +61,14 @@ export class StakingPoolService {
).attach(address);
}

async getStart(): Promise<BigNumber> {
return this.pool.connect(this.signerService.signer).start();
}

async getEnd(): Promise<BigNumber> {
return this.pool.connect(this.signerService.signer).end();
}

async getHardCap(): Promise<BigNumber> {
return this.pool.connect(this.signerService.signer).hardCap();
}
Expand All @@ -69,6 +77,18 @@ export class StakingPoolService {
return this.pool.connect(this.signerService.signer).contributionLimit();
}

/**
* @description
* @param value
*/
async partialWithdraw(value: BigNumber | number): Promise<ContractReceipt> {
value = BigNumber.from(value);

const transaction = await this.pool.connect(this.signerService.signer).unstake(value);

return transaction.wait();
}

/**
* @description Locks stake and starts accumulating reward
* @emits StakingPool.StakePut
Expand Down

0 comments on commit 7fea051

Please sign in to comment.