From be6554c5bd819e5e8432294456ec7eac819a2faa Mon Sep 17 00:00:00 2001 From: PJ O'Leary Date: Thu, 25 Sep 2025 18:33:42 -0700 Subject: [PATCH 01/22] skeleton --- .gitmodules | 3 + foundry.lock | 17 ++++++ foundry.toml | 3 + lib/contracts | 1 + remappings.txt | 1 + script/SFLUVStorageSlots.s.sol | 15 +++++ src/ISFLUVZapperErrors.sol | 7 +++ src/SFLUVZapperv1.sol | 104 +++++++++++++++++++++++++++++++++ 8 files changed, 151 insertions(+) create mode 100644 foundry.lock create mode 160000 lib/contracts create mode 100644 script/SFLUVStorageSlots.s.sol create mode 100644 src/ISFLUVZapperErrors.sol create mode 100644 src/SFLUVZapperv1.sol diff --git a/.gitmodules b/.gitmodules index f27e450..21e6a46 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/openzeppelin-foundry-upgrades"] path = lib/openzeppelin-foundry-upgrades url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades +[submodule "lib/contracts"] + path = lib/contracts + url = https://github.com/berachain/contracts diff --git a/foundry.lock b/foundry.lock new file mode 100644 index 0000000..dae5eb5 --- /dev/null +++ b/foundry.lock @@ -0,0 +1,17 @@ +{ + "lib/forge-std": { + "rev": "77041d2ce690e692d6e03cc812b57d1ddaa4d505" + }, + "lib/openzeppelin-contracts-upgradeable": { + "rev": "60b305a8f3ff0c7688f02ac470417b6bbf1c4d27" + }, + "lib/openzeppelin-foundry-upgrades": { + "rev": "cbce1e00305e943aa1661d43f41e5ac72c662b07" + }, + "lib/contracts": { + "rev": "c20aac9e2137dde3efbd0d1bd164d70cbf54a9fa" + }, + "lib/openzeppelin-contracts": { + "rev": "e4f70216d759d8e6a64144a9e1f7bbeed78e7079" + } +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 21f761a..f895590 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,3 +8,6 @@ build_info = true extra_output = ["storageLayout"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + +[etherscan] +berachain = { key = "${ETHERSCAN_API_KEY}", url = "https://rpc.berachain.com" } diff --git a/lib/contracts b/lib/contracts new file mode 160000 index 0000000..c20aac9 --- /dev/null +++ b/lib/contracts @@ -0,0 +1 @@ +Subproject commit c20aac9e2137dde3efbd0d1bd164d70cbf54a9fa diff --git a/remappings.txt b/remappings.txt index 2039af3..241b3db 100644 --- a/remappings.txt +++ b/remappings.txt @@ -6,3 +6,4 @@ halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/ openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ openzeppelin-contracts/=lib/openzeppelin-contracts/ openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/ +@berachain/contracts/=lib/contracts/src/ diff --git a/script/SFLUVStorageSlots.s.sol b/script/SFLUVStorageSlots.s.sol new file mode 100644 index 0000000..7e47b3a --- /dev/null +++ b/script/SFLUVStorageSlots.s.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +import {Script, console} from "forge-std/Script.sol"; + +contract SFLUVStorageSlotScript is Script { + function setUp() public {} + + function run() public pure { + bytes32 SFLUVZapperStorage = + keccak256(abi.encode(uint256(keccak256("SFLUV.storage.SFLUVZapper")) - 1)) & ~bytes32(uint256(0xff)); + + console.log("SFLUVZapper Storage: ", vm.toString(SFLUVZapperStorage)); + } +} diff --git a/src/ISFLUVZapperErrors.sol b/src/ISFLUVZapperErrors.sol new file mode 100644 index 0000000..2fdb88d --- /dev/null +++ b/src/ISFLUVZapperErrors.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +interface ISFLUVZapperErrors { + // Signature: 0xd92e233d + error ZeroAddress(); +} \ No newline at end of file diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol new file mode 100644 index 0000000..1fa0f19 --- /dev/null +++ b/src/SFLUVZapperv1.sol @@ -0,0 +1,104 @@ +pragma solidity ^0.8.26; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20WrapperUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +import "@berachain/contracts/honey/IHoneyFactory.sol"; + +import "./ISFLUVZapperErrors.sol"; +import "./SFLUVv2.sol"; + +struct SFLUVZapperStorageInit { + address lzbridge; + address honeyFactory; + address sfluv; + address byusd; +} + +contract SFLUVZapperv1 is + Initializable, + AccessControlUpgradeable, + UUPSUpgradeable, + ISFLUVZapperErrors +{ + /* STORAGE */ + + struct SFLUVZapperStorage { + address lzbridge; + IHoneyFactory honeyFactory; + SFLUVv2 sfluv; + IERC20 byusd; + } + + // keccak256(abi.encode(uint256(keccak256("SFLUV.storage.SFLUVZapper")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant SFLUVZapperStorageLocation = + 0x4387e05f939ab688c86fd2a91847d45cedf6bd707f41c3ea87ea7383b559aa00; + + function _getSFLUVZapperStorage() + private + pure + returns (SFLUVZapperStorage storage $) + { + assembly { + $.slot := SFLUVZapperStorageLocation + } + } + + modifier onlySFLUVRole(bytes32 role) { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + + if (!$.sfluv.hasRole(role, _msgSender())) + revert AccessControlUnauthorizedAccount(_msgSender(), role); + _; + } + + constructor() { + _disableInitializers(); + } + + function initialize( + address _governance, + SFLUVZapperStorageInit calldata _storage + ) public { + __AccessControl_init(); + __UUPSUpgradeable_init(); + __Storage_init(_storage); + + _grantRole(DEFAULT_ADMIN_ROLE, _governance); + } + + function __Storage_init( + SFLUVZapperStorageInit calldata s + ) internal onlyInitializing { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + + if (s.lzbridge == address(0)) revert ZeroAddress(); + $.lzbridge = s.lzbridge; + + if (s.honeyFactory == address(0)) revert ZeroAddress(); + $.honeyFactory = IHoneyFactory(s.honeyFactory); + + if (s.sfluv == address(0)) revert ZeroAddress(); + $.sfluv = SFLUVv2(s.sfluv); + + if (s.byusd == address(0)) revert ZeroAddress(); + $.byusd = IERC20(s.byusd); + } + + function zapIn(uint256 amount) external {} + + function zapInTo(uint256 amount, address to) public {} + + function zapOut(uint256 amount) external {} + + function zapOutTo(uint256 amount, address to) public {} + + function zapOutAndLzBridgeTo(uint256 amount, address to) public {} + + function _authorizeUpgrade( + address newImplementation + ) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {} +} \ No newline at end of file From 685c91801b0b99574592c08fcc8a302f83658d87 Mon Sep 17 00:00:00 2001 From: PJ O'Leary Date: Mon, 6 Oct 2025 15:00:17 -0700 Subject: [PATCH 02/22] zapIn and zapOut implemented, no tests --- foundry.lock | 12 +++--- script/DeploySFLUVZapperv1.s.sol | 45 +++++++++++++++++++++ script/IDeployErrors.sol | 9 +++++ src/ISFLUVZapperErrors.sol | 4 ++ src/SFLUVZapperv1.sol | 68 +++++++++++++++++++++++++++++--- 5 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 script/DeploySFLUVZapperv1.s.sol create mode 100644 script/IDeployErrors.sol diff --git a/foundry.lock b/foundry.lock index dae5eb5..abdb7b3 100644 --- a/foundry.lock +++ b/foundry.lock @@ -1,17 +1,17 @@ { + "lib/contracts": { + "rev": "c20aac9e2137dde3efbd0d1bd164d70cbf54a9fa" + }, "lib/forge-std": { "rev": "77041d2ce690e692d6e03cc812b57d1ddaa4d505" }, - "lib/openzeppelin-contracts-upgradeable": { - "rev": "60b305a8f3ff0c7688f02ac470417b6bbf1c4d27" - }, "lib/openzeppelin-foundry-upgrades": { "rev": "cbce1e00305e943aa1661d43f41e5ac72c662b07" }, - "lib/contracts": { - "rev": "c20aac9e2137dde3efbd0d1bd164d70cbf54a9fa" - }, "lib/openzeppelin-contracts": { "rev": "e4f70216d759d8e6a64144a9e1f7bbeed78e7079" + }, + "lib/openzeppelin-contracts-upgradeable": { + "rev": "60b305a8f3ff0c7688f02ac470417b6bbf1c4d27" } } \ No newline at end of file diff --git a/script/DeploySFLUVZapperv1.s.sol b/script/DeploySFLUVZapperv1.s.sol new file mode 100644 index 0000000..d9a9955 --- /dev/null +++ b/script/DeploySFLUVZapperv1.s.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.8.26; + +import "forge-std/Script.sol"; +import {SFLUVZapperv1, SFLUVZapperStorageInit} from "../src/SFLUVZapperv1.sol"; +import {SFLUVv2} from "../src/SFLUVv2.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IDeployErrors} from "./IDeployErrors.sol"; + +contract DeploySFLUVv2 is Script, IDeployErrors { + + function run() public { + address lzbridge = vm.envAddress("LZ_BRIDGE_ADDRESS"); + address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); + address sfluv = vm.envAddress("SFLUV_ADDRESS"); + address byusd = vm.envAddress("BYUSD_ADDRESS"); + + + SFLUVZapperStorageInit memory s = SFLUVZapperStorageInit( + lzbridge, + honeyFactory, + sfluv, + byusd + ); + + vm.startBroadcast(); + + SFLUVv2 token = SFLUVv2(sfluv); + + bool isAdmin = token.hasRole(token.DEFAULT_ADMIN_ROLE(), msg.sender); + if (!isAdmin) revert NotAdmin(); + + SFLUVZapperv1 impl = new SFLUVZapperv1(); + + ERC1967Proxy proxy = new ERC1967Proxy(address(impl), + abi.encodeCall(impl.initialize, (msg.sender, s))); + + token.grantRole(token.MINTER_ROLE(), address(proxy)); + token.grantRole(token.REDEEMER_ROLE(), address(proxy)); + + vm.stopBroadcast(); + + console.log("UUPS Proxy Address:", address(proxy)); + } +} \ No newline at end of file diff --git a/script/IDeployErrors.sol b/script/IDeployErrors.sol new file mode 100644 index 0000000..7ec7809 --- /dev/null +++ b/script/IDeployErrors.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +interface IDeployErrors { + // Signature: 0xd92e233d + error ZeroAddress(); + + error NotAdmin(); +} \ No newline at end of file diff --git a/src/ISFLUVZapperErrors.sol b/src/ISFLUVZapperErrors.sol index 2fdb88d..ab4aa93 100644 --- a/src/ISFLUVZapperErrors.sol +++ b/src/ISFLUVZapperErrors.sol @@ -4,4 +4,8 @@ pragma solidity ^0.8.26; interface ISFLUVZapperErrors { // Signature: 0xd92e233d error ZeroAddress(); + + error TransferFailed(address token, address to, address from, uint256 value); + error MintFailed(); + error RedeemFailed(); } \ No newline at end of file diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 1fa0f19..8fcdde6 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -7,6 +7,7 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@berachain/contracts/honey/IHoneyFactory.sol"; +import "@berachain/contracts/honey/Honey.sol"; import "./ISFLUVZapperErrors.sol"; import "./SFLUVv2.sol"; @@ -24,6 +25,9 @@ contract SFLUVZapperv1 is UUPSUpgradeable, ISFLUVZapperErrors { + bytes32 public constant MINTER_ROLE = keccak256("MINTER"); + bytes32 public constant REDEEMER_ROLE = keccak256("REDEEMER"); + /* STORAGE */ struct SFLUVZapperStorage { @@ -88,15 +92,69 @@ contract SFLUVZapperv1 is $.byusd = IERC20(s.byusd); } - function zapIn(uint256 amount) external {} + function zapIn(uint256 amount) external onlySFLUVRole(MINTER_ROLE) { + _zapInTo(amount, _msgSender()); + } + + function zapInTo(uint256 amount, address to) public onlySFLUVRole(MINTER_ROLE) { + _zapInTo(amount, to); + } + + + function zapOut(uint256 amount) external onlySFLUVRole(REDEEMER_ROLE) { + _zapOutTo(amount, _msgSender()); + } + + function zapOutTo(uint256 amount, address to) public onlySFLUVRole(REDEEMER_ROLE) { + _zapOutTo(amount, to); + } + + function zapOutAndLzBridgeTo(uint256 amount, address to) public onlySFLUVRole(REDEEMER_ROLE) {} + + /* INTERNAL */ + - function zapInTo(uint256 amount, address to) public {} + function _zapInTo(uint256 amount, address to) private { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); - function zapOut(uint256 amount) external {} + bool success = $.byusd.transferFrom( + _msgSender(), + address(this), + amount + ); + if (!success) revert TransferFailed(address($.byusd), address(this), _msgSender(), amount); + + uint256 honeyAmount = $.honeyFactory.mint( + address($.byusd), + amount, + address(this), + false + ); + + success = $.sfluv.depositFor(to, honeyAmount); + if (!success) revert MintFailed(); + } - function zapOutTo(uint256 amount, address to) public {} + function _zapOutTo(uint256 amount, address to) private { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); - function zapOutAndLzBridgeTo(uint256 amount, address to) public {} + bool success = $.sfluv.transferFrom( + _msgSender(), + address(this), + amount + ); + if (!success) revert TransferFailed(address($.sfluv), address(this), _msgSender(), amount); + + success = $.sfluv.withdrawTo(address(this), amount); + if (!success) revert RedeemFailed(); + + $.honeyFactory.redeem( + address($.byusd), + amount, + to, + false + ); + } function _authorizeUpgrade( address newImplementation From c08fd39c495162b1f1720d90b25b09291e2a507b Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Wed, 8 Oct 2025 10:54:45 -0700 Subject: [PATCH 03/22] working on zapper tests, zapper is not initializing --- test/SFLUVZapperv1.sol | 97 ++++++++++++++++++++++++++++++++++++++++++ test/SFLUVv2.t.sol | 2 +- 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 test/SFLUVZapperv1.sol diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol new file mode 100644 index 0000000..2c2ff9e --- /dev/null +++ b/test/SFLUVZapperv1.sol @@ -0,0 +1,97 @@ +pragma solidity ^0.8.26; + +import "../src/MockCoin.sol"; +import {SFLUVv2} from "../src/SFLUVv2.sol"; +import {SFLUVZapperv1, SFLUVZapperStorageInit} from "../src/SFLUVZapperv1.sol"; +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; +import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import "@berachain/contracts/honey/IHoneyFactory.sol"; + +interface BYUSD { + function balanceOf(address account) external view returns (uint256); + function symbol() external view returns (string memory); + function decimals() external view returns (uint8); + function approve(address spender, uint256 amount) external returns (bool); + function transfer(address recipient, uint256 amount) external returns (bool); + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); +} + +contract SFLUVZapperTest is Test { + MockCoin public mockCoin; + SFLUVv2 public testLUVCoin; + SFLUVZapperv1 public testSFLUVZapper; + BYUSD public testBYUSD; + + address internal gov; + address internal payer; + address internal payee; + + uint public oneEther = 1 ether; // TODO: elsewhere? + + ERC1967Proxy zapperproxy; + + function setUp() public { + string memory forkURL = vm.envString('FORK_URL'); + uint256 forkBlock = vm.envUint('FORK_BLOCK'); + + + vm.createSelectFork(forkURL, forkBlock); + // mockCoin = new MockCoin(); + address sfluv = vm.envAddress('SFLUV_ADDRESS'); + address byusd = vm.envAddress("BYUSD_ADDRESS"); + testLUVCoin = SFLUVv2(sfluv); + testBYUSD = BYUSD(byusd); + testSFLUVZapper = new SFLUVZapperv1(); + + gov = makeAddr("gov"); + payer = makeAddr("payer"); + payee = makeAddr("payee"); + + address lzbridge = address(0x1); + address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); + + + + SFLUVZapperStorageInit memory testStorage = SFLUVZapperStorageInit( + lzbridge, + honeyFactory, + sfluv, + byusd + ); + + console.log(lzbridge, honeyFactory, sfluv, byusd); + console.log(address(testSFLUVZapper)); + zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (gov, testStorage))); + testSFLUVZapper = SFLUVZapperv1(address(zapperproxy)); + } + + + function testWrappingFunctionality() public { + // Prank w/ BYUSD whale address + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testBYUSD.transfer(address(testSFLUVZapper), 100); + uint256 testBalance = testBYUSD.balanceOf(address(testSFLUVZapper)); + console.log(testBalance); + // Send BYUSD to Zapper contract + + // Zap BYUSD to SFLUV + // Send SFLUV to reciever + // Confirm reciever has the right amount of SFLUV + + } + + function testUnwrappingFunctionality() public { + + // Prank w/ SFLUV whale address + // Send SFLUV to Zapper contract + // Zap SFLUV to BYUSD + // Send BYUSD to reciever + // Confirm reciever has the right amount of BYUSD + + + } + +} diff --git a/test/SFLUVv2.t.sol b/test/SFLUVv2.t.sol index f98086b..27a599c 100644 --- a/test/SFLUVv2.t.sol +++ b/test/SFLUVv2.t.sol @@ -92,4 +92,4 @@ contract SFLUVv2Test is Test { assert(testLUVCoin.hasRole(testLUVCoin.MINTER_ROLE(), payee)); } -} \ No newline at end of file +} From 5ecea1a03a98b38c3921e92e156bd9edc80dbfb9 Mon Sep 17 00:00:00 2001 From: PJ O'Leary Date: Wed, 8 Oct 2025 12:17:23 -0700 Subject: [PATCH 04/22] initializer modifier added to zapper --- src/SFLUVZapperv1.sol | 5 ++++- test/SFLUVZapperv1.sol | 4 +--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 8fcdde6..383ea1c 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -66,7 +66,10 @@ contract SFLUVZapperv1 is function initialize( address _governance, SFLUVZapperStorageInit calldata _storage - ) public { + ) + initializer + public + { __AccessControl_init(); __UUPSUpgradeable_init(); __Storage_init(_storage); diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index 2c2ff9e..9c1debb 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -53,14 +53,12 @@ contract SFLUVZapperTest is Test { address lzbridge = address(0x1); address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); - - SFLUVZapperStorageInit memory testStorage = SFLUVZapperStorageInit( lzbridge, honeyFactory, sfluv, byusd - ); + ); console.log(lzbridge, honeyFactory, sfluv, byusd); console.log(address(testSFLUVZapper)); From 4383047b5c6b4f8328cb923e564eeab03d535867 Mon Sep 17 00:00:00 2001 From: PJ O'Leary Date: Fri, 16 Jan 2026 14:51:33 -0800 Subject: [PATCH 05/22] migrator check-in --- .gitmodules | 3 +++ lib/devtools | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/devtools diff --git a/.gitmodules b/.gitmodules index 21e6a46..4bb6bc3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "lib/contracts"] path = lib/contracts url = https://github.com/berachain/contracts +[submodule "lib/devtools"] + path = lib/devtools + url = https://github.com/LayerZero-Labs/devtools diff --git a/lib/devtools b/lib/devtools new file mode 160000 index 0000000..6c95cd3 --- /dev/null +++ b/lib/devtools @@ -0,0 +1 @@ +Subproject commit 6c95cd3c1d96ae9b3594a352f6f209f3a7733bcb From 382bc2add0d402e01fc06c8335b7ba37eca3a152 Mon Sep 17 00:00:00 2001 From: PJ O'Leary Date: Fri, 16 Jan 2026 15:47:37 -0800 Subject: [PATCH 06/22] zap and bridge functionality added --- .gitmodules | 10 ++++++++-- foundry.lock | 12 +++++++++--- lib/bera-contracts | 1 + lib/contracts | 2 +- lib/core | 1 + lib/{devtools => layerzero} | 0 lib/lz-address-book | 1 + remappings.txt | 11 +++++++++-- src/SFLUVZapperv1.sol | 36 +++++++++++++++++++++++++++++------- 9 files changed, 59 insertions(+), 15 deletions(-) create mode 160000 lib/bera-contracts create mode 160000 lib/core rename lib/{devtools => layerzero} (100%) create mode 160000 lib/lz-address-book diff --git a/.gitmodules b/.gitmodules index 4bb6bc3..530dbbf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,6 +13,12 @@ [submodule "lib/contracts"] path = lib/contracts url = https://github.com/berachain/contracts -[submodule "lib/devtools"] - path = lib/devtools +[submodule "lib/bera-contracts"] + path = lib/bera-contracts + url = https://github.com/berachain/contracts +[submodule "lib/layerzero"] + path = lib/layerzero url = https://github.com/LayerZero-Labs/devtools +[submodule "lib/lz-address-book"] + path = lib/lz-address-book + url = https://github.com/LayerZero-Labs/lz-address-book diff --git a/foundry.lock b/foundry.lock index abdb7b3..956b5f2 100644 --- a/foundry.lock +++ b/foundry.lock @@ -1,17 +1,23 @@ { + "lib/bera-contracts": { + "rev": "b7562f2bcd4bef30c9833469ddb98b8fbc419cc9" + }, "lib/contracts": { - "rev": "c20aac9e2137dde3efbd0d1bd164d70cbf54a9fa" + "rev": "b7562f2bcd4bef30c9833469ddb98b8fbc419cc9" }, "lib/forge-std": { "rev": "77041d2ce690e692d6e03cc812b57d1ddaa4d505" }, - "lib/openzeppelin-foundry-upgrades": { - "rev": "cbce1e00305e943aa1661d43f41e5ac72c662b07" + "lib/layerzero": { + "rev": "6c95cd3c1d96ae9b3594a352f6f209f3a7733bcb" }, "lib/openzeppelin-contracts": { "rev": "e4f70216d759d8e6a64144a9e1f7bbeed78e7079" }, "lib/openzeppelin-contracts-upgradeable": { "rev": "60b305a8f3ff0c7688f02ac470417b6bbf1c4d27" + }, + "lib/openzeppelin-foundry-upgrades": { + "rev": "cbce1e00305e943aa1661d43f41e5ac72c662b07" } } \ No newline at end of file diff --git a/lib/bera-contracts b/lib/bera-contracts new file mode 160000 index 0000000..b7562f2 --- /dev/null +++ b/lib/bera-contracts @@ -0,0 +1 @@ +Subproject commit b7562f2bcd4bef30c9833469ddb98b8fbc419cc9 diff --git a/lib/contracts b/lib/contracts index c20aac9..b7562f2 160000 --- a/lib/contracts +++ b/lib/contracts @@ -1 +1 @@ -Subproject commit c20aac9e2137dde3efbd0d1bd164d70cbf54a9fa +Subproject commit b7562f2bcd4bef30c9833469ddb98b8fbc419cc9 diff --git a/lib/core b/lib/core new file mode 160000 index 0000000..97020e7 --- /dev/null +++ b/lib/core @@ -0,0 +1 @@ +Subproject commit 97020e77fbaba9b01326965554c2c3ca866f5a1b diff --git a/lib/devtools b/lib/layerzero similarity index 100% rename from lib/devtools rename to lib/layerzero diff --git a/lib/lz-address-book b/lib/lz-address-book new file mode 160000 index 0000000..ea3e016 --- /dev/null +++ b/lib/lz-address-book @@ -0,0 +1 @@ +Subproject commit ea3e016155d678fa3e14f1af67f13447c927a5b3 diff --git a/remappings.txt b/remappings.txt index 241b3db..7e3c0c1 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,9 +1,16 @@ @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ -@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/ forge-std/=lib/forge-std/src/ halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/ openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ openzeppelin-contracts/=lib/openzeppelin-contracts/ openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/ -@berachain/contracts/=lib/contracts/src/ +@berachain/contracts=lib/bera-contracts/src/ +solady/src/=lib/bera-contracts/lib/solady/src +@layerzerolabs/lz-evm-protocol-v2/=lib/lz-address-book/lib/LayerZero-v2/packages/layerzero-v2/evm/protocol/ +@layerzerolabs/lz-evm-messagelib-v2/=lib/lz-address-book/lib/LayerZero-v2/packages/layerzero-v2/evm/messagelib/ +@layerzerolabs/oft-evm/=lib/lz-address-book/lib/devtools/packages/oft-evm/ +@layerzerolabs/oapp-evm/=lib/lz-address-book/lib/devtools/packages/oapp-evm/ +@openzeppelin/contracts/=lib/lz-address-book/lib/openzeppelin-contracts/contracts/ +solidity-bytes-utils/=lib/lz-address-book/lib/solidity-bytes-utils/ +forge-std/=lib/forge-std/src/ \ No newline at end of file diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 383ea1c..f13156d 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -1,13 +1,15 @@ pragma solidity ^0.8.26; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20WrapperUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; - import "@berachain/contracts/honey/IHoneyFactory.sol"; import "@berachain/contracts/honey/Honey.sol"; +import { IOFT, SendParam, MessagingFee, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; +import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol"; +import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol"; import "./ISFLUVZapperErrors.sol"; import "./SFLUVv2.sol"; @@ -31,10 +33,10 @@ contract SFLUVZapperv1 is /* STORAGE */ struct SFLUVZapperStorage { - address lzbridge; + IOFT lzbridge; IHoneyFactory honeyFactory; SFLUVv2 sfluv; - IERC20 byusd; + IERC20Metadata byusd; } // keccak256(abi.encode(uint256(keccak256("SFLUV.storage.SFLUVZapper")) - 1)) & ~bytes32(uint256(0xff)) @@ -83,7 +85,7 @@ contract SFLUVZapperv1 is SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); if (s.lzbridge == address(0)) revert ZeroAddress(); - $.lzbridge = s.lzbridge; + $.lzbridge = IOFT(s.lzbridge); if (s.honeyFactory == address(0)) revert ZeroAddress(); $.honeyFactory = IHoneyFactory(s.honeyFactory); @@ -92,7 +94,7 @@ contract SFLUVZapperv1 is $.sfluv = SFLUVv2(s.sfluv); if (s.byusd == address(0)) revert ZeroAddress(); - $.byusd = IERC20(s.byusd); + $.byusd = IERC20Metadata(s.byusd); } function zapIn(uint256 amount) external onlySFLUVRole(MINTER_ROLE) { @@ -112,7 +114,27 @@ contract SFLUVZapperv1 is _zapOutTo(amount, to); } - function zapOutAndLzBridgeTo(uint256 amount, address to) public onlySFLUVRole(REDEEMER_ROLE) {} + function zapOutAndLzBridgeTo(SendParam calldata lzParam) public onlySFLUVRole(REDEEMER_ROLE) { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + + uint256 zapAmount = FixedPointMathLib.mulDiv( + lzParam.amountLD, + 10 ** $.sfluv.decimals(), + 10 ** $.byusd.decimals() + ); + + _zapOutTo(zapAmount, address(this)); + + MessagingFee memory fee = $.lzbridge.quoteSend(lzParam, false); + ( + MessagingReceipt memory mReceipt, + OFTReceipt memory oReceipt + ) = $.lzbridge.send( + lzParam, + fee, + address(this) + ); + } /* INTERNAL */ From cf632c83cffcc16b0efe05eb38e4d4bc6a10d047 Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Sat, 17 Jan 2026 09:43:27 -0800 Subject: [PATCH 07/22] stashing before branch switch --- lib/core | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lib/core diff --git a/lib/core b/lib/core deleted file mode 160000 index 97020e7..0000000 --- a/lib/core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 97020e77fbaba9b01326965554c2c3ca866f5a1b From ac0996a1796c3f1806d2ae76faac826c71dc1dd1 Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Sat, 17 Jan 2026 09:47:38 -0800 Subject: [PATCH 08/22] making new dev branch --- lib/contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/contracts b/lib/contracts index b7562f2..c20aac9 160000 --- a/lib/contracts +++ b/lib/contracts @@ -1 +1 @@ -Subproject commit b7562f2bcd4bef30c9833469ddb98b8fbc419cc9 +Subproject commit c20aac9e2137dde3efbd0d1bd164d70cbf54a9fa From cb393ac53feb669b2a6ed38ecc1f628813cc8135 Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Sat, 17 Jan 2026 10:56:22 -0800 Subject: [PATCH 09/22] stashing before switch --- src/SFLUVZapperv1.sol | 2 +- test/SFLUVZapperv1.sol | 126 +++++++++++++++++++++++++++++++---------- 2 files changed, 98 insertions(+), 30 deletions(-) diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index f13156d..0034a29 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -184,4 +184,4 @@ contract SFLUVZapperv1 is function _authorizeUpgrade( address newImplementation ) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {} -} \ No newline at end of file +} diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index 9c1debb..11a8341 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -1,3 +1,4 @@ + pragma solidity ^0.8.26; import "../src/MockCoin.sol"; @@ -7,9 +8,11 @@ import {Test} from "forge-std/Test.sol"; import "forge-std/console.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; +import "@openzeppelin/contracts/utils/math/Math.sol"; import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; import "@berachain/contracts/honey/IHoneyFactory.sol"; + interface BYUSD { function balanceOf(address account) external view returns (uint256); function symbol() external view returns (string memory); @@ -20,35 +23,34 @@ interface BYUSD { } contract SFLUVZapperTest is Test { - MockCoin public mockCoin; SFLUVv2 public testLUVCoin; SFLUVZapperv1 public testSFLUVZapper; BYUSD public testBYUSD; - address internal gov; - address internal payer; - address internal payee; + address internal peon; + address defaultAdmin = address(0x90496e23825aD0C8107d04671e6a27f30630Fc35); uint public oneEther = 1 ether; // TODO: elsewhere? ERC1967Proxy zapperproxy; + error AccessControlUnauthorizedAccount(address account, bytes32 role); + + function setUp() public { + string memory forkURL = vm.envString('FORK_URL'); uint256 forkBlock = vm.envUint('FORK_BLOCK'); vm.createSelectFork(forkURL, forkBlock); - // mockCoin = new MockCoin(); address sfluv = vm.envAddress('SFLUV_ADDRESS'); address byusd = vm.envAddress("BYUSD_ADDRESS"); testLUVCoin = SFLUVv2(sfluv); testBYUSD = BYUSD(byusd); testSFLUVZapper = new SFLUVZapperv1(); - gov = makeAddr("gov"); - payer = makeAddr("payer"); - payee = makeAddr("payee"); + peon = makeAddr("peon"); address lzbridge = address(0x1); address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); @@ -60,36 +62,102 @@ contract SFLUVZapperTest is Test { byusd ); - console.log(lzbridge, honeyFactory, sfluv, byusd); - console.log(address(testSFLUVZapper)); - zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (gov, testStorage))); + zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (defaultAdmin, testStorage))); testSFLUVZapper = SFLUVZapperv1(address(zapperproxy)); + vm.startPrank(defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ADMIN_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.MINTER_ROLE(), address(testSFLUVZapper)); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), address(testSFLUVZapper)); + vm.stopPrank(); + } + + + function testPermissionedWrappingFunctionality() public { + // For easy decimal conversion + uint byusdExp = 10**testBYUSD.decimals(); + uint sfluvExp = 10**testLUVCoin.decimals(); + // This should not change because we cut off the fork at a determined block, but just in case + uint startingAdminLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + // Prank w/ BYUSD whale address + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + // Transfer from BYUSD Whale acct to defaultAdmin + testBYUSD.transfer(defaultAdmin, 1000 * byusdExp); + //Prank w defaultAdmin account + vm.startPrank(defaultAdmin); + // defaultAdmin approves the SFLUV zapper to move 100 of its (defaultAdmin's) BYUSD + testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); + //SFLUV zapper takes 50 BYUSD to be converted to SFLUV + testSFLUVZapper.zapIn(50 * byusdExp); + vm.stopPrank(); + // assert that defaultAdmin has 50 sfluv (50 more than it had before) and 950 byusd + assert(testLUVCoin.balanceOf(defaultAdmin) == ((50 * sfluvExp) + startingAdminLUVBalance)); + assert(testBYUSD.balanceOf(defaultAdmin) == 950 * byusdExp); } - - function testWrappingFunctionality() public { + function testUnpermissionedWrappingFunctionality() public { + // For easy decimal conversion + uint byusdExp = 10**testBYUSD.decimals(); + uint sfluvExp = 10**testLUVCoin.decimals(); // Prank w/ BYUSD whale address vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); - testBYUSD.transfer(address(testSFLUVZapper), 100); - uint256 testBalance = testBYUSD.balanceOf(address(testSFLUVZapper)); - console.log(testBalance); - // Send BYUSD to Zapper contract - - // Zap BYUSD to SFLUV - // Send SFLUV to reciever - // Confirm reciever has the right amount of SFLUV - + // Transfer from BYUSD Whale acct to peon + testBYUSD.transfer(peon, 1000 * byusdExp); + // Prank w peon + vm.startPrank(peon); + // peon approves the SFLUV zapper to move 100 of its (peon's) BYUSD + testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); + bytes32 myRole = testSFLUVZapper.MINTER_ROLE(); + //SFLUV zapper takes 50 BYUSD to be converted to SFLUV + vm.expectRevert(abi.encodeWithSelector( + AccessControlUnauthorizedAccount.selector, + peon, + myRole + )); + testSFLUVZapper.zapIn(50 * byusdExp); } function testUnwrappingFunctionality() public { + // For easy decimal conversion + uint byusdExp = 10**testBYUSD.decimals(); + uint sfluvExp = 10**testLUVCoin.decimals(); + uint honeyCharge = 5 * byusdExp; + uint denominator = 10000; + uint honeyFee = honeyCharge / denominator; + uint conversionAmount = 25; + uint honeyRake = honeyFee * conversionAmount; + // This should not change because we cut off the fork at a determined block, but just in case + uint startingAdminLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + uint startingAdminBYUSDBalance = testBYUSD.balanceOf(defaultAdmin); + // Prank w defaultAdmin account + vm.startPrank(defaultAdmin); + // defaultAdmin approves the SFLUV zapper to move 100 of its (defaultAdmin's) SFLUV + testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); + //SFLUV zapper takes 25 SFLUV to be converted to BYUSD + testSFLUVZapper.zapOut(conversionAmount * sfluvExp); + vm.stopPrank(); + // assert that defaultAdmin has 25 less SFLUV that it started with, and 975 byusd + assert(testLUVCoin.balanceOf(defaultAdmin) == (startingAdminLUVBalance - (conversionAmount * sfluvExp))); + assert(testBYUSD.balanceOf(defaultAdmin) == ((conversionAmount * byusdExp) - (honeyRake))); + } - // Prank w/ SFLUV whale address - // Send SFLUV to Zapper contract - // Zap SFLUV to BYUSD - // Send BYUSD to reciever - // Confirm reciever has the right amount of BYUSD - - + function testUnpermissionedUnwrappingFunctionality() public { + // For easy decimal conversion + uint byusdExp = 10**testBYUSD.decimals(); + uint sfluvExp = 10**testLUVCoin.decimals(); + // Prank w peon + vm.startPrank(peon); + // peon approves the SFLUV zapper to move 100 of its (peons) SFLUV + testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); + bytes32 myRole = testSFLUVZapper.REDEEMER_ROLE(); + //SFLUV zapper takes 50 BYUSD to be converted to SFLUV + vm.expectRevert(abi.encodeWithSelector( + AccessControlUnauthorizedAccount.selector, + peon, + myRole + )); + testSFLUVZapper.zapOut(50 * byusdExp); + vm.stopPrank(); } } From 6385c9499dd3aefc7e01878a23b964aaaac6bbfe Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Tue, 20 Jan 2026 16:17:01 -0800 Subject: [PATCH 10/22] checking in before trying to fix remappings --- remappings.txt | 2 +- script/DeploySFLUVZapperv1.s.sol | 16 ++-- src/ILiquidityPool.sol | 30 +++++++ src/SFLUVZapperv1.sol | 140 ++++++++++++++++++++++++++++--- test/SFLUVZapperv1.sol | 92 ++++++++++++++++---- 5 files changed, 249 insertions(+), 31 deletions(-) create mode 100644 src/ILiquidityPool.sol diff --git a/remappings.txt b/remappings.txt index 7e3c0c1..ee31704 100644 --- a/remappings.txt +++ b/remappings.txt @@ -13,4 +13,4 @@ solady/src/=lib/bera-contracts/lib/solady/src @layerzerolabs/oapp-evm/=lib/lz-address-book/lib/devtools/packages/oapp-evm/ @openzeppelin/contracts/=lib/lz-address-book/lib/openzeppelin-contracts/contracts/ solidity-bytes-utils/=lib/lz-address-book/lib/solidity-bytes-utils/ -forge-std/=lib/forge-std/src/ \ No newline at end of file +forge-std/=lib/forge-std/src/ diff --git a/script/DeploySFLUVZapperv1.s.sol b/script/DeploySFLUVZapperv1.s.sol index d9a9955..000b82b 100644 --- a/script/DeploySFLUVZapperv1.s.sol +++ b/script/DeploySFLUVZapperv1.s.sol @@ -10,17 +10,21 @@ import {IDeployErrors} from "./IDeployErrors.sol"; contract DeploySFLUVv2 is Script, IDeployErrors { function run() public { - address lzbridge = vm.envAddress("LZ_BRIDGE_ADDRESS"); address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); address sfluv = vm.envAddress("SFLUV_ADDRESS"); address byusd = vm.envAddress("BYUSD_ADDRESS"); + address honey = vm.envAddress("HONEY_ADDRESS"); + address honeyToBYUSDPool = vm.envAddress("HONEY_BYUSD_POOL_ADDRESS"); + address byusdVault = vm.envAddress("BYUSD_VAULT_ADDRESS"); SFLUVZapperStorageInit memory s = SFLUVZapperStorageInit( - lzbridge, - honeyFactory, - sfluv, - byusd + honeyFactory, + sfluv, + byusd, + honey, + honeyToBYUSDPool, + byusdVault ); vm.startBroadcast(); @@ -42,4 +46,4 @@ contract DeploySFLUVv2 is Script, IDeployErrors { console.log("UUPS Proxy Address:", address(proxy)); } -} \ No newline at end of file +} diff --git a/src/ILiquidityPool.sol b/src/ILiquidityPool.sol new file mode 100644 index 0000000..fbb8559 --- /dev/null +++ b/src/ILiquidityPool.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +interface ILiquidityPool { + /// Swap token0 <-> token1 + /// @param recipient Address receiving output tokens + /// @param zeroForOne True = token0 → token1, false = token1 → token0 + /// @param amountSpecified Positive = exact input, negative = exact output + /// @param sqrtPriceLimitX96 Q64.96 price limit, 0 = no limit + /// @param data Bytes passed to callback + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external returns (int256 amount0Delta, int256 amount1Delta); + + function token0() external view returns (address); + function token1() external view returns (address); + function slot0() external view returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ); +} diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 0034a29..499db81 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -7,18 +7,23 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@berachain/contracts/honey/IHoneyFactory.sol"; import "@berachain/contracts/honey/Honey.sol"; -import { IOFT, SendParam, MessagingFee, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; +import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol"; import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol"; +import { ILiquidityPool} from "./ILiquidityPool.sol"; import "./ISFLUVZapperErrors.sol"; import "./SFLUVv2.sol"; +interface IBYUSD is IERC20Metadata, IOFT {} + struct SFLUVZapperStorageInit { - address lzbridge; address honeyFactory; address sfluv; address byusd; + address honey; + address honeyToBYUSDPool; + address byusdVault; } contract SFLUVZapperv1 is @@ -33,10 +38,15 @@ contract SFLUVZapperv1 is /* STORAGE */ struct SFLUVZapperStorage { - IOFT lzbridge; IHoneyFactory honeyFactory; SFLUVv2 sfluv; - IERC20Metadata byusd; + + IBYUSD byusd; + IERC20Metadata honey; + + ILiquidityPool honeyToBYUSDPool; + + address byusdVault; } // keccak256(abi.encode(uint256(keccak256("SFLUV.storage.SFLUVZapper")) - 1)) & ~bytes32(uint256(0xff)) @@ -84,9 +94,6 @@ contract SFLUVZapperv1 is ) internal onlyInitializing { SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); - if (s.lzbridge == address(0)) revert ZeroAddress(); - $.lzbridge = IOFT(s.lzbridge); - if (s.honeyFactory == address(0)) revert ZeroAddress(); $.honeyFactory = IHoneyFactory(s.honeyFactory); @@ -94,7 +101,19 @@ contract SFLUVZapperv1 is $.sfluv = SFLUVv2(s.sfluv); if (s.byusd == address(0)) revert ZeroAddress(); - $.byusd = IERC20Metadata(s.byusd); + $.byusd = IBYUSD(s.byusd); + + if (s.honey == address(0)) revert ZeroAddress(); + $.honey = IERC20Metadata(s.honey); + + if (s.honeyToBYUSDPool == address(0)) revert ZeroAddress(); + $.honeyToBYUSDPool = ILiquidityPool(s.honeyToBYUSDPool); + + if (s.byusdVault == address(0)) revert ZeroAddress(); + $.byusdVault = s.byusdVault; + + $.byusd.approve(s.honeyFactory, type(uint256).max); + $.honeyFactory.honey().approve(s.sfluv, type(uint256).max); } function zapIn(uint256 amount) external onlySFLUVRole(MINTER_ROLE) { @@ -125,17 +144,46 @@ contract SFLUVZapperv1 is _zapOutTo(zapAmount, address(this)); - MessagingFee memory fee = $.lzbridge.quoteSend(lzParam, false); + MessagingFee memory fee = $.byusd.quoteSend(lzParam, false); ( MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt - ) = $.lzbridge.send( + ) = $.byusd.send( lzParam, fee, address(this) ); } + function unwrapSwapAndBridge(SendParam calldata lzParam) public onlySFLUVRole(REDEEMER_ROLE) returns (MessagingReceipt memory, OFTReceipt memory) { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + + uint256 zapAmount = FixedPointMathLib.mulDiv( + lzParam.amountLD, + 10 ** $.sfluv.decimals(), + 10 ** $.byusd.decimals() + ); + + return(_unwrapSwapAndBridge(zapAmount, lzParam)); + } + + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata + ) external { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + + require(msg.sender == address($.honeyToBYUSDPool), "unauthorized pool"); + + if (amount0Delta > 0) { + IERC20Metadata($.honeyToBYUSDPool.token0()).transfer(msg.sender, uint256(amount0Delta)); + } + if (amount1Delta > 0) { + IERC20Metadata($.honeyToBYUSDPool.token1()).transfer(msg.sender, uint256(amount1Delta)); + } + } + /* INTERNAL */ @@ -181,6 +229,78 @@ contract SFLUVZapperv1 is ); } + function _unwrapSwapAndBridge(uint256 amount, SendParam calldata lzParam) private + returns (MessagingReceipt memory, OFTReceipt memory) + { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + // 1. Pull SFLUV + bool success = $.sfluv.transferFrom(_msgSender(), address(this), amount); + if (!success) revert TransferFailed(address($.sfluv), address(this), _msgSender(), amount); + + // 2. Unwrap to HONEY + success = $.sfluv.withdrawTo(address(this), amount); + if (!success) revert RedeemFailed(); + + uint256 honeyBalance = $.honey.balanceOf(address(this)); + + //2.5 tracking BYUSD before redeem/swap process just in case not enough is gotten + uint256 byusdBefore = $.byusd.balanceOf(address(this)); + + // 3. Check BYUSD inside Honey, and keep track of how much is redeemed + uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault); + + if (byusdAvailable > 0) { + uint256 byusdAvailableInHoney = byusdAvailable * 1e12; // 6 → 18 + uint256 redeemAmount = honeyBalance < byusdAvailableInHoney + ? honeyBalance + : byusdAvailableInHoney; + + + $.honeyFactory.redeem(address($.byusd), redeemAmount, address(this), false); + honeyBalance -= redeemAmount; + } + + // 4. Swap remaining HONEY via pool if needed + if (honeyBalance > 0) { + (uint160 sqrtPriceX96,,,,,,) = $.honeyToBYUSDPool.slot0(); + + // 95% minimum output + // sqrt(1 / 0.95) ≈ 1.025978352 + uint256 NUM = 1_025_978_352; // scaled by 1e9 + uint256 DEN = 1_000_000_000; + + uint160 sqrtPriceLimitX96 = uint160( + (uint256(sqrtPriceX96) * NUM) / DEN + ); + + $.honeyToBYUSDPool.swap( + address(this), + false, + int256(honeyBalance), + sqrtPriceLimitX96, + "" + ); + } + + uint256 byusdAfter = $.byusd.balanceOf(address(this)); + uint256 byusdAccumulated = byusdAfter - byusdBefore; + require(byusdAccumulated >= (amount * 1e6 * 95 / 100), "insufficient BYUSD from swap/redemption"); + + // 5. Bridge final BYUSD balance + MessagingFee memory fee = $.byusd.quoteSend(lzParam, false); + require( + $.byusd.balanceOf(address(this)) >= lzParam.amountLD, + "insufficient BYUSD after swap" + ); + + ( + MessagingReceipt memory mReceipt, + OFTReceipt memory oReceipt + ) = $.byusd.send(lzParam, fee, address(this)); + return (mReceipt, oReceipt); +} + + function _authorizeUpgrade( address newImplementation ) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {} diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index 11a8341..bc9527b 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -11,21 +11,17 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; import "@berachain/contracts/honey/IHoneyFactory.sol"; +import { IBYUSD } from "../src/SFLUVZapperv1.sol"; +import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; +import { MessagingReceipt, MessagingFee } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; + -interface BYUSD { - function balanceOf(address account) external view returns (uint256); - function symbol() external view returns (string memory); - function decimals() external view returns (uint8); - function approve(address spender, uint256 amount) external returns (bool); - function transfer(address recipient, uint256 amount) external returns (bool); - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); -} contract SFLUVZapperTest is Test { SFLUVv2 public testLUVCoin; SFLUVZapperv1 public testSFLUVZapper; - BYUSD public testBYUSD; + IBYUSD public testBYUSD; address internal peon; address defaultAdmin = address(0x90496e23825aD0C8107d04671e6a27f30630Fc35); @@ -46,20 +42,23 @@ contract SFLUVZapperTest is Test { vm.createSelectFork(forkURL, forkBlock); address sfluv = vm.envAddress('SFLUV_ADDRESS'); address byusd = vm.envAddress("BYUSD_ADDRESS"); + address honey = vm.envAddress("HONEY_ADDRESS"); + address honeyToBYUSDPool = vm.envAddress("HONEY_BYUSD_POOL_ADDRESS"); + address byusdVault = vm.envAddress("BYUSD_VAULT_ADDRESS"); testLUVCoin = SFLUVv2(sfluv); - testBYUSD = BYUSD(byusd); + testBYUSD = IBYUSD(byusd); testSFLUVZapper = new SFLUVZapperv1(); peon = makeAddr("peon"); - - address lzbridge = address(0x1); address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); SFLUVZapperStorageInit memory testStorage = SFLUVZapperStorageInit( - lzbridge, honeyFactory, sfluv, - byusd + byusd, + honey, + honeyToBYUSDPool, + byusdVault ); zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (defaultAdmin, testStorage))); @@ -160,4 +159,69 @@ contract SFLUVZapperTest is Test { vm.stopPrank(); } + function testUnwrapSwapAndBridge() public { + // ---------------------------- + // Setup decimal helpers + // ---------------------------- + uint256 byusdExp = 10**testBYUSD.decimals(); + uint256 sfluvExp = 10**testLUVCoin.decimals(); + + // Send some BYUSD to defaultAdmin from whale + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testBYUSD.transfer(defaultAdmin, 1000 * byusdExp); + + // Approve zapper + vm.startPrank(defaultAdmin); + testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); + + // Wrap 100 BYUSD into SFLUV first + testSFLUVZapper.zapIn(100 * byusdExp); + + uint256 startingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + console.log("Starting SFLUV balance:", startingLUVBalance); + + // ---------------------------- + // Construct SendParam + // ---------------------------- + SendParam memory lzParam = SendParam({ + dstEid: 30101, + to: bytes32(uint256(uint160(address(defaultAdmin)))), // spoofed ETH address + amountLD: 50 * byusdExp, + minAmountLD: 0, + extraOptions: "", + composeMsg: "", + oftCmd: "" +}); + + // ---------------------------- + // Call unwrap/swap/bridge + // ---------------------------- + (MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt) = + testSFLUVZapper.unwrapSwapAndBridge(lzParam); + + // ---------------------------- + // Logging receipts for inspection + // ---------------------------- + console.log("MessagingReceipt:"); + console.log(" dstEid:", mReceipt.dstEid); + console.log(" srcEid:", mReceipt.srcEid); + console.log(" nonce:", mReceipt.nonce); + console.log(" success:", mReceipt.success ? 1 : 0); + + console.log("OFTReceipt:"); + console.log(" amount:", oReceipt.amount); + console.log(" fee:", oReceipt.fee); + console.log(" success:", oReceipt.success ? 1 : 0); + + // ---------------------------- + // Verify SFLUV balance decreased + // ---------------------------- + uint256 endingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + console.log("Ending SFLUV balance:", endingLUVBalance); + assert(endingLUVBalance < startingLUVBalance); + + vm.stopPrank(); } + + + } From e8c806fa1c85fd002f280af3eaa28f938e9ffa6a Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Thu, 22 Jan 2026 15:19:44 -0800 Subject: [PATCH 11/22] zapper finished and needs security review before deployment --- src/SFLUVZapperv1.sol | 36 ++++++++++++++++++++++++++++++++---- test/SFLUVZapperv1.sol | 42 +++++++++++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 499db81..3e0c9b0 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -11,7 +11,7 @@ import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/in import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol"; import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol"; import { ILiquidityPool} from "./ILiquidityPool.sol"; - +import "forge-std/console.sol"; import "./ISFLUVZapperErrors.sol"; import "./SFLUVv2.sol"; @@ -113,6 +113,7 @@ contract SFLUVZapperv1 is $.byusdVault = s.byusdVault; $.byusd.approve(s.honeyFactory, type(uint256).max); + $.byusd.approve(s.byusd, type(uint256).max); $.honeyFactory.honey().approve(s.sfluv, type(uint256).max); } @@ -232,19 +233,28 @@ contract SFLUVZapperv1 is function _unwrapSwapAndBridge(uint256 amount, SendParam calldata lzParam) private returns (MessagingReceipt memory, OFTReceipt memory) { + console.log("LZParam passed in:"); + console.log(" dstEid:", lzParam.dstEid); + console.logBytes32(lzParam.to); + console.log(" amountLD:", lzParam.amountLD); + console.log(" minAmountLD:", lzParam.minAmountLD); + console.log("amount of SLFUV to unwrap:", amount); + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); // 1. Pull SFLUV bool success = $.sfluv.transferFrom(_msgSender(), address(this), amount); if (!success) revert TransferFailed(address($.sfluv), address(this), _msgSender(), amount); + console.log("SFLUV transferred into Zapper contract:", amount); // 2. Unwrap to HONEY success = $.sfluv.withdrawTo(address(this), amount); if (!success) revert RedeemFailed(); - uint256 honeyBalance = $.honey.balanceOf(address(this)); + console.log("HONEY balance after redeem:", honeyBalance); //2.5 tracking BYUSD before redeem/swap process just in case not enough is gotten uint256 byusdBefore = $.byusd.balanceOf(address(this)); + console.log("BYUSD balance before redeem/swap:", byusdBefore); // 3. Check BYUSD inside Honey, and keep track of how much is redeemed uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault); @@ -258,6 +268,7 @@ contract SFLUVZapperv1 is $.honeyFactory.redeem(address($.byusd), redeemAmount, address(this), false); honeyBalance -= redeemAmount; + console.log("Redeemed BYUSD from HONEY:", redeemAmount / 1e12); } // 4. Swap remaining HONEY via pool if needed @@ -280,27 +291,44 @@ contract SFLUVZapperv1 is sqrtPriceLimitX96, "" ); + console.log("Swapped remaining HONEY for BYUSD:", honeyBalance); } uint256 byusdAfter = $.byusd.balanceOf(address(this)); + console.log("BYUSD balance after redeem/swap:", byusdAfter); uint256 byusdAccumulated = byusdAfter - byusdBefore; - require(byusdAccumulated >= (amount * 1e6 * 95 / 100), "insufficient BYUSD from swap/redemption"); + console.log("amount we're comparing BYUSD accumulated against:", lzParam.amountLD * 95 / 100); + console.log("BYUSD accumulated from redeem/swap:", byusdAccumulated); + require(byusdAccumulated >= (lzParam.amountLD * 95 / 100), "insufficient BYUSD from swap/redemption"); // 5. Bridge final BYUSD balance MessagingFee memory fee = $.byusd.quoteSend(lzParam, false); + fee.nativeFee = fee.nativeFee * 10; require( $.byusd.balanceOf(address(this)) >= lzParam.amountLD, "insufficient BYUSD after swap" ); + console.log("Zapper Bera balance before bridging:", address(this).balance); + console.log("Native fee for bridging:", fee.nativeFee); + console.log("LZ token fee for bridging:", fee.lzTokenFee); + console.log("Address of this contract:", address(this)); + console.log("BYUSD's bera balance before bridging:", address($.byusd).balance); + console.log("zapper BYUSD balance before bridging:", $.byusd.balanceOf(address(this))); + console.logBytes32(lzParam.to); ( MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt - ) = $.byusd.send(lzParam, fee, address(this)); + ) = $.byusd.send{ value: fee.nativeFee }(lzParam, fee, address(this)); + + console.log("zapper BYUSD balance after bridging:", $.byusd.balanceOf(address(this))); + console.log("Bridging BYUSD amountLD:", lzParam.amountLD); return (mReceipt, oReceipt); } + + function _authorizeUpgrade( address newImplementation ) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {} diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index bc9527b..d061ffe 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -14,16 +14,30 @@ import "@berachain/contracts/honey/IHoneyFactory.sol"; import { IBYUSD } from "../src/SFLUVZapperv1.sol"; import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; import { MessagingReceipt, MessagingFee } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; +import "forge-std/console.sol"; + +interface IWBERA { + // ERC20 standard functions you might need + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 amount) external returns (bool); + function approve(address spender, uint256 amount) external returns (bool); + // unwrap WBERA to native BERA + function withdraw(uint256 amount) external; + // optional: wrap BERA into WBERA + function deposit() external payable; +} contract SFLUVZapperTest is Test { SFLUVv2 public testLUVCoin; SFLUVZapperv1 public testSFLUVZapper; IBYUSD public testBYUSD; + IWBERA public wbera; address internal peon; + address vitEth = address(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); address defaultAdmin = address(0x90496e23825aD0C8107d04671e6a27f30630Fc35); uint public oneEther = 1 ether; // TODO: elsewhere? @@ -45,6 +59,8 @@ contract SFLUVZapperTest is Test { address honey = vm.envAddress("HONEY_ADDRESS"); address honeyToBYUSDPool = vm.envAddress("HONEY_BYUSD_POOL_ADDRESS"); address byusdVault = vm.envAddress("BYUSD_VAULT_ADDRESS"); + address wrappedNative = vm.envAddress("WRAPPED_NATIVE_ADDRESS"); + wbera = IWBERA(wrappedNative); testLUVCoin = SFLUVv2(sfluv); testBYUSD = IBYUSD(byusd); testSFLUVZapper = new SFLUVZapperv1(); @@ -170,9 +186,20 @@ contract SFLUVZapperTest is Test { vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); testBYUSD.transfer(defaultAdmin, 1000 * byusdExp); + // Send some BYUSD to SFLuvZapper from whale + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testBYUSD.transfer(address(testSFLUVZapper), 100 * byusdExp); + + vm.deal(address(testSFLUVZapper), 100 * sfluvExp); + console.log("SFLUVZapper BERA balance:", address(testSFLUVZapper).balance); + vm.deal(address(testBYUSD), 100 * sfluvExp); + console.log("BYUSD BERA balance:", address(testBYUSD).balance); + + console.log("approving zapper"); // Approve zapper vm.startPrank(defaultAdmin); testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); + testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); // Wrap 100 BYUSD into SFLUV first testSFLUVZapper.zapIn(100 * byusdExp); @@ -185,7 +212,7 @@ contract SFLUVZapperTest is Test { // ---------------------------- SendParam memory lzParam = SendParam({ dstEid: 30101, - to: bytes32(uint256(uint160(address(defaultAdmin)))), // spoofed ETH address + to: bytes32(uint256(uint160(vitEth))), // spoofed ETH address amountLD: 50 * byusdExp, minAmountLD: 0, extraOptions: "", @@ -203,15 +230,16 @@ contract SFLUVZapperTest is Test { // Logging receipts for inspection // ---------------------------- console.log("MessagingReceipt:"); - console.log(" dstEid:", mReceipt.dstEid); - console.log(" srcEid:", mReceipt.srcEid); + console.logBytes32(mReceipt.guid); console.log(" nonce:", mReceipt.nonce); - console.log(" success:", mReceipt.success ? 1 : 0); + console.log(" messaging fee - nativeFee:", mReceipt.fee.nativeFee); + console.log(" messaging fee - lzTokenFee:", mReceipt.fee.lzTokenFee); + + console.log("OFTReceipt:"); - console.log(" amount:", oReceipt.amount); - console.log(" fee:", oReceipt.fee); - console.log(" success:", oReceipt.success ? 1 : 0); + console.log(" amountSent:", oReceipt.amountSentLD); + console.log(" amountRecieved:", oReceipt.amountReceivedLD); // ---------------------------- // Verify SFLUV balance decreased From 50473ba73f2093f4808b7ecf78f2773cab940d25 Mon Sep 17 00:00:00 2001 From: Paul O'Leary Date: Fri, 23 Jan 2026 09:12:24 -0800 Subject: [PATCH 12/22] WIP - review and tweaks. --- NOTES.txt | 7 ++ script/DeploySFLUVZapperv1.s.sol | 7 +- script/GrantSFLUVv2.s.sol | 5 +- src/SFLUVZapperv1.sol | 191 ++++++++++++++++--------------- 4 files changed, 117 insertions(+), 93 deletions(-) diff --git a/NOTES.txt b/NOTES.txt index d1e8020..ad6b5b3 100644 --- a/NOTES.txt +++ b/NOTES.txt @@ -41,3 +41,10 @@ src/SFLUVv2.sol:SFLUVv2 \ --etherscan-api-key WMP6HJHJ9NAK8A63SNAG1G4KHAUKU5X4SQ \ --verifier-url 'https://api.berascan.com/api' \ --watch + +# deploy zapper +forge script script/DeploySFLUVZapperv1.s.sol:DeploySFLUVZapperv1 --rpc-url https://rpc.berachain.com --private-key $PRIVATE_KEY --broadcast +0xE82b2109f437fB66C1C09dc0ff95b0435C7836C4 + +# grant zapper mainnet +forge script script/GrantSFLUVZapperv1.s.sol:GrantSFLUVZapperv1 --rpc-url https://rpc.berachain.com --private-key $PRIVATE_KEY --broadcast diff --git a/script/DeploySFLUVZapperv1.s.sol b/script/DeploySFLUVZapperv1.s.sol index 000b82b..2efd5d8 100644 --- a/script/DeploySFLUVZapperv1.s.sol +++ b/script/DeploySFLUVZapperv1.s.sol @@ -7,7 +7,7 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IDeployErrors} from "./IDeployErrors.sol"; -contract DeploySFLUVv2 is Script, IDeployErrors { +contract DeploySFLUVZapperv1 is Script, IDeployErrors { function run() public { address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); @@ -34,6 +34,11 @@ contract DeploySFLUVv2 is Script, IDeployErrors { bool isAdmin = token.hasRole(token.DEFAULT_ADMIN_ROLE(), msg.sender); if (!isAdmin) revert NotAdmin(); + bool isMinterAdmin = token.hasRole(token.MINTER_ADMIN_ROLE(), msg.sender); + require(isMinterAdmin); + bool isRedeemerAdmin = token.hasRole(token.REDEEMER_ADMIN_ROLE(), msg.sender); + require(isRedeemerAdmin); + SFLUVZapperv1 impl = new SFLUVZapperv1(); ERC1967Proxy proxy = new ERC1967Proxy(address(impl), diff --git a/script/GrantSFLUVv2.s.sol b/script/GrantSFLUVv2.s.sol index 6207b69..665c2c5 100644 --- a/script/GrantSFLUVv2.s.sol +++ b/script/GrantSFLUVv2.s.sol @@ -15,7 +15,8 @@ contract GrantSFLUVv2 is Script { function run() public { vm.startBroadcast(); SFLUVv2 impl = SFLUVv2(SFLUV_V2_ADDR); - impl.grantRole(impl.MINTER_ADMIN_ROLE(), TARGET_ADDR); + // impl.grantRole(impl.MINTER_ADMIN_ROLE(), TARGET_ADDR); + impl.grantRole(impl.REDEEMER_ADMIN_ROLE(), TARGET_ADDR); vm.stopBroadcast(); } -} \ No newline at end of file +} diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 3e0c9b0..fdf3a2d 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -33,7 +33,10 @@ contract SFLUVZapperv1 is ISFLUVZapperErrors { bytes32 public constant MINTER_ROLE = keccak256("MINTER"); + bytes32 public constant MINTER_ADMIN_ROLE = keccak256("MINTER_ADMIN"); + bytes32 public constant REDEEMER_ROLE = keccak256("REDEEMER"); + bytes32 public constant REDEEMER_ADMIN_ROLE = keccak256("REDEEMER_ADMIN"); /* STORAGE */ @@ -87,6 +90,9 @@ contract SFLUVZapperv1 is __Storage_init(_storage); _grantRole(DEFAULT_ADMIN_ROLE, _governance); + + _setRoleAdmin(MINTER_ROLE, MINTER_ADMIN_ROLE); + _setRoleAdmin(REDEEMER_ROLE, REDEEMER_ADMIN_ROLE); } function __Storage_init( @@ -149,7 +155,7 @@ contract SFLUVZapperv1 is ( MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt - ) = $.byusd.send( + ) = $.byusd.send{ value: fee.nativeFee }( lzParam, fee, address(this) @@ -187,7 +193,6 @@ contract SFLUVZapperv1 is /* INTERNAL */ - function _zapInTo(uint256 amount, address to) private { SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); @@ -231,103 +236,109 @@ contract SFLUVZapperv1 is } function _unwrapSwapAndBridge(uint256 amount, SendParam calldata lzParam) private - returns (MessagingReceipt memory, OFTReceipt memory) - { - console.log("LZParam passed in:"); - console.log(" dstEid:", lzParam.dstEid); - console.logBytes32(lzParam.to); - console.log(" amountLD:", lzParam.amountLD); - console.log(" minAmountLD:", lzParam.minAmountLD); - console.log("amount of SLFUV to unwrap:", amount); - - SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); - // 1. Pull SFLUV - bool success = $.sfluv.transferFrom(_msgSender(), address(this), amount); - if (!success) revert TransferFailed(address($.sfluv), address(this), _msgSender(), amount); - console.log("SFLUV transferred into Zapper contract:", amount); - - // 2. Unwrap to HONEY - success = $.sfluv.withdrawTo(address(this), amount); - if (!success) revert RedeemFailed(); - uint256 honeyBalance = $.honey.balanceOf(address(this)); - console.log("HONEY balance after redeem:", honeyBalance); - - //2.5 tracking BYUSD before redeem/swap process just in case not enough is gotten - uint256 byusdBefore = $.byusd.balanceOf(address(this)); - console.log("BYUSD balance before redeem/swap:", byusdBefore); - - // 3. Check BYUSD inside Honey, and keep track of how much is redeemed - uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault); - - if (byusdAvailable > 0) { - uint256 byusdAvailableInHoney = byusdAvailable * 1e12; // 6 → 18 - uint256 redeemAmount = honeyBalance < byusdAvailableInHoney - ? honeyBalance - : byusdAvailableInHoney; - - - $.honeyFactory.redeem(address($.byusd), redeemAmount, address(this), false); - honeyBalance -= redeemAmount; - console.log("Redeemed BYUSD from HONEY:", redeemAmount / 1e12); - } + returns (MessagingReceipt memory, OFTReceipt memory) { + console.log("LZParam passed in:"); + console.log(" dstEid:", lzParam.dstEid); + console.logBytes32(lzParam.to); + console.log(" amountLD:", lzParam.amountLD); + console.log(" minAmountLD:", lzParam.minAmountLD); + console.log("amount of SFLUV to unwrap:", amount); - // 4. Swap remaining HONEY via pool if needed - if (honeyBalance > 0) { - (uint160 sqrtPriceX96,,,,,,) = $.honeyToBYUSDPool.slot0(); + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + // 1. Pull SFLUV + bool success = $.sfluv.transferFrom(_msgSender(), address(this), amount); + if (!success) revert TransferFailed(address($.sfluv), address(this), _msgSender(), amount); + console.log("SFLUV transferred into Zapper contract:", amount); - // 95% minimum output - // sqrt(1 / 0.95) ≈ 1.025978352 - uint256 NUM = 1_025_978_352; // scaled by 1e9 - uint256 DEN = 1_000_000_000; + // 2. Unwrap to HONEY + success = $.sfluv.withdrawTo(address(this), amount); + if (!success) revert RedeemFailed(); + uint256 honeyBalance = $.honey.balanceOf(address(this)); + console.log("HONEY balance after redeem:", honeyBalance); - uint160 sqrtPriceLimitX96 = uint160( - (uint256(sqrtPriceX96) * NUM) / DEN - ); + // 2.5 tracking BYUSD before redeem/swap process just in case not enough is gotten + uint256 byusdBefore = $.byusd.balanceOf(address(this)); + console.log("BYUSD balance before redeem/swap:", byusdBefore); - $.honeyToBYUSDPool.swap( - address(this), - false, - int256(honeyBalance), - sqrtPriceLimitX96, - "" - ); - console.log("Swapped remaining HONEY for BYUSD:", honeyBalance); - } + // 3. Check BYUSD inside Honey, and keep track of how much is redeemed + uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault); - uint256 byusdAfter = $.byusd.balanceOf(address(this)); - console.log("BYUSD balance after redeem/swap:", byusdAfter); - uint256 byusdAccumulated = byusdAfter - byusdBefore; - console.log("amount we're comparing BYUSD accumulated against:", lzParam.amountLD * 95 / 100); - console.log("BYUSD accumulated from redeem/swap:", byusdAccumulated); - require(byusdAccumulated >= (lzParam.amountLD * 95 / 100), "insufficient BYUSD from swap/redemption"); - - // 5. Bridge final BYUSD balance - MessagingFee memory fee = $.byusd.quoteSend(lzParam, false); - fee.nativeFee = fee.nativeFee * 10; - require( - $.byusd.balanceOf(address(this)) >= lzParam.amountLD, - "insufficient BYUSD after swap" - ); - - console.log("Zapper Bera balance before bridging:", address(this).balance); - console.log("Native fee for bridging:", fee.nativeFee); - console.log("LZ token fee for bridging:", fee.lzTokenFee); - console.log("Address of this contract:", address(this)); - console.log("BYUSD's bera balance before bridging:", address($.byusd).balance); - console.log("zapper BYUSD balance before bridging:", $.byusd.balanceOf(address(this))); - console.logBytes32(lzParam.to); - ( - MessagingReceipt memory mReceipt, - OFTReceipt memory oReceipt - ) = $.byusd.send{ value: fee.nativeFee }(lzParam, fee, address(this)); + if (byusdAvailable > 0) { + uint256 byusdAvailableInHoney = byusdAvailable * 1e12; // 6 → 18 + uint256 redeemAmount = honeyBalance < byusdAvailableInHoney + ? honeyBalance + : byusdAvailableInHoney; - console.log("zapper BYUSD balance after bridging:", $.byusd.balanceOf(address(this))); - console.log("Bridging BYUSD amountLD:", lzParam.amountLD); - return (mReceipt, oReceipt); -} + $.honeyFactory.redeem(address($.byusd), redeemAmount, address(this), false); + honeyBalance -= redeemAmount; + console.log("Redeemed BYUSD from HONEY:", redeemAmount / 1e12); + } + + // 4. Swap remaining HONEY via pool if needed + if (honeyBalance > 0) { + (uint160 sqrtPriceX96,,,,,,) = $.honeyToBYUSDPool.slot0(); + + // 95% minimum output + // sqrt(1 / 0.95) ≈ 1.025978352 + uint256 NUM = 1_025_978_352; // scaled by 1e9 + uint256 DEN = 1_000_000_000; + + uint160 sqrtPriceLimitX96 = uint160( + (uint256(sqrtPriceX96) * NUM) / DEN + ); + + $.honeyToBYUSDPool.swap( + address(this), + false, + int256(honeyBalance), + sqrtPriceLimitX96, + "" + ); + console.log("Swapped remaining HONEY for BYUSD:", honeyBalance); + } + + uint256 byusdAfter = $.byusd.balanceOf(address(this)); + console.log("BYUSD balance after redeem/swap:", byusdAfter); + uint256 byusdAccumulated = byusdAfter - byusdBefore; + console.log("amount we're comparing BYUSD accumulated against:", lzParam.amountLD * 95 / 100); + console.log("BYUSD accumulated from redeem/swap:", byusdAccumulated); + require(byusdAccumulated >= (lzParam.amountLD * 95 / 100), "insufficient BYUSD from swap/redemption"); + + // 5. Bridge final BYUSD balance + MessagingFee memory fee = $.byusd.quoteSend(lzParam, false); + require( + $.byusd.balanceOf(address(this)) >= lzParam.amountLD, + "insufficient BYUSD after swap" + ); + + console.log("Zapper Bera balance before bridging:", address(this).balance); + console.log("Native fee for bridging:", fee.nativeFee); + console.log("LZ token fee for bridging:", fee.lzTokenFee); + console.log("Address of this contract:", address(this)); + console.log("BYUSD's bera balance before bridging:", address($.byusd).balance); + console.log("zapper BYUSD balance before bridging:", $.byusd.balanceOf(address(this))); + console.logBytes32(lzParam.to); + ( + MessagingReceipt memory mReceipt, + OFTReceipt memory oReceipt + ) = $.byusd.send{ value: fee.nativeFee }(lzParam, fee, address(this)); + console.log("zapper BYUSD balance after bridging:", $.byusd.balanceOf(address(this))); + console.log("Bridging BYUSD amountLD:", lzParam.amountLD); + return (mReceipt, oReceipt); + } + // allow receipt of BERA + receive() external payable {} + function recoverTo(address payable to) public onlyRole(DEFAULT_ADMIN_ROLE) { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + to.send(address(this).balance); // recover all native funds + uint256 byusdBalance = $.byusd.balanceOf(address(this)); + if (byusdBalance > 0) { + $.byusd.approve(to, byusdBalance); // recover all byusd funds + } + } function _authorizeUpgrade( address newImplementation From f7b5bb80525fa90153293ebdedb81900339015fd Mon Sep 17 00:00:00 2001 From: Paul O'Leary Date: Fri, 23 Jan 2026 09:14:48 -0800 Subject: [PATCH 13/22] WIP - more --- .gitignore | 2 ++ script/GrantSFLUVZapperv1.s.sol | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 script/GrantSFLUVZapperv1.s.sol diff --git a/.gitignore b/.gitignore index 7194a02..c567d06 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ cache/ out/ +broadcast/ + # Ignores development broadcast logs !/broadcast /broadcast/*/31337/ diff --git a/script/GrantSFLUVZapperv1.s.sol b/script/GrantSFLUVZapperv1.s.sol new file mode 100644 index 0000000..3f2b5c5 --- /dev/null +++ b/script/GrantSFLUVZapperv1.s.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.8.26; + +import "forge-std/Script.sol"; +// import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SFLUVZapperv1} from "../src/SFLUVZapperv1.sol"; + +contract GrantSFLUVZapperv1 is Script { + + // mainnet + address constant private SFLUV_ZAPPER_ADDR = 0xE82b2109f437fB66C1C09dc0ff95b0435C7836C4; + + // sanchez test account + address constant private TARGET_ADDR = 0x8b631C26537a784082A55528eaD52271ce88572e; + + function run() public { + vm.startBroadcast(); + SFLUVZapperv1 impl = SFLUVZapperv1(SFLUV_ZAPPER_ADDR); + impl.grantRole(impl.REDEEMER_ROLE(), TARGET_ADDR); + vm.stopBroadcast(); + } +} From a85861237d07d554b5e6f1a76bcff504667e773b Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Fri, 23 Jan 2026 10:36:07 -0800 Subject: [PATCH 14/22] changed unwrap function parameters --- src/SFLUVZapperv1.sol | 44 ++++++++++++++++++++++++------------------ test/SFLUVZapperv1.sol | 18 ++--------------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index fdf3a2d..9791cb3 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -162,16 +162,9 @@ contract SFLUVZapperv1 is ); } - function unwrapSwapAndBridge(SendParam calldata lzParam) public onlySFLUVRole(REDEEMER_ROLE) returns (MessagingReceipt memory, OFTReceipt memory) { - SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); - - uint256 zapAmount = FixedPointMathLib.mulDiv( - lzParam.amountLD, - 10 ** $.sfluv.decimals(), - 10 ** $.byusd.decimals() - ); - - return(_unwrapSwapAndBridge(zapAmount, lzParam)); + function unwrapSwapAndBridge(uint256 amount, address to) public onlySFLUVRole(REDEEMER_ROLE) returns (MessagingReceipt memory, OFTReceipt memory) { + //amount is 18 decimals, for SFLUV + return(_unwrapSwapAndBridge(amount, to)); } function uniswapV3SwapCallback( @@ -235,14 +228,16 @@ contract SFLUVZapperv1 is ); } - function _unwrapSwapAndBridge(uint256 amount, SendParam calldata lzParam) private + function _unwrapSwapAndBridge(uint256 amount, address to) private returns (MessagingReceipt memory, OFTReceipt memory) { - console.log("LZParam passed in:"); - console.log(" dstEid:", lzParam.dstEid); - console.logBytes32(lzParam.to); - console.log(" amountLD:", lzParam.amountLD); - console.log(" minAmountLD:", lzParam.minAmountLD); - console.log("amount of SFLUV to unwrap:", amount); + uint byusdAmount = FixedPointMathLib.mulDiv( + amount, + 10 ** _getSFLUVZapperStorage().byusd.decimals(), + 10 ** _getSFLUVZapperStorage().sfluv.decimals() + ); + console.log(" amount passed in:", amount); + console.log(" BYUSD amount passed in:", byusdAmount); + console.log("address passed in:", to); SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); // 1. Pull SFLUV @@ -300,9 +295,20 @@ contract SFLUVZapperv1 is uint256 byusdAfter = $.byusd.balanceOf(address(this)); console.log("BYUSD balance after redeem/swap:", byusdAfter); uint256 byusdAccumulated = byusdAfter - byusdBefore; - console.log("amount we're comparing BYUSD accumulated against:", lzParam.amountLD * 95 / 100); + console.log("amount we're comparing BYUSD accumulated against:", byusdAmount * 95 / 100); console.log("BYUSD accumulated from redeem/swap:", byusdAccumulated); - require(byusdAccumulated >= (lzParam.amountLD * 95 / 100), "insufficient BYUSD from swap/redemption"); + require(byusdAccumulated >= (byusdAmount * 95 / 100), "insufficient BYUSD from swap/redemption"); + + // Prepare LZ send params + SendParam memory lzParam = SendParam({ + dstEid: 30101, //ETH layerzero eid + to: bytes32(uint256(uint160(to))), + amountLD: byusdAmount, + minAmountLD: byusdAmount, + extraOptions: "", + composeMsg: "", + oftCmd: "" + }); // 5. Bridge final BYUSD balance MessagingFee memory fee = $.byusd.quoteSend(lzParam, false); diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index d061ffe..0a36d67 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -78,7 +78,7 @@ contract SFLUVZapperTest is Test { ); zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (defaultAdmin, testStorage))); - testSFLUVZapper = SFLUVZapperv1(address(zapperproxy)); + testSFLUVZapper = SFLUVZapperv1(payable(zapperproxy)); vm.startPrank(defaultAdmin); testLUVCoin.grantRole(testLUVCoin.REDEEMER_ADMIN_ROLE(), defaultAdmin); testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), defaultAdmin); @@ -207,24 +207,11 @@ contract SFLUVZapperTest is Test { uint256 startingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); console.log("Starting SFLUV balance:", startingLUVBalance); - // ---------------------------- - // Construct SendParam - // ---------------------------- - SendParam memory lzParam = SendParam({ - dstEid: 30101, - to: bytes32(uint256(uint160(vitEth))), // spoofed ETH address - amountLD: 50 * byusdExp, - minAmountLD: 0, - extraOptions: "", - composeMsg: "", - oftCmd: "" -}); - // ---------------------------- // Call unwrap/swap/bridge // ---------------------------- (MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt) = - testSFLUVZapper.unwrapSwapAndBridge(lzParam); + testSFLUVZapper.unwrapSwapAndBridge(50 * sfluvExp, vitEth); // ---------------------------- // Logging receipts for inspection @@ -251,5 +238,4 @@ contract SFLUVZapperTest is Test { vm.stopPrank(); } - } From beb23183bcd47c1539ea230edab452c5f8280db1 Mon Sep 17 00:00:00 2001 From: Paul O'Leary Date: Sun, 25 Jan 2026 09:20:48 -0800 Subject: [PATCH 15/22] WIP - fixes reentrancy. correct byusd in honey calc. etc. --- NOTES.txt | 6 ++- script/DeploySFLUVZapperv1.s.sol | 1 - script/GrantSFLUVZapperv1.s.sol | 11 ++-- script/GrantSFLUVv2.s.sol | 4 +- src/SFLUVZapperv1.sol | 86 +++++++++++++++++++------------- 5 files changed, 67 insertions(+), 41 deletions(-) diff --git a/NOTES.txt b/NOTES.txt index ad6b5b3..9d1fbd2 100644 --- a/NOTES.txt +++ b/NOTES.txt @@ -44,7 +44,11 @@ src/SFLUVv2.sol:SFLUVv2 \ # deploy zapper forge script script/DeploySFLUVZapperv1.s.sol:DeploySFLUVZapperv1 --rpc-url https://rpc.berachain.com --private-key $PRIVATE_KEY --broadcast -0xE82b2109f437fB66C1C09dc0ff95b0435C7836C4 +0xd0EBD0495750899D18b915BDeba789E2defdC394 # grant zapper mainnet forge script script/GrantSFLUVZapperv1.s.sol:GrantSFLUVZapperv1 --rpc-url https://rpc.berachain.com --private-key $PRIVATE_KEY --broadcast + +https://api.etherscan.io/v2/api?chainid=80094&action=verify-contract&apikey=WMP6HJHJ9NAK8A63SNAG1G4KHAUKU5X4SQ + +forge verify-contract --watch --chain-id 80094 0x94d86383085bDde798C3F9Cb6d06f73B5DcCC11e src/SFLUVZapperv1.sol:SFLUVZapperv1 --verifier etherscan --etherscan-api-key WMP6HJHJ9NAK8A63SNAG1G4KHAUKU5X4SQ diff --git a/script/DeploySFLUVZapperv1.s.sol b/script/DeploySFLUVZapperv1.s.sol index 2efd5d8..41e377d 100644 --- a/script/DeploySFLUVZapperv1.s.sol +++ b/script/DeploySFLUVZapperv1.s.sol @@ -17,7 +17,6 @@ contract DeploySFLUVZapperv1 is Script, IDeployErrors { address honeyToBYUSDPool = vm.envAddress("HONEY_BYUSD_POOL_ADDRESS"); address byusdVault = vm.envAddress("BYUSD_VAULT_ADDRESS"); - SFLUVZapperStorageInit memory s = SFLUVZapperStorageInit( honeyFactory, sfluv, diff --git a/script/GrantSFLUVZapperv1.s.sol b/script/GrantSFLUVZapperv1.s.sol index 3f2b5c5..6050adb 100644 --- a/script/GrantSFLUVZapperv1.s.sol +++ b/script/GrantSFLUVZapperv1.s.sol @@ -7,15 +7,20 @@ import {SFLUVZapperv1} from "../src/SFLUVZapperv1.sol"; contract GrantSFLUVZapperv1 is Script { // mainnet - address constant private SFLUV_ZAPPER_ADDR = 0xE82b2109f437fB66C1C09dc0ff95b0435C7836C4; + address constant private SFLUV_ZAPPER_ADDR = 0xd0EBD0495750899D18b915BDeba789E2defdC394; // sanchez test account address constant private TARGET_ADDR = 0x8b631C26537a784082A55528eaD52271ce88572e; function run() public { vm.startBroadcast(); - SFLUVZapperv1 impl = SFLUVZapperv1(SFLUV_ZAPPER_ADDR); - impl.grantRole(impl.REDEEMER_ROLE(), TARGET_ADDR); + SFLUVZapperv1 impl = SFLUVZapperv1(payable(SFLUV_ZAPPER_ADDR)); + if (!impl.hasRole(impl.REDEEMER_ADMIN_ROLE(), msg.sender)) { + impl.grantRole(impl.REDEEMER_ADMIN_ROLE(), msg.sender); + } + if (!impl.hasRole(impl.REDEEMER_ROLE(), TARGET_ADDR)) { + impl.grantRole(impl.REDEEMER_ROLE(), TARGET_ADDR); + } vm.stopBroadcast(); } } diff --git a/script/GrantSFLUVv2.s.sol b/script/GrantSFLUVv2.s.sol index 665c2c5..237ca3c 100644 --- a/script/GrantSFLUVv2.s.sol +++ b/script/GrantSFLUVv2.s.sol @@ -10,13 +10,13 @@ contract GrantSFLUVv2 is Script { address constant private SFLUV_V2_ADDR = 0x881cAd4f885c6701D8481c0eD347f6d35444eA7e; // mainnet gov account - address constant private TARGET_ADDR = 0x90496e23825aD0C8107d04671e6a27f30630Fc35; + address constant private TARGET_ADDR = 0x8b631C26537a784082A55528eaD52271ce88572e; function run() public { vm.startBroadcast(); SFLUVv2 impl = SFLUVv2(SFLUV_V2_ADDR); // impl.grantRole(impl.MINTER_ADMIN_ROLE(), TARGET_ADDR); - impl.grantRole(impl.REDEEMER_ADMIN_ROLE(), TARGET_ADDR); + impl.grantRole(impl.REDEEMER_ROLE(), TARGET_ADDR); vm.stopBroadcast(); } } diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 9791cb3..e575d21 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -5,7 +5,9 @@ import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol" import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20WrapperUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "@berachain/contracts/honey/IHoneyFactory.sol"; +import "@berachain/contracts/honey/HoneyFactory.sol"; import "@berachain/contracts/honey/Honey.sol"; import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol"; @@ -29,14 +31,16 @@ struct SFLUVZapperStorageInit { contract SFLUVZapperv1 is Initializable, AccessControlUpgradeable, + ReentrancyGuardUpgradeable, UUPSUpgradeable, ISFLUVZapperErrors { + // these roles need to match corresponding roles in SFLUVv2 bytes32 public constant MINTER_ROLE = keccak256("MINTER"); - bytes32 public constant MINTER_ADMIN_ROLE = keccak256("MINTER_ADMIN"); + // bytes32 public constant MINTER_ADMIN_ROLE = keccak256("MINTER_ADMIN"); bytes32 public constant REDEEMER_ROLE = keccak256("REDEEMER"); - bytes32 public constant REDEEMER_ADMIN_ROLE = keccak256("REDEEMER_ADMIN"); + // bytes32 public constant REDEEMER_ADMIN_ROLE = keccak256("REDEEMER_ADMIN"); /* STORAGE */ @@ -91,8 +95,8 @@ contract SFLUVZapperv1 is _grantRole(DEFAULT_ADMIN_ROLE, _governance); - _setRoleAdmin(MINTER_ROLE, MINTER_ADMIN_ROLE); - _setRoleAdmin(REDEEMER_ROLE, REDEEMER_ADMIN_ROLE); +// _setRoleAdmin(MINTER_ROLE, MINTER_ADMIN_ROLE); +// _setRoleAdmin(REDEEMER_ROLE, REDEEMER_ADMIN_ROLE); } function __Storage_init( @@ -123,24 +127,28 @@ contract SFLUVZapperv1 is $.honeyFactory.honey().approve(s.sfluv, type(uint256).max); } - function zapIn(uint256 amount) external onlySFLUVRole(MINTER_ROLE) { + function setLiquidityPool(address pool) public onlyRole(DEFAULT_ADMIN_ROLE) { + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); + $.honeyToBYUSDPool = ILiquidityPool(pool); + } + + function zapIn(uint256 amount) external onlySFLUVRole(MINTER_ROLE) nonReentrant { _zapInTo(amount, _msgSender()); } - function zapInTo(uint256 amount, address to) public onlySFLUVRole(MINTER_ROLE) { + function zapInTo(uint256 amount, address to) public onlySFLUVRole(MINTER_ROLE) nonReentrant { _zapInTo(amount, to); } - - function zapOut(uint256 amount) external onlySFLUVRole(REDEEMER_ROLE) { + function zapOut(uint256 amount) external onlySFLUVRole(REDEEMER_ROLE) nonReentrant { _zapOutTo(amount, _msgSender()); } - function zapOutTo(uint256 amount, address to) public onlySFLUVRole(REDEEMER_ROLE) { + function zapOutTo(uint256 amount, address to) public onlySFLUVRole(REDEEMER_ROLE) nonReentrant { _zapOutTo(amount, to); } - function zapOutAndLzBridgeTo(SendParam calldata lzParam) public onlySFLUVRole(REDEEMER_ROLE) { + function zapOutAndLzBridgeTo(SendParam calldata lzParam) public onlySFLUVRole(REDEEMER_ROLE) nonReentrant { SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); uint256 zapAmount = FixedPointMathLib.mulDiv( @@ -235,31 +243,39 @@ contract SFLUVZapperv1 is 10 ** _getSFLUVZapperStorage().byusd.decimals(), 10 ** _getSFLUVZapperStorage().sfluv.decimals() ); - console.log(" amount passed in:", amount); - console.log(" BYUSD amount passed in:", byusdAmount); - console.log("address passed in:", to); +// console.log("amount passed in:", amount); +// console.log("BYUSD amount passed in:", byusdAmount); +// console.log("address passed in:", to); SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); // 1. Pull SFLUV bool success = $.sfluv.transferFrom(_msgSender(), address(this), amount); if (!success) revert TransferFailed(address($.sfluv), address(this), _msgSender(), amount); - console.log("SFLUV transferred into Zapper contract:", amount); +// console.log("SFLUV transferred into Zapper contract:", amount); // 2. Unwrap to HONEY success = $.sfluv.withdrawTo(address(this), amount); if (!success) revert RedeemFailed(); uint256 honeyBalance = $.honey.balanceOf(address(this)); - console.log("HONEY balance after redeem:", honeyBalance); +// console.log("HONEY balance after redeem:", honeyBalance); - // 2.5 tracking BYUSD before redeem/swap process just in case not enough is gotten + // 2.5 tracking BYUSD before redeem/swap process just in case amount is insufficient uint256 byusdBefore = $.byusd.balanceOf(address(this)); - console.log("BYUSD balance before redeem/swap:", byusdBefore); +// console.log("BYUSD balance before redeem/swap:", byusdBefore); // 3. Check BYUSD inside Honey, and keep track of how much is redeemed - uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault); - - if (byusdAvailable > 0) { - uint256 byusdAvailableInHoney = byusdAvailable * 1e12; // 6 → 18 + // needs to actually respect this calc - but not public + // function _getSharesWithoutFees(address asset) internal view returns (uint256) { + // return vaults[asset].balanceOf(address(this)) - collectedAssetFees[asset]; + // } + HoneyFactory hf = HoneyFactory(address($.honeyFactory)); + // *** this gets the balance of BYUSD in HONEY in BYUSD decimals + // uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault) - byusdCurrentFees; + uint256 byusdCurrentFees = hf.collectedAssetFees(address($.byusd)); + // this gets the balance of BYUSD in HONEY decimals - i.e. 'shares' + uint256 byusdAvailableInHoney = hf.vaults(address($.byusd)).balanceOf(address($.honeyFactory)) - byusdCurrentFees; + + if (byusdAvailableInHoney > 0) { uint256 redeemAmount = honeyBalance < byusdAvailableInHoney ? honeyBalance : byusdAvailableInHoney; @@ -289,14 +305,14 @@ contract SFLUVZapperv1 is sqrtPriceLimitX96, "" ); - console.log("Swapped remaining HONEY for BYUSD:", honeyBalance); +// console.log("Swapped remaining HONEY for BYUSD:", honeyBalance); } uint256 byusdAfter = $.byusd.balanceOf(address(this)); - console.log("BYUSD balance after redeem/swap:", byusdAfter); +// console.log("BYUSD balance after redeem/swap:", byusdAfter); uint256 byusdAccumulated = byusdAfter - byusdBefore; - console.log("amount we're comparing BYUSD accumulated against:", byusdAmount * 95 / 100); - console.log("BYUSD accumulated from redeem/swap:", byusdAccumulated); +// console.log("amount we're comparing BYUSD accumulated against:", byusdAmount * 95 / 100); +// console.log("BYUSD accumulated from redeem/swap:", byusdAccumulated); require(byusdAccumulated >= (byusdAmount * 95 / 100), "insufficient BYUSD from swap/redemption"); // Prepare LZ send params @@ -317,20 +333,21 @@ contract SFLUVZapperv1 is "insufficient BYUSD after swap" ); - console.log("Zapper Bera balance before bridging:", address(this).balance); - console.log("Native fee for bridging:", fee.nativeFee); - console.log("LZ token fee for bridging:", fee.lzTokenFee); - console.log("Address of this contract:", address(this)); - console.log("BYUSD's bera balance before bridging:", address($.byusd).balance); - console.log("zapper BYUSD balance before bridging:", $.byusd.balanceOf(address(this))); - console.logBytes32(lzParam.to); +// console.log("Zapper Bera balance before bridging:", address(this).balance); +// console.log("Native fee for bridging:", fee.nativeFee); +// console.log("LZ token fee for bridging:", fee.lzTokenFee); +// console.log("Address of this contract:", address(this)); +// console.log("BYUSD's bera balance before bridging:", address($.byusd).balance); +// console.log("zapper BYUSD balance before bridging:", $.byusd.balanceOf(address(this))); +// console.logBytes32(lzParam.to); + ( MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt ) = $.byusd.send{ value: fee.nativeFee }(lzParam, fee, address(this)); - console.log("zapper BYUSD balance after bridging:", $.byusd.balanceOf(address(this))); - console.log("Bridging BYUSD amountLD:", lzParam.amountLD); +// console.log("zapper BYUSD balance after bridging:", $.byusd.balanceOf(address(this))); +// console.log("Bridging BYUSD amountLD:", lzParam.amountLD); return (mReceipt, oReceipt); } @@ -343,6 +360,7 @@ contract SFLUVZapperv1 is uint256 byusdBalance = $.byusd.balanceOf(address(this)); if (byusdBalance > 0) { $.byusd.approve(to, byusdBalance); // recover all byusd funds + $.byusd.transfer(to, byusdBalance); } } From a6d167af570ae0278f16b378f58094b88cc80289 Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Mon, 26 Jan 2026 16:45:58 -0800 Subject: [PATCH 16/22] fixed liquidity pool bug and wrote test for it --- src/ILiquidityPool.sol | 2 +- src/SFLUVZapperv1.sol | 2 ++ test/SFLUVZapperv1.sol | 53 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/ILiquidityPool.sol b/src/ILiquidityPool.sol index fbb8559..d5ef8b7 100644 --- a/src/ILiquidityPool.sol +++ b/src/ILiquidityPool.sol @@ -24,7 +24,7 @@ interface ILiquidityPool { uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, - uint8 feeProtocol, + uint32 feeProtocol, bool unlocked ); } diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index e575d21..0fac4a3 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -272,6 +272,8 @@ contract SFLUVZapperv1 is // *** this gets the balance of BYUSD in HONEY in BYUSD decimals // uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault) - byusdCurrentFees; uint256 byusdCurrentFees = hf.collectedAssetFees(address($.byusd)); + console.log("BYUSD current fees in HONEY and BYUSD:", byusdCurrentFees / 1e12, byusdCurrentFees); + console.log("BYUSD vault balance in HONEY and BYUSD:", hf.vaults(address($.byusd)).balanceOf(address($.honeyFactory)) / 1e12, hf.vaults(address($.byusd)).balanceOf(address($.honeyFactory))); // this gets the balance of BYUSD in HONEY decimals - i.e. 'shares' uint256 byusdAvailableInHoney = hf.vaults(address($.byusd)).balanceOf(address($.honeyFactory)) - byusdCurrentFees; diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index 0a36d67..aeacf62 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -15,6 +15,9 @@ import { IBYUSD } from "../src/SFLUVZapperv1.sol"; import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; import { MessagingReceipt, MessagingFee } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; import "forge-std/console.sol"; +import { Honey } from "lib/bera-contracts/src/honey/Honey.sol"; +import { ILiquidityPool} from "../src/ILiquidityPool.sol"; + interface IWBERA { @@ -35,11 +38,14 @@ contract SFLUVZapperTest is Test { SFLUVZapperv1 public testSFLUVZapper; IBYUSD public testBYUSD; IWBERA public wbera; + Honey public honeyToken; + ILiquidityPool public liquidityPool; address internal peon; address vitEth = address(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); address defaultAdmin = address(0x90496e23825aD0C8107d04671e6a27f30630Fc35); + uint public oneEther = 1 ether; // TODO: elsewhere? ERC1967Proxy zapperproxy; @@ -63,10 +69,13 @@ contract SFLUVZapperTest is Test { wbera = IWBERA(wrappedNative); testLUVCoin = SFLUVv2(sfluv); testBYUSD = IBYUSD(byusd); + liquidityPool = ILiquidityPool(honeyToBYUSDPool); testSFLUVZapper = new SFLUVZapperv1(); peon = makeAddr("peon"); address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); + honeyToken = Honey(honey); + SFLUVZapperStorageInit memory testStorage = SFLUVZapperStorageInit( honeyFactory, @@ -238,4 +247,48 @@ contract SFLUVZapperTest is Test { vm.stopPrank(); } + function testLiquidityPool () public { + console.log("Honey Balance of Whale", honeyToken.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); + console.log("Honey Balance of Zapper", honeyToken.balanceOf(address(testSFLUVZapper))); + console.log("Honey Decimals", honeyToken.decimals()); + vm.startPrank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + honeyToken.transfer(address(testSFLUVZapper), 1000 * 1e18); + console.log("Honey Balance of Zapper", honeyToken.balanceOf(address(testSFLUVZapper))); + vm.stopPrank(); + + ( + uint160 sqrtPriceX96,, + , + , + , + , +) = liquidityPool.slot0(); + + console.log("Slo0 contents:"); + console.log(" sqrtPriceX96:", sqrtPriceX96); + // 95% minimum output + // sqrt(1 / 0.95) ≈ 1.025978352 + uint256 NUM = 1_025_978_352; // scaled by 1e9 + uint256 DEN = 1_000_000_000; + + uint256 sqrtPriceLimitX96 = uint256( + (uint256(sqrtPriceX96) * NUM) / DEN + ); + + vm.startPrank(address(testSFLUVZapper)); + liquidityPool.swap( + address(testSFLUVZapper), + false, + int256(500 * 1e18), + uint160(sqrtPriceLimitX96), + "" + ); + vm.stopPrank(); + + console.log("BYUSD Balance of Zapper", testBYUSD.balanceOf(address(testSFLUVZapper))); + console.log("Honey Balance of Zapper", honeyToken.balanceOf(address(testSFLUVZapper))); + assert(testBYUSD.balanceOf(address(testSFLUVZapper)) > 490000000); + assert(honeyToken.balanceOf(address(testSFLUVZapper)) < 510000000000000000000); +} + } From 455e0026c258e4b8f7a5896ed2a31309843d67a6 Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Wed, 28 Jan 2026 16:59:50 -0800 Subject: [PATCH 17/22] Added test for unwrapSwapAndBridge that does redeem and liquidity pool in same transaction --- src/SFLUVZapperv1.sol | 10 ++++-- test/SFLUVZapperv1.sol | 72 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 0fac4a3..fffb6b7 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -257,6 +257,7 @@ contract SFLUVZapperv1 is success = $.sfluv.withdrawTo(address(this), amount); if (!success) revert RedeemFailed(); uint256 honeyBalance = $.honey.balanceOf(address(this)); + console.log("HONEY balance:", honeyBalance); // console.log("HONEY balance after redeem:", honeyBalance); // 2.5 tracking BYUSD before redeem/swap process just in case amount is insufficient @@ -272,10 +273,13 @@ contract SFLUVZapperv1 is // *** this gets the balance of BYUSD in HONEY in BYUSD decimals // uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault) - byusdCurrentFees; uint256 byusdCurrentFees = hf.collectedAssetFees(address($.byusd)); - console.log("BYUSD current fees in HONEY and BYUSD:", byusdCurrentFees / 1e12, byusdCurrentFees); - console.log("BYUSD vault balance in HONEY and BYUSD:", hf.vaults(address($.byusd)).balanceOf(address($.honeyFactory)) / 1e12, hf.vaults(address($.byusd)).balanceOf(address($.honeyFactory))); + console.log("BYUSD Vault from environment variables:", $.byusdVault); + console.log("BYUSD vault address from HoneyFactory:", address(hf.vaults(address($.byusd)))); + console.log("BYUSD current fees:", byusdCurrentFees); + console.log("BYUSD vault balance in BYUSD:", $.byusd.balanceOf(address(hf.vaults(address($.byusd))))); // this gets the balance of BYUSD in HONEY decimals - i.e. 'shares' - uint256 byusdAvailableInHoney = hf.vaults(address($.byusd)).balanceOf(address($.honeyFactory)) - byusdCurrentFees; + uint256 byusdAvailableInHoney = ($.byusd.balanceOf(address(hf.vaults(address($.byusd)))) * 1e12) - (2 * byusdCurrentFees); + console.log("BYUSD available in HONEY:", byusdAvailableInHoney); if (byusdAvailableInHoney > 0) { uint256 redeemAmount = honeyBalance < byusdAvailableInHoney diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index aeacf62..7574b2a 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -17,6 +17,7 @@ import { MessagingReceipt, MessagingFee } from "@layerzerolabs/lz-evm-protocol-v import "forge-std/console.sol"; import { Honey } from "lib/bera-contracts/src/honey/Honey.sol"; import { ILiquidityPool} from "../src/ILiquidityPool.sol"; +import { HoneyFactory } from "lib/bera-contracts/src/honey/HoneyFactory.sol"; @@ -40,6 +41,7 @@ contract SFLUVZapperTest is Test { IWBERA public wbera; Honey public honeyToken; ILiquidityPool public liquidityPool; + HoneyFactory public testHoneyFactory; address internal peon; address vitEth = address(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); @@ -66,10 +68,12 @@ contract SFLUVZapperTest is Test { address honeyToBYUSDPool = vm.envAddress("HONEY_BYUSD_POOL_ADDRESS"); address byusdVault = vm.envAddress("BYUSD_VAULT_ADDRESS"); address wrappedNative = vm.envAddress("WRAPPED_NATIVE_ADDRESS"); + address honeyFactoryAddress = vm.envAddress("HONEY_FACTORY_ADDRESS"); wbera = IWBERA(wrappedNative); testLUVCoin = SFLUVv2(sfluv); testBYUSD = IBYUSD(byusd); liquidityPool = ILiquidityPool(honeyToBYUSDPool); + testHoneyFactory = HoneyFactory(honeyFactoryAddress); testSFLUVZapper = new SFLUVZapperv1(); peon = makeAddr("peon"); @@ -185,11 +189,46 @@ contract SFLUVZapperTest is Test { } function testUnwrapSwapAndBridge() public { + + // Switch to fork where HONEY is barely funded with BYUSD + string memory forkURL = vm.envString('FORK_URL'); + uint256 currentBlockFork = vm.envUint('UP_TO_DATE_FORK_BLOCK'); + vm.createSelectFork(forkURL, currentBlockFork); + // ---------------------------- + // Setup SFLUVZapper + // ---------------------------- + address sfluv = vm.envAddress('SFLUV_ADDRESS'); + address byusd = vm.envAddress("BYUSD_ADDRESS"); + address honey = vm.envAddress("HONEY_ADDRESS"); + address honeyToBYUSDPool = vm.envAddress("HONEY_BYUSD_POOL_ADDRESS"); + address byusdVault = vm.envAddress("BYUSD_VAULT_ADDRESS"); + address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); + address sfluvWhale = address(0x234D25189D22947C3B0a39959eEEfAc36c022BE0); + + SFLUVZapperStorageInit memory testStorage = SFLUVZapperStorageInit( + honeyFactory, + sfluv, + byusd, + honey, + honeyToBYUSDPool, + byusdVault + ); + + testSFLUVZapper = new SFLUVZapperv1(); + zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (defaultAdmin, testStorage))); + testSFLUVZapper = SFLUVZapperv1(payable(zapperproxy)); + vm.startPrank(defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ADMIN_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.MINTER_ROLE(), address(testSFLUVZapper)); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), address(testSFLUVZapper)); + vm.stopPrank(); // ---------------------------- // Setup decimal helpers // ---------------------------- uint256 byusdExp = 10**testBYUSD.decimals(); uint256 sfluvExp = 10**testLUVCoin.decimals(); + console.log("Is BYUSD Pegged?:", testHoneyFactory.isPegged(address(testBYUSD))); // Send some BYUSD to defaultAdmin from whale vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); @@ -205,22 +244,31 @@ contract SFLUVZapperTest is Test { console.log("BYUSD BERA balance:", address(testBYUSD).balance); console.log("approving zapper"); + console.log("New Zapper Address:", address(testSFLUVZapper)); + vm.startPrank(sfluvWhale); + testLUVCoin.transfer(defaultAdmin, 100 * sfluvExp); + vm.stopPrank(); // Approve zapper vm.startPrank(defaultAdmin); testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); + console.log("BYUSD Vault Balance before zapin:", testBYUSD.balanceOf(vm.envAddress("BYUSD_VAULT_ADDRESS"))); + console.log("SFLUV whale SFLUV balance:", testLUVCoin.balanceOf(sfluvWhale)); + - // Wrap 100 BYUSD into SFLUV first - testSFLUVZapper.zapIn(100 * byusdExp); + // Wrap 50 BYUSD into SFLUV first + testSFLUVZapper.zapIn(50 * byusdExp); uint256 startingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); console.log("Starting SFLUV balance:", startingLUVBalance); + console.log("BYUSD Vault Balance: ", testBYUSD.balanceOf(vm.envAddress("BYUSD_VAULT_ADDRESS"))); + console.log("Collected fees call before unwrap:", testHoneyFactory.collectedAssetFees(address(testBYUSD))); // ---------------------------- // Call unwrap/swap/bridge // ---------------------------- (MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt) = - testSFLUVZapper.unwrapSwapAndBridge(50 * sfluvExp, vitEth); + testSFLUVZapper.unwrapSwapAndBridge(100 * sfluvExp, vitEth); // ---------------------------- // Logging receipts for inspection @@ -289,6 +337,22 @@ contract SFLUVZapperTest is Test { console.log("Honey Balance of Zapper", honeyToken.balanceOf(address(testSFLUVZapper))); assert(testBYUSD.balanceOf(address(testSFLUVZapper)) > 490000000); assert(honeyToken.balanceOf(address(testSFLUVZapper)) < 510000000000000000000); -} + + } + + function testHoneyManipulation() public { + console.log("Whale BYUSD Balance: ", testBYUSD.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); + console.log("Whale Honey Balance: ", honeyToken.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); + console.log("Is BYUSD Pegged?:", testHoneyFactory.isPegged(address(testBYUSD))); + vm.startPrank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testBYUSD.approve(address(testHoneyFactory), 100 * 1e6); + testHoneyFactory.mint(address(testBYUSD), 100 * 1e6, 0x4Be03f781C497A489E3cB0287833452cA9B9E80B, false); + console.log("Whale BYUSD Balance: ", testBYUSD.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); + console.log("Whale Honey Balance: ", honeyToken.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); + testHoneyFactory.redeem(address(testBYUSD), 100 * 1e18, 0x4Be03f781C497A489E3cB0287833452cA9B9E80B, false); + console.log("Whale BYUSD Balance: ", testBYUSD.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); + console.log("Whale Honey Balance: ", honeyToken.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); + vm.stopPrank(); } +} From 7669dbfbb14abf8eeb6e78bf167a83fbdf046a99 Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Thu, 29 Jan 2026 15:28:58 -0800 Subject: [PATCH 18/22] Improved test coverage, added comments to tests, removed useless logs --- src/SFLUVZapperv1.sol | 53 +---- test/SFLUVZapperv1.sol | 460 ++++++++++++++++++++++------------------- 2 files changed, 257 insertions(+), 256 deletions(-) diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index fffb6b7..b20e365 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -13,7 +13,6 @@ import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/in import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol"; import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol"; import { ILiquidityPool} from "./ILiquidityPool.sol"; -import "forge-std/console.sol"; import "./ISFLUVZapperErrors.sol"; import "./SFLUVv2.sol"; @@ -43,18 +42,17 @@ contract SFLUVZapperv1 is // bytes32 public constant REDEEMER_ADMIN_ROLE = keccak256("REDEEMER_ADMIN"); /* STORAGE */ +struct SFLUVZapperStorage { + IHoneyFactory honeyFactory; + SFLUVv2 sfluv; - struct SFLUVZapperStorage { - IHoneyFactory honeyFactory; - SFLUVv2 sfluv; + IBYUSD byusd; + IERC20Metadata honey; - IBYUSD byusd; - IERC20Metadata honey; + ILiquidityPool honeyToBYUSDPool; - ILiquidityPool honeyToBYUSDPool; - - address byusdVault; - } + address byusdVault; +} // keccak256(abi.encode(uint256(keccak256("SFLUV.storage.SFLUVZapper")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant SFLUVZapperStorageLocation = @@ -95,8 +93,6 @@ contract SFLUVZapperv1 is _grantRole(DEFAULT_ADMIN_ROLE, _governance); -// _setRoleAdmin(MINTER_ROLE, MINTER_ADMIN_ROLE); -// _setRoleAdmin(REDEEMER_ROLE, REDEEMER_ADMIN_ROLE); } function __Storage_init( @@ -243,43 +239,29 @@ contract SFLUVZapperv1 is 10 ** _getSFLUVZapperStorage().byusd.decimals(), 10 ** _getSFLUVZapperStorage().sfluv.decimals() ); -// console.log("amount passed in:", amount); -// console.log("BYUSD amount passed in:", byusdAmount); -// console.log("address passed in:", to); + SFLUVZapperStorage storage $ = _getSFLUVZapperStorage(); // 1. Pull SFLUV bool success = $.sfluv.transferFrom(_msgSender(), address(this), amount); if (!success) revert TransferFailed(address($.sfluv), address(this), _msgSender(), amount); -// console.log("SFLUV transferred into Zapper contract:", amount); + // 2. Unwrap to HONEY success = $.sfluv.withdrawTo(address(this), amount); if (!success) revert RedeemFailed(); uint256 honeyBalance = $.honey.balanceOf(address(this)); - console.log("HONEY balance:", honeyBalance); -// console.log("HONEY balance after redeem:", honeyBalance); // 2.5 tracking BYUSD before redeem/swap process just in case amount is insufficient uint256 byusdBefore = $.byusd.balanceOf(address(this)); -// console.log("BYUSD balance before redeem/swap:", byusdBefore); // 3. Check BYUSD inside Honey, and keep track of how much is redeemed - // needs to actually respect this calc - but not public - // function _getSharesWithoutFees(address asset) internal view returns (uint256) { - // return vaults[asset].balanceOf(address(this)) - collectedAssetFees[asset]; - // } HoneyFactory hf = HoneyFactory(address($.honeyFactory)); // *** this gets the balance of BYUSD in HONEY in BYUSD decimals // uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault) - byusdCurrentFees; uint256 byusdCurrentFees = hf.collectedAssetFees(address($.byusd)); - console.log("BYUSD Vault from environment variables:", $.byusdVault); - console.log("BYUSD vault address from HoneyFactory:", address(hf.vaults(address($.byusd)))); - console.log("BYUSD current fees:", byusdCurrentFees); - console.log("BYUSD vault balance in BYUSD:", $.byusd.balanceOf(address(hf.vaults(address($.byusd))))); // this gets the balance of BYUSD in HONEY decimals - i.e. 'shares' uint256 byusdAvailableInHoney = ($.byusd.balanceOf(address(hf.vaults(address($.byusd)))) * 1e12) - (2 * byusdCurrentFees); - console.log("BYUSD available in HONEY:", byusdAvailableInHoney); if (byusdAvailableInHoney > 0) { uint256 redeemAmount = honeyBalance < byusdAvailableInHoney @@ -288,7 +270,6 @@ contract SFLUVZapperv1 is $.honeyFactory.redeem(address($.byusd), redeemAmount, address(this), false); honeyBalance -= redeemAmount; - console.log("Redeemed BYUSD from HONEY:", redeemAmount / 1e12); } // 4. Swap remaining HONEY via pool if needed @@ -311,14 +292,10 @@ contract SFLUVZapperv1 is sqrtPriceLimitX96, "" ); -// console.log("Swapped remaining HONEY for BYUSD:", honeyBalance); } uint256 byusdAfter = $.byusd.balanceOf(address(this)); -// console.log("BYUSD balance after redeem/swap:", byusdAfter); uint256 byusdAccumulated = byusdAfter - byusdBefore; -// console.log("amount we're comparing BYUSD accumulated against:", byusdAmount * 95 / 100); -// console.log("BYUSD accumulated from redeem/swap:", byusdAccumulated); require(byusdAccumulated >= (byusdAmount * 95 / 100), "insufficient BYUSD from swap/redemption"); // Prepare LZ send params @@ -339,21 +316,11 @@ contract SFLUVZapperv1 is "insufficient BYUSD after swap" ); -// console.log("Zapper Bera balance before bridging:", address(this).balance); -// console.log("Native fee for bridging:", fee.nativeFee); -// console.log("LZ token fee for bridging:", fee.lzTokenFee); -// console.log("Address of this contract:", address(this)); -// console.log("BYUSD's bera balance before bridging:", address($.byusd).balance); -// console.log("zapper BYUSD balance before bridging:", $.byusd.balanceOf(address(this))); -// console.logBytes32(lzParam.to); - ( MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt ) = $.byusd.send{ value: fee.nativeFee }(lzParam, fee, address(this)); -// console.log("zapper BYUSD balance after bridging:", $.byusd.balanceOf(address(this))); -// console.log("Bridging BYUSD amountLD:", lzParam.amountLD); return (mReceipt, oReceipt); } diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index 7574b2a..b6c94a2 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -55,148 +55,211 @@ contract SFLUVZapperTest is Test { error AccessControlUnauthorizedAccount(address account, bytes32 role); - function setUp() public { +function setUp() public { + // Create fork using an old fork block with a heavily funded HONEY pool + string memory forkURL = vm.envString('FORK_URL'); + uint256 forkBlock = vm.envUint('FORK_BLOCK'); + vm.createSelectFork(forkURL, forkBlock); - string memory forkURL = vm.envString('FORK_URL'); - uint256 forkBlock = vm.envUint('FORK_BLOCK'); + // Pull SFLUV Zapper storage variables from environment and set up other necessary addresses + address sfluv = vm.envAddress('SFLUV_ADDRESS'); + address byusd = vm.envAddress("BYUSD_ADDRESS"); + address honey = vm.envAddress("HONEY_ADDRESS"); + address honeyToBYUSDPool = vm.envAddress("HONEY_BYUSD_POOL_ADDRESS"); + address byusdVault = vm.envAddress("BYUSD_VAULT_ADDRESS"); + address wrappedNative = vm.envAddress("WRAPPED_NATIVE_ADDRESS"); + address honeyFactoryAddress = vm.envAddress("HONEY_FACTORY_ADDRESS"); + peon = makeAddr("peon"); + + // Create contract instances out of addresses + wbera = IWBERA(wrappedNative); + testLUVCoin = SFLUVv2(sfluv); + testBYUSD = IBYUSD(byusd); + liquidityPool = ILiquidityPool(honeyToBYUSDPool); + testHoneyFactory = HoneyFactory(honeyFactoryAddress); + honeyToken = Honey(honey); + + // Create new Zapper instance + testSFLUVZapper = new SFLUVZapperv1(); - vm.createSelectFork(forkURL, forkBlock); - address sfluv = vm.envAddress('SFLUV_ADDRESS'); - address byusd = vm.envAddress("BYUSD_ADDRESS"); - address honey = vm.envAddress("HONEY_ADDRESS"); - address honeyToBYUSDPool = vm.envAddress("HONEY_BYUSD_POOL_ADDRESS"); - address byusdVault = vm.envAddress("BYUSD_VAULT_ADDRESS"); - address wrappedNative = vm.envAddress("WRAPPED_NATIVE_ADDRESS"); - address honeyFactoryAddress = vm.envAddress("HONEY_FACTORY_ADDRESS"); - wbera = IWBERA(wrappedNative); - testLUVCoin = SFLUVv2(sfluv); - testBYUSD = IBYUSD(byusd); - liquidityPool = ILiquidityPool(honeyToBYUSDPool); - testHoneyFactory = HoneyFactory(honeyFactoryAddress); - testSFLUVZapper = new SFLUVZapperv1(); + // Set up storage struct by calling initialize using addresses from environment variables + SFLUVZapperStorageInit memory testStorage = SFLUVZapperStorageInit( + honeyFactoryAddress, + sfluv, + byusd, + honey, + honeyToBYUSDPool, + byusdVault + ); + + // Deploy proxy pointing to zapper implementation + zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (defaultAdmin, testStorage))); - peon = makeAddr("peon"); - address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); - honeyToken = Honey(honey); + // Cast proxy address to zapper interface and make it payable + testSFLUVZapper = SFLUVZapperv1(payable(zapperproxy)); + // Prank from defaultAdmin to give necessary permissions + vm.startPrank(defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ADMIN_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.MINTER_ROLE(), address(testSFLUVZapper)); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), address(testSFLUVZapper)); + vm.stopPrank(); + } - SFLUVZapperStorageInit memory testStorage = SFLUVZapperStorageInit( - honeyFactory, - sfluv, - byusd, - honey, - honeyToBYUSDPool, - byusdVault - ); - zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (defaultAdmin, testStorage))); - testSFLUVZapper = SFLUVZapperv1(payable(zapperproxy)); - vm.startPrank(defaultAdmin); - testLUVCoin.grantRole(testLUVCoin.REDEEMER_ADMIN_ROLE(), defaultAdmin); - testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), defaultAdmin); - testLUVCoin.grantRole(testLUVCoin.MINTER_ROLE(), address(testSFLUVZapper)); - testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), address(testSFLUVZapper)); - vm.stopPrank(); - } - - - function testPermissionedWrappingFunctionality() public { - // For easy decimal conversion - uint byusdExp = 10**testBYUSD.decimals(); - uint sfluvExp = 10**testLUVCoin.decimals(); - // This should not change because we cut off the fork at a determined block, but just in case - uint startingAdminLUVBalance = testLUVCoin.balanceOf(defaultAdmin); - // Prank w/ BYUSD whale address - vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); - // Transfer from BYUSD Whale acct to defaultAdmin - testBYUSD.transfer(defaultAdmin, 1000 * byusdExp); - //Prank w defaultAdmin account - vm.startPrank(defaultAdmin); - // defaultAdmin approves the SFLUV zapper to move 100 of its (defaultAdmin's) BYUSD - testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); - //SFLUV zapper takes 50 BYUSD to be converted to SFLUV - testSFLUVZapper.zapIn(50 * byusdExp); - vm.stopPrank(); - // assert that defaultAdmin has 50 sfluv (50 more than it had before) and 950 byusd - assert(testLUVCoin.balanceOf(defaultAdmin) == ((50 * sfluvExp) + startingAdminLUVBalance)); - assert(testBYUSD.balanceOf(defaultAdmin) == 950 * byusdExp); - } +function testPermissionedWrappingFunctionality() public { + // For easy decimal conversion + uint byusdExp = 10**testBYUSD.decimals(); + uint sfluvExp = 10**testLUVCoin.decimals(); + // This should not change because we cut off the fork at a determined block, but just in case + uint startingAdminLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + // Prank w/ BYUSD whale address + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + // Transfer from BYUSD Whale acct to defaultAdmin + testBYUSD.transfer(defaultAdmin, 1000 * byusdExp); + //Prank w defaultAdmin account + vm.startPrank(defaultAdmin); + // defaultAdmin approves the SFLUV zapper to move 100 of its (defaultAdmin's) BYUSD + testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); + //SFLUV zapper takes 50 BYUSD to be converted to SFLUV + testSFLUVZapper.zapIn(50 * byusdExp); + vm.stopPrank(); + // assert that defaultAdmin has 50 sfluv (50 more than it had before) and 950 byusd + assert(testLUVCoin.balanceOf(defaultAdmin) == ((50 * sfluvExp) + startingAdminLUVBalance)); + assert(testBYUSD.balanceOf(defaultAdmin) == 950 * byusdExp); +} - function testUnpermissionedWrappingFunctionality() public { - // For easy decimal conversion - uint byusdExp = 10**testBYUSD.decimals(); - uint sfluvExp = 10**testLUVCoin.decimals(); - // Prank w/ BYUSD whale address - vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); - // Transfer from BYUSD Whale acct to peon - testBYUSD.transfer(peon, 1000 * byusdExp); - // Prank w peon - vm.startPrank(peon); - // peon approves the SFLUV zapper to move 100 of its (peon's) BYUSD - testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); - bytes32 myRole = testSFLUVZapper.MINTER_ROLE(); - //SFLUV zapper takes 50 BYUSD to be converted to SFLUV - vm.expectRevert(abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - peon, - myRole - )); - testSFLUVZapper.zapIn(50 * byusdExp); - } +function testUnpermissionedWrappingFunctionality() public { + // For easy decimal conversion + uint byusdExp = 10**testBYUSD.decimals(); + uint sfluvExp = 10**testLUVCoin.decimals(); + // Prank w/ BYUSD whale address + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + // Transfer from BYUSD Whale acct to peon + testBYUSD.transfer(peon, 1000 * byusdExp); + // Prank w peon + vm.startPrank(peon); + // peon approves the SFLUV zapper to move 100 of its (peon's) BYUSD + testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); + bytes32 myRole = testSFLUVZapper.MINTER_ROLE(); + //SFLUV zapper takes 50 BYUSD to be converted to SFLUV + vm.expectRevert(abi.encodeWithSelector( + AccessControlUnauthorizedAccount.selector, + peon, + myRole + )); + testSFLUVZapper.zapIn(50 * byusdExp); +} - function testUnwrappingFunctionality() public { - // For easy decimal conversion - uint byusdExp = 10**testBYUSD.decimals(); - uint sfluvExp = 10**testLUVCoin.decimals(); - uint honeyCharge = 5 * byusdExp; - uint denominator = 10000; - uint honeyFee = honeyCharge / denominator; - uint conversionAmount = 25; - uint honeyRake = honeyFee * conversionAmount; - // This should not change because we cut off the fork at a determined block, but just in case - uint startingAdminLUVBalance = testLUVCoin.balanceOf(defaultAdmin); - uint startingAdminBYUSDBalance = testBYUSD.balanceOf(defaultAdmin); - // Prank w defaultAdmin account - vm.startPrank(defaultAdmin); - // defaultAdmin approves the SFLUV zapper to move 100 of its (defaultAdmin's) SFLUV - testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); - //SFLUV zapper takes 25 SFLUV to be converted to BYUSD - testSFLUVZapper.zapOut(conversionAmount * sfluvExp); - vm.stopPrank(); - // assert that defaultAdmin has 25 less SFLUV that it started with, and 975 byusd - assert(testLUVCoin.balanceOf(defaultAdmin) == (startingAdminLUVBalance - (conversionAmount * sfluvExp))); - assert(testBYUSD.balanceOf(defaultAdmin) == ((conversionAmount * byusdExp) - (honeyRake))); - } +function testUnwrappingFunctionality() public { + // For easy decimal conversion + uint byusdExp = 10**testBYUSD.decimals(); + uint sfluvExp = 10**testLUVCoin.decimals(); + uint honeyCharge = 5 * byusdExp; + uint denominator = 10000; + uint honeyFee = honeyCharge / denominator; + uint conversionAmount = 25; + uint honeyRake = honeyFee * conversionAmount; + // This should not change because we cut off the fork at a determined block, but just in case + uint startingAdminLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + // Prank w defaultAdmin account + vm.startPrank(defaultAdmin); + // defaultAdmin approves the SFLUV zapper to move 100 of its (defaultAdmin's) SFLUV + testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); + //SFLUV zapper takes 25 SFLUV to be converted to BYUSD + testSFLUVZapper.zapOut(conversionAmount * sfluvExp); + vm.stopPrank(); + // assert that defaultAdmin has 25 less SFLUV that it started with, and 975 byusd + assert(testLUVCoin.balanceOf(defaultAdmin) == (startingAdminLUVBalance - (conversionAmount * sfluvExp))); + assert(testBYUSD.balanceOf(defaultAdmin) == ((conversionAmount * byusdExp) - (honeyRake))); +} - function testUnpermissionedUnwrappingFunctionality() public { - // For easy decimal conversion - uint byusdExp = 10**testBYUSD.decimals(); - uint sfluvExp = 10**testLUVCoin.decimals(); - // Prank w peon - vm.startPrank(peon); - // peon approves the SFLUV zapper to move 100 of its (peons) SFLUV - testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); - bytes32 myRole = testSFLUVZapper.REDEEMER_ROLE(); - //SFLUV zapper takes 50 BYUSD to be converted to SFLUV - vm.expectRevert(abi.encodeWithSelector( - AccessControlUnauthorizedAccount.selector, - peon, - myRole - )); - testSFLUVZapper.zapOut(50 * byusdExp); - vm.stopPrank(); - } +function testUnpermissionedUnwrappingFunctionality() public { + // For easy decimal conversion + uint byusdExp = 10**testBYUSD.decimals(); + uint sfluvExp = 10**testLUVCoin.decimals(); + // Prank w peon + vm.startPrank(peon); + // peon approves the SFLUV zapper to move 100 of its (peons) SFLUV + testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); + bytes32 myRole = testSFLUVZapper.REDEEMER_ROLE(); + //SFLUV zapper takes 50 BYUSD to be converted to SFLUV + vm.expectRevert(abi.encodeWithSelector( + AccessControlUnauthorizedAccount.selector, + peon, + myRole + )); + testSFLUVZapper.zapOut(50 * byusdExp); + vm.stopPrank(); +} + +function testUnwrapRedeemAndBridge() public { + + // Setup decimal helpers + uint256 byusdExp = 10**testBYUSD.decimals(); + uint256 sfluvExp = 10**testLUVCoin.decimals(); + + // Send some BYUSD to defaultAdmin from whale + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testBYUSD.transfer(defaultAdmin, 1000 * byusdExp); - function testUnwrapSwapAndBridge() public { + // Send some BYUSD to SFLuvZapper from whale + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testBYUSD.transfer(address(testSFLUVZapper), 100 * byusdExp); + + // Fund zapper and BYUSD contract with BERA for messaging fees + vm.deal(address(testSFLUVZapper), 100 * sfluvExp); + vm.deal(address(testBYUSD), 100 * sfluvExp); + + // Default Admin approves zapper to move $100 worth of BYUSD and SFLUV + vm.startPrank(defaultAdmin); + testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); + testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); + + + + // Record original SFLUV balance then wrap 50 BYUSD into SFLUV + uint256 orininalBalance = testLUVCoin.balanceOf(address(defaultAdmin)); + testSFLUVZapper.zapIn(50 * byusdExp); + + //Check that that the default admin has correct SFLUV balance + assert(testLUVCoin.balanceOf(defaultAdmin) == (50 * sfluvExp) + orininalBalance); + + + // Call unwrap/swap/bridge - honey is funded with enough BYUSD in this test so the liquidity pool is not used + (MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt) = + testSFLUVZapper.unwrapSwapAndBridge(50 * sfluvExp, vitEth); + + vm.stopPrank(); + + // Logging MessagingReceipt for inspection + console.log("MessagingReceipt:"); + console.logBytes32(mReceipt.guid); + console.log(" nonce:", mReceipt.nonce); + console.log(" messaging fee - nativeFee:", mReceipt.fee.nativeFee); + console.log(" messaging fee - lzTokenFee:", mReceipt.fee.lzTokenFee); + + // Logging OFTReceipt for inspection, assert that the amount sent is correct + console.log("OFTReceipt:"); + console.log(" amountSent:", oReceipt.amountSentLD); + console.log(" amountRecieved:", oReceipt.amountReceivedLD); + assert(oReceipt.amountSentLD == 50 * byusdExp); + + + // Verify SFLUV is now back to original balance + assert(testLUVCoin.balanceOf(defaultAdmin) == orininalBalance); +} + +function testUnwrapSplitRedeemBridge() public { // Switch to fork where HONEY is barely funded with BYUSD string memory forkURL = vm.envString('FORK_URL'); uint256 currentBlockFork = vm.envUint('UP_TO_DATE_FORK_BLOCK'); vm.createSelectFork(forkURL, currentBlockFork); - // ---------------------------- - // Setup SFLUVZapper - // ---------------------------- + + // Setup SFLUVZapper, same as in setUp(), this is necesary because we switched forks and the new fork doesn't have the zapper deployed yet address sfluv = vm.envAddress('SFLUV_ADDRESS'); address byusd = vm.envAddress("BYUSD_ADDRESS"); address honey = vm.envAddress("HONEY_ADDRESS"); @@ -223,12 +286,10 @@ contract SFLUVZapperTest is Test { testLUVCoin.grantRole(testLUVCoin.MINTER_ROLE(), address(testSFLUVZapper)); testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), address(testSFLUVZapper)); vm.stopPrank(); - // ---------------------------- + // Setup decimal helpers - // ---------------------------- uint256 byusdExp = 10**testBYUSD.decimals(); uint256 sfluvExp = 10**testLUVCoin.decimals(); - console.log("Is BYUSD Pegged?:", testHoneyFactory.isPegged(address(testBYUSD))); // Send some BYUSD to defaultAdmin from whale vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); @@ -238,121 +299,94 @@ contract SFLUVZapperTest is Test { vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); testBYUSD.transfer(address(testSFLUVZapper), 100 * byusdExp); + // Fund zapper and BYUSD contract with BERA for messaging fees vm.deal(address(testSFLUVZapper), 100 * sfluvExp); - console.log("SFLUVZapper BERA balance:", address(testSFLUVZapper).balance); vm.deal(address(testBYUSD), 100 * sfluvExp); - console.log("BYUSD BERA balance:", address(testBYUSD).balance); - console.log("approving zapper"); - console.log("New Zapper Address:", address(testSFLUVZapper)); + // Record starting SFLUV balance + uint256 startingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + + // Transfer 100 SFLUV from whale to defaultAdmin vm.startPrank(sfluvWhale); testLUVCoin.transfer(defaultAdmin, 100 * sfluvExp); vm.stopPrank(); - // Approve zapper + + // Approve zapper to move defaultAdmin's BYUSD and SFLUV vm.startPrank(defaultAdmin); testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); - console.log("BYUSD Vault Balance before zapin:", testBYUSD.balanceOf(vm.envAddress("BYUSD_VAULT_ADDRESS"))); - console.log("SFLUV whale SFLUV balance:", testLUVCoin.balanceOf(sfluvWhale)); + // Wrap 50 BYUSD into SFLUV first, and assert that the BYUSD vault balance is now just barely above 50 + uint byusdAmountToWrap = 50 * byusdExp; + uint amountPlusBuffer = byusdAmountToWrap + 3e6; // adding 5 BYUSD buffer + testSFLUVZapper.zapIn(byusdAmountToWrap); + assert(testBYUSD.balanceOf(vm.envAddress("BYUSD_VAULT_ADDRESS")) <= amountPlusBuffer); - // Wrap 50 BYUSD into SFLUV first - testSFLUVZapper.zapIn(50 * byusdExp); + // Assert that defaultAdmin has at least 149 extra SFLUV now (this accounts for a 1% at max ZapIn slippage) + assert(testLUVCoin.balanceOf(defaultAdmin) >= (149 * sfluvExp) + startingLUVBalance); - uint256 startingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); - console.log("Starting SFLUV balance:", startingLUVBalance); - console.log("BYUSD Vault Balance: ", testBYUSD.balanceOf(vm.envAddress("BYUSD_VAULT_ADDRESS"))); - console.log("Collected fees call before unwrap:", testHoneyFactory.collectedAssetFees(address(testBYUSD))); + // Reset starting LUV balance to current balance + startingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); - // ---------------------------- - // Call unwrap/swap/bridge - // ---------------------------- + // Call unwrap/swap/bridge - honey is underfunded with BYUSD in this test so the liquidity pool is used (MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt) = testSFLUVZapper.unwrapSwapAndBridge(100 * sfluvExp, vitEth); + vm.stopPrank(); + - // ---------------------------- // Logging receipts for inspection - // ---------------------------- console.log("MessagingReceipt:"); console.logBytes32(mReceipt.guid); console.log(" nonce:", mReceipt.nonce); console.log(" messaging fee - nativeFee:", mReceipt.fee.nativeFee); console.log(" messaging fee - lzTokenFee:", mReceipt.fee.lzTokenFee); - - + // Logging OFTReceipt for inspection, assert that the amount sent is correct console.log("OFTReceipt:"); console.log(" amountSent:", oReceipt.amountSentLD); console.log(" amountRecieved:", oReceipt.amountReceivedLD); + assert(oReceipt.amountSentLD == 100 * byusdExp); - // ---------------------------- - // Verify SFLUV balance decreased - // ---------------------------- + // Verify SFLUV balance decreased by 100 uint256 endingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); - console.log("Ending SFLUV balance:", endingLUVBalance); - assert(endingLUVBalance < startingLUVBalance); - - vm.stopPrank(); + assert(endingLUVBalance == startingLUVBalance - (100 * sfluvExp)); } - function testLiquidityPool () public { - console.log("Honey Balance of Whale", honeyToken.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); - console.log("Honey Balance of Zapper", honeyToken.balanceOf(address(testSFLUVZapper))); - console.log("Honey Decimals", honeyToken.decimals()); - vm.startPrank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); - honeyToken.transfer(address(testSFLUVZapper), 1000 * 1e18); - console.log("Honey Balance of Zapper", honeyToken.balanceOf(address(testSFLUVZapper))); - vm.stopPrank(); - - ( - uint160 sqrtPriceX96,, - , - , - , - , -) = liquidityPool.slot0(); - - console.log("Slo0 contents:"); - console.log(" sqrtPriceX96:", sqrtPriceX96); - // 95% minimum output - // sqrt(1 / 0.95) ≈ 1.025978352 - uint256 NUM = 1_025_978_352; // scaled by 1e9 - uint256 DEN = 1_000_000_000; - - uint256 sqrtPriceLimitX96 = uint256( - (uint256(sqrtPriceX96) * NUM) / DEN - ); - - vm.startPrank(address(testSFLUVZapper)); - liquidityPool.swap( - address(testSFLUVZapper), - false, - int256(500 * 1e18), - uint160(sqrtPriceLimitX96), - "" - ); - vm.stopPrank(); - - console.log("BYUSD Balance of Zapper", testBYUSD.balanceOf(address(testSFLUVZapper))); - console.log("Honey Balance of Zapper", honeyToken.balanceOf(address(testSFLUVZapper))); - assert(testBYUSD.balanceOf(address(testSFLUVZapper)) > 490000000); - assert(honeyToken.balanceOf(address(testSFLUVZapper)) < 510000000000000000000); - +function testLiquidityPool () public { + // Send some HONEY to the zapper from whale + vm.startPrank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + honeyToken.transfer(address(testSFLUVZapper), 1000 * 1e18); + vm.stopPrank(); - } + // Assert the zapper has 1000 HONEY + assert(honeyToken.balanceOf(address(testSFLUVZapper)) == 1000 * 1e18); + + // Pull initial sqrtPriceX96 from the liquidity pool + (uint160 sqrtPriceX96,,,,,,) = liquidityPool.slot0(); + + // 95% minimum output + // sqrt(1 / 0.95) ≈ 1.025978352 + // Determine slippage tolerance + uint256 NUM = 1_025_978_352; // scaled by 1e9 + uint256 DEN = 1_000_000_000; + + uint256 sqrtPriceLimitX96 = uint256( + (uint256(sqrtPriceX96) * NUM) / DEN + ); + + // swap 500 HONEY for BYUSD using the liquidity pool + vm.startPrank(address(testSFLUVZapper)); + liquidityPool.swap( + address(testSFLUVZapper), + false, + int256(500 * 1e18), + uint160(sqrtPriceLimitX96), + "" + ); + vm.stopPrank(); - function testHoneyManipulation() public { - console.log("Whale BYUSD Balance: ", testBYUSD.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); - console.log("Whale Honey Balance: ", honeyToken.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); - console.log("Is BYUSD Pegged?:", testHoneyFactory.isPegged(address(testBYUSD))); - vm.startPrank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); - testBYUSD.approve(address(testHoneyFactory), 100 * 1e6); - testHoneyFactory.mint(address(testBYUSD), 100 * 1e6, 0x4Be03f781C497A489E3cB0287833452cA9B9E80B, false); - console.log("Whale BYUSD Balance: ", testBYUSD.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); - console.log("Whale Honey Balance: ", honeyToken.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); - testHoneyFactory.redeem(address(testBYUSD), 100 * 1e18, 0x4Be03f781C497A489E3cB0287833452cA9B9E80B, false); - console.log("Whale BYUSD Balance: ", testBYUSD.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); - console.log("Whale Honey Balance: ", honeyToken.balanceOf(0x4Be03f781C497A489E3cB0287833452cA9B9E80B)); - vm.stopPrank(); + // Assert that after the swap, the zapper has more than 490 BYUSD and 500 HONEY + assert(testBYUSD.balanceOf(address(testSFLUVZapper)) > 490000000); + assert(honeyToken.balanceOf(address(testSFLUVZapper)) == 500000000000000000000); } } From e669b267272da11c353a408846b3815d3deb935c Mon Sep 17 00:00:00 2001 From: Paul O'Leary Date: Thu, 29 Jan 2026 16:22:29 -0800 Subject: [PATCH 19/22] WIP - initial upgrader, etc. --- NOTES.txt | 17 +++++++------- script/GrantSFLUVZapperv1.s.sol | 26 ---------------------- script/UpgradeSFLUVZapper.sol | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 34 deletions(-) delete mode 100644 script/GrantSFLUVZapperv1.s.sol create mode 100644 script/UpgradeSFLUVZapper.sol diff --git a/NOTES.txt b/NOTES.txt index 9d1fbd2..6a6f36e 100644 --- a/NOTES.txt +++ b/NOTES.txt @@ -31,14 +31,14 @@ cast balance --erc20 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 0xf39Fd6e51aad88 # sfluv balance = anvil address 0 cast balance --erc20 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --rpc-url http://127.0.0.1:8545 -forge verify-contract 0x881cAd4f885c6701D8481c0eD347f6d35444eA7e src/SFLUVv2.sol:SFLUVv2 --verifier-url 'https://api.berascan.com/api' --etherscan-api-key WMP6HJHJ9NAK8A63SNAG1G4KHAUKU5X4SQ --num-of-optimizations 200 +forge verify-contract 0x881cAd4f885c6701D8481c0eD347f6d35444eA7e src/SFLUVv2.sol:SFLUVv2 --verifier-url 'https://api.berascan.com/api' --etherscan-api-key $$ETHERSCAN_API_KEY --num-of-optimizations 200 forge verify-contract --watch --chain-id 80094 0x881cAd4f885c6701D8481c0eD347f6d35444eA7e src/SFLUVv2.sol:SFLUVv2 --verifier-url 'https://api.berascan.com/api' forge verify-contract 0x881cAd4f885c6701D8481c0eD347f6d35444eA7e \ src/SFLUVv2.sol:SFLUVv2 \ --rpc-url https://rpc.berachain.com \ ---etherscan-api-key WMP6HJHJ9NAK8A63SNAG1G4KHAUKU5X4SQ \ +--etherscan-api-key $$ETHERSCAN_API_KEY \ --verifier-url 'https://api.berascan.com/api' \ --watch @@ -46,9 +46,10 @@ src/SFLUVv2.sol:SFLUVv2 \ forge script script/DeploySFLUVZapperv1.s.sol:DeploySFLUVZapperv1 --rpc-url https://rpc.berachain.com --private-key $PRIVATE_KEY --broadcast 0xd0EBD0495750899D18b915BDeba789E2defdC394 -# grant zapper mainnet -forge script script/GrantSFLUVZapperv1.s.sol:GrantSFLUVZapperv1 --rpc-url https://rpc.berachain.com --private-key $PRIVATE_KEY --broadcast - -https://api.etherscan.io/v2/api?chainid=80094&action=verify-contract&apikey=WMP6HJHJ9NAK8A63SNAG1G4KHAUKU5X4SQ - -forge verify-contract --watch --chain-id 80094 0x94d86383085bDde798C3F9Cb6d06f73B5DcCC11e src/SFLUVZapperv1.sol:SFLUVZapperv1 --verifier etherscan --etherscan-api-key WMP6HJHJ9NAK8A63SNAG1G4KHAUKU5X4SQ +forge script script/UpgradeSFLUVZapper.sol:UpgradeSFLUVZapper \ + --rpc-url https://rpc.berachain.com \ + --private-key $PRIVATE_KEY \ + --broadcast \ + --verify \ + --etherscan-api-key $ETHERSCAN_API_KEY \ + -vvvv diff --git a/script/GrantSFLUVZapperv1.s.sol b/script/GrantSFLUVZapperv1.s.sol deleted file mode 100644 index 6050adb..0000000 --- a/script/GrantSFLUVZapperv1.s.sol +++ /dev/null @@ -1,26 +0,0 @@ -pragma solidity ^0.8.26; - -import "forge-std/Script.sol"; -// import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SFLUVZapperv1} from "../src/SFLUVZapperv1.sol"; - -contract GrantSFLUVZapperv1 is Script { - - // mainnet - address constant private SFLUV_ZAPPER_ADDR = 0xd0EBD0495750899D18b915BDeba789E2defdC394; - - // sanchez test account - address constant private TARGET_ADDR = 0x8b631C26537a784082A55528eaD52271ce88572e; - - function run() public { - vm.startBroadcast(); - SFLUVZapperv1 impl = SFLUVZapperv1(payable(SFLUV_ZAPPER_ADDR)); - if (!impl.hasRole(impl.REDEEMER_ADMIN_ROLE(), msg.sender)) { - impl.grantRole(impl.REDEEMER_ADMIN_ROLE(), msg.sender); - } - if (!impl.hasRole(impl.REDEEMER_ROLE(), TARGET_ADDR)) { - impl.grantRole(impl.REDEEMER_ROLE(), TARGET_ADDR); - } - vm.stopBroadcast(); - } -} diff --git a/script/UpgradeSFLUVZapper.sol b/script/UpgradeSFLUVZapper.sol new file mode 100644 index 0000000..34da9e6 --- /dev/null +++ b/script/UpgradeSFLUVZapper.sol @@ -0,0 +1,39 @@ +pragma solidity ^0.8.26; + +import "forge-std/Script.sol"; +import {SFLUVZapperv1} from "../src/SFLUVZapperv1.sol"; +// import {SFLUVZapperv2} from "../src/SFLUVZapperv2.sol"; // Your new implementation +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import {IDeployErrors} from "./IDeployErrors.sol"; + +interface IUUPSUpgradeable { + function upgradeToAndCall(address newImplementation, bytes memory data) external payable; +} + +contract UpgradeSFLUVZapper is Script, IDeployErrors { + + function run() public { + address payable proxyAddress = payable(vm.envAddress("SFLUV_ZAPPER_PROXY_ADDRESS")); + + vm.startBroadcast(); + + // Get reference to the proxy (using the old interface) + SFLUVZapperv1 proxy = SFLUVZapperv1(proxyAddress); + + // Verify caller has upgrade authority + if (!proxy.hasRole(proxy.DEFAULT_ADMIN_ROLE(), msg.sender)) revert NotAdmin(); + + // Deploy new implementation + SFLUVZapperv1 newImpl = new SFLUVZapperv1(); + + console.log("New Implementation:", address(newImpl)); + + // For simple upgrade without reinitialization: + IUUPSUpgradeable(proxyAddress).upgradeToAndCall(address(newImpl), ""); + + console.log("Upgrade successful!"); + console.log("Proxy Address:", proxyAddress); + + vm.stopBroadcast(); + } +} From f853b360a18b9fc22bdfbed81d31188f8e9fe673 Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Thu, 5 Feb 2026 11:28:27 -0800 Subject: [PATCH 20/22] commiting before starting USDC zapper --- src/SFLUVZapperv1.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index b20e365..5354491 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -257,10 +257,7 @@ struct SFLUVZapperStorage { // 3. Check BYUSD inside Honey, and keep track of how much is redeemed HoneyFactory hf = HoneyFactory(address($.honeyFactory)); - // *** this gets the balance of BYUSD in HONEY in BYUSD decimals - // uint256 byusdAvailable = $.byusd.balanceOf($.byusdVault) - byusdCurrentFees; uint256 byusdCurrentFees = hf.collectedAssetFees(address($.byusd)); - // this gets the balance of BYUSD in HONEY decimals - i.e. 'shares' uint256 byusdAvailableInHoney = ($.byusd.balanceOf(address(hf.vaults(address($.byusd)))) * 1e12) - (2 * byusdCurrentFees); if (byusdAvailableInHoney > 0) { From c33de11041a7e7f95cf03aa7bdfd62f2f7f64e5b Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Thu, 5 Feb 2026 14:40:54 -0800 Subject: [PATCH 21/22] vibe coded a USDC->Base zapper --- .../80094/run-1770331106882.json | 94 ++++++ .../80094/run-1770331165315.json | 309 ++++++++++++++++++ .../80094/run-latest.json | 309 ++++++++++++++++++ lib/bera-contracts | 2 +- script/DeploySFLUVUSDCeZapper.s.sol | 57 ++++ src/SFLUVUSDCeZapper.sol | 283 ++++++++++++++++ test/SFLUVUSDCeZapper.sol | 279 ++++++++++++++++ test/SFLUVZapperv1.sol | 3 +- 8 files changed, 1334 insertions(+), 2 deletions(-) create mode 100644 broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-1770331106882.json create mode 100644 broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-1770331165315.json create mode 100644 broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-latest.json create mode 100644 script/DeploySFLUVUSDCeZapper.s.sol create mode 100644 src/SFLUVUSDCeZapper.sol create mode 100644 test/SFLUVUSDCeZapper.sol diff --git a/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-1770331106882.json b/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-1770331106882.json new file mode 100644 index 0000000..628ac40 --- /dev/null +++ b/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-1770331106882.json @@ -0,0 +1,94 @@ +{ + "transactions": [ + { + "hash": null, + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "function": "grantRole(bytes32,address)", + "arguments": [ + "0x14dfd053822731770e1bb7dd6729fc1694bee325c911630f09a2dc3351850c9f", + "0x90496e23825aD0C8107d04671e6a27f30630Fc35" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "gas": "0x1432c", + "value": "0x0", + "input": "0x2f2ff15d14dfd053822731770e1bb7dd6729fc1694bee325c911630f09a2dc3351850c9f00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35", + "nonce": "0x15", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": null, + "transactionType": "CREATE", + "contractName": "SFLUVUSDCeZapper", + "contractAddress": "0x73ace74ca2c841ca1b78a9093428a5e1e35ecb21", + "function": null, + "arguments": null, + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "gas": "0x519611", + "value": "0x0", + "input": "0x60a06040523073ffffffffffffffffffffffffffffffffffffffff1660809073ffffffffffffffffffffffffffffffffffffffff16815250348015610042575f80fd5b5061005161005660201b60201c565b6101d1565b5f61006561015460201b60201c565b9050805f0160089054906101000a900460ff16156100af576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8016815f015f9054906101000a900467ffffffffffffffff1667ffffffffffffffff16146101515767ffffffffffffffff815f015f6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d267ffffffffffffffff60405161014891906101b8565b60405180910390a15b50565b5f8061016461016d60201b60201c565b90508091505090565b5f7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005f1b905090565b5f67ffffffffffffffff82169050919050565b6101b281610196565b82525050565b5f6020820190506101cb5f8301846101a9565b92915050565b6080516149046101f75f395f8181611452015281816114a7015261166601526149045ff3fe608060405260043610610117575f3560e01c80636cab20fd1161009f578063ad3cb1cc11610063578063ad3cb1cc1461039d578063ae103854146103c7578063d547741f146103ef578063ed85863b14610417578063fa461e331461043f5761011e565b80636cab20fd146102a85780637fa46ab4146102d057806391d14854146102fa578063a217fddf14610336578063ab920833146103605761011e565b80632f2ff15d116100e65780632f2ff15d146101ea57806336568abe146102125780634b9687891461023a5780634f1ef2861461026257806352d1902d1461027e5761011e565b806301ffc9a714610122578063081338bb1461015e57806319a806fe14610186578063248a9ca3146101ae5761011e565b3661011e57005b5f80fd5b34801561012d575f80fd5b506101486004803603810190610143919061326a565b610467565b60405161015591906132af565b60405180910390f35b348015610169575f80fd5b50610184600480360381019061017f9190613322565b6104e0565b005b348015610191575f80fd5b506101ac60048036038101906101a79190613322565b610620565b005b3480156101b9575f80fd5b506101d460048036038101906101cf9190613380565b61067e565b6040516101e191906133ba565b60405180910390f35b3480156101f5575f80fd5b50610210600480360381019061020b91906133d3565b6106a8565b005b34801561021d575f80fd5b50610238600480360381019061023391906133d3565b6106ca565b005b348015610245575f80fd5b50610260600480360381019061025b9190613322565b610745565b005b61027c6004803603810190610277919061354d565b6107a3565b005b348015610289575f80fd5b506102926107c2565b60405161029f91906133ba565b60405180910390f35b3480156102b3575f80fd5b506102ce60048036038101906102c991906135e2565b6107f3565b005b3480156102db575f80fd5b506102e4610a9e565b6040516102f191906133ba565b60405180910390f35b348015610305575f80fd5b50610320600480360381019061031b91906133d3565b610ac2565b60405161032d91906132af565b60405180910390f35b348015610341575f80fd5b5061034a610b33565b60405161035791906133ba565b60405180910390f35b34801561036b575f80fd5b5061038660048036038101906103819190613640565b610b39565b604051610394929190613758565b60405180910390f35b3480156103a8575f80fd5b506103b1610c8a565b6040516103be91906137df565b60405180910390f35b3480156103d2575f80fd5b506103ed60048036038101906103e89190613838565b610cc3565b005b3480156103fa575f80fd5b50610415600480360381019061041091906133d3565b610d02565b005b348015610422575f80fd5b5061043d60048036038101906104389190613886565b610d24565b005b34801561044a575f80fd5b5061046560048036038101906104609190613955565b610ec3565b005b5f7f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806104d957506104d88261118e565b5b9050919050565b5f801b6104ec816111f7565b5f6104f561120b565b905082816004015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b81526004016105da9291906139e4565b6020604051808303815f875af11580156105f6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061061a9190613a35565b50505050565b5f801b61062c816111f7565b5f61063561120b565b905082816006015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050565b5f80610688611232565b9050805f015f8481526020019081526020015f2060010154915050919050565b6106b18261067e565b6106ba816111f7565b6106c48383611259565b50505050565b6106d2611351565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610736576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107408282611358565b505050565b5f801b610751816111f7565b5f61075a61120b565b905082816005015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050565b6107ab611450565b6107b482611536565b6107be8282611546565b5050565b5f6107cb611664565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b905090565b5f801b6107ff816111f7565b5f61080861120b565b90505f8373ffffffffffffffffffffffffffffffffffffffff164760405161082f90613a8d565b5f6040518083038185875af1925050503d805f8114610869576040519150601f19603f3d011682016040523d82523d5f602084013e61086e565b606091505b50509050806108b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a990613aeb565b60405180910390fd5b5f826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161090f9190613b09565b602060405180830381865afa15801561092a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061094e9190613b36565b90505f811115610a9757826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b386836040518363ffffffff1660e01b81526004016109b6929190613bbc565b6020604051808303815f875af11580156109d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109f69190613a35565b50826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb86836040518363ffffffff1660e01b8152600401610a55929190613bbc565b6020604051808303815f875af1158015610a71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a959190613a35565b505b5050505050565b7f966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce6381565b5f80610acc611232565b9050805f015f8581526020019081526020015f205f015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1691505092915050565b5f801b81565b610b416131a2565b610b496131d4565b7f966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce635f610b7361120b565b9050806001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166391d1485483610bbd611351565b6040518363ffffffff1660e01b8152600401610bda929190613be3565b602060405180830381865afa158015610bf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c199190613a35565b610c6357610c25611351565b826040517fe2517d3f000000000000000000000000000000000000000000000000000000008152600401610c5a929190613c0a565b60405180910390fd5b610c6b6116eb565b610c75868661173f565b93509350610c816123fa565b50509250929050565b6040518060400160405280600581526020017f352e302e3000000000000000000000000000000000000000000000000000000081525081565b5f801b610ccf816111f7565b5f610cd861120b565b9050828160060160146101000a81548163ffffffff021916908363ffffffff160217905550505050565b610d0b8261067e565b610d14816111f7565b610d1e8383611358565b50505050565b5f610d2d612411565b90505f815f0160089054906101000a900460ff161590505f825f015f9054906101000a900467ffffffffffffffff1690505f808267ffffffffffffffff16148015610d755750825b90505f60018367ffffffffffffffff16148015610da857505f3073ffffffffffffffffffffffffffffffffffffffff163b145b905081158015610db6575080155b15610ded576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001855f015f6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055508315610e3a576001855f0160086101000a81548160ff0219169083151502179055505b610e42612424565b610e4a61242e565b610e5386612438565b610e5f5f801b88611259565b508315610eba575f855f0160086101000a81548160ff0219169083151502179055507fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d26001604051610eb19190613c6a565b60405180910390a15b50505050505050565b5f610ecc61120b565b9050806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f5f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f5690613ccd565b60405180910390fd5b5f85131561107357806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fd3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ff79190613cff565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33876040518363ffffffff1660e01b81526004016110319291906139e4565b6020604051808303815f875af115801561104d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110719190613a35565b505b5f84131561118757806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110e7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061110b9190613cff565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33866040518363ffffffff1660e01b81526004016111459291906139e4565b6020604051808303815f875af1158015611161573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111859190613a35565b505b5050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b61120881611203611351565b612d6f565b50565b5f7f4b0052a517f33f1f1b7e6b3a321c7f53e23bc343d8df724c8dc01d1fd1b97f00905090565b5f7f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800905090565b5f80611263611232565b905061126f8484610ac2565b611346576001815f015f8681526020019081526020015f205f015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506112e2611351565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4600191505061134b565b5f9150505b92915050565b5f33905090565b5f80611362611232565b905061136e8484610ac2565b15611445575f815f015f8681526020019081526020015f205f015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506113e1611351565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a4600191505061144a565b5f9150505b92915050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614806114fd57507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166114e4612dc0565b73ffffffffffffffffffffffffffffffffffffffff1614155b15611534576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f801b611542816111f7565b5050565b8173ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156115ae57506040513d601f19601f820116820180604052508101906115ab9190613d3e565b60015b6115ef57816040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526004016115e69190613b09565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b811461165557806040517faa1d49a400000000000000000000000000000000000000000000000000000000815260040161164c91906133ba565b60405180910390fd5b61165f8383612e13565b505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16146116e9576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f6116f4612e85565b90506002815f015403611733576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002815f018190555050565b6117476131a2565b61174f6131d4565b5f61189f8561175c61120b565b6003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117c7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117eb9190613d9f565b600a6117f79190613f26565b6117ff61120b565b6001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561186a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061188e9190613d9f565b600a61189a9190613f26565b612eac565b90505f6118aa61120b565b90505f816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd6118f4611351565b308a6040518463ffffffff1660e01b815260040161191493929190613f70565b6020604051808303815f875af1158015611930573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119549190613a35565b9050806119c857816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1630611988611351565b896040517fcd3f16590000000000000000000000000000000000000000000000000000000081526004016119bf9493929190613fa5565b60405180910390fd5b816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663205c287830896040518363ffffffff1660e01b8152600401611a269291906139e4565b6020604051808303815f875af1158015611a42573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a669190613a35565b905080611a9f576040517fbb1205c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f826002015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611afc9190613b09565b602060405180830381865afa158015611b17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b3b9190613b36565b90505f836003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611b9a9190613b09565b602060405180830381865afa158015611bb5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bd99190613b36565b90505f845f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f8173ffffffffffffffffffffffffffffffffffffffff166364f76eaa876003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401611c5e9190613b09565b602060405180830381865afa158015611c79573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c9d9190613b36565b90505f816002611cad9190613fe8565b64e8d4a51000886003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a082318a6005015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401611d329190613b09565b602060405180830381865afa158015611d4d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d719190613b36565b611d7b9190613fe8565b611d859190614029565b90505f811115611e79575f818610611d9d5781611d9f565b855b9050875f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ed596315896003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683305f6040518563ffffffff1660e01b8152600401611e25949392919061405c565b5f604051808303815f875af1158015611e40573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190611e68919061415f565b508086611e759190614029565b9550505b5f851115612002575f876006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015611eee573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f129190614251565b50505050505090505f633d272ff090505f633b9aca0090505f81838573ffffffffffffffffffffffffffffffffffffffff16611f4e9190613fe8565b611f58919061431b565b90508a6006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663128acb08305f8c856040518563ffffffff1660e01b8152600401611fbc9493929190614399565b60408051808303815f875af1158015611fd7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ffb9190614403565b5050505050505b5f876003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161205f9190613b09565b602060405180830381865afa15801561207a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061209e9190613b36565b90505f85826120ad9190614029565b90506064605f8b6120be9190613fe8565b6120c8919061431b565b81101561210a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612101906144b1565b60405180910390fd5b5f6040518060e001604052808b60060160149054906101000a900463ffffffff1663ffffffff1681526020018f73ffffffffffffffffffffffffffffffffffffffff165f1b81526020018c81526020016064605f8e6121699190613fe8565b612173919061431b565b815260200160405180602001604052805f815250815260200160405180602001604052805f815250815260200160405180602001604052805f81525081525090505f8a6004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633b6f743b835f6040518363ffffffff1660e01b81526004016122139291906145d7565b6040805180830381865afa15801561222d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122519190614656565b905081606001518b6003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016122b49190613b09565b602060405180830381865afa1580156122cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f39190613b36565b1015612334576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161232b906146cb565b60405180910390fd5b5f808c6004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c7c7f5b3845f01518686306040518563ffffffff1660e01b815260040161239a93929190614716565b60c06040518083038185885af11580156123b6573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906123db919061482a565b9150915081819f509f5050505050505050505050505050509250929050565b5f612403612e85565b90506001815f018190555050565b5f8061241b612ed9565b90508091505090565b61242c612f02565b565b612436612f02565b565b612440612f02565b5f61244961120b565b90505f73ffffffffffffffffffffffffffffffffffffffff16825f0160208101906124749190613322565b73ffffffffffffffffffffffffffffffffffffffff16036124c1576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f0160208101906124d39190613322565b815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff1682602001602081019061253d9190613322565b73ffffffffffffffffffffffffffffffffffffffff160361258a576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81602001602081019061259d9190613322565b816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260400160208101906126089190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612655576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160400160208101906126689190613322565b816002015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260600160208101906126d39190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612720576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160600160208101906127339190613322565b816003015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff1682608001602081019061279e9190613322565b73ffffffffffffffffffffffffffffffffffffffff16036127eb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160800160208101906127fe9190613322565b816004015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260a00160208101906128699190613322565b73ffffffffffffffffffffffffffffffffffffffff16036128b6576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160a00160208101906128c99190613322565b816005015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260c00160208101906129349190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612981576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160c00160208101906129949190613322565b816006015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f8260e00160208101906129e99190613838565b63ffffffff1603612a26576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160e0016020810190612a399190613838565b8160060160146101000a81548163ffffffff021916908363ffffffff160217905550806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3825f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612afb9291906139e4565b6020604051808303815f875af1158015612b17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b3b9190613a35565b50806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612bdd9291906139e4565b6020604051808303815f875af1158015612bf9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c1d9190613a35565b50805f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166336b2c4b26040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c89573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cad91906148a3565b73ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612d2a9291906139e4565b6020604051808303815f875af1158015612d46573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d6a9190613a35565b505050565b612d798282610ac2565b612dbc5780826040517fe2517d3f000000000000000000000000000000000000000000000000000000008152600401612db3929190613c0a565b60405180910390fd5b5050565b5f612dec7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b612f42565b5f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b612e1c82612f4b565b8173ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a25f81511115612e7857612e728282613014565b50612e81565b612e80613105565b5b5050565b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00905090565b5f828402905081838583041485151702612ecd5763ad251c275f526004601cfd5b81810490509392505050565b5f7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005f1b905090565b612f0a613141565b612f40576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f819050919050565b5f8173ffffffffffffffffffffffffffffffffffffffff163b03612fa657806040517f4c9c8ce3000000000000000000000000000000000000000000000000000000008152600401612f9d9190613b09565b60405180910390fd5b80612fd27f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b612f42565b5f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60605f613021848461315f565b905080801561305757505f613034613173565b118061305657505f8473ffffffffffffffffffffffffffffffffffffffff163b115b5b1561306c5761306461317a565b9150506130ff565b80156130af57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016130a69190613b09565b60405180910390fd5b5f6130b8613173565b11156130cb576130c6613197565b6130fd565b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b92915050565b5f34111561313f576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f61314a612411565b5f0160089054906101000a900460ff16905090565b5f805f835160208501865af4905092915050565b5f3d905090565b606060405190503d81523d5f602083013e3d602001810160405290565b6040513d5f823e3d81fd5b60405180606001604052805f80191681526020015f67ffffffffffffffff1681526020016131ce6131ec565b81525090565b60405180604001604052805f81526020015f81525090565b60405180604001604052805f81526020015f81525090565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61324981613215565b8114613253575f80fd5b50565b5f8135905061326481613240565b92915050565b5f6020828403121561327f5761327e61320d565b5b5f61328c84828501613256565b91505092915050565b5f8115159050919050565b6132a981613295565b82525050565b5f6020820190506132c25f8301846132a0565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6132f1826132c8565b9050919050565b613301816132e7565b811461330b575f80fd5b50565b5f8135905061331c816132f8565b92915050565b5f602082840312156133375761333661320d565b5b5f6133448482850161330e565b91505092915050565b5f819050919050565b61335f8161334d565b8114613369575f80fd5b50565b5f8135905061337a81613356565b92915050565b5f602082840312156133955761339461320d565b5b5f6133a28482850161336c565b91505092915050565b6133b48161334d565b82525050565b5f6020820190506133cd5f8301846133ab565b92915050565b5f80604083850312156133e9576133e861320d565b5b5f6133f68582860161336c565b92505060206134078582860161330e565b9150509250929050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61345f82613419565b810181811067ffffffffffffffff8211171561347e5761347d613429565b5b80604052505050565b5f613490613204565b905061349c8282613456565b919050565b5f67ffffffffffffffff8211156134bb576134ba613429565b5b6134c482613419565b9050602081019050919050565b828183375f83830152505050565b5f6134f16134ec846134a1565b613487565b90508281526020810184848401111561350d5761350c613415565b5b6135188482856134d1565b509392505050565b5f82601f83011261353457613533613411565b5b81356135448482602086016134df565b91505092915050565b5f80604083850312156135635761356261320d565b5b5f6135708582860161330e565b925050602083013567ffffffffffffffff81111561359157613590613211565b5b61359d85828601613520565b9150509250929050565b5f6135b1826132c8565b9050919050565b6135c1816135a7565b81146135cb575f80fd5b50565b5f813590506135dc816135b8565b92915050565b5f602082840312156135f7576135f661320d565b5b5f613604848285016135ce565b91505092915050565b5f819050919050565b61361f8161360d565b8114613629575f80fd5b50565b5f8135905061363a81613616565b92915050565b5f80604083850312156136565761365561320d565b5b5f6136638582860161362c565b92505060206136748582860161330e565b9150509250929050565b6136878161334d565b82525050565b5f67ffffffffffffffff82169050919050565b6136a98161368d565b82525050565b6136b88161360d565b82525050565b604082015f8201516136d25f8501826136af565b5060208201516136e560208501826136af565b50505050565b608082015f8201516136ff5f85018261367e565b50602082015161371260208501826136a0565b50604082015161372560408501826136be565b50505050565b604082015f82015161373f5f8501826136af565b50602082015161375260208501826136af565b50505050565b5f60c08201905061376b5f8301856136eb565b613778608083018461372b565b9392505050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6137b18261377f565b6137bb8185613789565b93506137cb818560208601613799565b6137d481613419565b840191505092915050565b5f6020820190508181035f8301526137f781846137a7565b905092915050565b5f63ffffffff82169050919050565b613817816137ff565b8114613821575f80fd5b50565b5f813590506138328161380e565b92915050565b5f6020828403121561384d5761384c61320d565b5b5f61385a84828501613824565b91505092915050565b5f80fd5b5f610100828403121561387d5761387c613863565b5b81905092915050565b5f80610120838503121561389d5761389c61320d565b5b5f6138aa8582860161330e565b92505060206138bb85828601613867565b9150509250929050565b5f819050919050565b6138d7816138c5565b81146138e1575f80fd5b50565b5f813590506138f2816138ce565b92915050565b5f80fd5b5f80fd5b5f8083601f84011261391557613914613411565b5b8235905067ffffffffffffffff811115613932576139316138f8565b5b60208301915083600182028301111561394e5761394d6138fc565b5b9250929050565b5f805f806060858703121561396d5761396c61320d565b5b5f61397a878288016138e4565b945050602061398b878288016138e4565b935050604085013567ffffffffffffffff8111156139ac576139ab613211565b5b6139b887828801613900565b925092505092959194509250565b6139cf816132e7565b82525050565b6139de8161360d565b82525050565b5f6040820190506139f75f8301856139c6565b613a0460208301846139d5565b9392505050565b613a1481613295565b8114613a1e575f80fd5b50565b5f81519050613a2f81613a0b565b92915050565b5f60208284031215613a4a57613a4961320d565b5b5f613a5784828501613a21565b91505092915050565b5f81905092915050565b50565b5f613a785f83613a60565b9150613a8382613a6a565b5f82019050919050565b5f613a9782613a6d565b9150819050919050565b7f6e6174697665207472616e73666572206661696c6564000000000000000000005f82015250565b5f613ad5601683613789565b9150613ae082613aa1565b602082019050919050565b5f6020820190508181035f830152613b0281613ac9565b9050919050565b5f602082019050613b1c5f8301846139c6565b92915050565b5f81519050613b3081613616565b92915050565b5f60208284031215613b4b57613b4a61320d565b5b5f613b5884828501613b22565b91505092915050565b5f819050919050565b5f613b84613b7f613b7a846132c8565b613b61565b6132c8565b9050919050565b5f613b9582613b6a565b9050919050565b5f613ba682613b8b565b9050919050565b613bb681613b9c565b82525050565b5f604082019050613bcf5f830185613bad565b613bdc60208301846139d5565b9392505050565b5f604082019050613bf65f8301856133ab565b613c0360208301846139c6565b9392505050565b5f604082019050613c1d5f8301856139c6565b613c2a60208301846133ab565b9392505050565b5f819050919050565b5f613c54613c4f613c4a84613c31565b613b61565b61368d565b9050919050565b613c6481613c3a565b82525050565b5f602082019050613c7d5f830184613c5b565b92915050565b7f756e617574686f72697a656420706f6f6c0000000000000000000000000000005f82015250565b5f613cb7601183613789565b9150613cc282613c83565b602082019050919050565b5f6020820190508181035f830152613ce481613cab565b9050919050565b5f81519050613cf9816132f8565b92915050565b5f60208284031215613d1457613d1361320d565b5b5f613d2184828501613ceb565b91505092915050565b5f81519050613d3881613356565b92915050565b5f60208284031215613d5357613d5261320d565b5b5f613d6084828501613d2a565b91505092915050565b5f60ff82169050919050565b613d7e81613d69565b8114613d88575f80fd5b50565b5f81519050613d9981613d75565b92915050565b5f60208284031215613db457613db361320d565b5b5f613dc184828501613d8b565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f8160011c9050919050565b5f808291508390505b6001851115613e4c57808604811115613e2857613e27613dca565b5b6001851615613e375780820291505b8081029050613e4585613df7565b9450613e0c565b94509492505050565b5f82613e645760019050613f1f565b81613e71575f9050613f1f565b8160018114613e875760028114613e9157613ec0565b6001915050613f1f565b60ff841115613ea357613ea2613dca565b5b8360020a915084821115613eba57613eb9613dca565b5b50613f1f565b5060208310610133831016604e8410600b8410161715613ef55782820a905083811115613ef057613eef613dca565b5b613f1f565b613f028484846001613e03565b92509050818404811115613f1957613f18613dca565b5b81810290505b9392505050565b5f613f308261360d565b9150613f3b83613d69565b9250613f687fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484613e55565b905092915050565b5f606082019050613f835f8301866139c6565b613f9060208301856139c6565b613f9d60408301846139d5565b949350505050565b5f608082019050613fb85f8301876139c6565b613fc560208301866139c6565b613fd260408301856139c6565b613fdf60608301846139d5565b95945050505050565b5f613ff28261360d565b9150613ffd8361360d565b925082820261400b8161360d565b9150828204841483151761402257614021613dca565b5b5092915050565b5f6140338261360d565b915061403e8361360d565b925082820390508181111561405657614055613dca565b5b92915050565b5f60808201905061406f5f8301876139c6565b61407c60208301866139d5565b61408960408301856139c6565b61409660608301846132a0565b95945050505050565b5f67ffffffffffffffff8211156140b9576140b8613429565b5b602082029050602081019050919050565b5f6140dc6140d78461409f565b613487565b905080838252602082019050602084028301858111156140ff576140fe6138fc565b5b835b8181101561412857806141148882613b22565b845260208401935050602081019050614101565b5050509392505050565b5f82601f83011261414657614145613411565b5b81516141568482602086016140ca565b91505092915050565b5f602082840312156141745761417361320d565b5b5f82015167ffffffffffffffff81111561419157614190613211565b5b61419d84828501614132565b91505092915050565b6141af816132c8565b81146141b9575f80fd5b50565b5f815190506141ca816141a6565b92915050565b5f8160020b9050919050565b6141e5816141d0565b81146141ef575f80fd5b50565b5f81519050614200816141dc565b92915050565b5f61ffff82169050919050565b61421c81614206565b8114614226575f80fd5b50565b5f8151905061423781614213565b92915050565b5f8151905061424b8161380e565b92915050565b5f805f805f805f60e0888a03121561426c5761426b61320d565b5b5f6142798a828b016141bc565b975050602061428a8a828b016141f2565b965050604061429b8a828b01614229565b95505060606142ac8a828b01614229565b94505060806142bd8a828b01614229565b93505060a06142ce8a828b0161423d565b92505060c06142df8a828b01613a21565b91505092959891949750929550565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6143258261360d565b91506143308361360d565b9250826143405761433f6142ee565b5b828204905092915050565b614354816138c5565b82525050565b614363816132c8565b82525050565b5f82825260208201905092915050565b5f6143845f83614369565b915061438f82613a6a565b5f82019050919050565b5f60a0820190506143ac5f8301876139c6565b6143b960208301866132a0565b6143c6604083018561434b565b6143d3606083018461435a565b81810360808301526143e481614379565b905095945050505050565b5f815190506143fd816138ce565b92915050565b5f80604083850312156144195761441861320d565b5b5f614426858286016143ef565b9250506020614437858286016143ef565b9150509250929050565b7f696e73756666696369656e7420555344432e652066726f6d20737761702f72655f8201527f64656d7074696f6e000000000000000000000000000000000000000000000000602082015250565b5f61449b602883613789565b91506144a682614441565b604082019050919050565b5f6020820190508181035f8301526144c88161448f565b9050919050565b6144d8816137ff565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f614502826144de565b61450c81856144e8565b935061451c818560208601613799565b61452581613419565b840191505092915050565b5f60e083015f8301516145455f8601826144cf565b506020830151614558602086018261367e565b50604083015161456b60408601826136af565b50606083015161457e60608601826136af565b506080830151848203608086015261459682826144f8565b91505060a083015184820360a08601526145b082826144f8565b91505060c083015184820360c08601526145ca82826144f8565b9150508091505092915050565b5f6040820190508181035f8301526145ef8185614530565b90506145fe60208301846132a0565b9392505050565b5f80fd5b5f6040828403121561461e5761461d614605565b5b6146286040613487565b90505f61463784828501613b22565b5f83015250602061464a84828501613b22565b60208301525092915050565b5f6040828403121561466b5761466a61320d565b5b5f61467884828501614609565b91505092915050565b7f696e73756666696369656e7420555344432e65206166746572207377617000005f82015250565b5f6146b5601e83613789565b91506146c082614681565b602082019050919050565b5f6020820190508181035f8301526146e2816146a9565b9050919050565b604082015f8201516146fd5f8501826136af565b50602082015161471060208501826136af565b50505050565b5f6080820190508181035f83015261472e8186614530565b905061473d60208301856146e9565b61474a60608301846139c6565b949350505050565b61475b8161368d565b8114614765575f80fd5b50565b5f8151905061477681614752565b92915050565b5f6080828403121561479157614790614605565b5b61479b6060613487565b90505f6147aa84828501613d2a565b5f8301525060206147bd84828501614768565b60208301525060406147d184828501614609565b60408301525092915050565b5f604082840312156147f2576147f1614605565b5b6147fc6040613487565b90505f61480b84828501613b22565b5f83015250602061481e84828501613b22565b60208301525092915050565b5f8060c083850312156148405761483f61320d565b5b5f61484d8582860161477c565b925050608061485e858286016147dd565b9150509250929050565b5f614872826132e7565b9050919050565b61488281614868565b811461488c575f80fd5b50565b5f8151905061489d81614879565b92915050565b5f602082840312156148b8576148b761320d565b5b5f6148c58482850161488f565b9150509291505056fea26469706673582212207c40d7f04f816c3167b299ad67eeebeace17a565af378f26e04db286e14ad85464736f6c634300081a0033", + "nonce": "0x16", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": null, + "transactionType": "CREATE", + "contractName": "ERC1967Proxy", + "contractAddress": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "function": null, + "arguments": [ + "0x73aCE74Ca2c841ca1B78a9093428A5E1E35ECB21", + "0xed85863b00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35000000000000000000000000a4afef880f5ce1f63c9fb48f661e27f8b4216401000000000000000000000000881cad4f885c6701d8481c0ed347f6d35444ea7e000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000549943e04f40284185054145c6e4e9568c1d3241000000000000000000000000af54be5b6eec24d6bfacf1cce4eaf680a823939800000000000000000000000083e672c9949af428687ecc9b6a3ba74db7bb0ed0000000000000000000000000b16ffd445d3c785476f87017b79423ec9057406b00000000000000000000000000000000000000000000000000000000000075e8" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "gas": "0x951b1", + "value": "0x0", + "input": "0x608060405260405161062f38038061062f833981810160405281019061002591906104cd565b610035828261003c60201b60201c565b505061054f565b61004b826100c060201b60201c565b8173ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a25f815111156100ad576100a7828261018f60201b60201c565b506100bc565b6100bb61029e60201b60201c565b5b5050565b5f8173ffffffffffffffffffffffffffffffffffffffff163b0361011b57806040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526004016101129190610536565b60405180910390fd5b8061014d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b6102da60201b60201c565b5f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60605f6101a284846102e360201b60201c565b90508080156101de57505f6101bb6102f760201b60201c565b11806101dd57505f8473ffffffffffffffffffffffffffffffffffffffff163b115b5b156101f9576101f16102fe60201b60201c565b915050610298565b801561023c57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016102339190610536565b60405180910390fd5b5f61024b6102f760201b60201c565b11156102645761025f61031b60201b60201c565b610296565b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b92915050565b5f3411156102d8576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f819050919050565b5f805f835160208501865af4905092915050565b5f3d905090565b606060405190503d81523d5f602083013e3d602001810160405290565b6040513d5f823e3d81fd5b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61036082610337565b9050919050565b61037081610356565b811461037a575f80fd5b50565b5f8151905061038b81610367565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6103df82610399565b810181811067ffffffffffffffff821117156103fe576103fd6103a9565b5b80604052505050565b5f610410610326565b905061041c82826103d6565b919050565b5f67ffffffffffffffff82111561043b5761043a6103a9565b5b61044482610399565b9050602081019050919050565b8281835e5f83830152505050565b5f61047161046c84610421565b610407565b90508281526020810184848401111561048d5761048c610395565b5b610498848285610451565b509392505050565b5f82601f8301126104b4576104b3610391565b5b81516104c484826020860161045f565b91505092915050565b5f80604083850312156104e3576104e261032f565b5b5f6104f08582860161037d565b925050602083015167ffffffffffffffff81111561051157610510610333565b5b61051d858286016104a0565b9150509250929050565b61053081610356565b82525050565b5f6020820190506105495f830184610527565b92915050565b60d48061055b5f395ff3fe6080604052600a600c565b005b60186014601a565b6026565b565b5f60216044565b905090565b365f80375f80365f845af43d5f803e805f81146040573d5ff35b3d5ffd5b5f606e7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b6095565b5f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f81905091905056fea264697066735822122016ca1c4645f0320c6d4356c7b97646a5108f4256e2a4bf94fc99ce8fb00ac2f064736f6c634300081a003300000000000000000000000073ace74ca2c841ca1b78a9093428a5e1e35ecb2100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000124ed85863b00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35000000000000000000000000a4afef880f5ce1f63c9fb48f661e27f8b4216401000000000000000000000000881cad4f885c6701d8481c0ed347f6d35444ea7e000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000549943e04f40284185054145c6e4e9568c1d3241000000000000000000000000af54be5b6eec24d6bfacf1cce4eaf680a823939800000000000000000000000083e672c9949af428687ecc9b6a3ba74db7bb0ed0000000000000000000000000b16ffd445d3c785476f87017b79423ec9057406b00000000000000000000000000000000000000000000000000000000000075e800000000000000000000000000000000000000000000000000000000", + "nonce": "0x17", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": null, + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "function": "grantRole(bytes32,address)", + "arguments": [ + "0x966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce63", + "0x16170D3Bf5cdbAAaf87fa3ED8d945680965080b3" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "gas": "0x1432c", + "value": "0x0", + "input": "0x2f2ff15d966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce6300000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "nonce": "0x18", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1770331106882, + "chain": 80094, + "commit": "b688ac6" +} \ No newline at end of file diff --git a/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-1770331165315.json b/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-1770331165315.json new file mode 100644 index 0000000..cb26abb --- /dev/null +++ b/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-1770331165315.json @@ -0,0 +1,309 @@ +{ + "transactions": [ + { + "hash": "0x7e6df163d5b47ec64fa18d93c4afe20dedf0b14b3a369dbc3a76ae108dc8f861", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "function": "grantRole(bytes32,address)", + "arguments": [ + "0x14dfd053822731770e1bb7dd6729fc1694bee325c911630f09a2dc3351850c9f", + "0x90496e23825aD0C8107d04671e6a27f30630Fc35" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "gas": "0x1432c", + "value": "0x0", + "input": "0x2f2ff15d14dfd053822731770e1bb7dd6729fc1694bee325c911630f09a2dc3351850c9f00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35", + "nonce": "0x15", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xc3afd6cf70e8fd3f9b0786e42fc12d9b572f24118e7bd987026d3eca01d75d35", + "transactionType": "CREATE", + "contractName": "SFLUVUSDCeZapper", + "contractAddress": "0x73ace74ca2c841ca1b78a9093428a5e1e35ecb21", + "function": null, + "arguments": null, + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "gas": "0x519611", + "value": "0x0", + "input": "0x60a06040523073ffffffffffffffffffffffffffffffffffffffff1660809073ffffffffffffffffffffffffffffffffffffffff16815250348015610042575f80fd5b5061005161005660201b60201c565b6101d1565b5f61006561015460201b60201c565b9050805f0160089054906101000a900460ff16156100af576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8016815f015f9054906101000a900467ffffffffffffffff1667ffffffffffffffff16146101515767ffffffffffffffff815f015f6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d267ffffffffffffffff60405161014891906101b8565b60405180910390a15b50565b5f8061016461016d60201b60201c565b90508091505090565b5f7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005f1b905090565b5f67ffffffffffffffff82169050919050565b6101b281610196565b82525050565b5f6020820190506101cb5f8301846101a9565b92915050565b6080516149046101f75f395f8181611452015281816114a7015261166601526149045ff3fe608060405260043610610117575f3560e01c80636cab20fd1161009f578063ad3cb1cc11610063578063ad3cb1cc1461039d578063ae103854146103c7578063d547741f146103ef578063ed85863b14610417578063fa461e331461043f5761011e565b80636cab20fd146102a85780637fa46ab4146102d057806391d14854146102fa578063a217fddf14610336578063ab920833146103605761011e565b80632f2ff15d116100e65780632f2ff15d146101ea57806336568abe146102125780634b9687891461023a5780634f1ef2861461026257806352d1902d1461027e5761011e565b806301ffc9a714610122578063081338bb1461015e57806319a806fe14610186578063248a9ca3146101ae5761011e565b3661011e57005b5f80fd5b34801561012d575f80fd5b506101486004803603810190610143919061326a565b610467565b60405161015591906132af565b60405180910390f35b348015610169575f80fd5b50610184600480360381019061017f9190613322565b6104e0565b005b348015610191575f80fd5b506101ac60048036038101906101a79190613322565b610620565b005b3480156101b9575f80fd5b506101d460048036038101906101cf9190613380565b61067e565b6040516101e191906133ba565b60405180910390f35b3480156101f5575f80fd5b50610210600480360381019061020b91906133d3565b6106a8565b005b34801561021d575f80fd5b50610238600480360381019061023391906133d3565b6106ca565b005b348015610245575f80fd5b50610260600480360381019061025b9190613322565b610745565b005b61027c6004803603810190610277919061354d565b6107a3565b005b348015610289575f80fd5b506102926107c2565b60405161029f91906133ba565b60405180910390f35b3480156102b3575f80fd5b506102ce60048036038101906102c991906135e2565b6107f3565b005b3480156102db575f80fd5b506102e4610a9e565b6040516102f191906133ba565b60405180910390f35b348015610305575f80fd5b50610320600480360381019061031b91906133d3565b610ac2565b60405161032d91906132af565b60405180910390f35b348015610341575f80fd5b5061034a610b33565b60405161035791906133ba565b60405180910390f35b34801561036b575f80fd5b5061038660048036038101906103819190613640565b610b39565b604051610394929190613758565b60405180910390f35b3480156103a8575f80fd5b506103b1610c8a565b6040516103be91906137df565b60405180910390f35b3480156103d2575f80fd5b506103ed60048036038101906103e89190613838565b610cc3565b005b3480156103fa575f80fd5b50610415600480360381019061041091906133d3565b610d02565b005b348015610422575f80fd5b5061043d60048036038101906104389190613886565b610d24565b005b34801561044a575f80fd5b5061046560048036038101906104609190613955565b610ec3565b005b5f7f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806104d957506104d88261118e565b5b9050919050565b5f801b6104ec816111f7565b5f6104f561120b565b905082816004015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b81526004016105da9291906139e4565b6020604051808303815f875af11580156105f6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061061a9190613a35565b50505050565b5f801b61062c816111f7565b5f61063561120b565b905082816006015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050565b5f80610688611232565b9050805f015f8481526020019081526020015f2060010154915050919050565b6106b18261067e565b6106ba816111f7565b6106c48383611259565b50505050565b6106d2611351565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610736576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107408282611358565b505050565b5f801b610751816111f7565b5f61075a61120b565b905082816005015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050565b6107ab611450565b6107b482611536565b6107be8282611546565b5050565b5f6107cb611664565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b905090565b5f801b6107ff816111f7565b5f61080861120b565b90505f8373ffffffffffffffffffffffffffffffffffffffff164760405161082f90613a8d565b5f6040518083038185875af1925050503d805f8114610869576040519150601f19603f3d011682016040523d82523d5f602084013e61086e565b606091505b50509050806108b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a990613aeb565b60405180910390fd5b5f826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161090f9190613b09565b602060405180830381865afa15801561092a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061094e9190613b36565b90505f811115610a9757826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b386836040518363ffffffff1660e01b81526004016109b6929190613bbc565b6020604051808303815f875af11580156109d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109f69190613a35565b50826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb86836040518363ffffffff1660e01b8152600401610a55929190613bbc565b6020604051808303815f875af1158015610a71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a959190613a35565b505b5050505050565b7f966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce6381565b5f80610acc611232565b9050805f015f8581526020019081526020015f205f015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1691505092915050565b5f801b81565b610b416131a2565b610b496131d4565b7f966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce635f610b7361120b565b9050806001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166391d1485483610bbd611351565b6040518363ffffffff1660e01b8152600401610bda929190613be3565b602060405180830381865afa158015610bf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c199190613a35565b610c6357610c25611351565b826040517fe2517d3f000000000000000000000000000000000000000000000000000000008152600401610c5a929190613c0a565b60405180910390fd5b610c6b6116eb565b610c75868661173f565b93509350610c816123fa565b50509250929050565b6040518060400160405280600581526020017f352e302e3000000000000000000000000000000000000000000000000000000081525081565b5f801b610ccf816111f7565b5f610cd861120b565b9050828160060160146101000a81548163ffffffff021916908363ffffffff160217905550505050565b610d0b8261067e565b610d14816111f7565b610d1e8383611358565b50505050565b5f610d2d612411565b90505f815f0160089054906101000a900460ff161590505f825f015f9054906101000a900467ffffffffffffffff1690505f808267ffffffffffffffff16148015610d755750825b90505f60018367ffffffffffffffff16148015610da857505f3073ffffffffffffffffffffffffffffffffffffffff163b145b905081158015610db6575080155b15610ded576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001855f015f6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055508315610e3a576001855f0160086101000a81548160ff0219169083151502179055505b610e42612424565b610e4a61242e565b610e5386612438565b610e5f5f801b88611259565b508315610eba575f855f0160086101000a81548160ff0219169083151502179055507fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d26001604051610eb19190613c6a565b60405180910390a15b50505050505050565b5f610ecc61120b565b9050806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f5f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f5690613ccd565b60405180910390fd5b5f85131561107357806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fd3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ff79190613cff565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33876040518363ffffffff1660e01b81526004016110319291906139e4565b6020604051808303815f875af115801561104d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110719190613a35565b505b5f84131561118757806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110e7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061110b9190613cff565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33866040518363ffffffff1660e01b81526004016111459291906139e4565b6020604051808303815f875af1158015611161573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111859190613a35565b505b5050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b61120881611203611351565b612d6f565b50565b5f7f4b0052a517f33f1f1b7e6b3a321c7f53e23bc343d8df724c8dc01d1fd1b97f00905090565b5f7f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800905090565b5f80611263611232565b905061126f8484610ac2565b611346576001815f015f8681526020019081526020015f205f015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506112e2611351565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4600191505061134b565b5f9150505b92915050565b5f33905090565b5f80611362611232565b905061136e8484610ac2565b15611445575f815f015f8681526020019081526020015f205f015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506113e1611351565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a4600191505061144a565b5f9150505b92915050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614806114fd57507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166114e4612dc0565b73ffffffffffffffffffffffffffffffffffffffff1614155b15611534576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f801b611542816111f7565b5050565b8173ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156115ae57506040513d601f19601f820116820180604052508101906115ab9190613d3e565b60015b6115ef57816040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526004016115e69190613b09565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b811461165557806040517faa1d49a400000000000000000000000000000000000000000000000000000000815260040161164c91906133ba565b60405180910390fd5b61165f8383612e13565b505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16146116e9576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f6116f4612e85565b90506002815f015403611733576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002815f018190555050565b6117476131a2565b61174f6131d4565b5f61189f8561175c61120b565b6003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117c7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117eb9190613d9f565b600a6117f79190613f26565b6117ff61120b565b6001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561186a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061188e9190613d9f565b600a61189a9190613f26565b612eac565b90505f6118aa61120b565b90505f816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd6118f4611351565b308a6040518463ffffffff1660e01b815260040161191493929190613f70565b6020604051808303815f875af1158015611930573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119549190613a35565b9050806119c857816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1630611988611351565b896040517fcd3f16590000000000000000000000000000000000000000000000000000000081526004016119bf9493929190613fa5565b60405180910390fd5b816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663205c287830896040518363ffffffff1660e01b8152600401611a269291906139e4565b6020604051808303815f875af1158015611a42573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a669190613a35565b905080611a9f576040517fbb1205c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f826002015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611afc9190613b09565b602060405180830381865afa158015611b17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b3b9190613b36565b90505f836003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611b9a9190613b09565b602060405180830381865afa158015611bb5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bd99190613b36565b90505f845f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f8173ffffffffffffffffffffffffffffffffffffffff166364f76eaa876003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401611c5e9190613b09565b602060405180830381865afa158015611c79573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c9d9190613b36565b90505f816002611cad9190613fe8565b64e8d4a51000886003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a082318a6005015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401611d329190613b09565b602060405180830381865afa158015611d4d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d719190613b36565b611d7b9190613fe8565b611d859190614029565b90505f811115611e79575f818610611d9d5781611d9f565b855b9050875f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ed596315896003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683305f6040518563ffffffff1660e01b8152600401611e25949392919061405c565b5f604051808303815f875af1158015611e40573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190611e68919061415f565b508086611e759190614029565b9550505b5f851115612002575f876006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015611eee573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f129190614251565b50505050505090505f633d272ff090505f633b9aca0090505f81838573ffffffffffffffffffffffffffffffffffffffff16611f4e9190613fe8565b611f58919061431b565b90508a6006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663128acb08305f8c856040518563ffffffff1660e01b8152600401611fbc9493929190614399565b60408051808303815f875af1158015611fd7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ffb9190614403565b5050505050505b5f876003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161205f9190613b09565b602060405180830381865afa15801561207a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061209e9190613b36565b90505f85826120ad9190614029565b90506064605f8b6120be9190613fe8565b6120c8919061431b565b81101561210a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612101906144b1565b60405180910390fd5b5f6040518060e001604052808b60060160149054906101000a900463ffffffff1663ffffffff1681526020018f73ffffffffffffffffffffffffffffffffffffffff165f1b81526020018c81526020016064605f8e6121699190613fe8565b612173919061431b565b815260200160405180602001604052805f815250815260200160405180602001604052805f815250815260200160405180602001604052805f81525081525090505f8a6004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633b6f743b835f6040518363ffffffff1660e01b81526004016122139291906145d7565b6040805180830381865afa15801561222d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122519190614656565b905081606001518b6003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016122b49190613b09565b602060405180830381865afa1580156122cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f39190613b36565b1015612334576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161232b906146cb565b60405180910390fd5b5f808c6004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c7c7f5b3845f01518686306040518563ffffffff1660e01b815260040161239a93929190614716565b60c06040518083038185885af11580156123b6573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906123db919061482a565b9150915081819f509f5050505050505050505050505050509250929050565b5f612403612e85565b90506001815f018190555050565b5f8061241b612ed9565b90508091505090565b61242c612f02565b565b612436612f02565b565b612440612f02565b5f61244961120b565b90505f73ffffffffffffffffffffffffffffffffffffffff16825f0160208101906124749190613322565b73ffffffffffffffffffffffffffffffffffffffff16036124c1576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f0160208101906124d39190613322565b815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff1682602001602081019061253d9190613322565b73ffffffffffffffffffffffffffffffffffffffff160361258a576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81602001602081019061259d9190613322565b816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260400160208101906126089190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612655576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160400160208101906126689190613322565b816002015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260600160208101906126d39190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612720576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160600160208101906127339190613322565b816003015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff1682608001602081019061279e9190613322565b73ffffffffffffffffffffffffffffffffffffffff16036127eb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160800160208101906127fe9190613322565b816004015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260a00160208101906128699190613322565b73ffffffffffffffffffffffffffffffffffffffff16036128b6576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160a00160208101906128c99190613322565b816005015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260c00160208101906129349190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612981576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160c00160208101906129949190613322565b816006015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f8260e00160208101906129e99190613838565b63ffffffff1603612a26576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160e0016020810190612a399190613838565b8160060160146101000a81548163ffffffff021916908363ffffffff160217905550806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3825f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612afb9291906139e4565b6020604051808303815f875af1158015612b17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b3b9190613a35565b50806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612bdd9291906139e4565b6020604051808303815f875af1158015612bf9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c1d9190613a35565b50805f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166336b2c4b26040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c89573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cad91906148a3565b73ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612d2a9291906139e4565b6020604051808303815f875af1158015612d46573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d6a9190613a35565b505050565b612d798282610ac2565b612dbc5780826040517fe2517d3f000000000000000000000000000000000000000000000000000000008152600401612db3929190613c0a565b60405180910390fd5b5050565b5f612dec7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b612f42565b5f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b612e1c82612f4b565b8173ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a25f81511115612e7857612e728282613014565b50612e81565b612e80613105565b5b5050565b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00905090565b5f828402905081838583041485151702612ecd5763ad251c275f526004601cfd5b81810490509392505050565b5f7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005f1b905090565b612f0a613141565b612f40576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f819050919050565b5f8173ffffffffffffffffffffffffffffffffffffffff163b03612fa657806040517f4c9c8ce3000000000000000000000000000000000000000000000000000000008152600401612f9d9190613b09565b60405180910390fd5b80612fd27f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b612f42565b5f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60605f613021848461315f565b905080801561305757505f613034613173565b118061305657505f8473ffffffffffffffffffffffffffffffffffffffff163b115b5b1561306c5761306461317a565b9150506130ff565b80156130af57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016130a69190613b09565b60405180910390fd5b5f6130b8613173565b11156130cb576130c6613197565b6130fd565b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b92915050565b5f34111561313f576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f61314a612411565b5f0160089054906101000a900460ff16905090565b5f805f835160208501865af4905092915050565b5f3d905090565b606060405190503d81523d5f602083013e3d602001810160405290565b6040513d5f823e3d81fd5b60405180606001604052805f80191681526020015f67ffffffffffffffff1681526020016131ce6131ec565b81525090565b60405180604001604052805f81526020015f81525090565b60405180604001604052805f81526020015f81525090565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61324981613215565b8114613253575f80fd5b50565b5f8135905061326481613240565b92915050565b5f6020828403121561327f5761327e61320d565b5b5f61328c84828501613256565b91505092915050565b5f8115159050919050565b6132a981613295565b82525050565b5f6020820190506132c25f8301846132a0565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6132f1826132c8565b9050919050565b613301816132e7565b811461330b575f80fd5b50565b5f8135905061331c816132f8565b92915050565b5f602082840312156133375761333661320d565b5b5f6133448482850161330e565b91505092915050565b5f819050919050565b61335f8161334d565b8114613369575f80fd5b50565b5f8135905061337a81613356565b92915050565b5f602082840312156133955761339461320d565b5b5f6133a28482850161336c565b91505092915050565b6133b48161334d565b82525050565b5f6020820190506133cd5f8301846133ab565b92915050565b5f80604083850312156133e9576133e861320d565b5b5f6133f68582860161336c565b92505060206134078582860161330e565b9150509250929050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61345f82613419565b810181811067ffffffffffffffff8211171561347e5761347d613429565b5b80604052505050565b5f613490613204565b905061349c8282613456565b919050565b5f67ffffffffffffffff8211156134bb576134ba613429565b5b6134c482613419565b9050602081019050919050565b828183375f83830152505050565b5f6134f16134ec846134a1565b613487565b90508281526020810184848401111561350d5761350c613415565b5b6135188482856134d1565b509392505050565b5f82601f83011261353457613533613411565b5b81356135448482602086016134df565b91505092915050565b5f80604083850312156135635761356261320d565b5b5f6135708582860161330e565b925050602083013567ffffffffffffffff81111561359157613590613211565b5b61359d85828601613520565b9150509250929050565b5f6135b1826132c8565b9050919050565b6135c1816135a7565b81146135cb575f80fd5b50565b5f813590506135dc816135b8565b92915050565b5f602082840312156135f7576135f661320d565b5b5f613604848285016135ce565b91505092915050565b5f819050919050565b61361f8161360d565b8114613629575f80fd5b50565b5f8135905061363a81613616565b92915050565b5f80604083850312156136565761365561320d565b5b5f6136638582860161362c565b92505060206136748582860161330e565b9150509250929050565b6136878161334d565b82525050565b5f67ffffffffffffffff82169050919050565b6136a98161368d565b82525050565b6136b88161360d565b82525050565b604082015f8201516136d25f8501826136af565b5060208201516136e560208501826136af565b50505050565b608082015f8201516136ff5f85018261367e565b50602082015161371260208501826136a0565b50604082015161372560408501826136be565b50505050565b604082015f82015161373f5f8501826136af565b50602082015161375260208501826136af565b50505050565b5f60c08201905061376b5f8301856136eb565b613778608083018461372b565b9392505050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6137b18261377f565b6137bb8185613789565b93506137cb818560208601613799565b6137d481613419565b840191505092915050565b5f6020820190508181035f8301526137f781846137a7565b905092915050565b5f63ffffffff82169050919050565b613817816137ff565b8114613821575f80fd5b50565b5f813590506138328161380e565b92915050565b5f6020828403121561384d5761384c61320d565b5b5f61385a84828501613824565b91505092915050565b5f80fd5b5f610100828403121561387d5761387c613863565b5b81905092915050565b5f80610120838503121561389d5761389c61320d565b5b5f6138aa8582860161330e565b92505060206138bb85828601613867565b9150509250929050565b5f819050919050565b6138d7816138c5565b81146138e1575f80fd5b50565b5f813590506138f2816138ce565b92915050565b5f80fd5b5f80fd5b5f8083601f84011261391557613914613411565b5b8235905067ffffffffffffffff811115613932576139316138f8565b5b60208301915083600182028301111561394e5761394d6138fc565b5b9250929050565b5f805f806060858703121561396d5761396c61320d565b5b5f61397a878288016138e4565b945050602061398b878288016138e4565b935050604085013567ffffffffffffffff8111156139ac576139ab613211565b5b6139b887828801613900565b925092505092959194509250565b6139cf816132e7565b82525050565b6139de8161360d565b82525050565b5f6040820190506139f75f8301856139c6565b613a0460208301846139d5565b9392505050565b613a1481613295565b8114613a1e575f80fd5b50565b5f81519050613a2f81613a0b565b92915050565b5f60208284031215613a4a57613a4961320d565b5b5f613a5784828501613a21565b91505092915050565b5f81905092915050565b50565b5f613a785f83613a60565b9150613a8382613a6a565b5f82019050919050565b5f613a9782613a6d565b9150819050919050565b7f6e6174697665207472616e73666572206661696c6564000000000000000000005f82015250565b5f613ad5601683613789565b9150613ae082613aa1565b602082019050919050565b5f6020820190508181035f830152613b0281613ac9565b9050919050565b5f602082019050613b1c5f8301846139c6565b92915050565b5f81519050613b3081613616565b92915050565b5f60208284031215613b4b57613b4a61320d565b5b5f613b5884828501613b22565b91505092915050565b5f819050919050565b5f613b84613b7f613b7a846132c8565b613b61565b6132c8565b9050919050565b5f613b9582613b6a565b9050919050565b5f613ba682613b8b565b9050919050565b613bb681613b9c565b82525050565b5f604082019050613bcf5f830185613bad565b613bdc60208301846139d5565b9392505050565b5f604082019050613bf65f8301856133ab565b613c0360208301846139c6565b9392505050565b5f604082019050613c1d5f8301856139c6565b613c2a60208301846133ab565b9392505050565b5f819050919050565b5f613c54613c4f613c4a84613c31565b613b61565b61368d565b9050919050565b613c6481613c3a565b82525050565b5f602082019050613c7d5f830184613c5b565b92915050565b7f756e617574686f72697a656420706f6f6c0000000000000000000000000000005f82015250565b5f613cb7601183613789565b9150613cc282613c83565b602082019050919050565b5f6020820190508181035f830152613ce481613cab565b9050919050565b5f81519050613cf9816132f8565b92915050565b5f60208284031215613d1457613d1361320d565b5b5f613d2184828501613ceb565b91505092915050565b5f81519050613d3881613356565b92915050565b5f60208284031215613d5357613d5261320d565b5b5f613d6084828501613d2a565b91505092915050565b5f60ff82169050919050565b613d7e81613d69565b8114613d88575f80fd5b50565b5f81519050613d9981613d75565b92915050565b5f60208284031215613db457613db361320d565b5b5f613dc184828501613d8b565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f8160011c9050919050565b5f808291508390505b6001851115613e4c57808604811115613e2857613e27613dca565b5b6001851615613e375780820291505b8081029050613e4585613df7565b9450613e0c565b94509492505050565b5f82613e645760019050613f1f565b81613e71575f9050613f1f565b8160018114613e875760028114613e9157613ec0565b6001915050613f1f565b60ff841115613ea357613ea2613dca565b5b8360020a915084821115613eba57613eb9613dca565b5b50613f1f565b5060208310610133831016604e8410600b8410161715613ef55782820a905083811115613ef057613eef613dca565b5b613f1f565b613f028484846001613e03565b92509050818404811115613f1957613f18613dca565b5b81810290505b9392505050565b5f613f308261360d565b9150613f3b83613d69565b9250613f687fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484613e55565b905092915050565b5f606082019050613f835f8301866139c6565b613f9060208301856139c6565b613f9d60408301846139d5565b949350505050565b5f608082019050613fb85f8301876139c6565b613fc560208301866139c6565b613fd260408301856139c6565b613fdf60608301846139d5565b95945050505050565b5f613ff28261360d565b9150613ffd8361360d565b925082820261400b8161360d565b9150828204841483151761402257614021613dca565b5b5092915050565b5f6140338261360d565b915061403e8361360d565b925082820390508181111561405657614055613dca565b5b92915050565b5f60808201905061406f5f8301876139c6565b61407c60208301866139d5565b61408960408301856139c6565b61409660608301846132a0565b95945050505050565b5f67ffffffffffffffff8211156140b9576140b8613429565b5b602082029050602081019050919050565b5f6140dc6140d78461409f565b613487565b905080838252602082019050602084028301858111156140ff576140fe6138fc565b5b835b8181101561412857806141148882613b22565b845260208401935050602081019050614101565b5050509392505050565b5f82601f83011261414657614145613411565b5b81516141568482602086016140ca565b91505092915050565b5f602082840312156141745761417361320d565b5b5f82015167ffffffffffffffff81111561419157614190613211565b5b61419d84828501614132565b91505092915050565b6141af816132c8565b81146141b9575f80fd5b50565b5f815190506141ca816141a6565b92915050565b5f8160020b9050919050565b6141e5816141d0565b81146141ef575f80fd5b50565b5f81519050614200816141dc565b92915050565b5f61ffff82169050919050565b61421c81614206565b8114614226575f80fd5b50565b5f8151905061423781614213565b92915050565b5f8151905061424b8161380e565b92915050565b5f805f805f805f60e0888a03121561426c5761426b61320d565b5b5f6142798a828b016141bc565b975050602061428a8a828b016141f2565b965050604061429b8a828b01614229565b95505060606142ac8a828b01614229565b94505060806142bd8a828b01614229565b93505060a06142ce8a828b0161423d565b92505060c06142df8a828b01613a21565b91505092959891949750929550565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6143258261360d565b91506143308361360d565b9250826143405761433f6142ee565b5b828204905092915050565b614354816138c5565b82525050565b614363816132c8565b82525050565b5f82825260208201905092915050565b5f6143845f83614369565b915061438f82613a6a565b5f82019050919050565b5f60a0820190506143ac5f8301876139c6565b6143b960208301866132a0565b6143c6604083018561434b565b6143d3606083018461435a565b81810360808301526143e481614379565b905095945050505050565b5f815190506143fd816138ce565b92915050565b5f80604083850312156144195761441861320d565b5b5f614426858286016143ef565b9250506020614437858286016143ef565b9150509250929050565b7f696e73756666696369656e7420555344432e652066726f6d20737761702f72655f8201527f64656d7074696f6e000000000000000000000000000000000000000000000000602082015250565b5f61449b602883613789565b91506144a682614441565b604082019050919050565b5f6020820190508181035f8301526144c88161448f565b9050919050565b6144d8816137ff565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f614502826144de565b61450c81856144e8565b935061451c818560208601613799565b61452581613419565b840191505092915050565b5f60e083015f8301516145455f8601826144cf565b506020830151614558602086018261367e565b50604083015161456b60408601826136af565b50606083015161457e60608601826136af565b506080830151848203608086015261459682826144f8565b91505060a083015184820360a08601526145b082826144f8565b91505060c083015184820360c08601526145ca82826144f8565b9150508091505092915050565b5f6040820190508181035f8301526145ef8185614530565b90506145fe60208301846132a0565b9392505050565b5f80fd5b5f6040828403121561461e5761461d614605565b5b6146286040613487565b90505f61463784828501613b22565b5f83015250602061464a84828501613b22565b60208301525092915050565b5f6040828403121561466b5761466a61320d565b5b5f61467884828501614609565b91505092915050565b7f696e73756666696369656e7420555344432e65206166746572207377617000005f82015250565b5f6146b5601e83613789565b91506146c082614681565b602082019050919050565b5f6020820190508181035f8301526146e2816146a9565b9050919050565b604082015f8201516146fd5f8501826136af565b50602082015161471060208501826136af565b50505050565b5f6080820190508181035f83015261472e8186614530565b905061473d60208301856146e9565b61474a60608301846139c6565b949350505050565b61475b8161368d565b8114614765575f80fd5b50565b5f8151905061477681614752565b92915050565b5f6080828403121561479157614790614605565b5b61479b6060613487565b90505f6147aa84828501613d2a565b5f8301525060206147bd84828501614768565b60208301525060406147d184828501614609565b60408301525092915050565b5f604082840312156147f2576147f1614605565b5b6147fc6040613487565b90505f61480b84828501613b22565b5f83015250602061481e84828501613b22565b60208301525092915050565b5f8060c083850312156148405761483f61320d565b5b5f61484d8582860161477c565b925050608061485e858286016147dd565b9150509250929050565b5f614872826132e7565b9050919050565b61488281614868565b811461488c575f80fd5b50565b5f8151905061489d81614879565b92915050565b5f602082840312156148b8576148b761320d565b5b5f6148c58482850161488f565b9150509291505056fea26469706673582212207c40d7f04f816c3167b299ad67eeebeace17a565af378f26e04db286e14ad85464736f6c634300081a0033", + "nonce": "0x16", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionType": "CREATE", + "contractName": "ERC1967Proxy", + "contractAddress": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "function": null, + "arguments": [ + "0x73aCE74Ca2c841ca1B78a9093428A5E1E35ECB21", + "0xed85863b00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35000000000000000000000000a4afef880f5ce1f63c9fb48f661e27f8b4216401000000000000000000000000881cad4f885c6701d8481c0ed347f6d35444ea7e000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000549943e04f40284185054145c6e4e9568c1d3241000000000000000000000000af54be5b6eec24d6bfacf1cce4eaf680a823939800000000000000000000000083e672c9949af428687ecc9b6a3ba74db7bb0ed0000000000000000000000000b16ffd445d3c785476f87017b79423ec9057406b00000000000000000000000000000000000000000000000000000000000075e8" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "gas": "0x951b1", + "value": "0x0", + "input": "0x608060405260405161062f38038061062f833981810160405281019061002591906104cd565b610035828261003c60201b60201c565b505061054f565b61004b826100c060201b60201c565b8173ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a25f815111156100ad576100a7828261018f60201b60201c565b506100bc565b6100bb61029e60201b60201c565b5b5050565b5f8173ffffffffffffffffffffffffffffffffffffffff163b0361011b57806040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526004016101129190610536565b60405180910390fd5b8061014d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b6102da60201b60201c565b5f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60605f6101a284846102e360201b60201c565b90508080156101de57505f6101bb6102f760201b60201c565b11806101dd57505f8473ffffffffffffffffffffffffffffffffffffffff163b115b5b156101f9576101f16102fe60201b60201c565b915050610298565b801561023c57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016102339190610536565b60405180910390fd5b5f61024b6102f760201b60201c565b11156102645761025f61031b60201b60201c565b610296565b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b92915050565b5f3411156102d8576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f819050919050565b5f805f835160208501865af4905092915050565b5f3d905090565b606060405190503d81523d5f602083013e3d602001810160405290565b6040513d5f823e3d81fd5b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61036082610337565b9050919050565b61037081610356565b811461037a575f80fd5b50565b5f8151905061038b81610367565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6103df82610399565b810181811067ffffffffffffffff821117156103fe576103fd6103a9565b5b80604052505050565b5f610410610326565b905061041c82826103d6565b919050565b5f67ffffffffffffffff82111561043b5761043a6103a9565b5b61044482610399565b9050602081019050919050565b8281835e5f83830152505050565b5f61047161046c84610421565b610407565b90508281526020810184848401111561048d5761048c610395565b5b610498848285610451565b509392505050565b5f82601f8301126104b4576104b3610391565b5b81516104c484826020860161045f565b91505092915050565b5f80604083850312156104e3576104e261032f565b5b5f6104f08582860161037d565b925050602083015167ffffffffffffffff81111561051157610510610333565b5b61051d858286016104a0565b9150509250929050565b61053081610356565b82525050565b5f6020820190506105495f830184610527565b92915050565b60d48061055b5f395ff3fe6080604052600a600c565b005b60186014601a565b6026565b565b5f60216044565b905090565b365f80375f80365f845af43d5f803e805f81146040573d5ff35b3d5ffd5b5f606e7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b6095565b5f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f81905091905056fea264697066735822122016ca1c4645f0320c6d4356c7b97646a5108f4256e2a4bf94fc99ce8fb00ac2f064736f6c634300081a003300000000000000000000000073ace74ca2c841ca1b78a9093428a5e1e35ecb2100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000124ed85863b00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35000000000000000000000000a4afef880f5ce1f63c9fb48f661e27f8b4216401000000000000000000000000881cad4f885c6701d8481c0ed347f6d35444ea7e000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000549943e04f40284185054145c6e4e9568c1d3241000000000000000000000000af54be5b6eec24d6bfacf1cce4eaf680a823939800000000000000000000000083e672c9949af428687ecc9b6a3ba74db7bb0ed0000000000000000000000000b16ffd445d3c785476f87017b79423ec9057406b00000000000000000000000000000000000000000000000000000000000075e800000000000000000000000000000000000000000000000000000000", + "nonce": "0x17", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x220c8c3612cf7e867b621449b0daccf3821e7a820e4a4119e3c6bc1376e82ad7", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "function": "grantRole(bytes32,address)", + "arguments": [ + "0x966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce63", + "0x16170D3Bf5cdbAAaf87fa3ED8d945680965080b3" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "gas": "0x1432c", + "value": "0x0", + "input": "0x2f2ff15d966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce6300000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "nonce": "0x18", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xdcf9", + "logs": [ + { + "address": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x14dfd053822731770e1bb7dd6729fc1694bee325c911630f09a2dc3351850c9f", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35" + ], + "data": "0x", + "blockHash": "0xc01952001804aa751ffa85a4a99066eb6473510d65ec4e99add9f72cf23aee5a", + "blockNumber": "0xaf1ef1", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x7e6df163d5b47ec64fa18d93c4afe20dedf0b14b3a369dbc3a76ae108dc8f861", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + } + ], + "logsBloom": "0x00000004000000000200000000000000400000000000000000000000000000000000000000200000000000000000000000080000000000000000000000000000000000000000000000000000000800400000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000001000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000400000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x7e6df163d5b47ec64fa18d93c4afe20dedf0b14b3a369dbc3a76ae108dc8f861", + "transactionIndex": "0x0", + "blockHash": "0xc01952001804aa751ffa85a4a99066eb6473510d65ec4e99add9f72cf23aee5a", + "blockNumber": "0xaf1ef1", + "gasUsed": "0xdcf9", + "effectiveGasPrice": "0xcbd", + "blobGasPrice": "0x1", + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x3ec235", + "logs": [ + { + "address": "0x73ace74ca2c841ca1b78a9093428a5e1e35ecb21", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0xc3afd6cf70e8fd3f9b0786e42fc12d9b572f24118e7bd987026d3eca01d75d35", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000800000000000000000000800000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xc3afd6cf70e8fd3f9b0786e42fc12d9b572f24118e7bd987026d3eca01d75d35", + "transactionIndex": "0x0", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "gasUsed": "0x3ec235", + "effectiveGasPrice": "0xcb2", + "blobGasPrice": "0x1", + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": null, + "contractAddress": "0x73ace74ca2c841ca1b78a9093428a5e1e35ecb21" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x45ed5b", + "logs": [ + { + "address": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000073ace74ca2c841ca1b78a9093428a5e1e35ecb21" + ], + "data": "0x", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x1", + "removed": false + }, + { + "address": "0x549943e04f40284185054145c6e4e9568c1d3241", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x00000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "0x000000000000000000000000a4afef880f5ce1f63c9fb48f661e27f8b4216401" + ], + "data": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x2", + "removed": false + }, + { + "address": "0x549943e04f40284185054145c6e4e9568c1d3241", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x00000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "0x000000000000000000000000af54be5b6eec24d6bfacf1cce4eaf680a8239398" + ], + "data": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x3", + "removed": false + }, + { + "address": "0xfcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x00000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "0x000000000000000000000000881cad4f885c6701d8481c0ed347f6d35444ea7e" + ], + "data": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x4", + "removed": false + }, + { + "address": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35" + ], + "data": "0x", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x5", + "removed": false + }, + { + "address": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x6", + "removed": false + } + ], + "logsBloom": "0x00000004000000000200000000000000410000000000000000000002000000000000000000000000000800000000000000080400000000000000000100200000000000000000000000000000000886000000000000000000000000000000000001000000020000000000000000000800000000000000000000000000000000000000000020000000000800000000008000000000000080000000000000400000020008100000000000002000000000000000010000002000041000120000002000000020000000000000000000200000000000008004000100000000000020000010000000000000200000000000000000000000000000000000000000020000", + "type": "0x2", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "gasUsed": "0x72b26", + "effectiveGasPrice": "0xcb2", + "blobGasPrice": "0x1", + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": null, + "contractAddress": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x46ca54", + "logs": [ + { + "address": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce63", + "0x00000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35" + ], + "data": "0x", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x220c8c3612cf7e867b621449b0daccf3821e7a820e4a4119e3c6bc1376e82ad7", + "transactionIndex": "0x2", + "logIndex": "0x7", + "removed": false + } + ], + "logsBloom": "0x00000004000000000200000000000000400000000000000000000000000000000000000000200000000000000000000000080080000000000000000000000000000000000000000000000000000804000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000000000000000000000000000010000000000001000000000000000000000000000000000000000000000000000000000000100004000000000000000000000000000000000000000000400000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x220c8c3612cf7e867b621449b0daccf3821e7a820e4a4119e3c6bc1376e82ad7", + "transactionIndex": "0x2", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "gasUsed": "0xdcf9", + "effectiveGasPrice": "0xcb2", + "blobGasPrice": "0x1", + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1770331165315, + "chain": 80094, + "commit": "b688ac6" +} \ No newline at end of file diff --git a/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-latest.json b/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-latest.json new file mode 100644 index 0000000..cb26abb --- /dev/null +++ b/broadcast/DeploySFLUVUSDCeZapper.s.sol/80094/run-latest.json @@ -0,0 +1,309 @@ +{ + "transactions": [ + { + "hash": "0x7e6df163d5b47ec64fa18d93c4afe20dedf0b14b3a369dbc3a76ae108dc8f861", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "function": "grantRole(bytes32,address)", + "arguments": [ + "0x14dfd053822731770e1bb7dd6729fc1694bee325c911630f09a2dc3351850c9f", + "0x90496e23825aD0C8107d04671e6a27f30630Fc35" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "gas": "0x1432c", + "value": "0x0", + "input": "0x2f2ff15d14dfd053822731770e1bb7dd6729fc1694bee325c911630f09a2dc3351850c9f00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35", + "nonce": "0x15", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xc3afd6cf70e8fd3f9b0786e42fc12d9b572f24118e7bd987026d3eca01d75d35", + "transactionType": "CREATE", + "contractName": "SFLUVUSDCeZapper", + "contractAddress": "0x73ace74ca2c841ca1b78a9093428a5e1e35ecb21", + "function": null, + "arguments": null, + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "gas": "0x519611", + "value": "0x0", + "input": "0x60a06040523073ffffffffffffffffffffffffffffffffffffffff1660809073ffffffffffffffffffffffffffffffffffffffff16815250348015610042575f80fd5b5061005161005660201b60201c565b6101d1565b5f61006561015460201b60201c565b9050805f0160089054906101000a900460ff16156100af576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8016815f015f9054906101000a900467ffffffffffffffff1667ffffffffffffffff16146101515767ffffffffffffffff815f015f6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d267ffffffffffffffff60405161014891906101b8565b60405180910390a15b50565b5f8061016461016d60201b60201c565b90508091505090565b5f7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005f1b905090565b5f67ffffffffffffffff82169050919050565b6101b281610196565b82525050565b5f6020820190506101cb5f8301846101a9565b92915050565b6080516149046101f75f395f8181611452015281816114a7015261166601526149045ff3fe608060405260043610610117575f3560e01c80636cab20fd1161009f578063ad3cb1cc11610063578063ad3cb1cc1461039d578063ae103854146103c7578063d547741f146103ef578063ed85863b14610417578063fa461e331461043f5761011e565b80636cab20fd146102a85780637fa46ab4146102d057806391d14854146102fa578063a217fddf14610336578063ab920833146103605761011e565b80632f2ff15d116100e65780632f2ff15d146101ea57806336568abe146102125780634b9687891461023a5780634f1ef2861461026257806352d1902d1461027e5761011e565b806301ffc9a714610122578063081338bb1461015e57806319a806fe14610186578063248a9ca3146101ae5761011e565b3661011e57005b5f80fd5b34801561012d575f80fd5b506101486004803603810190610143919061326a565b610467565b60405161015591906132af565b60405180910390f35b348015610169575f80fd5b50610184600480360381019061017f9190613322565b6104e0565b005b348015610191575f80fd5b506101ac60048036038101906101a79190613322565b610620565b005b3480156101b9575f80fd5b506101d460048036038101906101cf9190613380565b61067e565b6040516101e191906133ba565b60405180910390f35b3480156101f5575f80fd5b50610210600480360381019061020b91906133d3565b6106a8565b005b34801561021d575f80fd5b50610238600480360381019061023391906133d3565b6106ca565b005b348015610245575f80fd5b50610260600480360381019061025b9190613322565b610745565b005b61027c6004803603810190610277919061354d565b6107a3565b005b348015610289575f80fd5b506102926107c2565b60405161029f91906133ba565b60405180910390f35b3480156102b3575f80fd5b506102ce60048036038101906102c991906135e2565b6107f3565b005b3480156102db575f80fd5b506102e4610a9e565b6040516102f191906133ba565b60405180910390f35b348015610305575f80fd5b50610320600480360381019061031b91906133d3565b610ac2565b60405161032d91906132af565b60405180910390f35b348015610341575f80fd5b5061034a610b33565b60405161035791906133ba565b60405180910390f35b34801561036b575f80fd5b5061038660048036038101906103819190613640565b610b39565b604051610394929190613758565b60405180910390f35b3480156103a8575f80fd5b506103b1610c8a565b6040516103be91906137df565b60405180910390f35b3480156103d2575f80fd5b506103ed60048036038101906103e89190613838565b610cc3565b005b3480156103fa575f80fd5b50610415600480360381019061041091906133d3565b610d02565b005b348015610422575f80fd5b5061043d60048036038101906104389190613886565b610d24565b005b34801561044a575f80fd5b5061046560048036038101906104609190613955565b610ec3565b005b5f7f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806104d957506104d88261118e565b5b9050919050565b5f801b6104ec816111f7565b5f6104f561120b565b905082816004015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b81526004016105da9291906139e4565b6020604051808303815f875af11580156105f6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061061a9190613a35565b50505050565b5f801b61062c816111f7565b5f61063561120b565b905082816006015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050565b5f80610688611232565b9050805f015f8481526020019081526020015f2060010154915050919050565b6106b18261067e565b6106ba816111f7565b6106c48383611259565b50505050565b6106d2611351565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610736576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107408282611358565b505050565b5f801b610751816111f7565b5f61075a61120b565b905082816005015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050565b6107ab611450565b6107b482611536565b6107be8282611546565b5050565b5f6107cb611664565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b905090565b5f801b6107ff816111f7565b5f61080861120b565b90505f8373ffffffffffffffffffffffffffffffffffffffff164760405161082f90613a8d565b5f6040518083038185875af1925050503d805f8114610869576040519150601f19603f3d011682016040523d82523d5f602084013e61086e565b606091505b50509050806108b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a990613aeb565b60405180910390fd5b5f826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161090f9190613b09565b602060405180830381865afa15801561092a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061094e9190613b36565b90505f811115610a9757826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b386836040518363ffffffff1660e01b81526004016109b6929190613bbc565b6020604051808303815f875af11580156109d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109f69190613a35565b50826003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb86836040518363ffffffff1660e01b8152600401610a55929190613bbc565b6020604051808303815f875af1158015610a71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a959190613a35565b505b5050505050565b7f966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce6381565b5f80610acc611232565b9050805f015f8581526020019081526020015f205f015f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1691505092915050565b5f801b81565b610b416131a2565b610b496131d4565b7f966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce635f610b7361120b565b9050806001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166391d1485483610bbd611351565b6040518363ffffffff1660e01b8152600401610bda929190613be3565b602060405180830381865afa158015610bf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c199190613a35565b610c6357610c25611351565b826040517fe2517d3f000000000000000000000000000000000000000000000000000000008152600401610c5a929190613c0a565b60405180910390fd5b610c6b6116eb565b610c75868661173f565b93509350610c816123fa565b50509250929050565b6040518060400160405280600581526020017f352e302e3000000000000000000000000000000000000000000000000000000081525081565b5f801b610ccf816111f7565b5f610cd861120b565b9050828160060160146101000a81548163ffffffff021916908363ffffffff160217905550505050565b610d0b8261067e565b610d14816111f7565b610d1e8383611358565b50505050565b5f610d2d612411565b90505f815f0160089054906101000a900460ff161590505f825f015f9054906101000a900467ffffffffffffffff1690505f808267ffffffffffffffff16148015610d755750825b90505f60018367ffffffffffffffff16148015610da857505f3073ffffffffffffffffffffffffffffffffffffffff163b145b905081158015610db6575080155b15610ded576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001855f015f6101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055508315610e3a576001855f0160086101000a81548160ff0219169083151502179055505b610e42612424565b610e4a61242e565b610e5386612438565b610e5f5f801b88611259565b508315610eba575f855f0160086101000a81548160ff0219169083151502179055507fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d26001604051610eb19190613c6a565b60405180910390a15b50505050505050565b5f610ecc61120b565b9050806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f5f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f5690613ccd565b60405180910390fd5b5f85131561107357806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fd3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ff79190613cff565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33876040518363ffffffff1660e01b81526004016110319291906139e4565b6020604051808303815f875af115801561104d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110719190613a35565b505b5f84131561118757806006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110e7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061110b9190613cff565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33866040518363ffffffff1660e01b81526004016111459291906139e4565b6020604051808303815f875af1158015611161573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111859190613a35565b505b5050505050565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b61120881611203611351565b612d6f565b50565b5f7f4b0052a517f33f1f1b7e6b3a321c7f53e23bc343d8df724c8dc01d1fd1b97f00905090565b5f7f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800905090565b5f80611263611232565b905061126f8484610ac2565b611346576001815f015f8681526020019081526020015f205f015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506112e2611351565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4600191505061134b565b5f9150505b92915050565b5f33905090565b5f80611362611232565b905061136e8484610ac2565b15611445575f815f015f8681526020019081526020015f205f015f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506113e1611351565b73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16857ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a4600191505061144a565b5f9150505b92915050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614806114fd57507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166114e4612dc0565b73ffffffffffffffffffffffffffffffffffffffff1614155b15611534576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f801b611542816111f7565b5050565b8173ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156115ae57506040513d601f19601f820116820180604052508101906115ab9190613d3e565b60015b6115ef57816040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526004016115e69190613b09565b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b811461165557806040517faa1d49a400000000000000000000000000000000000000000000000000000000815260040161164c91906133ba565b60405180910390fd5b61165f8383612e13565b505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff16146116e9576040517fe07c8dba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f6116f4612e85565b90506002815f015403611733576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002815f018190555050565b6117476131a2565b61174f6131d4565b5f61189f8561175c61120b565b6003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117c7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117eb9190613d9f565b600a6117f79190613f26565b6117ff61120b565b6001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561186a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061188e9190613d9f565b600a61189a9190613f26565b612eac565b90505f6118aa61120b565b90505f816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd6118f4611351565b308a6040518463ffffffff1660e01b815260040161191493929190613f70565b6020604051808303815f875af1158015611930573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119549190613a35565b9050806119c857816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1630611988611351565b896040517fcd3f16590000000000000000000000000000000000000000000000000000000081526004016119bf9493929190613fa5565b60405180910390fd5b816001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663205c287830896040518363ffffffff1660e01b8152600401611a269291906139e4565b6020604051808303815f875af1158015611a42573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a669190613a35565b905080611a9f576040517fbb1205c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f826002015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611afc9190613b09565b602060405180830381865afa158015611b17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b3b9190613b36565b90505f836003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611b9a9190613b09565b602060405180830381865afa158015611bb5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bd99190613b36565b90505f845f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f8173ffffffffffffffffffffffffffffffffffffffff166364f76eaa876003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401611c5e9190613b09565b602060405180830381865afa158015611c79573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c9d9190613b36565b90505f816002611cad9190613fe8565b64e8d4a51000886003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a082318a6005015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518263ffffffff1660e01b8152600401611d329190613b09565b602060405180830381865afa158015611d4d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d719190613b36565b611d7b9190613fe8565b611d859190614029565b90505f811115611e79575f818610611d9d5781611d9f565b855b9050875f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ed596315896003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683305f6040518563ffffffff1660e01b8152600401611e25949392919061405c565b5f604051808303815f875af1158015611e40573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190611e68919061415f565b508086611e759190614029565b9550505b5f851115612002575f876006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015611eee573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f129190614251565b50505050505090505f633d272ff090505f633b9aca0090505f81838573ffffffffffffffffffffffffffffffffffffffff16611f4e9190613fe8565b611f58919061431b565b90508a6006015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663128acb08305f8c856040518563ffffffff1660e01b8152600401611fbc9493929190614399565b60408051808303815f875af1158015611fd7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ffb9190614403565b5050505050505b5f876003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161205f9190613b09565b602060405180830381865afa15801561207a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061209e9190613b36565b90505f85826120ad9190614029565b90506064605f8b6120be9190613fe8565b6120c8919061431b565b81101561210a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612101906144b1565b60405180910390fd5b5f6040518060e001604052808b60060160149054906101000a900463ffffffff1663ffffffff1681526020018f73ffffffffffffffffffffffffffffffffffffffff165f1b81526020018c81526020016064605f8e6121699190613fe8565b612173919061431b565b815260200160405180602001604052805f815250815260200160405180602001604052805f815250815260200160405180602001604052805f81525081525090505f8a6004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633b6f743b835f6040518363ffffffff1660e01b81526004016122139291906145d7565b6040805180830381865afa15801561222d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122519190614656565b905081606001518b6003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016122b49190613b09565b602060405180830381865afa1580156122cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f39190613b36565b1015612334576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161232b906146cb565b60405180910390fd5b5f808c6004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c7c7f5b3845f01518686306040518563ffffffff1660e01b815260040161239a93929190614716565b60c06040518083038185885af11580156123b6573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906123db919061482a565b9150915081819f509f5050505050505050505050505050509250929050565b5f612403612e85565b90506001815f018190555050565b5f8061241b612ed9565b90508091505090565b61242c612f02565b565b612436612f02565b565b612440612f02565b5f61244961120b565b90505f73ffffffffffffffffffffffffffffffffffffffff16825f0160208101906124749190613322565b73ffffffffffffffffffffffffffffffffffffffff16036124c1576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f0160208101906124d39190613322565b815f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff1682602001602081019061253d9190613322565b73ffffffffffffffffffffffffffffffffffffffff160361258a576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81602001602081019061259d9190613322565b816001015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260400160208101906126089190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612655576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160400160208101906126689190613322565b816002015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260600160208101906126d39190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612720576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160600160208101906127339190613322565b816003015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff1682608001602081019061279e9190613322565b73ffffffffffffffffffffffffffffffffffffffff16036127eb576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160800160208101906127fe9190613322565b816004015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260a00160208101906128699190613322565b73ffffffffffffffffffffffffffffffffffffffff16036128b6576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160a00160208101906128c99190613322565b816005015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f73ffffffffffffffffffffffffffffffffffffffff168260c00160208101906129349190613322565b73ffffffffffffffffffffffffffffffffffffffff1603612981576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160c00160208101906129949190613322565b816006015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f8260e00160208101906129e99190613838565b63ffffffff1603612a26576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160e0016020810190612a399190613838565b8160060160146101000a81548163ffffffff021916908363ffffffff160217905550806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3825f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612afb9291906139e4565b6020604051808303815f875af1158015612b17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612b3b9190613a35565b50806003015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826004015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612bdd9291906139e4565b6020604051808303815f875af1158015612bf9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c1d9190613a35565b50805f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166336b2c4b26040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c89573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cad91906148a3565b73ffffffffffffffffffffffffffffffffffffffff1663095ea7b3826001015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518363ffffffff1660e01b8152600401612d2a9291906139e4565b6020604051808303815f875af1158015612d46573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d6a9190613a35565b505050565b612d798282610ac2565b612dbc5780826040517fe2517d3f000000000000000000000000000000000000000000000000000000008152600401612db3929190613c0a565b60405180910390fd5b5050565b5f612dec7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b612f42565b5f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b612e1c82612f4b565b8173ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a25f81511115612e7857612e728282613014565b50612e81565b612e80613105565b5b5050565b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00905090565b5f828402905081838583041485151702612ecd5763ad251c275f526004601cfd5b81810490509392505050565b5f7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005f1b905090565b612f0a613141565b612f40576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f819050919050565b5f8173ffffffffffffffffffffffffffffffffffffffff163b03612fa657806040517f4c9c8ce3000000000000000000000000000000000000000000000000000000008152600401612f9d9190613b09565b60405180910390fd5b80612fd27f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b612f42565b5f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60605f613021848461315f565b905080801561305757505f613034613173565b118061305657505f8473ffffffffffffffffffffffffffffffffffffffff163b115b5b1561306c5761306461317a565b9150506130ff565b80156130af57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016130a69190613b09565b60405180910390fd5b5f6130b8613173565b11156130cb576130c6613197565b6130fd565b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b92915050565b5f34111561313f576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f61314a612411565b5f0160089054906101000a900460ff16905090565b5f805f835160208501865af4905092915050565b5f3d905090565b606060405190503d81523d5f602083013e3d602001810160405290565b6040513d5f823e3d81fd5b60405180606001604052805f80191681526020015f67ffffffffffffffff1681526020016131ce6131ec565b81525090565b60405180604001604052805f81526020015f81525090565b60405180604001604052805f81526020015f81525090565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61324981613215565b8114613253575f80fd5b50565b5f8135905061326481613240565b92915050565b5f6020828403121561327f5761327e61320d565b5b5f61328c84828501613256565b91505092915050565b5f8115159050919050565b6132a981613295565b82525050565b5f6020820190506132c25f8301846132a0565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6132f1826132c8565b9050919050565b613301816132e7565b811461330b575f80fd5b50565b5f8135905061331c816132f8565b92915050565b5f602082840312156133375761333661320d565b5b5f6133448482850161330e565b91505092915050565b5f819050919050565b61335f8161334d565b8114613369575f80fd5b50565b5f8135905061337a81613356565b92915050565b5f602082840312156133955761339461320d565b5b5f6133a28482850161336c565b91505092915050565b6133b48161334d565b82525050565b5f6020820190506133cd5f8301846133ab565b92915050565b5f80604083850312156133e9576133e861320d565b5b5f6133f68582860161336c565b92505060206134078582860161330e565b9150509250929050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61345f82613419565b810181811067ffffffffffffffff8211171561347e5761347d613429565b5b80604052505050565b5f613490613204565b905061349c8282613456565b919050565b5f67ffffffffffffffff8211156134bb576134ba613429565b5b6134c482613419565b9050602081019050919050565b828183375f83830152505050565b5f6134f16134ec846134a1565b613487565b90508281526020810184848401111561350d5761350c613415565b5b6135188482856134d1565b509392505050565b5f82601f83011261353457613533613411565b5b81356135448482602086016134df565b91505092915050565b5f80604083850312156135635761356261320d565b5b5f6135708582860161330e565b925050602083013567ffffffffffffffff81111561359157613590613211565b5b61359d85828601613520565b9150509250929050565b5f6135b1826132c8565b9050919050565b6135c1816135a7565b81146135cb575f80fd5b50565b5f813590506135dc816135b8565b92915050565b5f602082840312156135f7576135f661320d565b5b5f613604848285016135ce565b91505092915050565b5f819050919050565b61361f8161360d565b8114613629575f80fd5b50565b5f8135905061363a81613616565b92915050565b5f80604083850312156136565761365561320d565b5b5f6136638582860161362c565b92505060206136748582860161330e565b9150509250929050565b6136878161334d565b82525050565b5f67ffffffffffffffff82169050919050565b6136a98161368d565b82525050565b6136b88161360d565b82525050565b604082015f8201516136d25f8501826136af565b5060208201516136e560208501826136af565b50505050565b608082015f8201516136ff5f85018261367e565b50602082015161371260208501826136a0565b50604082015161372560408501826136be565b50505050565b604082015f82015161373f5f8501826136af565b50602082015161375260208501826136af565b50505050565b5f60c08201905061376b5f8301856136eb565b613778608083018461372b565b9392505050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6137b18261377f565b6137bb8185613789565b93506137cb818560208601613799565b6137d481613419565b840191505092915050565b5f6020820190508181035f8301526137f781846137a7565b905092915050565b5f63ffffffff82169050919050565b613817816137ff565b8114613821575f80fd5b50565b5f813590506138328161380e565b92915050565b5f6020828403121561384d5761384c61320d565b5b5f61385a84828501613824565b91505092915050565b5f80fd5b5f610100828403121561387d5761387c613863565b5b81905092915050565b5f80610120838503121561389d5761389c61320d565b5b5f6138aa8582860161330e565b92505060206138bb85828601613867565b9150509250929050565b5f819050919050565b6138d7816138c5565b81146138e1575f80fd5b50565b5f813590506138f2816138ce565b92915050565b5f80fd5b5f80fd5b5f8083601f84011261391557613914613411565b5b8235905067ffffffffffffffff811115613932576139316138f8565b5b60208301915083600182028301111561394e5761394d6138fc565b5b9250929050565b5f805f806060858703121561396d5761396c61320d565b5b5f61397a878288016138e4565b945050602061398b878288016138e4565b935050604085013567ffffffffffffffff8111156139ac576139ab613211565b5b6139b887828801613900565b925092505092959194509250565b6139cf816132e7565b82525050565b6139de8161360d565b82525050565b5f6040820190506139f75f8301856139c6565b613a0460208301846139d5565b9392505050565b613a1481613295565b8114613a1e575f80fd5b50565b5f81519050613a2f81613a0b565b92915050565b5f60208284031215613a4a57613a4961320d565b5b5f613a5784828501613a21565b91505092915050565b5f81905092915050565b50565b5f613a785f83613a60565b9150613a8382613a6a565b5f82019050919050565b5f613a9782613a6d565b9150819050919050565b7f6e6174697665207472616e73666572206661696c6564000000000000000000005f82015250565b5f613ad5601683613789565b9150613ae082613aa1565b602082019050919050565b5f6020820190508181035f830152613b0281613ac9565b9050919050565b5f602082019050613b1c5f8301846139c6565b92915050565b5f81519050613b3081613616565b92915050565b5f60208284031215613b4b57613b4a61320d565b5b5f613b5884828501613b22565b91505092915050565b5f819050919050565b5f613b84613b7f613b7a846132c8565b613b61565b6132c8565b9050919050565b5f613b9582613b6a565b9050919050565b5f613ba682613b8b565b9050919050565b613bb681613b9c565b82525050565b5f604082019050613bcf5f830185613bad565b613bdc60208301846139d5565b9392505050565b5f604082019050613bf65f8301856133ab565b613c0360208301846139c6565b9392505050565b5f604082019050613c1d5f8301856139c6565b613c2a60208301846133ab565b9392505050565b5f819050919050565b5f613c54613c4f613c4a84613c31565b613b61565b61368d565b9050919050565b613c6481613c3a565b82525050565b5f602082019050613c7d5f830184613c5b565b92915050565b7f756e617574686f72697a656420706f6f6c0000000000000000000000000000005f82015250565b5f613cb7601183613789565b9150613cc282613c83565b602082019050919050565b5f6020820190508181035f830152613ce481613cab565b9050919050565b5f81519050613cf9816132f8565b92915050565b5f60208284031215613d1457613d1361320d565b5b5f613d2184828501613ceb565b91505092915050565b5f81519050613d3881613356565b92915050565b5f60208284031215613d5357613d5261320d565b5b5f613d6084828501613d2a565b91505092915050565b5f60ff82169050919050565b613d7e81613d69565b8114613d88575f80fd5b50565b5f81519050613d9981613d75565b92915050565b5f60208284031215613db457613db361320d565b5b5f613dc184828501613d8b565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f8160011c9050919050565b5f808291508390505b6001851115613e4c57808604811115613e2857613e27613dca565b5b6001851615613e375780820291505b8081029050613e4585613df7565b9450613e0c565b94509492505050565b5f82613e645760019050613f1f565b81613e71575f9050613f1f565b8160018114613e875760028114613e9157613ec0565b6001915050613f1f565b60ff841115613ea357613ea2613dca565b5b8360020a915084821115613eba57613eb9613dca565b5b50613f1f565b5060208310610133831016604e8410600b8410161715613ef55782820a905083811115613ef057613eef613dca565b5b613f1f565b613f028484846001613e03565b92509050818404811115613f1957613f18613dca565b5b81810290505b9392505050565b5f613f308261360d565b9150613f3b83613d69565b9250613f687fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484613e55565b905092915050565b5f606082019050613f835f8301866139c6565b613f9060208301856139c6565b613f9d60408301846139d5565b949350505050565b5f608082019050613fb85f8301876139c6565b613fc560208301866139c6565b613fd260408301856139c6565b613fdf60608301846139d5565b95945050505050565b5f613ff28261360d565b9150613ffd8361360d565b925082820261400b8161360d565b9150828204841483151761402257614021613dca565b5b5092915050565b5f6140338261360d565b915061403e8361360d565b925082820390508181111561405657614055613dca565b5b92915050565b5f60808201905061406f5f8301876139c6565b61407c60208301866139d5565b61408960408301856139c6565b61409660608301846132a0565b95945050505050565b5f67ffffffffffffffff8211156140b9576140b8613429565b5b602082029050602081019050919050565b5f6140dc6140d78461409f565b613487565b905080838252602082019050602084028301858111156140ff576140fe6138fc565b5b835b8181101561412857806141148882613b22565b845260208401935050602081019050614101565b5050509392505050565b5f82601f83011261414657614145613411565b5b81516141568482602086016140ca565b91505092915050565b5f602082840312156141745761417361320d565b5b5f82015167ffffffffffffffff81111561419157614190613211565b5b61419d84828501614132565b91505092915050565b6141af816132c8565b81146141b9575f80fd5b50565b5f815190506141ca816141a6565b92915050565b5f8160020b9050919050565b6141e5816141d0565b81146141ef575f80fd5b50565b5f81519050614200816141dc565b92915050565b5f61ffff82169050919050565b61421c81614206565b8114614226575f80fd5b50565b5f8151905061423781614213565b92915050565b5f8151905061424b8161380e565b92915050565b5f805f805f805f60e0888a03121561426c5761426b61320d565b5b5f6142798a828b016141bc565b975050602061428a8a828b016141f2565b965050604061429b8a828b01614229565b95505060606142ac8a828b01614229565b94505060806142bd8a828b01614229565b93505060a06142ce8a828b0161423d565b92505060c06142df8a828b01613a21565b91505092959891949750929550565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6143258261360d565b91506143308361360d565b9250826143405761433f6142ee565b5b828204905092915050565b614354816138c5565b82525050565b614363816132c8565b82525050565b5f82825260208201905092915050565b5f6143845f83614369565b915061438f82613a6a565b5f82019050919050565b5f60a0820190506143ac5f8301876139c6565b6143b960208301866132a0565b6143c6604083018561434b565b6143d3606083018461435a565b81810360808301526143e481614379565b905095945050505050565b5f815190506143fd816138ce565b92915050565b5f80604083850312156144195761441861320d565b5b5f614426858286016143ef565b9250506020614437858286016143ef565b9150509250929050565b7f696e73756666696369656e7420555344432e652066726f6d20737761702f72655f8201527f64656d7074696f6e000000000000000000000000000000000000000000000000602082015250565b5f61449b602883613789565b91506144a682614441565b604082019050919050565b5f6020820190508181035f8301526144c88161448f565b9050919050565b6144d8816137ff565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f614502826144de565b61450c81856144e8565b935061451c818560208601613799565b61452581613419565b840191505092915050565b5f60e083015f8301516145455f8601826144cf565b506020830151614558602086018261367e565b50604083015161456b60408601826136af565b50606083015161457e60608601826136af565b506080830151848203608086015261459682826144f8565b91505060a083015184820360a08601526145b082826144f8565b91505060c083015184820360c08601526145ca82826144f8565b9150508091505092915050565b5f6040820190508181035f8301526145ef8185614530565b90506145fe60208301846132a0565b9392505050565b5f80fd5b5f6040828403121561461e5761461d614605565b5b6146286040613487565b90505f61463784828501613b22565b5f83015250602061464a84828501613b22565b60208301525092915050565b5f6040828403121561466b5761466a61320d565b5b5f61467884828501614609565b91505092915050565b7f696e73756666696369656e7420555344432e65206166746572207377617000005f82015250565b5f6146b5601e83613789565b91506146c082614681565b602082019050919050565b5f6020820190508181035f8301526146e2816146a9565b9050919050565b604082015f8201516146fd5f8501826136af565b50602082015161471060208501826136af565b50505050565b5f6080820190508181035f83015261472e8186614530565b905061473d60208301856146e9565b61474a60608301846139c6565b949350505050565b61475b8161368d565b8114614765575f80fd5b50565b5f8151905061477681614752565b92915050565b5f6080828403121561479157614790614605565b5b61479b6060613487565b90505f6147aa84828501613d2a565b5f8301525060206147bd84828501614768565b60208301525060406147d184828501614609565b60408301525092915050565b5f604082840312156147f2576147f1614605565b5b6147fc6040613487565b90505f61480b84828501613b22565b5f83015250602061481e84828501613b22565b60208301525092915050565b5f8060c083850312156148405761483f61320d565b5b5f61484d8582860161477c565b925050608061485e858286016147dd565b9150509250929050565b5f614872826132e7565b9050919050565b61488281614868565b811461488c575f80fd5b50565b5f8151905061489d81614879565b92915050565b5f602082840312156148b8576148b761320d565b5b5f6148c58482850161488f565b9150509291505056fea26469706673582212207c40d7f04f816c3167b299ad67eeebeace17a565af378f26e04db286e14ad85464736f6c634300081a0033", + "nonce": "0x16", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionType": "CREATE", + "contractName": "ERC1967Proxy", + "contractAddress": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "function": null, + "arguments": [ + "0x73aCE74Ca2c841ca1B78a9093428A5E1E35ECB21", + "0xed85863b00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35000000000000000000000000a4afef880f5ce1f63c9fb48f661e27f8b4216401000000000000000000000000881cad4f885c6701d8481c0ed347f6d35444ea7e000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000549943e04f40284185054145c6e4e9568c1d3241000000000000000000000000af54be5b6eec24d6bfacf1cce4eaf680a823939800000000000000000000000083e672c9949af428687ecc9b6a3ba74db7bb0ed0000000000000000000000000b16ffd445d3c785476f87017b79423ec9057406b00000000000000000000000000000000000000000000000000000000000075e8" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "gas": "0x951b1", + "value": "0x0", + "input": "0x608060405260405161062f38038061062f833981810160405281019061002591906104cd565b610035828261003c60201b60201c565b505061054f565b61004b826100c060201b60201c565b8173ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a25f815111156100ad576100a7828261018f60201b60201c565b506100bc565b6100bb61029e60201b60201c565b5b5050565b5f8173ffffffffffffffffffffffffffffffffffffffff163b0361011b57806040517f4c9c8ce30000000000000000000000000000000000000000000000000000000081526004016101129190610536565b60405180910390fd5b8061014d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b6102da60201b60201c565b5f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60605f6101a284846102e360201b60201c565b90508080156101de57505f6101bb6102f760201b60201c565b11806101dd57505f8473ffffffffffffffffffffffffffffffffffffffff163b115b5b156101f9576101f16102fe60201b60201c565b915050610298565b801561023c57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016102339190610536565b60405180910390fd5b5f61024b6102f760201b60201c565b11156102645761025f61031b60201b60201c565b610296565b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b92915050565b5f3411156102d8576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f819050919050565b5f805f835160208501865af4905092915050565b5f3d905090565b606060405190503d81523d5f602083013e3d602001810160405290565b6040513d5f823e3d81fd5b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61036082610337565b9050919050565b61037081610356565b811461037a575f80fd5b50565b5f8151905061038b81610367565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6103df82610399565b810181811067ffffffffffffffff821117156103fe576103fd6103a9565b5b80604052505050565b5f610410610326565b905061041c82826103d6565b919050565b5f67ffffffffffffffff82111561043b5761043a6103a9565b5b61044482610399565b9050602081019050919050565b8281835e5f83830152505050565b5f61047161046c84610421565b610407565b90508281526020810184848401111561048d5761048c610395565b5b610498848285610451565b509392505050565b5f82601f8301126104b4576104b3610391565b5b81516104c484826020860161045f565b91505092915050565b5f80604083850312156104e3576104e261032f565b5b5f6104f08582860161037d565b925050602083015167ffffffffffffffff81111561051157610510610333565b5b61051d858286016104a0565b9150509250929050565b61053081610356565b82525050565b5f6020820190506105495f830184610527565b92915050565b60d48061055b5f395ff3fe6080604052600a600c565b005b60186014601a565b6026565b565b5f60216044565b905090565b365f80375f80365f845af43d5f803e805f81146040573d5ff35b3d5ffd5b5f606e7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b6095565b5f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f81905091905056fea264697066735822122016ca1c4645f0320c6d4356c7b97646a5108f4256e2a4bf94fc99ce8fb00ac2f064736f6c634300081a003300000000000000000000000073ace74ca2c841ca1b78a9093428a5e1e35ecb2100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000124ed85863b00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35000000000000000000000000a4afef880f5ce1f63c9fb48f661e27f8b4216401000000000000000000000000881cad4f885c6701d8481c0ed347f6d35444ea7e000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000549943e04f40284185054145c6e4e9568c1d3241000000000000000000000000af54be5b6eec24d6bfacf1cce4eaf680a823939800000000000000000000000083e672c9949af428687ecc9b6a3ba74db7bb0ed0000000000000000000000000b16ffd445d3c785476f87017b79423ec9057406b00000000000000000000000000000000000000000000000000000000000075e800000000000000000000000000000000000000000000000000000000", + "nonce": "0x17", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x220c8c3612cf7e867b621449b0daccf3821e7a820e4a4119e3c6bc1376e82ad7", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "function": "grantRole(bytes32,address)", + "arguments": [ + "0x966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce63", + "0x16170D3Bf5cdbAAaf87fa3ED8d945680965080b3" + ], + "transaction": { + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "gas": "0x1432c", + "value": "0x0", + "input": "0x2f2ff15d966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce6300000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "nonce": "0x18", + "chainId": "0x138de" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xdcf9", + "logs": [ + { + "address": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x14dfd053822731770e1bb7dd6729fc1694bee325c911630f09a2dc3351850c9f", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35" + ], + "data": "0x", + "blockHash": "0xc01952001804aa751ffa85a4a99066eb6473510d65ec4e99add9f72cf23aee5a", + "blockNumber": "0xaf1ef1", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x7e6df163d5b47ec64fa18d93c4afe20dedf0b14b3a369dbc3a76ae108dc8f861", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + } + ], + "logsBloom": "0x00000004000000000200000000000000400000000000000000000000000000000000000000200000000000000000000000080000000000000000000000000000000000000000000000000000000800400000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000001000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000400000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x7e6df163d5b47ec64fa18d93c4afe20dedf0b14b3a369dbc3a76ae108dc8f861", + "transactionIndex": "0x0", + "blockHash": "0xc01952001804aa751ffa85a4a99066eb6473510d65ec4e99add9f72cf23aee5a", + "blockNumber": "0xaf1ef1", + "gasUsed": "0xdcf9", + "effectiveGasPrice": "0xcbd", + "blobGasPrice": "0x1", + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x3ec235", + "logs": [ + { + "address": "0x73ace74ca2c841ca1b78a9093428a5e1e35ecb21", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0xc3afd6cf70e8fd3f9b0786e42fc12d9b572f24118e7bd987026d3eca01d75d35", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000800000000000000000000800000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xc3afd6cf70e8fd3f9b0786e42fc12d9b572f24118e7bd987026d3eca01d75d35", + "transactionIndex": "0x0", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "gasUsed": "0x3ec235", + "effectiveGasPrice": "0xcb2", + "blobGasPrice": "0x1", + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": null, + "contractAddress": "0x73ace74ca2c841ca1b78a9093428a5e1e35ecb21" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x45ed5b", + "logs": [ + { + "address": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x00000000000000000000000073ace74ca2c841ca1b78a9093428a5e1e35ecb21" + ], + "data": "0x", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x1", + "removed": false + }, + { + "address": "0x549943e04f40284185054145c6e4e9568c1d3241", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x00000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "0x000000000000000000000000a4afef880f5ce1f63c9fb48f661e27f8b4216401" + ], + "data": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x2", + "removed": false + }, + { + "address": "0x549943e04f40284185054145c6e4e9568c1d3241", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x00000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "0x000000000000000000000000af54be5b6eec24d6bfacf1cce4eaf680a8239398" + ], + "data": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x3", + "removed": false + }, + { + "address": "0xfcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x00000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "0x000000000000000000000000881cad4f885c6701d8481c0ed347f6d35444ea7e" + ], + "data": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x4", + "removed": false + }, + { + "address": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35" + ], + "data": "0x", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x5", + "removed": false + }, + { + "address": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "topics": [ + "0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "logIndex": "0x6", + "removed": false + } + ], + "logsBloom": "0x00000004000000000200000000000000410000000000000000000002000000000000000000000000000800000000000000080400000000000000000100200000000000000000000000000000000886000000000000000000000000000000000001000000020000000000000000000800000000000000000000000000000000000000000020000000000800000000008000000000000080000000000000400000020008100000000000002000000000000000010000002000041000120000002000000020000000000000000000200000000000008004000100000000000020000010000000000000200000000000000000000000000000000000000000020000", + "type": "0x2", + "transactionHash": "0x803c04002950808ade055bf7ae088a13ff96bbb9781210d4fedcebc59befb260", + "transactionIndex": "0x1", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "gasUsed": "0x72b26", + "effectiveGasPrice": "0xcb2", + "blobGasPrice": "0x1", + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": null, + "contractAddress": "0x16170d3bf5cdbaaaf87fa3ed8d945680965080b3" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x46ca54", + "logs": [ + { + "address": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x966f8194314eaee22f57867e94651373737ec999fd205661e0438047e341ce63", + "0x00000000000000000000000016170d3bf5cdbaaaf87fa3ed8d945680965080b3", + "0x00000000000000000000000090496e23825ad0c8107d04671e6a27f30630fc35" + ], + "data": "0x", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "blockTimestamp": "0x68e55d4e", + "transactionHash": "0x220c8c3612cf7e867b621449b0daccf3821e7a820e4a4119e3c6bc1376e82ad7", + "transactionIndex": "0x2", + "logIndex": "0x7", + "removed": false + } + ], + "logsBloom": "0x00000004000000000200000000000000400000000000000000000000000000000000000000200000000000000000000000080080000000000000000000000000000000000000000000000000000804000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000000000000000000000000000010000000000001000000000000000000000000000000000000000000000000000000000000100004000000000000000000000000000000000000000000400000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x220c8c3612cf7e867b621449b0daccf3821e7a820e4a4119e3c6bc1376e82ad7", + "transactionIndex": "0x2", + "blockHash": "0xa6815a97a85220a2bf83651fa496e51143219de198f88327f73578bbb91c5955", + "blockNumber": "0xaf1ef2", + "gasUsed": "0xdcf9", + "effectiveGasPrice": "0xcb2", + "blobGasPrice": "0x1", + "from": "0x90496e23825ad0c8107d04671e6a27f30630fc35", + "to": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1770331165315, + "chain": 80094, + "commit": "b688ac6" +} \ No newline at end of file diff --git a/lib/bera-contracts b/lib/bera-contracts index b7562f2..a52cafe 160000 --- a/lib/bera-contracts +++ b/lib/bera-contracts @@ -1 +1 @@ -Subproject commit b7562f2bcd4bef30c9833469ddb98b8fbc419cc9 +Subproject commit a52cafe986e579402d5fe809bc9ec3a0da97a274 diff --git a/script/DeploySFLUVUSDCeZapper.s.sol b/script/DeploySFLUVUSDCeZapper.s.sol new file mode 100644 index 0000000..e15a07c --- /dev/null +++ b/script/DeploySFLUVUSDCeZapper.s.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +import "forge-std/Script.sol"; +import {SFLUVUSDCeZapper, SFLUVUSDCeZapperStorageInit} from "../src/SFLUVUSDCeZapper.sol"; +import {SFLUVv2} from "../src/SFLUVv2.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {IDeployErrors} from "./IDeployErrors.sol"; + +contract DeploySFLUVUSDCeZapper is Script, IDeployErrors { + + function run() public { + address honeyFactory = vm.envAddress("HONEY_FACTORY_ADDRESS"); + address sfluv = vm.envAddress("SFLUV_ADDRESS"); + address honey = vm.envAddress("HONEY_ADDRESS"); + address usdc = vm.envAddress("USDC_E_ADDRESS"); + address usdcAdapter = vm.envAddress("USDC_E_ADAPTER_ADDRESS"); + address usdcVault = vm.envAddress("USDC_E_VAULT_ADDRESS"); + address honeyToUSDCPool = vm.envAddress("HONEY_USDC_POOL_ADDRESS"); + uint32 usdcDstEid = uint32(vm.envUint("USDC_DST_EID")); + + SFLUVUSDCeZapperStorageInit memory s = SFLUVUSDCeZapperStorageInit( + honeyFactory, + sfluv, + honey, + usdc, + usdcAdapter, + usdcVault, + honeyToUSDCPool, + usdcDstEid + ); + + vm.startBroadcast(); + + SFLUVv2 token = SFLUVv2(sfluv); + + bool isAdmin = token.hasRole(token.DEFAULT_ADMIN_ROLE(), msg.sender); + if (!isAdmin) revert NotAdmin(); + + bool isRedeemerAdmin = token.hasRole(token.REDEEMER_ADMIN_ROLE(), msg.sender); + if (!isRedeemerAdmin) { + token.grantRole(token.REDEEMER_ADMIN_ROLE(), msg.sender); + } + + SFLUVUSDCeZapper impl = new SFLUVUSDCeZapper(); + + ERC1967Proxy proxy = new ERC1967Proxy(address(impl), + abi.encodeCall(impl.initialize, (msg.sender, s)) + ); + + token.grantRole(token.REDEEMER_ROLE(), address(proxy)); + + vm.stopBroadcast(); + + console.log("UUPS Proxy Address:", address(proxy)); + } +} diff --git a/src/SFLUVUSDCeZapper.sol b/src/SFLUVUSDCeZapper.sol new file mode 100644 index 0000000..b439952 --- /dev/null +++ b/src/SFLUVUSDCeZapper.sol @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import "@berachain/contracts/honey/IHoneyFactory.sol"; +import "@berachain/contracts/honey/HoneyFactory.sol"; +import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; +import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol"; +import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol"; +import { ILiquidityPool } from "./ILiquidityPool.sol"; +import "./ISFLUVZapperErrors.sol"; +import "./SFLUVv2.sol"; + +interface IUSDCe is IERC20Metadata {} + +interface IOFTAdapter is IOFT {} + +struct SFLUVUSDCeZapperStorageInit { + address honeyFactory; + address sfluv; + address honey; + address usdc; + address usdcAdapter; + address usdcVault; + address honeyToUSDCPool; + uint32 usdcDstEid; +} + +contract SFLUVUSDCeZapper is + Initializable, + AccessControlUpgradeable, + ReentrancyGuardUpgradeable, + UUPSUpgradeable, + ISFLUVZapperErrors +{ + // these roles need to match corresponding roles in SFLUVv2 + bytes32 public constant REDEEMER_ROLE = keccak256("REDEEMER"); + + /* STORAGE */ + struct SFLUVUSDCeZapperStorage { + IHoneyFactory honeyFactory; + SFLUVv2 sfluv; + IERC20Metadata honey; + IUSDCe usdc; + IOFTAdapter usdcAdapter; + address usdcVault; + ILiquidityPool honeyToUSDCPool; + uint32 usdcDstEid; + } + + // keccak256(abi.encode(uint256(keccak256("SFLUV.storage.SFLUVUSDCeZapper")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant SFLUVUSDCeZapperStorageLocation = + 0x4b0052a517f33f1f1b7e6b3a321c7f53e23bc343d8df724c8dc01d1fd1b97f00; + + function _getSFLUVUSDCeZapperStorage() + private + pure + returns (SFLUVUSDCeZapperStorage storage $) + { + assembly { + $.slot := SFLUVUSDCeZapperStorageLocation + } + } + + modifier onlySFLUVRole(bytes32 role) { + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + + if (!$.sfluv.hasRole(role, _msgSender())) + revert AccessControlUnauthorizedAccount(_msgSender(), role); + _; + } + + constructor() { + _disableInitializers(); + } + + function initialize( + address _governance, + SFLUVUSDCeZapperStorageInit calldata _storage + ) + initializer + public + { + __AccessControl_init(); + __UUPSUpgradeable_init(); + __Storage_init(_storage); + + _grantRole(DEFAULT_ADMIN_ROLE, _governance); + } + + function __Storage_init( + SFLUVUSDCeZapperStorageInit calldata s + ) internal onlyInitializing { + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + + if (s.honeyFactory == address(0)) revert ZeroAddress(); + $.honeyFactory = IHoneyFactory(s.honeyFactory); + + if (s.sfluv == address(0)) revert ZeroAddress(); + $.sfluv = SFLUVv2(s.sfluv); + + if (s.honey == address(0)) revert ZeroAddress(); + $.honey = IERC20Metadata(s.honey); + + if (s.usdc == address(0)) revert ZeroAddress(); + $.usdc = IUSDCe(s.usdc); + + if (s.usdcAdapter == address(0)) revert ZeroAddress(); + $.usdcAdapter = IOFTAdapter(s.usdcAdapter); + + if (s.usdcVault == address(0)) revert ZeroAddress(); + $.usdcVault = s.usdcVault; + + if (s.honeyToUSDCPool == address(0)) revert ZeroAddress(); + $.honeyToUSDCPool = ILiquidityPool(s.honeyToUSDCPool); + + if (s.usdcDstEid == 0) revert ZeroAddress(); + $.usdcDstEid = s.usdcDstEid; + + $.usdc.approve(address($.honeyFactory), type(uint256).max); + $.usdc.approve(address($.usdcAdapter), type(uint256).max); + $.honeyFactory.honey().approve(address($.sfluv), type(uint256).max); + } + + function setHoneyToUSDCPool(address pool) public onlyRole(DEFAULT_ADMIN_ROLE) { + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + $.honeyToUSDCPool = ILiquidityPool(pool); + } + + function setUsdcDstEid(uint32 eid) public onlyRole(DEFAULT_ADMIN_ROLE) { + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + $.usdcDstEid = eid; + } + + function setUsdcAdapter(address adapter) public onlyRole(DEFAULT_ADMIN_ROLE) { + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + $.usdcAdapter = IOFTAdapter(adapter); + $.usdc.approve(address($.usdcAdapter), type(uint256).max); + } + + function setUsdcVault(address vault) public onlyRole(DEFAULT_ADMIN_ROLE) { + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + $.usdcVault = vault; + } + + function unwrapSwapAndBridgeUSDCe(uint256 amount, address to) + public + onlySFLUVRole(REDEEMER_ROLE) + nonReentrant + returns (MessagingReceipt memory, OFTReceipt memory) + { + // amount is 18 decimals, for SFLUV + return _unwrapSwapAndBridgeUSDCe(amount, to); + } + + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata + ) external { + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + + require(msg.sender == address($.honeyToUSDCPool), "unauthorized pool"); + + if (amount0Delta > 0) { + IERC20Metadata($.honeyToUSDCPool.token0()).transfer(msg.sender, uint256(amount0Delta)); + } + if (amount1Delta > 0) { + IERC20Metadata($.honeyToUSDCPool.token1()).transfer(msg.sender, uint256(amount1Delta)); + } + } + + /* INTERNAL */ + + function _unwrapSwapAndBridgeUSDCe(uint256 amount, address to) private + returns (MessagingReceipt memory, OFTReceipt memory) { + uint256 usdcAmount = FixedPointMathLib.mulDiv( + amount, + 10 ** _getSFLUVUSDCeZapperStorage().usdc.decimals(), + 10 ** _getSFLUVUSDCeZapperStorage().sfluv.decimals() + ); + + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + // 1. Pull SFLUV + bool success = $.sfluv.transferFrom(_msgSender(), address(this), amount); + if (!success) revert TransferFailed(address($.sfluv), address(this), _msgSender(), amount); + + // 2. Unwrap to HONEY + success = $.sfluv.withdrawTo(address(this), amount); + if (!success) revert RedeemFailed(); + uint256 honeyBalance = $.honey.balanceOf(address(this)); + + // 2.5 tracking USDC.e before redeem/swap process just in case amount is insufficient + uint256 usdcBefore = $.usdc.balanceOf(address(this)); + + // 3. Check USDC.e inside Honey, and keep track of how much is redeemed + HoneyFactory hf = HoneyFactory(address($.honeyFactory)); + uint256 usdcCurrentFees = hf.collectedAssetFees(address($.usdc)); + uint256 usdcAvailableInHoney = ($.usdc.balanceOf($.usdcVault) * 1e12) - (2 * usdcCurrentFees); + + if (usdcAvailableInHoney > 0) { + uint256 redeemAmount = honeyBalance < usdcAvailableInHoney + ? honeyBalance + : usdcAvailableInHoney; + + $.honeyFactory.redeem(address($.usdc), redeemAmount, address(this), false); + honeyBalance -= redeemAmount; + } + + // 4. Swap remaining HONEY via pool if needed + if (honeyBalance > 0) { + (uint160 sqrtPriceX96,,,,,,) = $.honeyToUSDCPool.slot0(); + + // 95% minimum output + // sqrt(1 / 0.95) ≈ 1.025978352 + uint256 NUM = 1_025_978_352; // scaled by 1e9 + uint256 DEN = 1_000_000_000; + + uint160 sqrtPriceLimitX96 = uint160( + (uint256(sqrtPriceX96) * NUM) / DEN + ); + + $.honeyToUSDCPool.swap( + address(this), + false, + int256(honeyBalance), + sqrtPriceLimitX96, + "" + ); + } + + uint256 usdcAfter = $.usdc.balanceOf(address(this)); + uint256 usdcAccumulated = usdcAfter - usdcBefore; + require(usdcAccumulated >= (usdcAmount * 95 / 100), "insufficient USDC.e from swap/redemption"); + + // Prepare LZ send params + SendParam memory lzParam = SendParam({ + dstEid: $.usdcDstEid, + to: bytes32(uint256(uint160(to))), + amountLD: usdcAmount, + minAmountLD: usdcAmount * 95 / 100, + extraOptions: "", + composeMsg: "", + oftCmd: "" + }); + + // 5. Bridge final USDC.e balance + MessagingFee memory fee = $.usdcAdapter.quoteSend(lzParam, false); + require( + $.usdc.balanceOf(address(this)) >= lzParam.minAmountLD, + "insufficient USDC.e after swap" + ); + + ( + MessagingReceipt memory mReceipt, + OFTReceipt memory oReceipt + ) = $.usdcAdapter.send{ value: fee.nativeFee }(lzParam, fee, address(this)); + + return (mReceipt, oReceipt); + } + + // allow receipt of BERA + receive() external payable {} + + function recoverTo(address payable to) public onlyRole(DEFAULT_ADMIN_ROLE) { + SFLUVUSDCeZapperStorage storage $ = _getSFLUVUSDCeZapperStorage(); + (bool ok, ) = to.call{ value: address(this).balance }(""); + require(ok, "native transfer failed"); + uint256 usdcBalance = $.usdc.balanceOf(address(this)); + if (usdcBalance > 0) { + $.usdc.approve(to, usdcBalance); // recover all usdc funds + $.usdc.transfer(to, usdcBalance); + } + } + + function _authorizeUpgrade( + address newImplementation + ) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {} +} diff --git a/test/SFLUVUSDCeZapper.sol b/test/SFLUVUSDCeZapper.sol new file mode 100644 index 0000000..bc6baec --- /dev/null +++ b/test/SFLUVUSDCeZapper.sol @@ -0,0 +1,279 @@ +pragma solidity ^0.8.26; + +import "../src/MockCoin.sol"; +import {SFLUVv2} from "../src/SFLUVv2.sol"; +import {SFLUVUSDCeZapper, SFLUVUSDCeZapperStorageInit} from "../src/SFLUVUSDCeZapper.sol"; +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; +import "@openzeppelin/contracts/utils/math/Math.sol"; +import "openzeppelin-foundry-upgrades/Upgrades.sol"; +import "@berachain/contracts/honey/IHoneyFactory.sol"; +import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; +import { MessagingReceipt, MessagingFee } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; +import { Honey } from "lib/bera-contracts/src/honey/Honey.sol"; +import { ILiquidityPool} from "../src/ILiquidityPool.sol"; +import { HoneyFactory } from "lib/bera-contracts/src/honey/HoneyFactory.sol"; + +interface IUSDCe { + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 amount) external returns (bool); + function approve(address spender, uint256 amount) external returns (bool); + function decimals() external view returns (uint8); +} + +interface ICollateralVaultView { + function asset() external view returns (address); + function totalAssets() external view returns (uint256); + function maxRedeem(address owner) external view returns (uint256); + function paused() external view returns (bool); +} + +contract SFLUVUSDCeZapperTest is Test { + SFLUVv2 public testLUVCoin; + SFLUVUSDCeZapper public testZapper; + IUSDCe public testUSDCe; + Honey public honeyToken; + ILiquidityPool public liquidityPool; + HoneyFactory public testHoneyFactory; + address public usdcVault; + + address internal peon; + address vitEth = address(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045); + address defaultAdmin = address(0x90496e23825aD0C8107d04671e6a27f30630Fc35); + + uint public oneEther = 1 ether; + + ERC1967Proxy zapperproxy; + + error AccessControlUnauthorizedAccount(address account, bytes32 role); + + function setUp() public { + string memory forkURL = vm.envString("FORK_URL"); + uint256 forkBlock = vm.envUint("FORK_BLOCK"); + vm.createSelectFork(forkURL, forkBlock); + + address sfluv = vm.envAddress("SFLUV_ADDRESS"); + address honey = vm.envAddress("HONEY_ADDRESS"); + address usdc = vm.envAddress("USDC_E_ADDRESS"); + address usdcAdapter = vm.envAddress("USDC_E_ADAPTER_ADDRESS"); + address usdcVaultAddress = vm.envAddress("USDC_E_VAULT_ADDRESS"); + address honeyToUSDCPool = vm.envAddress("HONEY_USDC_POOL_ADDRESS"); + address honeyFactoryAddress = vm.envAddress("HONEY_FACTORY_ADDRESS"); + uint32 usdcDstEid = uint32(vm.envUint("USDC_DST_EID")); + + peon = makeAddr("peon"); + + testLUVCoin = SFLUVv2(sfluv); + testUSDCe = IUSDCe(usdc); + liquidityPool = ILiquidityPool(honeyToUSDCPool); + testHoneyFactory = HoneyFactory(honeyFactoryAddress); + honeyToken = Honey(honey); + usdcVault = usdcVaultAddress; + + testZapper = new SFLUVUSDCeZapper(); + + SFLUVUSDCeZapperStorageInit memory testStorage = SFLUVUSDCeZapperStorageInit( + honeyFactoryAddress, + sfluv, + honey, + usdc, + usdcAdapter, + usdcVaultAddress, + honeyToUSDCPool, + usdcDstEid + ); + + zapperproxy = new ERC1967Proxy(address(testZapper), abi.encodeCall(testZapper.initialize, (defaultAdmin, testStorage))); + testZapper = SFLUVUSDCeZapper(payable(zapperproxy)); + + vm.startPrank(defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ADMIN_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), address(testZapper)); + vm.stopPrank(); + } + + function testUnpermissionedUnwrappingFunctionality() public { + uint sfluvExp = 10**testLUVCoin.decimals(); + + vm.startPrank(peon); + testLUVCoin.approve(address(testZapper), 100 * sfluvExp); + bytes32 myRole = testZapper.REDEEMER_ROLE(); + vm.expectRevert(abi.encodeWithSelector( + AccessControlUnauthorizedAccount.selector, + peon, + myRole + )); + testZapper.unwrapSwapAndBridgeUSDCe(50 * sfluvExp, peon); + vm.stopPrank(); + } + + function testUnwrapRedeemAndBridgeUSDCe() public { + uint256 usdcExp = 10**testUSDCe.decimals(); + uint256 sfluvExp = 10**testLUVCoin.decimals(); + + // Simulate zap-in via USDC.e to seed the Honey USDC.e vault and mint SFLUV + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testUSDCe.transfer(defaultAdmin, 100 * usdcExp); + + vm.startPrank(defaultAdmin); + testUSDCe.approve(address(testHoneyFactory), 50 * usdcExp); + uint256 honeyAmount = testHoneyFactory.mint(address(testUSDCe), 50 * usdcExp, defaultAdmin, false); + honeyToken.approve(address(testLUVCoin), honeyAmount); + testLUVCoin.depositFor(defaultAdmin, honeyAmount); + vm.stopPrank(); + + // Send some USDC.e to the zapper from whale + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testUSDCe.transfer(address(testZapper), 100 * usdcExp); + + // Fund zapper with BERA for messaging fees + vm.deal(address(testZapper), 100 * sfluvExp); + + vm.startPrank(defaultAdmin); + testLUVCoin.approve(address(testZapper), 100 * sfluvExp); + + uint256 originalBalance = testLUVCoin.balanceOf(address(defaultAdmin)); + + // Call unwrap/swap/bridge - honey is funded with enough USDC.e so the liquidity pool is not used + (MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt) = + testZapper.unwrapSwapAndBridgeUSDCe(50 * sfluvExp, vitEth); + + vm.stopPrank(); + + console.log("MessagingReceipt:"); + console.logBytes32(mReceipt.guid); + console.log(" nonce:", mReceipt.nonce); + console.log(" messaging fee - nativeFee:", mReceipt.fee.nativeFee); + console.log(" messaging fee - lzTokenFee:", mReceipt.fee.lzTokenFee); + + console.log("OFTReceipt:"); + console.log(" amountSent:", oReceipt.amountSentLD); + console.log(" amountRecieved:", oReceipt.amountReceivedLD); + assert(oReceipt.amountSentLD == 50 * usdcExp); + assert(oReceipt.amountReceivedLD >= (50 * usdcExp * 95 / 100)); + assert(oReceipt.amountReceivedLD >= (50 * usdcExp * 95 / 100)); + + assert(testLUVCoin.balanceOf(defaultAdmin) == originalBalance - (50 * sfluvExp)); + } + + function testUnwrapSplitRedeemBridgeUSDCe() public { + string memory forkURL = vm.envString("FORK_URL"); + uint256 currentBlockFork = 16654671; + vm.createSelectFork(forkURL, currentBlockFork); + + address sfluv = vm.envAddress("SFLUV_ADDRESS"); + address honey = vm.envAddress("HONEY_ADDRESS"); + address usdc = vm.envAddress("USDC_E_ADDRESS"); + address usdcAdapter = vm.envAddress("USDC_E_ADAPTER_ADDRESS"); + address usdcVaultAddress = vm.envAddress("USDC_E_VAULT_ADDRESS"); + address honeyToUSDCPool = vm.envAddress("HONEY_USDC_POOL_ADDRESS"); + address honeyFactoryAddress = vm.envAddress("HONEY_FACTORY_ADDRESS"); + uint32 usdcDstEid = uint32(vm.envUint("USDC_DST_EID")); + address sfluvWhale = address(0x234D25189D22947C3B0a39959eEEfAc36c022BE0); + + SFLUVUSDCeZapperStorageInit memory testStorage = SFLUVUSDCeZapperStorageInit( + honeyFactoryAddress, + sfluv, + honey, + usdc, + usdcAdapter, + usdcVaultAddress, + honeyToUSDCPool, + usdcDstEid + ); + + testZapper = new SFLUVUSDCeZapper(); + zapperproxy = new ERC1967Proxy(address(testZapper), abi.encodeCall(testZapper.initialize, (defaultAdmin, testStorage))); + testZapper = SFLUVUSDCeZapper(payable(zapperproxy)); + testLUVCoin = SFLUVv2(sfluv); + testUSDCe = IUSDCe(usdc); + testHoneyFactory = HoneyFactory(honeyFactoryAddress); + usdcVault = usdcVaultAddress; + + vm.startPrank(defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ADMIN_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), defaultAdmin); + testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), address(testZapper)); + vm.stopPrank(); + + uint256 usdcExp = 10**testUSDCe.decimals(); + uint256 sfluvExp = 10**testLUVCoin.decimals(); + + // Simulate zap-in via USDC.e so Honey vault has a bit over 50 USDC.e + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testUSDCe.transfer(defaultAdmin, 100 * usdcExp); + + vm.startPrank(defaultAdmin); + testUSDCe.approve(address(testHoneyFactory), 50 * usdcExp); + uint256 honeyAmount = testHoneyFactory.mint(address(testUSDCe), 50 * usdcExp, defaultAdmin, false); + honeyToken.approve(address(testLUVCoin), honeyAmount); + testLUVCoin.depositFor(defaultAdmin, honeyAmount); + vm.stopPrank(); + + // Send some USDC.e to the zapper from whale + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testUSDCe.transfer(address(testZapper), 100 * usdcExp); + + vm.deal(address(testZapper), 100 * sfluvExp); + + uint256 startingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + + vm.startPrank(sfluvWhale); + testLUVCoin.transfer(defaultAdmin, 50 * sfluvExp); + vm.stopPrank(); + + vm.startPrank(defaultAdmin); + testLUVCoin.approve(address(testZapper), 50 * sfluvExp); + + (MessagingReceipt memory mReceipt, OFTReceipt memory oReceipt) = + testZapper.unwrapSwapAndBridgeUSDCe(50 * sfluvExp, vitEth); + vm.stopPrank(); + + console.log("MessagingReceipt:"); + console.logBytes32(mReceipt.guid); + console.log(" nonce:", mReceipt.nonce); + console.log(" messaging fee - nativeFee:", mReceipt.fee.nativeFee); + console.log(" messaging fee - lzTokenFee:", mReceipt.fee.lzTokenFee); + + console.log("OFTReceipt:"); + console.log(" amountSent:", oReceipt.amountSentLD); + console.log(" amountRecieved:", oReceipt.amountReceivedLD); + assert(oReceipt.amountSentLD == 50 * usdcExp); + + uint256 endingLUVBalance = testLUVCoin.balanceOf(defaultAdmin); + assert(endingLUVBalance == startingLUVBalance); + } + + function testLiquidityPool() public { + vm.startPrank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + honeyToken.transfer(address(testZapper), 1000 * 1e18); + vm.stopPrank(); + + assert(honeyToken.balanceOf(address(testZapper)) == 1000 * 1e18); + + (uint160 sqrtPriceX96,,,,,,) = liquidityPool.slot0(); + + uint256 NUM = 1_025_978_352; + uint256 DEN = 1_000_000_000; + + uint256 sqrtPriceLimitX96 = uint256( + (uint256(sqrtPriceX96) * NUM) / DEN + ); + + vm.startPrank(address(testZapper)); + liquidityPool.swap( + address(testZapper), + false, + int256(500 * 1e18), + uint160(sqrtPriceLimitX96), + "" + ); + vm.stopPrank(); + + assert(testUSDCe.balanceOf(address(testZapper)) > 490000000); + assert(honeyToken.balanceOf(address(testZapper)) == 500000000000000000000); + } +} diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index b6c94a2..d622ca0 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -34,6 +34,7 @@ interface IWBERA { function deposit() external payable; } + contract SFLUVZapperTest is Test { SFLUVv2 public testLUVCoin; SFLUVZapperv1 public testSFLUVZapper; @@ -280,7 +281,7 @@ function testUnwrapSplitRedeemBridge() public { testSFLUVZapper = new SFLUVZapperv1(); zapperproxy = new ERC1967Proxy(address(testSFLUVZapper), abi.encodeCall(testSFLUVZapper.initialize, (defaultAdmin, testStorage))); testSFLUVZapper = SFLUVZapperv1(payable(zapperproxy)); - vm.startPrank(defaultAdmin); + vm.startPrank(defaultAdmin); testLUVCoin.grantRole(testLUVCoin.REDEEMER_ADMIN_ROLE(), defaultAdmin); testLUVCoin.grantRole(testLUVCoin.REDEEMER_ROLE(), defaultAdmin); testLUVCoin.grantRole(testLUVCoin.MINTER_ROLE(), address(testSFLUVZapper)); From 12e81f71b35735ce38e7e780481ee5ac2f1430cc Mon Sep 17 00:00:00 2001 From: Sanchez O'Leary Date: Wed, 11 Feb 2026 17:06:21 -0800 Subject: [PATCH 22/22] Handle Honey basket mode by skipping redeem in unwrap flow --- src/SFLUVZapperv1.sol | 27 +++++++++++++++++---------- test/SFLUVZapperv1.sol | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/SFLUVZapperv1.sol b/src/SFLUVZapperv1.sol index 5354491..f7dab4a 100644 --- a/src/SFLUVZapperv1.sol +++ b/src/SFLUVZapperv1.sol @@ -257,16 +257,23 @@ struct SFLUVZapperStorage { // 3. Check BYUSD inside Honey, and keep track of how much is redeemed HoneyFactory hf = HoneyFactory(address($.honeyFactory)); - uint256 byusdCurrentFees = hf.collectedAssetFees(address($.byusd)); - uint256 byusdAvailableInHoney = ($.byusd.balanceOf(address(hf.vaults(address($.byusd)))) * 1e12) - (2 * byusdCurrentFees); - - if (byusdAvailableInHoney > 0) { - uint256 redeemAmount = honeyBalance < byusdAvailableInHoney - ? honeyBalance - : byusdAvailableInHoney; - - $.honeyFactory.redeem(address($.byusd), redeemAmount, address(this), false); - honeyBalance -= redeemAmount; + // If Honey is in basket mode for redeems, skip redeem and route entirely through pool swap. + if (!hf.isBasketModeEnabled(false)) { + uint256 byusdCurrentFees = hf.collectedAssetFees(address($.byusd)); + uint256 byusdInVault = $.byusd.balanceOf(address(hf.vaults(address($.byusd)))) * 1e12; + uint256 redeemFeeBuffer = 2 * byusdCurrentFees; + uint256 byusdAvailableInHoney = byusdInVault > redeemFeeBuffer + ? byusdInVault - redeemFeeBuffer + : 0; + + if (byusdAvailableInHoney > 0) { + uint256 redeemAmount = honeyBalance < byusdAvailableInHoney + ? honeyBalance + : byusdAvailableInHoney; + + $.honeyFactory.redeem(address($.byusd), redeemAmount, address(this), false); + honeyBalance -= redeemAmount; + } } // 4. Swap remaining HONEY via pool if needed diff --git a/test/SFLUVZapperv1.sol b/test/SFLUVZapperv1.sol index d622ca0..f765d2a 100644 --- a/test/SFLUVZapperv1.sol +++ b/test/SFLUVZapperv1.sol @@ -253,6 +253,44 @@ function testUnwrapRedeemAndBridge() public { assert(testLUVCoin.balanceOf(defaultAdmin) == orininalBalance); } +function testUnwrapBasketModeUsesPoolSwapOnly() public { + uint256 byusdExp = 10**testBYUSD.decimals(); + uint256 sfluvExp = 10**testLUVCoin.decimals(); + + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testBYUSD.transfer(defaultAdmin, 1000 * byusdExp); + + vm.prank(0x4Be03f781C497A489E3cB0287833452cA9B9E80B); + testBYUSD.transfer(address(testSFLUVZapper), 100 * byusdExp); + + vm.deal(address(testSFLUVZapper), 100 * sfluvExp); + vm.deal(address(testBYUSD), 100 * sfluvExp); + + vm.startPrank(defaultAdmin); + testBYUSD.approve(address(testSFLUVZapper), 100 * byusdExp); + testLUVCoin.approve(address(testSFLUVZapper), 100 * sfluvExp); + + uint256 originalBalance = testLUVCoin.balanceOf(address(defaultAdmin)); + testSFLUVZapper.zapIn(50 * byusdExp); + + vm.mockCall( + address(testHoneyFactory), + abi.encodeWithSelector(HoneyFactory.isBasketModeEnabled.selector, false), + abi.encode(true) + ); + vm.mockCallRevert( + address(testHoneyFactory), + abi.encodeWithSelector(IHoneyFactory.redeem.selector), + "redeem path should be skipped in basket mode" + ); + + (, OFTReceipt memory oReceipt) = testSFLUVZapper.unwrapSwapAndBridge(50 * sfluvExp, vitEth); + vm.stopPrank(); + + assert(oReceipt.amountSentLD == 50 * byusdExp); + assert(testLUVCoin.balanceOf(defaultAdmin) == originalBalance); +} + function testUnwrapSplitRedeemBridge() public { // Switch to fork where HONEY is barely funded with BYUSD