laconicd/tests-solidity/suites/proxy/test/depositable_delegate_proxy.js
Brett Sun c9639c3860
tests: add solidity test suites (#487)
* tests: add solidity test suite

* tests: remove require strings

* Update tests-solidity/init-test-node.sh

* Update tests-solidity/init-test-node.sh

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
2020-09-01 17:16:28 -04:00

156 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { bn } = require('@aragon/contract-helpers-test')
const { assertAmountOfEvents, assertEvent, assertRevert, assertOutOfGas, assertBn } = require('@aragon/contract-helpers-test/src/asserts')
// Mocks
const DepositableDelegateProxyMock = artifacts.require('DepositableDelegateProxyMock')
const EthSender = artifacts.require('EthSender')
const ProxyTargetWithoutFallback = artifacts.require('ProxyTargetWithoutFallback')
const ProxyTargetWithFallback = artifacts.require('ProxyTargetWithFallback')
const TX_BASE_GAS = 21000
const SEND_ETH_GAS = TX_BASE_GAS + 9999 // <10k gas is the threshold for depositing
const PROXY_FORWARD_GAS = TX_BASE_GAS + 2e6 // high gas amount to ensure that the proxy forwards the call
const FALLBACK_SETUP_GAS = 100 // rough estimation of how much gas it spends before executing the fallback code
const SOLIDITY_TRANSFER_GAS = 2300
contract('DepositableDelegateProxy', ([ sender ]) => {
let ethSender, proxy, target, proxyTargetWithoutFallbackBase, proxyTargetWithFallbackBase
// Initial setup
before(async () => {
ethSender = await EthSender.new()
proxyTargetWithoutFallbackBase = await ProxyTargetWithoutFallback.new()
proxyTargetWithFallbackBase = await ProxyTargetWithFallback.new()
})
beforeEach(async () => {
proxy = await DepositableDelegateProxyMock.new()
target = await ProxyTargetWithFallback.at(proxy.address)
})
const itForwardsToImplementationIfGasIsOverThreshold = () => {
context('when implementation address is set', () => {
const itSuccessfullyForwardsCall = () => {
it('forwards call with data', async () => {
const receipt = await target.ping({ gas: PROXY_FORWARD_GAS })
assertAmountOfEvents(receipt, 'Pong')
})
}
context('when implementation has a fallback', () => {
beforeEach(async () => {
await proxy.setImplementationOnMock(proxyTargetWithFallbackBase.address)
})
itSuccessfullyForwardsCall()
it('can receive ETH [@skip-on-coverage]', async () => {
const receipt = await target.sendTransaction({ value: 1, gas: SEND_ETH_GAS + FALLBACK_SETUP_GAS })
assertAmountOfEvents(receipt, 'ReceivedEth')
})
})
context('when implementation doesn\'t have a fallback', () => {
beforeEach(async () => {
await proxy.setImplementationOnMock(proxyTargetWithoutFallbackBase.address)
})
itSuccessfullyForwardsCall()
it('reverts when sending ETH', async () => {
await assertRevert(target.sendTransaction({ value: 1, gas: PROXY_FORWARD_GAS }))
})
})
})
context('when implementation address is not set', () => {
it('reverts when a function is called', async () => {
await assertRevert(target.ping({ gas: PROXY_FORWARD_GAS }))
})
it('reverts when sending ETH', async () => {
await assertRevert(target.sendTransaction({ value: 1, gas: PROXY_FORWARD_GAS }))
})
})
}
const itRevertsOnInvalidDeposits = () => {
it('reverts when call has data', async () => {
await proxy.setImplementationOnMock(proxyTargetWithoutFallbackBase.address)
await assertRevert(target.ping({ gas: SEND_ETH_GAS }))
})
it('reverts when call sends 0 value', async () => {
await assertRevert(proxy.sendTransaction({ value: 0, gas: SEND_ETH_GAS }))
})
}
context('when proxy is set as depositable', () => {
beforeEach(async () => {
await proxy.enableDepositsOnMock()
})
context('when call gas is below the forwarding threshold', () => {
const value = bn(100)
const assertSendEthToProxy = async ({ value, gas, shouldOOG }) => {
const initialBalance = bn(await web3.eth.getBalance(proxy.address))
const sendEthAction = () => proxy.sendTransaction({ from: sender, gas, value })
if (shouldOOG) {
await assertOutOfGas(sendEthAction())
assertBn(bn(await web3.eth.getBalance(proxy.address)), initialBalance, 'Target balance should be the same as before')
} else {
const receipt = await sendEthAction()
assertBn(bn(await web3.eth.getBalance(proxy.address)), initialBalance.add(value), 'Target balance should be correct')
assertAmountOfEvents(receipt, 'ProxyDeposit', { decodeForAbi: DepositableDelegateProxyMock.abi })
assertEvent(receipt, 'ProxyDeposit', { decodeForAbi: DepositableDelegateProxyMock.abi, expectedArgs: { sender, value } })
return receipt
}
}
it('can receive ETH', async () => {
await assertSendEthToProxy({ value, gas: SEND_ETH_GAS })
})
it('cannot receive ETH if sent with a small amount of gas [@skip-on-coverage]', async () => {
const oogDecrease = 250
// deposit cannot be done with this amount of gas
const gas = TX_BASE_GAS + SOLIDITY_TRANSFER_GAS - oogDecrease
await assertSendEthToProxy({ shouldOOG: true, value, gas })
})
it('can receive ETH from contract [@skip-on-coverage]', async () => {
const receipt = await ethSender.sendEth(proxy.address, { value })
assertAmountOfEvents(receipt, 'ProxyDeposit', { decodeForAbi: proxy.abi })
assertEvent(receipt, 'ProxyDeposit', { decodeForAbi: proxy.abi, expectedArgs: { sender: ethSender.address, value } })
})
itRevertsOnInvalidDeposits()
})
context('when call gas is over forwarding threshold', () => {
itForwardsToImplementationIfGasIsOverThreshold()
})
})
context('when proxy is not set as depositable', () => {
context('when call gas is below the forwarding threshold', () => {
it('reverts when depositing ETH', async () => {
await assertRevert(proxy.sendTransaction({ value: 1, gas: SEND_ETH_GAS }))
})
itRevertsOnInvalidDeposits()
})
context('when call gas is over forwarding threshold', () => {
itForwardsToImplementationIfGasIsOverThreshold()
})
})
})