|
1 | 1 | import { Signer } from '@ethersproject/abstract-signer'; |
| 2 | +import { SignatureLike } from '@ethersproject/bytes'; |
2 | 3 | import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; |
3 | 4 | import { expect } from 'chai'; |
4 | | -import { utils, Contract } from 'ethers'; |
| 5 | +import { utils, Contract, ContractFactory } from 'ethers'; |
5 | 6 | import { deployMockContract, MockContract } from 'ethereum-waffle'; |
6 | 7 | import hre, { ethers } from 'hardhat'; |
7 | 8 |
|
| 9 | +import { delegateSignature } from '../helpers/delegateSignature'; |
8 | 10 | import { signPermit } from '../helpers/signPermit'; |
9 | 11 |
|
10 | | -const { getContractFactory, getSigners, provider } = ethers; |
| 12 | +const { constants, getContractFactory, getSigners, provider } = ethers; |
| 13 | +const { AddressZero } = constants; |
11 | 14 | const { artifacts } = hre; |
12 | 15 | const { getNetwork } = provider; |
13 | 16 | const { parseEther: toWei, splitSignature } = utils; |
14 | 17 |
|
15 | 18 | describe('EIP2612PermitAndDeposit', () => { |
16 | 19 | let wallet: SignerWithAddress; |
17 | 20 | let wallet2: SignerWithAddress; |
18 | | - let wallet3: SignerWithAddress; |
| 21 | + let prizeStrategyManager: SignerWithAddress; |
19 | 22 |
|
20 | 23 | let permitAndDeposit: Contract; |
21 | 24 | let usdc: Contract; |
22 | | - let prizePool: MockContract; |
| 25 | + let PrizePoolHarness: ContractFactory; |
| 26 | + let prizePool: Contract; |
| 27 | + let ticket: Contract; |
| 28 | + let yieldSourceStub: MockContract; |
23 | 29 |
|
24 | 30 | let chainId: number; |
25 | 31 |
|
26 | | - type EIP2612PermitAndDepositTo = { |
| 32 | + type EIP2612PermitAndDepositToAndDelegate = { |
27 | 33 | prizePool: string; |
28 | 34 | fromWallet?: SignerWithAddress; |
29 | 35 | to: string; |
30 | 36 | amount: string; |
| 37 | + delegateAddress: string; |
31 | 38 | }; |
32 | 39 |
|
33 | | - async function permitAndDepositTo({ |
| 40 | + async function permitAndDepositToAndDelegate({ |
34 | 41 | prizePool, |
35 | 42 | fromWallet, |
36 | 43 | to, |
37 | 44 | amount, |
38 | | - }: EIP2612PermitAndDepositTo) { |
| 45 | + delegateAddress, |
| 46 | + }: EIP2612PermitAndDepositToAndDelegate) { |
39 | 47 | if (!fromWallet) { |
40 | 48 | fromWallet = wallet; |
41 | 49 | } |
42 | 50 |
|
43 | | - const deadline = new Date().getTime(); |
| 51 | + const { user, delegate, deadline, v, r, s } = await delegateSignature({ |
| 52 | + ticket, |
| 53 | + userWallet: fromWallet, |
| 54 | + delegate: delegateAddress, |
| 55 | + }); |
| 56 | + |
| 57 | + const delegateSign: SignatureLike = { v, r, s }; |
44 | 58 |
|
45 | | - let permit = await signPermit( |
46 | | - wallet, |
| 59 | + const permit = await signPermit( |
| 60 | + fromWallet, |
47 | 61 | { |
48 | 62 | name: 'USD Coin', |
49 | 63 | version: '1', |
50 | 64 | chainId, |
51 | 65 | verifyingContract: usdc.address, |
52 | 66 | }, |
53 | 67 | { |
54 | | - owner: wallet.address, |
| 68 | + owner: user, |
55 | 69 | spender: permitAndDeposit.address, |
56 | 70 | value: amount, |
57 | 71 | nonce: 0, |
58 | 72 | deadline, |
59 | 73 | }, |
60 | 74 | ); |
61 | 75 |
|
62 | | - let { v, r, s } = splitSignature(permit.sig); |
63 | | - |
64 | | - return permitAndDeposit |
65 | | - .connect(fromWallet) |
66 | | - .permitAndDepositTo( |
67 | | - usdc.address, |
68 | | - wallet.address, |
69 | | - amount, |
70 | | - deadline, |
71 | | - v, |
72 | | - r, |
73 | | - s, |
74 | | - prizePool, |
75 | | - to, |
76 | | - ); |
| 76 | + const permitSignature = splitSignature(permit.sig); |
| 77 | + |
| 78 | + return permitAndDeposit.permitAndDepositToAndDelegate( |
| 79 | + user, |
| 80 | + amount, |
| 81 | + deadline, |
| 82 | + permitSignature, |
| 83 | + delegateSign, |
| 84 | + prizePool, |
| 85 | + to, |
| 86 | + delegate, |
| 87 | + ); |
77 | 88 | } |
78 | 89 |
|
79 | 90 | beforeEach(async () => { |
80 | | - [wallet, wallet2, wallet3] = await getSigners(); |
| 91 | + [wallet, wallet2, prizeStrategyManager] = await getSigners(); |
81 | 92 |
|
82 | 93 | const network = await getNetwork(); |
83 | 94 | chainId = network.chainId; |
84 | 95 |
|
85 | 96 | const Usdc = await getContractFactory('EIP2612PermitMintable'); |
86 | 97 | usdc = await Usdc.deploy('USD Coin', 'USDC'); |
87 | 98 |
|
88 | | - const IPrizePool = await artifacts.readArtifact('IPrizePool'); |
89 | | - prizePool = await deployMockContract(wallet as Signer, IPrizePool.abi); |
| 99 | + const YieldSourceStub = await artifacts.readArtifact('YieldSourceStub'); |
| 100 | + yieldSourceStub = await deployMockContract(wallet as Signer, YieldSourceStub.abi); |
| 101 | + await yieldSourceStub.mock.depositToken.returns(usdc.address); |
90 | 102 |
|
91 | | - const EIP2612PermitAndDeposit = await getContractFactory('EIP2612PermitAndDeposit'); |
| 103 | + PrizePoolHarness = await getContractFactory('PrizePoolHarness', wallet); |
| 104 | + prizePool = await PrizePoolHarness.deploy(wallet.address, yieldSourceStub.address); |
92 | 105 |
|
| 106 | + const EIP2612PermitAndDeposit = await getContractFactory('EIP2612PermitAndDeposit'); |
93 | 107 | permitAndDeposit = await EIP2612PermitAndDeposit.deploy(); |
| 108 | + |
| 109 | + const Ticket = await getContractFactory('TicketHarness'); |
| 110 | + ticket = await Ticket.deploy('PoolTogether Usdc Ticket', 'PcUSDC', 18, prizePool.address); |
| 111 | + |
| 112 | + await prizePool.setTicket(ticket.address); |
| 113 | + await prizePool.setPrizeStrategy(prizeStrategyManager.address); |
94 | 114 | }); |
95 | 115 |
|
96 | | - describe('permitAndDepositTo()', () => { |
97 | | - it('should work', async () => { |
| 116 | + describe('permitAndDepositToAndDelegate()', () => { |
| 117 | + it('should deposit and delegate to itself', async () => { |
| 118 | + const amount = toWei('100'); |
| 119 | + |
98 | 120 | await usdc.mint(wallet.address, toWei('1000')); |
99 | 121 |
|
100 | | - await prizePool.mock.depositTo.withArgs(wallet2.address, toWei('100')).returns(); |
| 122 | + await yieldSourceStub.mock.supplyTokenTo.withArgs(amount, prizePool.address).returns(); |
101 | 123 |
|
102 | | - await permitAndDepositTo({ |
| 124 | + await permitAndDepositToAndDelegate({ |
103 | 125 | prizePool: prizePool.address, |
104 | | - to: wallet2.address, |
| 126 | + to: wallet.address, |
105 | 127 | amount: '100000000000000000000', |
| 128 | + delegateAddress: wallet.address, |
106 | 129 | }); |
107 | 130 |
|
108 | | - expect(await usdc.allowance(permitAndDeposit.address, prizePool.address)).to.equal( |
109 | | - toWei('100'), |
110 | | - ); |
| 131 | + expect(await usdc.balanceOf(prizePool.address)).to.equal(amount); |
| 132 | + expect(await usdc.balanceOf(wallet.address)).to.equal(toWei('900')); |
| 133 | + expect(await ticket.balanceOf(wallet.address)).to.equal(amount); |
| 134 | + expect(await ticket.delegateOf(wallet.address)).to.equal(wallet.address); |
| 135 | + }); |
| 136 | + |
| 137 | + it('should deposit and delegate to someone else', async () => { |
| 138 | + const amount = toWei('100'); |
| 139 | + |
| 140 | + await usdc.mint(wallet.address, toWei('1000')); |
| 141 | + |
| 142 | + await yieldSourceStub.mock.supplyTokenTo.withArgs(amount, prizePool.address).returns(); |
111 | 143 |
|
112 | | - expect(await usdc.balanceOf(permitAndDeposit.address)).to.equal(toWei('100')); |
| 144 | + await permitAndDepositToAndDelegate({ |
| 145 | + prizePool: prizePool.address, |
| 146 | + to: wallet.address, |
| 147 | + amount: '100000000000000000000', |
| 148 | + delegateAddress: wallet2.address, |
| 149 | + }); |
| 150 | + |
| 151 | + expect(await usdc.balanceOf(prizePool.address)).to.equal(amount); |
113 | 152 | expect(await usdc.balanceOf(wallet.address)).to.equal(toWei('900')); |
| 153 | + expect(await ticket.balanceOf(wallet.address)).to.equal(amount); |
| 154 | + expect(await ticket.balanceOf(wallet2.address)).to.equal(toWei('0')); |
| 155 | + expect(await ticket.delegateOf(wallet.address)).to.equal(wallet2.address); |
| 156 | + expect(await ticket.delegateOf(wallet2.address)).to.equal(AddressZero); |
114 | 157 | }); |
115 | 158 |
|
116 | 159 | it('should not allow anyone else to use the signature', async () => { |
| 160 | + const amount = toWei('100'); |
| 161 | + |
117 | 162 | await usdc.mint(wallet.address, toWei('1000')); |
118 | 163 |
|
119 | | - await prizePool.mock.depositTo.withArgs(wallet2.address, toWei('100')).returns(); |
| 164 | + await yieldSourceStub.mock.supplyTokenTo.withArgs(amount, prizePool.address).returns(); |
120 | 165 |
|
121 | 166 | await expect( |
122 | | - permitAndDepositTo({ |
| 167 | + permitAndDepositToAndDelegate({ |
123 | 168 | prizePool: prizePool.address, |
124 | 169 | to: wallet2.address, |
125 | 170 | fromWallet: wallet2, |
126 | 171 | amount: '100000000000000000000', |
| 172 | + delegateAddress: wallet2.address, |
127 | 173 | }), |
128 | 174 | ).to.be.revertedWith('EIP2612PermitAndDeposit/only-signer'); |
129 | 175 | }); |
|
0 commit comments