Address.test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. const { accounts, contract, web3, config } = require('@openzeppelin/test-environment');
  2. const { balance, ether, expectRevert, send, expectEvent } = require('@openzeppelin/test-helpers');
  3. const { expect } = require('chai');
  4. const AddressImpl = contract.fromArtifact('AddressImpl');
  5. const EtherReceiver = contract.fromArtifact('EtherReceiverMock');
  6. const CallReceiverMock = contract.fromArtifact('CallReceiverMock');
  7. const coverage = config.coverage;
  8. describe('Address', function () {
  9. const [ recipient, other ] = accounts;
  10. beforeEach(async function () {
  11. this.mock = await AddressImpl.new();
  12. });
  13. describe('isContract', function () {
  14. it('returns false for account address', async function () {
  15. expect(await this.mock.isContract(other)).to.equal(false);
  16. });
  17. it('returns true for contract address', async function () {
  18. const contract = await AddressImpl.new();
  19. expect(await this.mock.isContract(contract.address)).to.equal(true);
  20. });
  21. });
  22. describe('sendValue', function () {
  23. beforeEach(async function () {
  24. this.recipientTracker = await balance.tracker(recipient);
  25. });
  26. context('when sender contract has no funds', function () {
  27. it('sends 0 wei', async function () {
  28. await this.mock.sendValue(other, 0);
  29. expect(await this.recipientTracker.delta()).to.be.bignumber.equal('0');
  30. });
  31. it('reverts when sending non-zero amounts', async function () {
  32. await expectRevert(this.mock.sendValue(other, 1), 'Address: insufficient balance');
  33. });
  34. });
  35. context('when sender contract has funds', function () {
  36. const funds = ether('1');
  37. beforeEach(async function () {
  38. await send.ether(other, this.mock.address, funds);
  39. });
  40. it('sends 0 wei', async function () {
  41. await this.mock.sendValue(recipient, 0);
  42. expect(await this.recipientTracker.delta()).to.be.bignumber.equal('0');
  43. });
  44. it('sends non-zero amounts', async function () {
  45. await this.mock.sendValue(recipient, funds.subn(1));
  46. expect(await this.recipientTracker.delta()).to.be.bignumber.equal(funds.subn(1));
  47. });
  48. it('sends the whole balance', async function () {
  49. await this.mock.sendValue(recipient, funds);
  50. expect(await this.recipientTracker.delta()).to.be.bignumber.equal(funds);
  51. expect(await balance.current(this.mock.address)).to.be.bignumber.equal('0');
  52. });
  53. it('reverts when sending more than the balance', async function () {
  54. await expectRevert(this.mock.sendValue(recipient, funds.addn(1)), 'Address: insufficient balance');
  55. });
  56. context('with contract recipient', function () {
  57. beforeEach(async function () {
  58. this.contractRecipient = await EtherReceiver.new();
  59. });
  60. it('sends funds', async function () {
  61. const tracker = await balance.tracker(this.contractRecipient.address);
  62. await this.contractRecipient.setAcceptEther(true);
  63. await this.mock.sendValue(this.contractRecipient.address, funds);
  64. expect(await tracker.delta()).to.be.bignumber.equal(funds);
  65. });
  66. it('reverts on recipient revert', async function () {
  67. await this.contractRecipient.setAcceptEther(false);
  68. await expectRevert(
  69. this.mock.sendValue(this.contractRecipient.address, funds),
  70. 'Address: unable to send value, recipient may have reverted',
  71. );
  72. });
  73. });
  74. });
  75. });
  76. describe('functionCall', function () {
  77. beforeEach(async function () {
  78. this.contractRecipient = await CallReceiverMock.new();
  79. });
  80. context('with valid contract receiver', function () {
  81. it('calls the requested function', async function () {
  82. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  83. name: 'mockFunction',
  84. type: 'function',
  85. inputs: [],
  86. }, []);
  87. const receipt = await this.mock.functionCall(this.contractRecipient.address, abiEncodedCall);
  88. expectEvent(receipt, 'CallReturnValue', { data: '0x1234' });
  89. await expectEvent.inTransaction(receipt.tx, CallReceiverMock, 'MockFunctionCalled');
  90. });
  91. it('reverts when the called function reverts with no reason', async function () {
  92. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  93. name: 'mockFunctionRevertsNoReason',
  94. type: 'function',
  95. inputs: [],
  96. }, []);
  97. await expectRevert(
  98. this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
  99. 'Address: low-level call failed',
  100. );
  101. });
  102. it('reverts when the called function reverts, bubbling up the revert reason', async function () {
  103. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  104. name: 'mockFunctionRevertsReason',
  105. type: 'function',
  106. inputs: [],
  107. }, []);
  108. await expectRevert(
  109. this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
  110. 'CallReceiverMock: reverting',
  111. );
  112. });
  113. // Skipped in a coverage mode due to coverage mode setting a block gas limit to 0xffffffffff
  114. // which cause a mockFunctionOutOfGas function to crash Ganache and the
  115. // subsequent tests before running out of gas.
  116. it('reverts when the called function runs out of gas', async function () {
  117. this.timeout(10000);
  118. if (coverage) { return this.skip(); }
  119. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  120. name: 'mockFunctionOutOfGas',
  121. type: 'function',
  122. inputs: [],
  123. }, []);
  124. await expectRevert(
  125. this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
  126. 'Address: low-level call failed',
  127. );
  128. });
  129. it('reverts when the called function throws', async function () {
  130. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  131. name: 'mockFunctionThrows',
  132. type: 'function',
  133. inputs: [],
  134. }, []);
  135. await expectRevert(
  136. this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
  137. 'Address: low-level call failed',
  138. );
  139. });
  140. it('reverts when function does not exist', async function () {
  141. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  142. name: 'mockFunctionDoesNotExist',
  143. type: 'function',
  144. inputs: [],
  145. }, []);
  146. await expectRevert(
  147. this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
  148. 'Address: low-level call failed',
  149. );
  150. });
  151. });
  152. context('with non-contract receiver', function () {
  153. it('reverts when address is not a contract', async function () {
  154. const [ recipient ] = accounts;
  155. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  156. name: 'mockFunction',
  157. type: 'function',
  158. inputs: [],
  159. }, []);
  160. await expectRevert(this.mock.functionCall(recipient, abiEncodedCall), 'Address: call to non-contract');
  161. });
  162. });
  163. });
  164. describe('functionCallWithValue', function () {
  165. beforeEach(async function () {
  166. this.contractRecipient = await CallReceiverMock.new();
  167. });
  168. context('with zero value', function () {
  169. it('calls the requested function', async function () {
  170. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  171. name: 'mockFunction',
  172. type: 'function',
  173. inputs: [],
  174. }, []);
  175. const receipt = await this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, 0);
  176. expectEvent(receipt, 'CallReturnValue', { data: '0x1234' });
  177. await expectEvent.inTransaction(receipt.tx, CallReceiverMock, 'MockFunctionCalled');
  178. });
  179. });
  180. context('with non-zero value', function () {
  181. const amount = ether('1.2');
  182. it('reverts if insufficient sender balance', async function () {
  183. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  184. name: 'mockFunction',
  185. type: 'function',
  186. inputs: [],
  187. }, []);
  188. await expectRevert(
  189. this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount),
  190. 'Address: insufficient balance for call',
  191. );
  192. });
  193. it('calls the requested function with existing value', async function () {
  194. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  195. name: 'mockFunction',
  196. type: 'function',
  197. inputs: [],
  198. }, []);
  199. const tracker = await balance.tracker(this.contractRecipient.address);
  200. await send.ether(other, this.mock.address, amount);
  201. const receipt = await this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount);
  202. expect(await tracker.delta()).to.be.bignumber.equal(amount);
  203. expectEvent(receipt, 'CallReturnValue', { data: '0x1234' });
  204. await expectEvent.inTransaction(receipt.tx, CallReceiverMock, 'MockFunctionCalled');
  205. });
  206. it('calls the requested function with transaction funds', async function () {
  207. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  208. name: 'mockFunction',
  209. type: 'function',
  210. inputs: [],
  211. }, []);
  212. const tracker = await balance.tracker(this.contractRecipient.address);
  213. expect(await balance.current(this.mock.address)).to.be.bignumber.equal('0');
  214. const receipt = await this.mock.functionCallWithValue(
  215. this.contractRecipient.address, abiEncodedCall, amount, { from: other, value: amount },
  216. );
  217. expect(await tracker.delta()).to.be.bignumber.equal(amount);
  218. expectEvent(receipt, 'CallReturnValue', { data: '0x1234' });
  219. await expectEvent.inTransaction(receipt.tx, CallReceiverMock, 'MockFunctionCalled');
  220. });
  221. it('reverts when calling non-payable functions', async function () {
  222. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  223. name: 'mockFunctionNonPayable',
  224. type: 'function',
  225. inputs: [],
  226. }, []);
  227. await send.ether(other, this.mock.address, amount);
  228. await expectRevert(
  229. this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount),
  230. 'Address: low-level call with value failed',
  231. );
  232. });
  233. });
  234. });
  235. describe('functionStaticCall', function () {
  236. beforeEach(async function () {
  237. this.contractRecipient = await CallReceiverMock.new();
  238. });
  239. it('calls the requested function', async function () {
  240. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  241. name: 'mockStaticFunction',
  242. type: 'function',
  243. inputs: [],
  244. }, []);
  245. const receipt = await this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall);
  246. expectEvent(receipt, 'CallReturnValue', { data: '0x1234' });
  247. });
  248. it('reverts on a non-static function', async function () {
  249. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  250. name: 'mockFunction',
  251. type: 'function',
  252. inputs: [],
  253. }, []);
  254. await expectRevert(
  255. this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall),
  256. 'Address: low-level static call failed',
  257. );
  258. });
  259. it('bubbles up revert reason', async function () {
  260. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  261. name: 'mockFunctionRevertsReason',
  262. type: 'function',
  263. inputs: [],
  264. }, []);
  265. await expectRevert(
  266. this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall),
  267. 'CallReceiverMock: reverting',
  268. );
  269. });
  270. it('reverts when address is not a contract', async function () {
  271. const [ recipient ] = accounts;
  272. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  273. name: 'mockFunction',
  274. type: 'function',
  275. inputs: [],
  276. }, []);
  277. await expectRevert(
  278. this.mock.functionStaticCall(recipient, abiEncodedCall),
  279. 'Address: static call to non-contract',
  280. );
  281. });
  282. });
  283. describe('functionDelegateCall', function () {
  284. beforeEach(async function () {
  285. this.contractRecipient = await CallReceiverMock.new();
  286. });
  287. it('delegate calls the requested function', async function () {
  288. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  289. name: 'mockFunctionWritesStorage',
  290. type: 'function',
  291. inputs: [],
  292. }, []);
  293. const receipt = await this.mock.functionDelegateCall(this.contractRecipient.address, abiEncodedCall);
  294. expectEvent(receipt, 'CallReturnValue', { data: '0x1234' });
  295. expect(await this.mock.sharedAnswer()).to.equal('42');
  296. });
  297. it('bubbles up revert reason', async function () {
  298. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  299. name: 'mockFunctionRevertsReason',
  300. type: 'function',
  301. inputs: [],
  302. }, []);
  303. await expectRevert(
  304. this.mock.functionDelegateCall(this.contractRecipient.address, abiEncodedCall),
  305. 'CallReceiverMock: reverting',
  306. );
  307. });
  308. it('reverts when address is not a contract', async function () {
  309. const [ recipient ] = accounts;
  310. const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
  311. name: 'mockFunction',
  312. type: 'function',
  313. inputs: [],
  314. }, []);
  315. await expectRevert(
  316. this.mock.functionDelegateCall(recipient, abiEncodedCall),
  317. 'Address: delegate call to non-contract',
  318. );
  319. });
  320. });
  321. });