Skip to content

Instantly share code, notes, and snippets.

@Turupawn
Created October 17, 2025 21:05
Show Gist options
  • Select an option

  • Save Turupawn/81e2b892b03e443a1d2d64405dcf3f5d to your computer and use it in GitHub Desktop.

Select an option

Save Turupawn/81e2b892b03e443a1d2d64405dcf3f5d to your computer and use it in GitHub Desktop.
Scroll DeFi Sepolia 2025 Workshop
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20 {
constructor() ERC20("My Simple ERC20", "MS") {
_mint(msg.sender, 21_000_000);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}
interface ISwapRouter02 {
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
}
contract UniV3Swap {
address SCROLL_SEPOLIA_UNIV3_ROUTER_V2 = 0x17AFD0263D6909Ba1F9a8EAC697f76532365Fb95;
// https://uniswap-showcase.sepolia.scroll.xyz/#/swap
// Token In: 0x5300000000000000000000000000000000000004
// Token Out: 0xD9692f1748aFEe00FACE2da35242417dd05a8615
// Amount In Max: 40000000000000
// Amount Out: 1000000000000000000
// Fee: 3000
function swap(address tokenIn, address tokenOut, uint256 amountInMax, uint256 amountOut, uint24 fee)
external
{
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountInMax);
IERC20(tokenIn).approve(SCROLL_SEPOLIA_UNIV3_ROUTER_V2, amountInMax);
ISwapRouter02.ExactOutputSingleParams memory params = ISwapRouter02
.ExactOutputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
fee: fee,
recipient: msg.sender,
amountOut: amountOut,
amountInMaximum: amountInMax,
sqrtPriceLimitX96: 0
});
uint256 amountIn = ISwapRouter02(SCROLL_SEPOLIA_UNIV3_ROUTER_V2).exactOutputSingle(params);
if (amountIn < amountInMax) {
IERC20(tokenIn).approve(SCROLL_SEPOLIA_UNIV3_ROUTER_V2, 0);
IERC20(tokenIn).transfer(msg.sender, amountInMax - amountIn);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
library DataTypes {
struct ReserveConfigurationMap {
uint256 data;
}
struct ReserveData {
ReserveConfigurationMap configuration;
uint128 liquidityIndex;
uint128 currentLiquidityRate;
uint128 variableBorrowIndex;
uint128 currentVariableBorrowRate;
uint128 currentStableBorrowRate;
uint40 lastUpdateTimestamp;
uint16 id;
address aTokenAddress;
address stableDebtTokenAddress;
address variableDebtTokenAddress;
address interestRateStrategyAddress;
uint128 accruedToTreasury;
uint128 unbacked;
uint128 isolationModeTotalDebt;
}
}
interface IPool {
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode, // 1 for Stable, 2 for Variable
uint16 referralCode,
address onBehalfOf) external;
function supply(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode) external;
function withdraw(
address asset,
uint256 amount,
address to) external returns (uint256);
function getReserveData(
address asset) external view returns (DataTypes.ReserveData memory);
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
contract AaveLender {
address public immutable AAVE_POOL_ADDRESS;
address public immutable STAKED_TOKEN_ADDRESS;
address public immutable ATOKEN_ADDRESS;
address public immutable OWNER;
mapping(address account => uint amount) public stakeByAccount;
uint public totalStake;
// Ejemplo en Scroll Sepolia
// Pool Address
// 0x48914C788295b5db23aF2b5F0B3BE775C4eA9440
// DAI
// 0x7984E363c38b590bB4CA35aEd5133Ef2c6619C40
// check at https://app.aave.com/
constructor(address aavePoolAddress, address stakedTokenAddress) {
AAVE_POOL_ADDRESS = aavePoolAddress;
STAKED_TOKEN_ADDRESS = stakedTokenAddress;
OWNER = msg.sender;
ATOKEN_ADDRESS = IPool(aavePoolAddress).getReserveData(stakedTokenAddress).aTokenAddress;
}
function stake(uint amount) public {
totalStake += amount;
stakeByAccount[msg.sender] += amount;
IERC20(STAKED_TOKEN_ADDRESS).transferFrom(msg.sender, address(this), amount);
IERC20(STAKED_TOKEN_ADDRESS).approve(AAVE_POOL_ADDRESS, amount);
IPool(AAVE_POOL_ADDRESS).supply(
STAKED_TOKEN_ADDRESS,
amount,
address(this),
0);
}
function unstake(uint amount) public {
require(amount <= stakeByAccount[msg.sender], "Not enough stake");
totalStake -= amount;
stakeByAccount[msg.sender] -= amount;
IPool(AAVE_POOL_ADDRESS).withdraw(
STAKED_TOKEN_ADDRESS,
amount,
msg.sender
);
}
function yieldEarned() public view returns(uint){
return IERC20(ATOKEN_ADDRESS).balanceOf(address(this)) - totalStake;
}
function withdraw(uint amount) public {
require(msg.sender == OWNER, "Sender is not owner");
require(amount <= yieldEarned(), "Maximum withdraw exceeded");
IPool(AAVE_POOL_ADDRESS).withdraw(
STAKED_TOKEN_ADDRESS,
amount,
msg.sender
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment