Skip to content

Commit f07f8a5

Browse files
authored
feat: Contract to withdraw stranded tokens (#180)
Contract to withdraw stranded tokens
1 parent cf72538 commit f07f8a5

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

contracts/utils/WithdrawTokens.sol

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
Copyright 2024 Index Cooperative
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
Unless required by applicable law or agreed to in writing, software
9+
distributed under the License is distributed on an "AS IS" BASIS,
10+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the License for the specific language governing permissions and
12+
limitations under the License.
13+
14+
SPDX-License-Identifier: Apache License, Version 2.0
15+
*/
16+
17+
pragma solidity 0.6.10;
18+
19+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
20+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
21+
22+
/**
23+
* @title WithdrawTokens
24+
* Contract to deploy to addresses where tokens (or eth) were accidentally sent
25+
*/
26+
contract WithdrawTokens is Ownable {
27+
function withdraw(IERC20 token, uint256 amount) external onlyOwner {
28+
if (address(token) == address(0)) {
29+
payable(msg.sender).transfer(amount);
30+
} else {
31+
token.transfer(msg.sender, amount);
32+
}
33+
}
34+
}

test/integration/arbitrum/flashMintLeveragedExtended.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type SwapData = {
3535
};
3636

3737
if (process.env.INTEGRATIONTEST) {
38-
describe.only("FlashMintLeveragedExtended - Integration Test", async () => {
38+
describe("FlashMintLeveragedExtended - Integration Test", async () => {
3939
const addresses = PRODUCTION_ADDRESSES;
4040
let owner: Account;
4141
let deployer: DeployHelper;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { BigNumber, Signer, constants, utils } from "ethers";
2+
import { expect } from "chai";
3+
import { impersonateAccount, setBlockNumber } from "@utils/test/testingUtils";
4+
import { WithdrawTokens__factory } from "../../../typechain";
5+
6+
if (process.env.INTEGRATIONTEST) {
7+
describe.only("WithdrawTokens", function () {
8+
const deployerAddress = "0x37e6365d4f6aE378467b0e24c9065Ce5f06D70bF";
9+
let deployerSigner: Signer;
10+
11+
setBlockNumber(236525000);
12+
before(async function () {
13+
deployerSigner = await impersonateAccount(deployerAddress);
14+
});
15+
16+
it("deploys to correct address", async function () {
17+
const nonce = 1556;
18+
const currentAccountNonce = await deployerSigner.getTransactionCount();
19+
20+
// Unfortunately we will have to spam a bunch of no-op transactions to get to the desired nonce
21+
// Costs should be negligible though 21000 gas per tx. (at 0.01 gwei gas price on arbitrum -> 210 gwei)
22+
for (let i = currentAccountNonce; i < nonce; i++) {
23+
await deployerSigner.sendTransaction({
24+
to: deployerAddress,
25+
value: BigNumber.from(0),
26+
nonce: i,
27+
});
28+
}
29+
const expectedAddress = "0x940ecb16416fe52856e8653b2958bfd556aa6a7e";
30+
const factory = await new WithdrawTokens__factory(deployerSigner);
31+
const contract = await factory.deploy({ nonce });
32+
await contract.deployed();
33+
expect(contract.address.toLowerCase()).to.equal(expectedAddress);
34+
35+
const contractEthBalance = await deployerSigner.provider.getBalance(contract.address);
36+
expect(contractEthBalance).to.gt(0);
37+
console.log(`Contract eth balance: ${utils.formatEther(contractEthBalance)}`);
38+
39+
const deployerBalanceBefore = await deployerSigner.getBalance();
40+
const tx = await contract.withdraw(constants.AddressZero, contractEthBalance);
41+
const receipt = await tx.wait();
42+
const deployerBalanceAfter = await deployerSigner.getBalance();
43+
const txCost = tx.gasPrice.mul(receipt.gasUsed);
44+
expect(deployerBalanceAfter).to.eq(deployerBalanceBefore.add(contractEthBalance).sub(txCost));
45+
});
46+
});
47+
}

0 commit comments

Comments
 (0)