diff --git a/.github/workflows/create.yml b/.github/workflows/create.yml deleted file mode 100644 index e0e9369..0000000 --- a/.github/workflows/create.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: "Create" - -# The workflow will run only when the "Use this template" button is used -on: - create: - -jobs: - create: - # We only run this action when the repository isn't the template repository. References: - # - https://docs.github.com/en/actions/learn-github-actions/contexts - # - https://docs.github.com/en/actions/learn-github-actions/expressions - if: ${{ !github.event.repository.is_template }} - permissions: "write-all" - runs-on: "ubuntu-latest" - steps: - - name: "Check out the repo" - uses: "actions/checkout@v4" - - - name: "Update package.json" - env: - GITHUB_REPOSITORY_DESCRIPTION: ${{ github.event.repository.description }} - run: - ./.github/scripts/rename.sh "$GITHUB_REPOSITORY" "$GITHUB_REPOSITORY_OWNER" "$GITHUB_REPOSITORY_DESCRIPTION" - - - name: "Add rename summary" - run: | - echo "## Commit result" >> $GITHUB_STEP_SUMMARY - echo "✅ Passed" >> $GITHUB_STEP_SUMMARY - - - name: "Remove files not needed in the user's copy of the template" - run: | - rm -f "./.github/FUNDING.yml" - rm -f "./.github/scripts/rename.sh" - rm -f "./.github/workflows/create.yml" - - - name: "Add remove summary" - run: | - echo "## Remove result" >> $GITHUB_STEP_SUMMARY - echo "✅ Passed" >> $GITHUB_STEP_SUMMARY - - - name: "Update commit" - uses: "stefanzweifel/git-auto-commit-action@v4" - with: - commit_message: "feat: initial commit" - commit_options: "--amend" - push_options: "--force" - skip_fetch: true - - - name: "Add commit summary" - run: | - echo "## Commit result" >> $GITHUB_STEP_SUMMARY - echo "✅ Passed" >> $GITHUB_STEP_SUMMARY diff --git a/src/adapters/rswETH/RswETHAdapter.sol b/src/adapters/rswETH/RswETHAdapter.sol new file mode 100644 index 0000000..223a1a2 --- /dev/null +++ b/src/adapters/rswETH/RswETHAdapter.sol @@ -0,0 +1,43 @@ +pragma solidity >=0.8.25; + +import { Adapter } from "@/adapters/Adapter.sol"; +import { IswETH, IswEXIT } from "@/adapters/swETH/ISwell.sol"; +import { wrap, unwrap } from "@prb/math/UD60x18.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; + +address constant RSWETH_TOKEN = 0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0; +address constant RSWEXIT = 0x58749C46Ffe97e4d79508a2C781C440f4756f064; + +uint256 constant MIN_AMOUNT = 5_000_000_000_000_000; // 0.005 ETH +uint256 constant MAX_AMOUNT = 500 ether; + +contract RswETHAdapter is Adapter { + function previewWithdraw(uint256 amount) external view returns (uint256) { + return wrap(amount).mul(wrap(IswETH(RSWETH_TOKEN).getRate())).unwrap(); + } + + function requestWithdraw(uint256 amount) external returns (uint256 tokenId, uint256 amountExpected) { + SafeTransferLib.safeApprove(RSWETH_TOKEN, RSWEXIT, amount); + amountExpected = wrap(amount).mul(wrap(IswETH(RSWETH_TOKEN).getRate())).unwrap(); + IswEXIT(RSWEXIT).createWithdrawRequest(amount); + tokenId = IswEXIT(RSWEXIT).getLastTokenIdCreated(); + } + + function claimWithdraw(uint256 tokenId) external returns (uint256 amount) { + uint256 balBefore = address(this).balance; + IswEXIT(RSWEXIT).finalizeWithdrawal(tokenId); + amount = address(this).balance - balBefore; + } + + function isFinalized(uint256 tokenId) external view returns (bool) { + return IswEXIT(RSWEXIT).getLastTokenIdProcessed() >= tokenId; + } + + function totalStaked() external view returns (uint256) { + return wrap(IswETH(RSWETH_TOKEN).totalSupply()).mul(wrap(IswETH(RSWETH_TOKEN).getRate())).unwrap(); + } + + function minMaxAmount() external pure returns (uint256 min, uint256 max) { + return (MIN_AMOUNT, MAX_AMOUNT); + } +} diff --git a/test/adapters/RswETHAdapter.t.sol b/test/adapters/RswETHAdapter.t.sol new file mode 100644 index 0000000..088e184 --- /dev/null +++ b/test/adapters/RswETHAdapter.t.sol @@ -0,0 +1,38 @@ +pragma solidity >=0.8.25; + +import { Test, console } from "forge-std/Test.sol"; +import { VmSafe } from "forge-std/Vm.sol"; +import { IswETH, IswEXIT } from "@/adapters/swETH/ISwell.sol"; +import { RswETHAdapter, RSWETH_TOKEN } from "@/adapters/rswETH/RswETHAdapter.sol"; +import { ERC721Receiver } from "@/utils/ERC721Receiver.sol"; +import { ERC20 } from "solady/tokens/ERC20.sol"; +import { AdapterDelegateCall } from "@/adapters/Adapter.sol"; + +address constant RSWETH_HOLDER = 0x3A0ee670EE34D889B52963bD20728dEcE4D9f8FE; + +// tokenId 6550 +// amountExpected 105898225379893452500 +// totalStaked 197304164246212470306573 + +contract SwEthAdapterTest is Test, ERC721Receiver { + RswETHAdapter adapter; + + using AdapterDelegateCall for RswETHAdapter; + + function setUp() public { + vm.createSelectFork(vm.envString("MAINNET_RPC"), 21_780_986); + adapter = new RswETHAdapter(); + vm.startPrank(RSWETH_HOLDER); + ERC20(RSWETH_TOKEN).transfer(address(this), 1000 ether); + vm.stopPrank(); + } + + function test_request_and_claim() public { + bytes memory data = adapter._delegatecall(abi.encodeWithSelector(adapter.requestWithdraw.selector, 100 ether)); + (uint256 tokenId, uint256 amountExpected) = abi.decode(data, (uint256, uint256)); + console.log("tokenId %s", tokenId); + console.log("amountExpected %s", amountExpected); + console.log("totalStaked %s", adapter.totalStaked()); + assertFalse(adapter.isFinalized(tokenId)); + } +}